blob: 3b458f369b0d446736da7762578c68df5313061b [file] [log] [blame]
# Copyright (c) 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
""" Utilities for generic build steps. """
import os
import shutil
import sys
from utils import file_utils
from py.utils import shell_utils
class DeviceDirs(object):
def __init__(self, perf_data_dir, gm_actual_dir, gm_expected_dir,
resource_dir, skimage_in_dir, skimage_expected_dir,
skimage_out_dir, skp_dir, skp_perf_dir,
playback_actual_images_dir, playback_actual_summaries_dir,
playback_expected_summaries_dir, tmp_dir):
self._perf_data_dir = perf_data_dir
self._gm_actual_dir = gm_actual_dir
self._gm_expected_dir = gm_expected_dir
self._resource_dir = resource_dir
self._skimage_in_dir = skimage_in_dir
self._skimage_expected_dir = skimage_expected_dir
self._skimage_out_dir = skimage_out_dir
self._skp_dir = skp_dir
self._skp_perf_dir = skp_perf_dir
self._playback_actual_images_dir = playback_actual_images_dir
self._playback_actual_summaries_dir = playback_actual_summaries_dir
self._playback_expected_summaries_dir = playback_expected_summaries_dir
self._tmp_dir = tmp_dir
def GMActualDir(self):
"""Holds images and JSON summary written out by the 'gm' tool."""
return self._gm_actual_dir
def GMExpectedDir(self):
"""Holds expectations JSON summary read by the 'gm' tool."""
return self._gm_expected_dir
def PerfDir(self):
return self._perf_data_dir
def PlaybackActualImagesDir(self):
"""Holds image files written out by the 'render_pictures' tool."""
return self._playback_actual_images_dir
def PlaybackActualSummariesDir(self):
"""Holds actual-result JSON summaries written by 'render_pictures' tool."""
return self._playback_actual_summaries_dir
def PlaybackExpectedSummariesDir(self):
"""Holds expected-result JSON summaries read by 'render_pictures' tool."""
return self._playback_expected_summaries_dir
def ResourceDir(self):
return self._resource_dir
def SKImageInDir(self):
return self._skimage_in_dir
def SKImageExpectedDir(self):
return self._skimage_expected_dir
def SKImageOutDir(self):
return self._skimage_out_dir
def SKPDir(self):
"""Holds SKP files that are consumed by RenderSKPs and BenchPictures."""
return self._skp_dir
def SKPPerfDir(self):
return self._skp_perf_dir
def TmpDir(self):
return self._tmp_dir
class DefaultBuildStepUtils:
""" Utilities to be used by subclasses of BuildStep.
The methods in this class define how certain high-level functions should work.
Each build step flavor should correspond to a subclass of BuildStepUtils which
may override any of these functions as appropriate for that flavor.
For example, the AndroidBuildStepUtils will override the functions for copying
files between the host and Android device, as well as the RunFlavoredCmd
function, so that commands may be run through ADB. """
def __init__(self, build_step_instance):
self._step = build_step_instance
def ListBuildStepExecutables(self):
""" Called by subclasses that may need to install the executables. """
return ['dm', 'tests', 'gm', 'render_pictures', 'render_pdfs',
'bench', 'bench_pictures', 'skimage', 'nanobench']
def _PathToBinary(self, binary):
""" Returns the path to the given built executable. """
return os.path.join('out', self._step.configuration, binary)
def RunFlavoredCmd(self, app, args):
""" Override this in new BuildStepUtils flavors. """
if (sys.platform == 'linux2' and 'x86_64' in self._step.builder_name
and not 'TSAN' in self._step.builder_name):
cmd = ['catchsegv', self._PathToBinary(app)]
else:
cmd = [self._PathToBinary(app)]
shell_utils.run(cmd + args)
def ReadFileOnDevice(self, filepath):
""" Read the contents of a file on the associated device. Subclasses should
override this method with one appropriate for reading the contents of a file
on the device side. """
with open(filepath) as f:
return f.read()
def CopyDirectoryContentsToDevice(self, host_dir, device_dir):
""" Copy the contents of a host-side directory to a clean directory on the
device side. Subclasses should override this method with one appropriate for
copying the contents of a host-side directory to a clean device-side
directory.
TODO(epoger): Clarify the description a bit: this method does not expect
device_dir to be an empty directory before it is called. Implementations
of this method for other device types create an empty directory at
device_dir as the first step.
"""
# For "normal" builders who don't have an attached device, we expect
# host_dir and device_dir to be the same.
if host_dir != device_dir:
raise ValueError('For builders who do not have attached devices, copying '
'from host to device is undefined and only allowed if '
'host_dir and device_dir are the same.')
def CopyDirectoryContentsToHost(self, device_dir, host_dir):
""" Copy the contents of a device-side directory to a clean directory on the
host side. Subclasses should override this method with one appropriate for
copying the contents of a device-side directory to a clean host-side
directory."""
# For "normal" builders who don't have an attached device, we expect
# host_dir and device_dir to be the same.
if host_dir != device_dir:
raise ValueError('For builders who do not have attached devices, copying '
'from host to device is undefined and only allowed if '
'host_dir and device_dir are the same.')
def PushFileToDevice(self, src, dst):
""" Copy the a single file from path "src" on the host to path "dst" on
the device. If the host IS the device we are testing, it's just a filecopy.
Subclasses should override this method with one appropriate for
pushing the file to the device. """
shutil.copy(src, dst)
def DeviceListDir(self, directory):
""" List the contents of a directory on the connected device. """
return os.listdir(directory)
def DevicePathExists(self, path):
""" Like os.path.exists(), but for a path on the connected device. """
return os.path.exists(path)
def DevicePathJoin(self, *args):
""" Like os.path.join(), but for paths that will target the connected
device. """
return os.sep.join(args)
def CreateCleanDeviceDirectory(self, directory):
""" Creates an empty directory on an attached device. Subclasses with
attached devices should override. """
file_utils.create_clean_local_dir(directory)
def CreateCleanHostDirectory(self, directory):
""" Creates an empty directory on the host. Can be overridden by subclasses,
but that should not be necessary. """
file_utils.create_clean_local_dir(directory)
def Install(self):
""" Install the Skia executables. """
pass
def RunGYP(self):
self.Compile('gyp')
def Compile(self, target):
""" Compile the Skia executables. """
# TODO(borenet): It would be nice to increase code sharing here.
if 'Win8' in self._step.builder_name:
os.environ['GYP_MSVS_VERSION'] = '2012'
print 'GYP_MSVS_VERSION="%s"' % os.environ['GYP_MSVS_VERSION']
os.environ['CHROME_PATH'] = os.path.join(os.path.expanduser('~'), 'src')
print 'CHROME_PATH="%s"' % os.environ['CHROME_PATH']
os.environ['GYP_DEFINES'] = self._step.args['gyp_defines']
print 'GYP_DEFINES="%s"' % os.environ['GYP_DEFINES']
make_cmd = 'make'
if os.name == 'nt':
make_cmd = 'make.bat'
cmd = [make_cmd,
target,
'BUILDTYPE=%s' % self._step.configuration,
]
cmd.extend(self._step.default_make_flags)
cmd.extend(self._step.make_flags)
# TODO(epoger): Maybe remove this once we fix the underlying problem in
# https://code.google.com/p/skia/issues/detail?id=2393 ('recurring RunGYP
# failures on multiple Test-Win7-ShuttleA-HD2000-* bots')
print 'about to run cmd %s' % cmd
cwd = os.getcwd()
print 'cwd is %s' % cwd
print 'contents of cwd are %s' % os.listdir(cwd)
shell_utils.run(cmd)
def MakeClean(self):
make_cmd = 'make'
if os.name == 'nt':
make_cmd = 'make.bat'
shell_utils.run([make_cmd, 'clean'])
def PreRun(self):
""" Preprocessing step to run before the BuildStep itself. """
pass
def GetDeviceDirs(self):
""" Set the directories which will be used by the BuildStep.
In the case of DefaultBuildStepUtils, host_dirs and device_dirs are the
same, which is why CopyDirectoryContentsToDevice() is a no-op.
"""
return DeviceDirs(
perf_data_dir=self._step.perf_data_dir,
# TODO(epoger): Instead, set gm_actual_dir to self._step._gm_actual_dir
# and remove os.path.join() with self._builder_name in postrender.py ?
# (CopyDirectoryContentsToHost fails if the paths are different when
# host==device, so why not just make them inherently equal?)
gm_actual_dir=os.path.join(os.pardir, os.pardir, 'gm', 'actual'),
gm_expected_dir=os.path.join(os.pardir, os.pardir, 'gm', 'expected'),
resource_dir=self._step.resource_dir,
skimage_in_dir=self._step.skimage_in_dir,
skimage_expected_dir=os.path.join(os.pardir, os.pardir, 'skimage',
'expected'),
skimage_out_dir=self._step.skimage_out_dir,
skp_dir=self._step.skp_dir,
skp_perf_dir=self._step.perf_data_dir,
playback_actual_images_dir=self._step.playback_actual_images_dir,
playback_actual_summaries_dir=self._step.playback_actual_summaries_dir,
playback_expected_summaries_dir=(
self._step.playback_expected_summaries_dir),
tmp_dir=os.path.join(os.pardir, 'tmp'))