Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(851)

Unified Diff: slave/skia_slave_scripts/build_step.py

Issue 648353002: Remove Skia's forked buildbot code (Closed) Base URL: https://skia.googlesource.com/buildbot.git@master
Patch Set: Address comment Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « slave/skia_slave_scripts/apply_patch.py ('k') | slave/skia_slave_scripts/cc_unittests.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: slave/skia_slave_scripts/build_step.py
diff --git a/slave/skia_slave_scripts/build_step.py b/slave/skia_slave_scripts/build_step.py
deleted file mode 100644
index 9cc611bbc1547b39ff71f07f0d11a8a4ebc22b37..0000000000000000000000000000000000000000
--- a/slave/skia_slave_scripts/build_step.py
+++ /dev/null
@@ -1,397 +0,0 @@
-# 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.
-
-"""Base class for all slave-side build steps. """
-
-import config
-# pylint: disable=W0611
-import flavor_utils
-import imp
-import multiprocessing
-import os
-import shlex
-import signal
-import subprocess
-import sys
-import time
-import traceback
-
-from playback_dirs import LocalSkpPlaybackDirs
-from playback_dirs import StorageSkpPlaybackDirs
-
-BUILDBOT_PATH = os.path.realpath(os.path.join(
- os.path.dirname(os.path.abspath(__file__)), os.pardir, os.pardir))
-
-# Add important directories to the PYTHONPATH
-sys.path.append(os.path.join(BUILDBOT_PATH))
-sys.path.append(os.path.join(BUILDBOT_PATH, 'site_config'))
-sys.path.append(os.path.join(BUILDBOT_PATH, 'master'))
-sys.path.insert(0, os.path.join(BUILDBOT_PATH, 'common'))
-
-import builder_name_schema
-import slave_hosts_cfg
-import slaves_cfg
-
-from py.utils import misc
-
-
-DEFAULT_TIMEOUT = 4800
-DEFAULT_NO_OUTPUT_TIMEOUT = 3600
-DEFAULT_NUM_CORES = 2
-
-
-GM_EXPECTATIONS_FILENAME = 'expected-results.json'
-GM_IGNORE_FAILURES_FILE = 'ignored-tests.txt'
-
-
-# multiprocessing.Value doesn't accept boolean types, so we have to use an int.
-INT_TRUE = 1
-INT_FALSE = 0
-build_step_stdout_has_written = multiprocessing.Value('i', INT_FALSE)
-
-
-class BuildStepWarning(Exception):
- pass
-
-
-class BuildStepFailure(Exception):
- pass
-
-
-class BuildStepTimeout(Exception):
- pass
-
-
-class BuildStepLogger(object):
- """ Override stdout so that we can keep track of when anything has been
- logged. This enables timeouts based on how long the process has gone without
- writing output.
- """
- def __init__(self):
- self.stdout = sys.stdout
- sys.stdout = self
- build_step_stdout_has_written.value = INT_FALSE
-
- def __del__(self):
- sys.stdout = self.stdout
-
- def fileno(self):
- return self.stdout.fileno()
-
- def write(self, data):
- build_step_stdout_has_written.value = INT_TRUE
- self.stdout.write(data)
-
- def flush(self):
- self.stdout.flush()
-
-
-def _GetBuildSlaveID(desired_slave_name):
- """ Returns the index of the build slave in the list of build slaves running
- on this machine, in the form of a string. """
- for host_dict in slave_hosts_cfg.SLAVE_HOSTS.itervalues():
- for slave_name, slave_id, _ in host_dict.slaves:
- if slave_name == desired_slave_name:
- return slave_id
- raise Exception('No build slave found with name %s' % desired_slave_name)
-
-
-class BuildStep(multiprocessing.Process):
-
- def __init__(self, args, attempts=1, timeout=DEFAULT_TIMEOUT,
- no_output_timeout=DEFAULT_NO_OUTPUT_TIMEOUT):
- """ Constructs a BuildStep instance.
-
- args: dictionary containing arguments to this BuildStep.
- attempts: how many times to try this BuildStep before giving up.
- timeout: maximum time allowed for this BuildStep.
- no_output_timeout: maximum time allowed for this BuildStep to run without
- any output.
- """
- multiprocessing.Process.__init__(self)
-
- self._args = dict(args)
-
- self.timeout = timeout
- self.no_output_timeout = no_output_timeout
- self.attempts = attempts
-
- self._builder_name = args['builder_name']
- self._build_number = args['build_number']
- self._slavename = os.environ['TESTING_SLAVENAME']
-
- # Change to the correct working directory. This is needed on Windows, where
- # our path lengths would otherwise be too long.
- if os.name == 'nt':
- curdir = os.getcwd()
- # The buildslave name and builder name combo is too long. Instead, obtain
- # a unique buildslave ID.
- buildslave_id = _GetBuildSlaveID(self._slavename)
- workdir = os.path.join('C:\\', buildslave_id, self._builder_name,
- curdir[curdir.rfind('build'):])
- print 'chdir to %s' % workdir
- if not os.path.isdir(workdir):
- os.makedirs(workdir)
- os.chdir(workdir)
-
- # Add CWD to the PYTHONPATH
- sys.path.append(os.getcwd())
-
- self._configuration = args['configuration']
- if os.name == 'nt' and 'x86_64' in self._builder_name:
- self._configuration += '_x64'
-
- self._target_platform = args['target_platform']
- self._deps_target_os = \
- None if args['deps_target_os'] == 'None' else args['deps_target_os']
- self._revision = \
- None if args['revision'] == 'None' or args['revision'] == 'HEAD' \
- else args['revision']
- self._got_revision = \
- None if args['got_revision'] == 'None' else args['got_revision']
-
- # Import the flavor-specific build step utils module.
- flavor = args.get('flavor', 'default')
- try:
- flavor_utils_module_name = '%s_build_step_utils' % flavor
- flavor_utils_path = os.path.join(os.path.dirname(__file__),
- 'flavor_utils',
- '%s.py' % flavor_utils_module_name)
- flavor_utils_module = imp.load_source(
- 'flavor_utils.%s' % flavor_utils_module_name, flavor_utils_path)
- flavor_utils_class_name = ''.join([part.title() for part in
- flavor.split('_')])
- flavor_utils_class = getattr(flavor_utils_module,
- '%sBuildStepUtils' % flavor_utils_class_name)
- self._flavor_utils = flavor_utils_class(self)
- except (ImportError, IOError) as e:
- raise Exception('Unrecognized build flavor: %s\n%s' % (flavor, e))
-
- # Trybots should use expectations from the corresponding waterfall bot.
- # This fixes https://code.google.com/p/skia/issues/detail?id=1552
- gm_expected_subdir = builder_name_schema.GetWaterfallBot(
- self._builder_name)
-
- # Figure out where we are going to store images generated by GM.
- self._gm_actual_basedir = os.path.join(os.pardir, os.pardir, 'gm', 'actual')
- self._gm_expected_dir = os.path.join('expectations', 'gm',
- gm_expected_subdir)
- self._gm_actual_dir = os.path.join(self._gm_actual_basedir,
- self._builder_name)
- self._dm_dir = os.path.join(os.pardir, os.pardir, 'dm')
-
- self._resource_dir = 'resources'
- self._make_flags = shlex.split(args['make_flags'].replace('"', ''))
- self._test_args = shlex.split(args['test_args'].replace('"', ''))
- self._gm_args = shlex.split(args['gm_args'].replace('"', ''))
- self._bench_args = shlex.split(args['bench_args'].replace('"', ''))
- self._is_try = args['is_try'] == 'True'
-
- self._default_make_flags = []
- self._default_ninja_flags = []
-
- # TODO(epoger): Throughout the buildbot code, we use various terms to refer
- # to the same thing: "skps", "pictures", "replay", "playback".
- # We should pick one of those terms, and rename things so that we are
- # consistent.
- # See https://codereview.chromium.org/295753002/ for additional discussion.
-
- # Adding the playback directory transfer objects.
- self._local_playback_dirs = LocalSkpPlaybackDirs(
- self._builder_name,
- None if args['perf_output_basedir'] == 'None'
- else args['perf_output_basedir'])
- self._storage_playback_dirs = StorageSkpPlaybackDirs(
- self._builder_name,
- None if args['perf_output_basedir'] == 'None'
- else args['perf_output_basedir'])
-
- self.skp_dir = self._local_playback_dirs.PlaybackSkpDir()
- self.playback_actual_images_dir = (
- self._local_playback_dirs.PlaybackActualImagesDir())
- self.playback_actual_summaries_dir = (
- self._local_playback_dirs.PlaybackActualSummariesDir())
- self.playback_expected_summaries_dir = (
- self._local_playback_dirs.PlaybackExpectedSummariesDir())
-
- # Figure out where we are going to store performance related data.
- if args['perf_output_basedir'] != 'None':
- self._perf_data_dir = os.path.join(args['perf_output_basedir'],
- self._builder_name, 'data')
- self._perf_graphs_dir = os.path.join(args['perf_output_basedir'],
- self._builder_name, 'graphs')
- self._perf_range_input_dir = os.path.join(
- args['perf_output_basedir'], self._builder_name, 'expectations')
- else:
- self._perf_data_dir = None
- self._perf_graphs_dir = None
- self._perf_range_input_dir = None
- self._skimage_in_dir = os.path.join(os.pardir, 'skimage_in')
-
- self._skimage_expected_dir = os.path.join('expectations', 'skimage')
-
- self._skimage_out_dir = os.path.join('out', self._configuration,
- 'skimage_out')
-
- self._device_dirs = self._flavor_utils.GetDeviceDirs()
-
- @property
- def configuration(self):
- return self._configuration
-
- @property
- def builder_name(self):
- return self._builder_name
-
- @property
- def args(self):
- return self._args
-
- # TODO(epoger): remove default_make_flags property once all builds use ninja
- @property
- def default_make_flags(self):
- return self._default_make_flags
-
- @property
- def default_ninja_flags(self):
- return self._default_ninja_flags
-
- @property
- def make_flags(self):
- return self._make_flags
-
- @property
- def perf_data_dir(self):
- return self._perf_data_dir
-
- @property
- def resource_dir(self):
- return self._resource_dir
-
- @property
- def skimage_in_dir(self):
- return self._skimage_in_dir
-
- @property
- def skimage_expected_dir(self):
- return self._skimage_expected_dir
-
- @property
- def skimage_out_dir(self):
- return self._skimage_out_dir
-
- @property
- def local_playback_dirs(self):
- return self._local_playback_dirs
-
- def _PreRun(self):
- """ Optional preprocessing step defined in the BuildStepUtils. """
- self._flavor_utils.PreRun()
-
- def _Run(self):
- """ Code to be run in a given BuildStep. No return value; throws exception
- on failure. Override this method in subclasses.
- """
- raise Exception('Cannot instantiate abstract BuildStep')
-
- def run(self):
- """ Internal method used by multiprocess.Process. _Run is provided to be
- overridden instead of this method to ensure that this implementation always
- runs.
- """
- # If a BuildStep has exceeded its allotted time, the parent process needs to
- # be able to kill the BuildStep process AND any which it has spawned,
- # without harming itself. On posix platforms, the terminate() method is
- # insufficient; it fails to kill the subprocesses launched by this process.
- # So, we use use the setpgrp() function to set a new process group for the
- # BuildStep process and its children and call os.killpg() to kill the group.
- if os.name == 'posix':
- os.setpgrp()
- try:
- self._Run()
- except BuildStepWarning as e:
- print e
- sys.exit(config.Master.retcode_warnings)
-
- def _WaitFunc(self, attempt):
- """ Waits a number of seconds depending upon the attempt number of a
- retry-able BuildStep before making the next attempt. This can be overridden
- by subclasses and should be defined for attempt in [0, self.attempts - 1]
-
- This default implementation is exponential; we double the wait time with
- each attempt, starting with a 15-second pause between the first and second
- attempts.
- """
- base_secs = 15
- wait = base_secs * (2 ** attempt)
- print 'Retrying in %d seconds...' % wait
- time.sleep(wait)
-
- @staticmethod
- def KillBuildStep(step):
- """ Kills a running BuildStep.
-
- step: the running BuildStep instance to kill.
- """
- # On posix platforms, the terminate() method is insufficient; it fails to
- # kill the subprocesses launched by this process. So, we use use the
- # setpgrp() function to set a new process group for the BuildStep process
- # and its children and call os.killpg() to kill the group.
- if os.name == 'posix':
- os.killpg(os.getpgid(step.pid), signal.SIGTERM)
- elif os.name == 'nt':
- subprocess.call(['taskkill', '/F', '/T', '/PID', str(step.pid)])
- else:
- step.terminate()
-
- @staticmethod
- def RunBuildStep(StepType):
- """ Run a BuildStep, possibly making multiple attempts and handling
- timeouts.
-
- StepType: class type which subclasses BuildStep, indicating what step should
- be run. StepType should override _Run().
- """
- # pylint: disable=W0612
- logger = BuildStepLogger()
- args = misc.ArgsToDict(sys.argv)
- attempt = 0
- while True:
- step = StepType(args=args)
- try:
- start_time = time.time()
- last_written_time = start_time
- # pylint: disable=W0212
- step._PreRun()
- step.start()
- while step.is_alive():
- current_time = time.time()
- if current_time - start_time > step.timeout:
- BuildStep.KillBuildStep(step)
- raise BuildStepTimeout('Build step exceeded timeout of %d seconds' %
- step.timeout)
- elif current_time - last_written_time > step.no_output_timeout:
- BuildStep.KillBuildStep(step)
- raise BuildStepTimeout(
- 'Build step exceeded %d seconds with no output' %
- step.no_output_timeout)
- time.sleep(1)
- if build_step_stdout_has_written.value == INT_TRUE:
- last_written_time = time.time()
- print 'Build Step Finished.'
- if step.exitcode == 0:
- return 0
- elif step.exitcode == config.Master.retcode_warnings:
- # A warning is considered to be an acceptable finishing state.
- return config.Master.retcode_warnings
- else:
- raise BuildStepFailure('Build step failed.')
- except Exception:
- print traceback.format_exc()
- if attempt + 1 >= step.attempts:
- raise
- # pylint: disable=W0212
- step._WaitFunc(attempt)
- attempt += 1
- print '**** %s, attempt %d ****' % (StepType.__name__, attempt + 1)
« no previous file with comments | « slave/skia_slave_scripts/apply_patch.py ('k') | slave/skia_slave_scripts/cc_unittests.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698