diff options
-rw-r--r-- | infra/scripts/legacy/scripts/common/chromium_utils.py | 1139 | ||||
-rw-r--r-- | infra/scripts/legacy/scripts/slave/build_directory.py | 51 | ||||
-rwxr-xr-x | infra/scripts/legacy/scripts/slave/gtest_slave_utils.py | 105 | ||||
-rwxr-xr-x | infra/scripts/legacy/scripts/slave/runtest.py | 8 | ||||
-rw-r--r-- | infra/scripts/legacy/scripts/slave/slave_utils.py | 498 |
5 files changed, 2 insertions, 1799 deletions
diff --git a/infra/scripts/legacy/scripts/common/chromium_utils.py b/infra/scripts/legacy/scripts/common/chromium_utils.py index b41cd44..89ebf8b 100644 --- a/infra/scripts/legacy/scripts/common/chromium_utils.py +++ b/infra/scripts/legacy/scripts/common/chromium_utils.py @@ -6,33 +6,22 @@ from contextlib import contextmanager import ast -import base64 import cStringIO import copy import errno import fnmatch import glob -import math -import multiprocessing +import json import os import re import shutil import socket import stat -import string # pylint: disable=W0402 import subprocess import sys import threading import time import traceback -import urllib -import zipfile -import zlib - -try: - import json # pylint: disable=F0401 -except ImportError: - import simplejson as json from common import env @@ -41,59 +30,6 @@ BUILD_DIR = os.path.realpath(os.path.join( os.path.dirname(__file__), os.pardir, os.pardir)) -WIN_LINK_FUNC = None -try: - if sys.platform.startswith('win'): - import ctypes - # There's 4 possibilities on Windows for links: - # 1. Symbolic file links; - # 2. Symbolic directory links; - # 3. Hardlinked files; - # 4. Junctioned directories. - # (Hardlinked directories don't really exist.) - # - # 7-Zip does not handle symbolic file links as we want (it puts the - # content of the link, not what it refers to, and reports "CRC Error" on - # extraction). It does work as expected for symbolic directory links. - # Because the majority of the large files are in the root of the staging - # directory, we do however need to handle file links, so we do this with - # hardlinking. Junctioning requires a huge whack of code, so we take the - # slightly odd tactic of using #2 and #3, but not #1 and #4. That is, - # hardlinks for files, but symbolic links for directories. - def _WIN_LINK_FUNC(src, dst): - print 'linking %s -> %s' % (src, dst) - if os.path.isdir(src): - if not ctypes.windll.kernel32.CreateSymbolicLinkA( - str(dst), str(os.path.abspath(src)), 1): - raise ctypes.WinError() - else: - if not ctypes.windll.kernel32.CreateHardLinkA(str(dst), str(src), 0): - raise ctypes.WinError() - WIN_LINK_FUNC = _WIN_LINK_FUNC -except ImportError: - # If we don't have ctypes or aren't on Windows, leave WIN_LINK_FUNC as None. - pass - - -# Wrapper around git that enforces a timeout. -GIT_BIN = os.path.join(BUILD_DIR, 'scripts', 'tools', 'git-with-timeout') - -# Wrapper around svn that enforces a timeout. -SVN_BIN = os.path.join(BUILD_DIR, 'scripts', 'tools', 'svn-with-timeout') - -# The Google Storage metadata key for the full commit position -GS_COMMIT_POSITION_KEY = 'Cr-Commit-Position' -# The Google Storage metadata key for the commit position number -GS_COMMIT_POSITION_NUMBER_KEY = 'Cr-Commit-Position-Number' -# The Google Storage metadata key for the Git commit hash -GS_GIT_COMMIT_KEY = 'Cr-Git-Commit' - -# Regular expression to identify a Git hash -GIT_COMMIT_HASH_RE = re.compile(r'[a-zA-Z0-9]{40}') -# -# Regular expression to parse a commit position -COMMIT_POSITION_RE = re.compile(r'([^@]+)@{#(\d+)}') - # Local errors. class MissingArgument(Exception): pass @@ -101,8 +37,6 @@ class PathNotFound(Exception): pass class ExternalError(Exception): pass -class NoIdentifiedRevision(Exception): - pass def IsWindows(): return sys.platform == 'cygwin' or sys.platform.startswith('win') @@ -113,319 +47,6 @@ def IsLinux(): def IsMac(): return sys.platform.startswith('darwin') -# For chromeos we need to end up with a different platform name, but the -# scripts use the values like sys.platform for both the build target and -# and the running OS, so this gives us a back door that can be hit to -# force different naming then the default for some of the chromeos build -# steps. -override_platform_name = None - - -def OverridePlatformName(name): - """Sets the override for PlatformName()""" - global override_platform_name - override_platform_name = name - - -def PlatformName(): - """Return a string to be used in paths for the platform.""" - if override_platform_name: - return override_platform_name - if IsWindows(): - return 'win32' - if IsLinux(): - return 'linux' - if IsMac(): - return 'mac' - raise NotImplementedError('Unknown platform "%s".' % sys.platform) - - -# Name of the file (inside the packaged build) containing revision number -# of that build. Also used for determining the latest packaged build. -FULL_BUILD_REVISION_FILENAME = 'FULL_BUILD_REVISION' - -def IsGitCommit(value): - """Returns: If a value is a Git commit hash. - - This only works on full Git commit hashes. A value qualifies as a Git commit - hash if it only contains hexadecimal numbers and is forty characters long. - """ - if value is None: - return False - return GIT_COMMIT_HASH_RE.match(str(value)) is not None - - -# GetParentClass allows a class instance to find its parent class using Python's -# inspect module. This allows a class instantiated from a module to access -# their parent class's methods even after the containing module has been -# re-imported and reloaded. -# -# Also see: -# http://code.google.com/p/chromium/issues/detail?id=34089 -# http://atlee.ca/blog/2008/11/21/python-reload-danger-here-be-dragons/ -# -def GetParentClass(obj, n=1): - import inspect - if inspect.isclass(obj): - return inspect.getmro(obj)[n] - else: - return inspect.getmro(obj.__class__)[n] - - -def MeanAndStandardDeviation(data): - """Calculates mean and standard deviation for the values in the list. - - Args: - data: list of numbers - - Returns: - Mean and standard deviation for the numbers in the list. - """ - n = len(data) - if n == 0: - return 0.0, 0.0 - mean = float(sum(data)) / n - variance = sum([(element - mean)**2 for element in data]) / n - return mean, math.sqrt(variance) - - -def FilteredMeanAndStandardDeviation(data): - """Calculates mean and standard deviation for the values in the list - ignoring first occurence of max value (unless there was only one sample). - - Args: - data: list of numbers - - Returns: - Mean and standard deviation for the numbers in the list ignoring - first occurence of max value. - """ - - def _FilterMax(array): - new_array = copy.copy(array) # making sure we are not creating side-effects - if len(new_array) != 1: - new_array.remove(max(new_array)) - return new_array - return MeanAndStandardDeviation(_FilterMax(data)) - -def HistogramPercentiles(histogram, percentiles): - if not 'buckets' in histogram or not 'count' in histogram: - return [] - computed_percentiles = _ComputePercentiles(histogram['buckets'], - histogram['count'], - percentiles) - output = [] - for p in computed_percentiles: - output.append({'percentile': p, 'value': computed_percentiles[p]}) - return output - -def GeomMeanAndStdDevFromHistogram(histogram): - if not 'buckets' in histogram: - return 0.0, 0.0 - count = 0 - sum_of_logs = 0 - for bucket in histogram['buckets']: - if 'high' in bucket: - bucket['mean'] = (bucket['low'] + bucket['high']) / 2.0 - else: - bucket['mean'] = bucket['low'] - if bucket['mean'] > 0: - sum_of_logs += math.log(bucket['mean']) * bucket['count'] - count += bucket['count'] - - if count == 0: - return 0.0, 0.0 - - sum_of_squares = 0 - geom_mean = math.exp(sum_of_logs / count) - for bucket in histogram['buckets']: - if bucket['mean'] > 0: - sum_of_squares += (bucket['mean'] - geom_mean) ** 2 * bucket['count'] - return geom_mean, math.sqrt(sum_of_squares / count) - -def _LinearInterpolate(x0, target, x1, y0, y1): - """Perform linear interpolation to estimate an intermediate value. - - We assume for some F, F(x0) == y0, and F(x1) == z1. - - We return an estimate for what F(target) should be, using linear - interpolation. - - Args: - x0: (Float) A location at which some function F() is known. - target: (Float) A location at which we need to estimate F(). - x1: (Float) A second location at which F() is known. - y0: (Float) The value of F(x0). - y1: (Float) The value of F(x1). - - Returns: - (Float) The estimated value of F(target). - """ - if x0 == x1: - return (y0 + y1) / 2 - return (y1 - y0) * (target - x0) / (x1 - x0) + y0 - -def _BucketInterpolate(last_percentage, target, next_percentage, bucket_min, - bucket_max): - """Estimate a minimum which should have the target % of samples below it. - - We do linear interpolation only if last_percentage and next_percentage are - adjacent, and hence we are in a linear section of a histogram. Once they - spread further apart we generally get exponentially broader buckets, and we - need to interpolate in the log domain (and exponentiate our result). - - Args: - last_percentage: (Float) This is the percentage of samples below bucket_min. - target: (Float) A percentage for which we need an estimated bucket. - next_percentage: (Float) This is the percentage of samples below bucket_max. - bucket_min: (Float) This is the lower value for samples in a bucket. - bucket_max: (Float) This exceeds the upper value for samples. - - Returns: - (Float) An estimate of what bucket cutoff would have probably had the target - percentage. - """ - log_domain = False - if bucket_min + 1.5 < bucket_max and bucket_min > 0: - log_domain = True - bucket_min = math.log(bucket_min) - bucket_max = math.log(bucket_max) - result = _LinearInterpolate( - last_percentage, target, next_percentage, bucket_min, bucket_max) - if log_domain: - result = math.exp(result) - return result - -def _ComputePercentiles(buckets, total, percentiles): - """Compute percentiles for the given histogram. - - Returns estimates for the bucket cutoffs that would probably have the taret - percentiles. - - Args: - buckets: (List) A list of buckets representing the histogram to analyze. - total: (Float) The total number of samples in the histogram. - percentiles: (Tuple) The percentiles we are interested in. - - Returns: - (Dictionary) Map from percentiles to bucket cutoffs. - """ - if not percentiles: - return {} - current_count = 0 - current_percentage = 0 - next_percentile_index = 0 - result = {} - for bucket in buckets: - if bucket['count'] > 0: - current_count += bucket['count'] - old_percentage = current_percentage - current_percentage = float(current_count) / total - - # Check whether we passed one of the percentiles we're interested in. - while (next_percentile_index < len(percentiles) and - current_percentage > percentiles[next_percentile_index]): - if not 'high' in bucket: - result[percentiles[next_percentile_index]] = bucket['low'] - else: - result[percentiles[next_percentile_index]] = float(_BucketInterpolate( - old_percentage, percentiles[next_percentile_index], - current_percentage, bucket['low'], bucket['high'])) - next_percentile_index += 1 - return result - -class InitializePartiallyWithArguments: - # pylint: disable=old-style-class - """Function currying implementation. - - Works for constructors too. Primary use is to be able to construct a class - with some constructor arguments beings set ahead of actual initialization. - Copy of an ASPN cookbook (#52549). - """ - - def __init__(self, clazz, *args, **kwargs): - self.clazz = clazz - self.pending = args[:] - self.kwargs = kwargs.copy() - - def __call__(self, *args, **kwargs): - if kwargs and self.kwargs: - kw = self.kwargs.copy() - kw.update(kwargs) - else: - kw = kwargs or self.kwargs - - return self.clazz(*(self.pending + args), **kw) - - -def Prepend(filepath, text): - """ Prepends text to the file. - - Creates the file if it does not exist. - """ - file_data = text - if os.path.exists(filepath): - file_data += open(filepath).read() - f = open(filepath, 'w') - f.write(file_data) - f.close() - - -def MakeWorldReadable(path): - """Change the permissions of the given path to make it world-readable. - This is often needed for archived files, so they can be served by web servers - or accessed by unprivileged network users.""" - - # No need to do anything special on Windows. - if IsWindows(): - return - - perms = stat.S_IMODE(os.stat(path)[stat.ST_MODE]) - if os.path.isdir(path): - # Directories need read and exec. - os.chmod(path, perms | 0555) - else: - os.chmod(path, perms | 0444) - - -def MakeParentDirectoriesWorldReadable(path): - """Changes the permissions of the given path and its parent directories - to make them world-readable. Stops on first directory which is - world-readable. This is often needed for archive staging directories, - so that they can be served by web servers or accessed by unprivileged - network users.""" - - # No need to do anything special on Windows. - if IsWindows(): - return - - while path != os.path.dirname(path): - current_permissions = stat.S_IMODE(os.stat(path)[stat.ST_MODE]) - if current_permissions & 0555 == 0555: - break - os.chmod(path, current_permissions | 0555) - path = os.path.dirname(path) - - -def MaybeMakeDirectory(*path): - """Creates an entire path, if it doesn't already exist.""" - file_path = os.path.join(*path) - try: - os.makedirs(file_path) - except OSError, e: - if e.errno != errno.EEXIST: - raise - - -def RemovePath(*path): - """Removes the file or directory at 'path', if it exists.""" - file_path = os.path.join(*path) - if os.path.exists(file_path): - if os.path.isdir(file_path): - RemoveDirectory(file_path) - else: - RemoveFile(file_path) - def RemoveFile(*path): """Removes the file located at 'path', if it exists.""" @@ -437,57 +58,6 @@ def RemoveFile(*path): raise -def MoveFile(path, new_path): - """Moves the file located at 'path' to 'new_path', if it exists.""" - try: - RemoveFile(new_path) - os.rename(path, new_path) - except OSError, e: - if e.errno != errno.ENOENT: - raise - - -def LocateFiles(pattern, root=os.curdir): - """Yeilds files matching pattern found in root and its subdirectories. - - An exception is thrown if root doesn't exist.""" - for path, _, files in os.walk(os.path.abspath(root)): - for filename in fnmatch.filter(files, pattern): - yield os.path.join(path, filename) - - -def RemoveFilesWildcards(file_wildcard, root=os.curdir): - """Removes files matching 'file_wildcard' in root and its subdirectories, if - any exists. - - An exception is thrown if root doesn't exist.""" - for item in LocateFiles(file_wildcard, root): - try: - os.remove(item) - except OSError, e: - if e.errno != errno.ENOENT: - raise - - -def RemoveGlobbedPaths(path_wildcard, root=os.curdir): - """Removes all paths matching 'path_wildcard' beneath root. - - Returns the list of paths removed. - - An exception is thrown if root doesn't exist.""" - if not os.path.exists(root): - raise OSError(2, 'No such file or directory', root) - - full_path_wildcard = os.path.join(path_wildcard, root) - paths = glob.glob(full_path_wildcard) - for path in paths: - # When glob returns directories they end in "/." - if path.endswith(os.sep + '.'): - path = path[:-2] - RemovePath(path) - return paths - - def RemoveDirectory(*path): """Recursively removes a directory, even if it's marked read-only. @@ -573,225 +143,6 @@ def RemoveDirectory(*path): remove_with_retry(os.rmdir, file_path) -def CopyFileToDir(src_path, dest_dir, dest_fn=None, link_ok=False): - """Copies the file found at src_path to the dest_dir directory, with metadata. - - If dest_fn is specified, the src_path is copied to that name in dest_dir, - otherwise it is copied to a file of the same name. - - Raises PathNotFound if either the file or the directory is not found. - """ - # Verify the file and directory separately so we can tell them apart and - # raise PathNotFound rather than shutil.copyfile's IOError. - if not os.path.isfile(src_path): - raise PathNotFound('Unable to find file %s' % src_path) - if not os.path.isdir(dest_dir): - raise PathNotFound('Unable to find dir %s' % dest_dir) - src_file = os.path.basename(src_path) - if dest_fn: - # If we have ctypes and the caller doesn't mind links, use that to - # try to make the copy faster on Windows. http://crbug.com/418702. - if link_ok and WIN_LINK_FUNC: - WIN_LINK_FUNC(src_path, os.path.join(dest_dir, dest_fn)) - else: - shutil.copy2(src_path, os.path.join(dest_dir, dest_fn)) - else: - shutil.copy2(src_path, os.path.join(dest_dir, src_file)) - - -def MakeZip(output_dir, archive_name, file_list, file_relative_dir, - raise_error=True, remove_archive_directory=True): - """Packs files into a new zip archive. - - Files are first copied into a directory within the output_dir named for - the archive_name, which will be created if necessary and emptied if it - already exists. The files are then then packed using archive names - relative to the output_dir. That is, if the zipfile is unpacked in place, - it will create a directory identical to the new archive_name directory, in - the output_dir. The zip file will be named as the archive_name, plus - '.zip'. - - Args: - output_dir: Absolute path to the directory in which the archive is to - be created. - archive_dir: Subdirectory of output_dir holding files to be added to - the new zipfile. - file_list: List of paths to files or subdirectories, relative to the - file_relative_dir. - file_relative_dir: Absolute path to the directory containing the files - and subdirectories in the file_list. - raise_error: Whether to raise a PathNotFound error if one of the files in - the list is not found. - remove_archive_directory: Whether to remove the archive staging directory - before copying files over to it. - - Returns: - A tuple consisting of (archive_dir, zip_file_path), where archive_dir - is the full path to the newly created archive_name subdirectory. - - Raises: - PathNotFound if any of the files in the list is not found, unless - raise_error is False, in which case the error will be ignored. - """ - - start_time = time.clock() - # Collect files into the archive directory. - archive_dir = os.path.join(output_dir, archive_name) - print 'output_dir: %s, archive_name: %s' % (output_dir, archive_name) - print 'archive_dir: %s, remove_archive_directory: %s, exists: %s' % ( - archive_dir, remove_archive_directory, os.path.exists(archive_dir)) - if remove_archive_directory and os.path.exists(archive_dir): - # Move it even if it's not a directory as expected. This can happen with - # FILES.cfg archive creation where we create an archive staging directory - # that is the same name as the ultimate archive name. - if not os.path.isdir(archive_dir): - print 'Moving old "%s" file to create same name directory.' % archive_dir - previous_archive_file = '%s.old' % archive_dir - MoveFile(archive_dir, previous_archive_file) - else: - print 'Removing %s' % archive_dir - RemoveDirectory(archive_dir) - print 'Now, os.path.exists(%s): %s' % ( - archive_dir, os.path.exists(archive_dir)) - MaybeMakeDirectory(archive_dir) - for needed_file in file_list: - needed_file = needed_file.rstrip() - # These paths are relative to the file_relative_dir. We need to copy - # them over maintaining the relative directories, where applicable. - src_path = os.path.join(file_relative_dir, needed_file) - dirname, basename = os.path.split(needed_file) - try: - if os.path.isdir(src_path): - if WIN_LINK_FUNC: - WIN_LINK_FUNC(src_path, os.path.join(archive_dir, needed_file)) - else: - shutil.copytree(src_path, os.path.join(archive_dir, needed_file), - symlinks=True) - elif dirname != '' and basename != '': - dest_dir = os.path.join(archive_dir, dirname) - MaybeMakeDirectory(dest_dir) - CopyFileToDir(src_path, dest_dir, basename, link_ok=True) - else: - CopyFileToDir(src_path, archive_dir, basename, link_ok=True) - except PathNotFound: - if raise_error: - raise - end_time = time.clock() - print 'Took %f seconds to create archive directory.' % (end_time - start_time) - - # Pack the zip file. - output_file = '%s.zip' % archive_dir - previous_file = '%s_old.zip' % archive_dir - MoveFile(output_file, previous_file) - - # If we have 7z, use that as it's much faster. See http://crbug.com/418702. - windows_zip_cmd = None - if os.path.exists('C:\\Program Files\\7-Zip\\7z.exe'): - windows_zip_cmd = ['C:\\Program Files\\7-Zip\\7z.exe', 'a', '-y', '-mx1'] - - # On Windows we use the python zip module; on Linux and Mac, we use the zip - # command as it will handle links and file bits (executable). Which is much - # easier then trying to do that with ZipInfo options. - start_time = time.clock() - if IsWindows() and not windows_zip_cmd: - print 'Creating %s' % output_file - - def _Addfiles(to_zip_file, dirname, files_to_add): - for this_file in files_to_add: - archive_name = this_file - this_path = os.path.join(dirname, this_file) - if os.path.isfile(this_path): - # Store files named relative to the outer output_dir. - archive_name = this_path.replace(output_dir + os.sep, '') - if os.path.getsize(this_path) == 0: - compress_method = zipfile.ZIP_STORED - else: - compress_method = zipfile.ZIP_DEFLATED - to_zip_file.write(this_path, archive_name, compress_method) - print 'Adding %s' % archive_name - zip_file = zipfile.ZipFile(output_file, 'w', zipfile.ZIP_DEFLATED, - allowZip64=True) - try: - os.path.walk(archive_dir, _Addfiles, zip_file) - finally: - zip_file.close() - else: - if IsMac() or IsLinux(): - zip_cmd = ['zip', '-yr1'] - else: - zip_cmd = windows_zip_cmd - saved_dir = os.getcwd() - os.chdir(os.path.dirname(archive_dir)) - command = zip_cmd + [output_file, os.path.basename(archive_dir)] - result = RunCommand(command) - os.chdir(saved_dir) - if result and raise_error: - raise ExternalError('zip failed: %s => %s' % - (str(command), result)) - end_time = time.clock() - print 'Took %f seconds to create zip.' % (end_time - start_time) - return (archive_dir, output_file) - - -def ExtractZip(filename, output_dir, verbose=True): - """ Extract the zip archive in the output directory. - """ - MaybeMakeDirectory(output_dir) - - # On Linux and Mac, we use the unzip command as it will - # handle links and file bits (executable), which is much - # easier then trying to do that with ZipInfo options. - # - # The Mac Version of unzip unfortunately does not support Zip64, whereas - # the python module does, so we have to fallback to the python zip module - # on Mac if the filesize is greater than 4GB. - # - # On Windows, try to use 7z if it is installed, otherwise fall back to python - # zip module and pray we don't have files larger than 512MB to unzip. - unzip_cmd = None - if ((IsMac() and os.path.getsize(filename) < 4 * 1024 * 1024 * 1024) - or IsLinux()): - unzip_cmd = ['unzip', '-o'] - elif IsWindows() and os.path.exists('C:\\Program Files\\7-Zip\\7z.exe'): - unzip_cmd = ['C:\\Program Files\\7-Zip\\7z.exe', 'x', '-y'] - - if unzip_cmd: - # Make sure path is absolute before changing directories. - filepath = os.path.abspath(filename) - saved_dir = os.getcwd() - os.chdir(output_dir) - command = unzip_cmd + [filepath] - result = RunCommand(command) - os.chdir(saved_dir) - if result: - raise ExternalError('unzip failed: %s => %s' % (str(command), result)) - else: - assert IsWindows() or IsMac() - zf = zipfile.ZipFile(filename) - # TODO(hinoka): This can be multiprocessed. - for name in zf.namelist(): - if verbose: - print 'Extracting %s' % name - zf.extract(name, output_dir) - if IsMac(): - # Restore permission bits. - os.chmod(os.path.join(output_dir, name), - zf.getinfo(name).external_attr >> 16L) - - -def WindowsPath(path): - """Returns a Windows mixed-style absolute path, given a Cygwin absolute path. - - The version of Python in the Chromium tree uses posixpath for os.path even - on Windows, so we convert to a mixed Windows path (that is, a Windows path - that uses forward slashes instead of backslashes) manually. - """ - # TODO(pamg): make this work for other drives too. - if path.startswith('/cygdrive/c/'): - return path.replace('/cygdrive/c/', 'C:/') - return path - - def FindUpwardParent(start_dir, *desired_list): """Finds the desired object's parent, searching upward from the start_dir. @@ -831,31 +182,6 @@ def FindUpward(start_dir, *desired_list): return os.path.join(parent, *desired_list) -def RunAndPrintDots(function): - """Starts a background thread that prints dots while the function runs.""" - - def Hook(*args, **kwargs): - event = threading.Event() - - def PrintDots(): - counter = 0 - while not event.isSet(): - event.wait(5) - sys.stdout.write('.') - counter = (counter + 1) % 80 - if not counter: - sys.stdout.write('\n') - sys.stdout.flush() - t = threading.Thread(target=PrintDots) - t.start() - try: - return function(*args, **kwargs) - finally: - event.set() - t.join() - return Hook - - class RunCommandFilter(object): """Class that should be subclassed to provide a filter for RunCommand.""" # Method could be a function @@ -873,19 +199,6 @@ class RunCommandFilter(object): return last_bits -class FilterCapture(RunCommandFilter): - """Captures the text and places it into an array.""" - def __init__(self): - RunCommandFilter.__init__(self) - self.text = [] - - def FilterLine(self, line): - self.text.append(line.rstrip()) - - def FilterDone(self, text): - self.text.append(text) - - def RunCommand(command, parser_func=None, filter_obj=None, pipes=None, print_cmd=True, timeout=None, max_time=None, **kwargs): """Runs the command list, printing its output and returning its exit status. @@ -1159,92 +472,6 @@ def GetCommandOutput(command): return output -def GetGClientCommand(platform=None): - """Returns the executable command name, depending on the platform. - """ - if not platform: - platform = sys.platform - if platform.startswith('win'): - # Windows doesn't want to depend on bash. - return 'gclient.bat' - else: - return 'gclient' - - -# Linux scripts use ssh to to move files to the archive host. -def SshMakeDirectory(host, dest_path): - """Creates the entire dest_path on the remote ssh host. - """ - command = ['ssh', host, 'mkdir', '-p', dest_path] - result = RunCommand(command) - if result: - raise ExternalError('Failed to ssh mkdir "%s" on "%s" (%s)' % - (dest_path, host, result)) - - -def SshMoveFile(host, src_path, dest_path): - """Moves src_path (if it exists) to dest_path on the remote host. - """ - command = ['ssh', host, 'test', '-e', src_path] - result = RunCommand(command) - if result: - # Nothing to do if src_path doesn't exist. - return result - - command = ['ssh', host, 'mv', src_path, dest_path] - result = RunCommand(command) - if result: - raise ExternalError('Failed to ssh mv "%s" -> "%s" on "%s" (%s)' % - (src_path, dest_path, host, result)) - - -def SshCopyFiles(srcs, host, dst): - """Copies the srcs file(s) to dst on the remote ssh host. - dst is expected to exist. - """ - command = ['scp', srcs, host + ':' + dst] - result = RunCommand(command) - if result: - raise ExternalError('Failed to scp "%s" to "%s" (%s)' % - (srcs, host + ':' + dst, result)) - - -def SshExtractZip(host, zipname, dst): - """extract the remote zip file to dst on the remote ssh host. - """ - command = ['ssh', host, 'unzip', '-o', '-d', dst, zipname] - result = RunCommand(command) - if result: - raise ExternalError('Failed to ssh unzip -o -d "%s" "%s" on "%s" (%s)' % - (dst, zipname, host, result)) - - # unzip will create directories with access 700, which is not often what we - # need. Fix the permissions for the whole archive. - command = ['ssh', host, 'chmod', '-R', '755', dst] - result = RunCommand(command) - if result: - raise ExternalError('Failed to ssh chmod -R 755 "%s" on "%s" (%s)' % - (dst, host, result)) - - -def SshCopyTree(srctree, host, dst): - """Recursively copies the srctree to dst on the remote ssh host. - For consistency with shutil, dst is expected to not exist. - """ - command = ['ssh', host, '[ -d "%s" ]' % dst] - result = RunCommand(command) - if result: - raise ExternalError('SshCopyTree destination directory "%s" already exists.' - % host + ':' + dst) - - SshMakeDirectory(host, os.path.dirname(dst)) - command = ['scp', '-r', '-p', srctree, host + ':' + dst] - result = RunCommand(command) - if result: - raise ExternalError('Failed to scp "%s" to "%s" (%s)' % - (srctree, host + ':' + dst, result)) - - def ListMasters(cue='master.cfg', include_public=True, include_internal=True): """Returns all the masters found.""" # Look for "internal" masters first. @@ -1296,14 +523,6 @@ def GetAllSlaves(fail_hard=False, include_public=True, include_internal=True): return slaves -def GetSlavesForHost(): - """Get slaves for a host, defaulting to current host.""" - hostname = os.getenv('TESTING_SLAVENAME') - if not hostname: - hostname = socket.getfqdn().split('.', 1)[0].lower() - return [s for s in GetAllSlaves() if s.get('hostname') == hostname] - - def GetActiveSubdir(): """Get current checkout's subdir, if checkout uses subdir layout.""" rootdir, subdir = os.path.split(os.path.dirname(BUILD_DIR)) @@ -1401,217 +620,6 @@ def convert_json(option, _, value, parser): setattr(parser.values, option.dest, json.loads(value)) -def b64_gz_json_encode(obj): - """Serialize a python object into base64.""" - # The |separators| argument is to densify the command line. - return base64.b64encode(zlib.compress( - json.dumps(obj or {}, sort_keys=True, separators=(',', ':')), 9)) - - -def convert_gz_json(option, _, value, parser): - """Provide an OptionParser callback to unmarshal a b64 gz JSON string.""" - setattr( - parser.values, option.dest, - json.loads(zlib.decompress(base64.b64decode(value)))) - - -def SafeTranslate(inputstr): - """Convert a free form string to one that can be used in a path. - - This is similar to the safeTranslate function in buildbot. - """ - - badchars_map = string.maketrans('\t !#$%&\'()*+,./:;<=>?@[\\]^{|}~', - '______________________________') - if isinstance(inputstr, unicode): - inputstr = inputstr.encode('utf8') - return inputstr.translate(badchars_map) - - -def GetPrimaryProject(options): - """Returns: (str) the key of the primary project, or 'None' if none exists. - """ - # The preferred way is to reference the 'primary_project' parameter. - result = options.build_properties.get('primary_project') - if result: - return result - - # TODO(dnj): The 'primary_repo' parameter is used by some scripts to indictate - # the primary project name. This is not consistently used and will be - # deprecated in favor of 'primary_project' once that is rolled out. - result = options.build_properties.get('primary_repo') - if not result: - # The 'primary_repo' property currently contains a trailing underscore. - # However, this isn't an obvious thing given its name, so we'll strip it - # here and remove that expectation. - return result.strip('_') - return None - - -def GetBuildSortKey(options, project=None): - """Reads a variety of sources to determine the current build revision. - - NOTE: Currently, the return value does not qualify branch name. This can - present a problem with git numbering scheme, where numbers are only unique - in the context of their respective branches. When this happens, this - function will return a branch name as part of the sort key and its callers - will need to adapt their naming/querying schemes to accommodate this. Until - then, we will return 'None' as the branch name. - (e.g., refs/foo/bar@{#12345} => ("refs/foo/bar", 12345) - - Args: - options: Command-line options structure - project: (str/None) If not None, the project to get the build sort key - for. Otherwise, the build-wide sort key will be used. - Returns: (branch, value) The qualified sortkey value - branch: (str/None) The name of the branch, or 'None' if there is no branch - context. Currently this always returns 'None'. - value: (int) The iteration value within the specified branch - Raises: (NoIdentifiedRevision) if no revision could be identified from the - supplied options. - """ - # Is there a commit position for this build key? - try: - return GetCommitPosition(options, project=project) - except NoIdentifiedRevision: - pass - - # Nope; derive the sort key from the 'got_[*_]revision' build properties. Note - # that this could be a Git commit (post flag day). - if project: - revision_key = 'got_%s_revision' % (project,) - else: - revision_key = 'got_revision' - revision = options.build_properties.get(revision_key) - if revision and not IsGitCommit(revision): - return None, int(revision) - raise NoIdentifiedRevision("Unable to identify revision for revision key " - "[%s]" % (revision_key,)) - - -def GetGitCommit(options, project=None): - """Returns the 'git' commit hash for the specified repository - - This function uses environmental options to identify the 'git' commit hash - for the specified repository. - - Args: - options: Command-line options structure - project: (str/None) The project key to use. If None, use the topmost - repository identification properties. - Raises: (NoIdentifiedRevision) if no git commit could be identified from the - supplied options. - """ - if project: - git_commit_key = 'got_%s_revision_git' % (project,) - else: - git_commit_key = 'got_revision_git' - commit = options.build_properties.get(git_commit_key) - if commit: - return commit - - # Is 'got_[_*]revision' itself is the Git commit? - if project: - commit_key = 'got_%s_revision' % (project,) - else: - commit_key = 'got_revision' - commit = options.build_properties.get(commit_key) - if commit and IsGitCommit(commit): - return commit - raise NoIdentifiedRevision("Unable to identify commit for commit key: %s" % ( - (git_commit_key, commit_key),)) - - -def GetSortableUploadPathForSortKey(branch, value, delimiter=None): - """Returns: (str) the canonical sort key path constructed from a sort key. - - Returns a canonical sort key path for a sort key. The result will be one of - the following forms: - - (Without Branch or With Branch=='refs/heads/master'): <value> (e.g., 12345) - - (With non-Master Branch): <branch-path>-<value> (e.g., - "refs_my-branch-12345") - - When a 'branch' is supplied, it is converted to a path-suitable form. This - conversion replaces undesirable characters ('/') with underscores. - - Note that when parsing the upload path, 'rsplit' should be used to isolate the - commit position value, as the branch path may have instances of the delimiter - in it. - - See 'GetBuildSortKey' for more information about sort keys. - - Args: - branch: (str/None) The sort key branch, or 'None' if there is no associated - branch. - value: (int) The sort key value. - delimiter: (str) The delimiter to insert in between <branch-path> and - <value> when constructing the branch-inclusive form. If omitted - (default), a hyphen ('-') will be used. - """ - if branch and branch != 'refs/heads/master': - delimiter = delimiter or '-' - branch = branch.replace('/', '_') - return '%s%s%s' % (branch, delimiter, value) - return str(value) - - -def ParseCommitPosition(value): - """Returns: The (branch, value) parsed from a commit position string. - - Args: - value: (str) The value to parse. - Raises: - ValueError: If a commit position could not be parsed from 'value'. - """ - match = COMMIT_POSITION_RE.match(value) - if not match: - raise ValueError("Failed to parse commit position from '%s'" % (value,)) - return match.group(1), int(match.group(2)) - - -def BuildCommitPosition(branch, value): - """Returns: A constructed commit position. - - An example commit position for branch 'refs/heads/master' value '12345' is: - refs/heads/master@{#12345} - - This value can be parsed via 'ParseCommitPosition'. - - Args: - branch: (str) The name of the commit position branch - value: (int): The commit position number. - """ - return '%s@{#%s}' % (branch, value) - - -def GetCommitPosition(options, project=None): - """Returns: (branch, value) The parsed commit position from build options. - - Returns the parsed commit position from the build options. This is identified - by examining the 'got_revision_cp' (or 'got_REPO_revision_cp', if 'project' is - specified) keys. - - Args: - options: Command-line options structure - project: (str/None) If not None, the project to get the build sort key - for. Otherwise, the build-wide sort key will be used. - Returns: (branch, value) The qualified commit position value - Raises: - NoIdentifiedRevision: if no revision could be identified from the - supplied options. - ValueError: If the supplied commit position failed to parse successfully. - """ - if project: - key = 'got_%s_revision_cp' % (project,) - else: - key = 'got_revision_cp' - cp = options.build_properties.get(key) - if not cp: - raise NoIdentifiedRevision("Unable to identify the commit position; the " - "build property is missing: %s" % (key,)) - return ParseCommitPosition(cp) - - def AddPropertiesOptions(option_parser): """Registers command line options for parsing build and factory properties. @@ -1634,89 +642,6 @@ def AddPropertiesOptions(option_parser): help='factory properties in JSON format') -def AddThirdPartyLibToPath(lib, override=False): - """Adds the specified dir in build/third_party to sys.path. - - Setting 'override' to true will place the directory in the beginning of - sys.path, useful for overriding previously set packages. - - NOTE: We would like to deprecate this method, as it allows (encourages?) - scripts to define their own one-off Python path sequences, creating a - difficult-to-manage state where different scripts and libraries have - different path expectations. Please don't use this method if possible; - it preferred to augment 'common.env' instead. - """ - libpath = os.path.abspath(os.path.join(BUILD_DIR, 'third_party', lib)) - if override: - sys.path.insert(0, libpath) - else: - sys.path.append(libpath) - - -def GetLKGR(): - """Connect to chromium LKGR server and get LKGR revision. - - On success, returns the LKGR and 'ok'. On error, returns None and the text of - the error message. - """ - - try: - conn = urllib.urlopen('https://chromium-status.appspot.com/lkgr') - except IOError: - return (None, 'Error connecting to LKGR server! Is your internet ' - 'connection working properly?') - try: - rev = int('\n'.join(conn.readlines())) - except IOError: - return (None, 'Error connecting to LKGR server! Is your internet ' - 'connection working properly?') - except ValueError: - return None, 'LKGR server returned malformed data! Aborting...' - finally: - conn.close() - - return rev, 'ok' - - -def AbsoluteCanonicalPath(*path): - """Return the most canonical path Python can provide.""" - - file_path = os.path.join(*path) - return os.path.realpath(os.path.abspath(os.path.expanduser(file_path))) - - -def IsolatedImportFromPath(path, extra_paths=None): - dir_path, module_file = os.path.split(path) - module_file = os.path.splitext(module_file)[0] - - saved = sys.path - sys.path = [dir_path] + (extra_paths or []) - try: - return __import__(module_file) - except ImportError: - pass - finally: - sys.path = saved - - -@contextmanager -def MultiPool(processes): - """Manages a multiprocessing.Pool making sure to close the pool when done. - - This will also call pool.terminate() when an exception is raised (and - re-raised the exception to the calling procedure can handle it). - """ - try: - pool = multiprocessing.Pool(processes=processes) - yield pool - pool.close() - except: - pool.terminate() - raise - finally: - pool.join() - - def ReadJsonAsUtf8(filename=None, text=None): """Read a json file or string and output a dict. @@ -1762,60 +687,6 @@ def ReadJsonAsUtf8(filename=None, text=None): return json.loads(text, object_hook=_decode_dict) -def GetMasterDevParameters(filename='master_cfg_params.json'): - """Look for master development parameter files in the master directory. - - Return the parsed content if the file exists, as a dictionary. - Every string value in the dictionary is utf8-encoded str. - - If the file is not found, returns an empty dict. This is on purpose, to - make the file optional. - """ - if os.path.isfile(filename): - return ReadJsonAsUtf8(filename=filename) - return {} - - -def FileExclusions(): - all_platforms = ['.landmines', 'obj', 'gen', '.ninja_deps', '.ninja_log'] - # Skip files that the testers don't care about. Mostly directories. - if IsWindows(): - # Remove obj or lib dir entries - return all_platforms + ['cfinstaller_archive', 'lib', 'installer_archive'] - if IsMac(): - return all_platforms + [ - # We don't need the arm bits v8 builds. - 'd8_arm', 'v8_shell_arm', - # pdfsqueeze is a build helper, no need to copy it to testers. - 'pdfsqueeze', - # We copy the framework into the app bundle, we don't need the second - # copy outside the app. - # TODO(mark): Since r28431, the copy in the build directory is actually - # used by tests. Putting two copies in the .zip isn't great, so maybe - # we can find another workaround. - # 'Chromium Framework.framework', - # 'Google Chrome Framework.framework', - # We copy the Helper into the app bundle, we don't need the second - # copy outside the app. - 'Chromium Helper.app', - 'Google Chrome Helper.app', - 'App Shim Socket', - '.deps', 'obj.host', 'obj.target', 'lib' - ] - if IsLinux(): - return all_platforms + [ - # intermediate build directories (full of .o, .d, etc.). - 'appcache', 'glue', 'lib.host', 'obj.host', - 'obj.target', 'src', '.deps', - # scons build cruft - '.sconsign.dblite', - # build helper, not needed on testers - 'mksnapshot', - ] - - return all_platforms - - def DatabaseSetup(buildmaster_config, require_dbconfig=False): """Read database credentials in the master directory.""" if os.path.isfile('.dbconfig'): @@ -1904,11 +775,3 @@ def GetSlavesFromBuilders(builders): }) return slaves - -def GetSlaveNamesForBuilder(builders, builder_name): - """Returns a list of slave hostnames for the given builder name.""" - slaves = [] - pool_names = builders['builders'][builder_name]['slave_pools'] - for pool_name in pool_names: - slaves.extend(builders['slave_pools'][pool_name]['slaves']) - return slaves diff --git a/infra/scripts/legacy/scripts/slave/build_directory.py b/infra/scripts/legacy/scripts/slave/build_directory.py index d7aa35b..d0686f5 100644 --- a/infra/scripts/legacy/scripts/slave/build_directory.py +++ b/infra/scripts/legacy/scripts/slave/build_directory.py @@ -33,20 +33,6 @@ def AreNinjaFilesNewerThanXcodeFiles(src_dir=None): return IsFileNewerThanFile(ninja_path, xcode_path) -def AreNinjaFilesNewerThanMSVSFiles(src_dir=None): - """Returns True if the generated ninja files are newer than the generated - msvs files. - - Parameters: - src_dir: The path to the src directory. If None, it's assumed to be - at src/ relative to the current working directory. - """ - src_dir = src_dir or 'src' - ninja_path = os.path.join(src_dir, 'out', 'Release', 'build.ninja') - msvs_path = os.path.join(src_dir, 'build', 'all.sln') - return IsFileNewerThanFile(ninja_path, msvs_path) - - def GetBuildOutputDirectory(src_dir=None, cros_board=None): """Returns the path to the build directory, relative to the checkout root. @@ -71,41 +57,6 @@ def GetBuildOutputDirectory(src_dir=None, cros_board=None): return os.path.join(src_dir, 'xcodebuild') if sys.platform == 'cygwin' or sys.platform.startswith('win'): - if AreNinjaFilesNewerThanMSVSFiles(src_dir): - return os.path.join(src_dir, 'out') - return os.path.join(src_dir, 'build') + return os.path.join(src_dir, 'out') raise NotImplementedError('Unexpected platform %s' % sys.platform) - - -def RmtreeExceptNinjaOrGomaFiles(build_output_dir): - """Recursively removes everything but ninja files from a build directory.""" - for root, _, files in os.walk(build_output_dir, topdown=False): - for f in files: - # For .manifest in particular, gyp windows ninja generates manifest - # files at generation time but clobber nukes at the beginning of - # compile, so make sure not to delete those generated files, otherwise - # compile will fail. - if (f.endswith('.ninja') or f.endswith('.manifest') or - f == 'args.gn' or - f.startswith('msvc') or # VS runtime DLLs. - f.startswith('pgort') or # VS PGO runtime DLL. - f in ('gyp-mac-tool', 'gyp-win-tool', - 'environment.x86', 'environment.x64')): - continue - # Keep goma related files. - if f == '.goma_deps': - continue - os.unlink(os.path.join(root, f)) - # Delete the directory if empty; this works because the walk is bottom-up. - try: - os.rmdir(root) - except OSError, e: - if e.errno in (39, 41, 66): - # If the directory isn't empty, ignore it. - # On Windows, os.rmdir will raise WindowsError with winerror 145, - # which e.errno is 41. - # On Linux, e.errno is 39. - pass - else: - raise diff --git a/infra/scripts/legacy/scripts/slave/gtest_slave_utils.py b/infra/scripts/legacy/scripts/slave/gtest_slave_utils.py index e35dd1d..2b4135c 100755 --- a/infra/scripts/legacy/scripts/slave/gtest_slave_utils.py +++ b/infra/scripts/legacy/scripts/slave/gtest_slave_utils.py @@ -4,13 +4,9 @@ # found in the LICENSE file. import logging -import optparse import os -import re -import sys from common import gtest_utils -from xml.dom import minidom from slave.gtest.json_results_generator import JSONResultsGenerator from slave.gtest.test_result import canonical_name from slave.gtest.test_result import TestResult @@ -44,38 +40,6 @@ def GetResultsMap(observer): return test_results_map -def GetResultsMapFromXML(results_xml): - """Parse the given results XML file and returns a map of TestResults.""" - - results_xml_file = None - try: - results_xml_file = open(results_xml) - except IOError: - logging.error('Cannot open file %s', results_xml) - return dict() - node = minidom.parse(results_xml_file).documentElement - results_xml_file.close() - - test_results_map = dict() - testcases = node.getElementsByTagName('testcase') - - for testcase in testcases: - name = testcase.getAttribute('name') - classname = testcase.getAttribute('classname') - test_name = '%s.%s' % (classname, name) - - failures = testcase.getElementsByTagName('failure') - not_run = testcase.getAttribute('status') == 'notrun' - elapsed = float(testcase.getAttribute('time')) - result = TestResult(test_name, - failed=bool(failures), - not_run=not_run, - elapsed_time=elapsed) - test_results_map[canonical_name(test_name)] = [result] - - return test_results_map - - def GenerateJSONResults(test_results_map, options): """Generates a JSON results file from the given test_results_map, returning the associated generator for use with UploadJSONResults, below. @@ -137,72 +101,3 @@ def UploadJSONResults(generator): if generator: generator.upload_json_files([FULL_RESULTS_FILENAME, TIMES_MS_FILENAME]) - -# For command-line testing. -def main(): - # Builder base URL where we have the archived test results. - # (Note: to be deprecated) - BUILDER_BASE_URL = 'http://build.chromium.org/buildbot/gtest_results/' - - option_parser = optparse.OptionParser() - option_parser.add_option('', '--test-type', default='', - help='Test type that generated the results XML,' - ' e.g. unit-tests.') - option_parser.add_option('', '--results-directory', default='./', - help='Output results directory source dir.') - option_parser.add_option('', '--input-results-xml', default='', - help='Test results xml file (input for us).' - ' default is TEST_TYPE.xml') - option_parser.add_option('', '--builder-base-url', default='', - help=('A URL where we have the archived test ' - 'results. (default=%sTEST_TYPE_results/)' - % BUILDER_BASE_URL)) - option_parser.add_option('', '--builder-name', - default='DUMMY_BUILDER_NAME', - help='The name of the builder shown on the ' - 'waterfall running this script e.g. WebKit.') - option_parser.add_option('', '--build-name', - default='DUMMY_BUILD_NAME', - help='The name of the builder used in its path, ' - 'e.g. webkit-rel.') - option_parser.add_option('', '--build-number', default='', - help='The build number of the builder running' - 'this script.') - option_parser.add_option('', '--test-results-server', - default='', - help='The test results server to upload the ' - 'results.') - option_parser.add_option('--master-name', default='', - help='The name of the buildbot master. ' - 'Both test-results-server and master-name ' - 'need to be specified to upload the results ' - 'to the server.') - option_parser.add_option('--webkit-revision', default='0', - help='The WebKit revision being tested. If not ' - 'given, defaults to 0.') - option_parser.add_option('--chrome-revision', default='0', - help='The Chromium revision being tested. If not ' - 'given, defaults to 0.') - - options = option_parser.parse_args()[0] - - if not options.test_type: - logging.error('--test-type needs to be specified.') - sys.exit(1) - - if not options.input_results_xml: - logging.error('--input-results-xml needs to be specified.') - sys.exit(1) - - if options.test_results_server and not options.master_name: - logging.warn('--test-results-server is given but ' - '--master-name is not specified; the results won\'t be ' - 'uploaded to the server.') - - results_map = GetResultsMapFromXML(options.input_results_xml) - generator = GenerateJSONResults(results_map, options) - UploadJSONResults(generator) - - -if '__main__' == __name__: - main() diff --git a/infra/scripts/legacy/scripts/slave/runtest.py b/infra/scripts/legacy/scripts/slave/runtest.py index fac25b9..42ee7db 100755 --- a/infra/scripts/legacy/scripts/slave/runtest.py +++ b/infra/scripts/legacy/scripts/slave/runtest.py @@ -11,22 +11,14 @@ build directory, e.g. chrome-release/build/. For a list of command-line options, call this script with '--help'. """ -import ast import copy -import datetime -import exceptions -import gzip -import hashlib -import json import logging import optparse import os import platform import re -import stat import subprocess import sys -import tempfile from common import chromium_utils from common import gtest_utils diff --git a/infra/scripts/legacy/scripts/slave/slave_utils.py b/infra/scripts/legacy/scripts/slave/slave_utils.py index 98435e1..76cf4e3 100644 --- a/infra/scripts/legacy/scripts/slave/slave_utils.py +++ b/infra/scripts/legacy/scripts/slave/slave_utils.py @@ -15,179 +15,12 @@ import tempfile import time from common import chromium_utils -from slave.bootstrap import ImportMasterConfigs # pylint: disable=W0611 -from common.chromium_utils import GetActiveMaster # pylint: disable=W0611 # These codes used to distinguish true errors from script warnings. ERROR_EXIT_CODE = 1 WARNING_EXIT_CODE = 88 -# Local errors. -class PageHeapError(Exception): - pass - - -# Cache the path to gflags.exe. -_gflags_exe = None - - -def SubversionExe(): - # TODO(pamg): move this into platform_utils to support Mac and Linux. - if chromium_utils.IsWindows(): - return 'svn.bat' # Find it in the user's path. - elif chromium_utils.IsLinux() or chromium_utils.IsMac(): - return 'svn' # Find it in the user's path. - else: - raise NotImplementedError( - 'Platform "%s" is not currently supported.' % sys.platform) - - -def GitExe(): - return 'git.bat' if chromium_utils.IsWindows() else 'git' - - -def SubversionCat(wc_dir): - """Output the content of specified files or URLs in SVN. - """ - try: - return chromium_utils.GetCommandOutput([SubversionExe(), 'cat', - wc_dir]) - except chromium_utils.ExternalError: - return None - - -class NotGitWorkingCopy(Exception): pass -class NotSVNWorkingCopy(Exception): pass -class NotAnyWorkingCopy(Exception): pass -class InvalidSVNRevision(Exception): pass - - -def ScrapeSVNInfoRevision(wc_dir, regexp): - """Runs 'svn info' on a working copy and applies the supplied regex and - returns the matched group as an int. - regexp can be either a compiled regex or a string regex. - throws NotSVNWorkingCopy if wc_dir is not in a working copy. - throws InvalidSVNRevision if matched group is not alphanumeric. - """ - if isinstance(regexp, (str, unicode)): - regexp = re.compile(regexp) - retval, svn_info = chromium_utils.GetStatusOutput([SubversionExe(), 'info', - wc_dir]) - if retval or 'is not a working copy' in svn_info: - raise NotSVNWorkingCopy(wc_dir) - match = regexp.search(svn_info) - if not match or not match.groups(): - raise InvalidSVNRevision( - '%s did not match in svn info %s.' % (regexp.pattern, svn_info)) - text = match.group(1) - if text.isalnum(): - return int(text) - else: - raise InvalidSVNRevision(text) - - -def SubversionRevision(wc_dir): - """Finds the last svn revision of a working copy and returns it as an int.""" - return ScrapeSVNInfoRevision(wc_dir, r'(?s).*Revision: (\d+).*') - - -def SubversionLastChangedRevision(wc_dir_or_file): - """Finds the last changed svn revision of a fs path returns it as an int.""" - return ScrapeSVNInfoRevision(wc_dir_or_file, - r'(?s).*Last Changed Rev: (\d+).*') - - -def GitHash(wc_dir): - """Finds the current commit hash of the wc_dir.""" - retval, text = chromium_utils.GetStatusOutput( - [GitExe(), 'rev-parse', 'HEAD'], cwd=wc_dir) - if retval or 'fatal: Not a git repository' in text: - raise NotGitWorkingCopy(wc_dir) - return text.strip() - - -def GetHashOrRevision(wc_dir): - """Gets the svn revision or git hash of wc_dir as a string. Throws - NotAnyWorkingCopy if neither are appropriate.""" - try: - return str(SubversionRevision(wc_dir)) - except NotSVNWorkingCopy: - pass - try: - return GitHash(wc_dir) - except NotGitWorkingCopy: - pass - raise NotAnyWorkingCopy(wc_dir) - - -def GitOrSubversion(wc_dir): - """Returns the VCS for the given directory. - - Returns: - 'svn' if the directory is a valid svn repo - 'git' if the directory is a valid git repo root - None otherwise - """ - ret, out = chromium_utils.GetStatusOutput([SubversionExe(), 'info', wc_dir]) - if not ret and 'is not a working copy' not in out: - return 'svn' - - ret, out = chromium_utils.GetStatusOutput( - [GitExe(), 'rev-parse', '--is-inside-work-tree'], cwd=wc_dir) - if not ret and 'fatal: Not a git repository' not in out: - return 'git' - - return None - - -def GetBuildRevisions(src_dir, webkit_dir=None, revision_dir=None): - """Parses build revisions out of the provided directories. - - Args: - src_dir: The source directory to be used to check the revision in. - webkit_dir: Optional WebKit directory, relative to src_dir. - revision_dir: If provided, this dir will be used for the build revision - instead of the mandatory src_dir. - - Returns a tuple of the build revision and (optional) WebKit revision. - NOTICE: These revisions are strings, since they can be both Subversion numbers - and Git hashes. - """ - abs_src_dir = os.path.abspath(src_dir) - webkit_revision = None - if webkit_dir: - webkit_dir = os.path.join(abs_src_dir, webkit_dir) - webkit_revision = GetHashOrRevision(webkit_dir) - - if revision_dir: - revision_dir = os.path.join(abs_src_dir, revision_dir) - build_revision = GetHashOrRevision(revision_dir) - else: - build_revision = GetHashOrRevision(src_dir) - return (build_revision, webkit_revision) - - -def GetZipFileNames(build_properties, build_revision, webkit_revision=None, - extract=False, use_try_buildnumber=True): - base_name = 'full-build-%s' % chromium_utils.PlatformName() - - if 'try' in build_properties.get('mastername', '') and use_try_buildnumber: - if extract: - if not build_properties.get('parent_buildnumber'): - raise Exception('build_props does not have parent data: %s' % - build_properties) - version_suffix = '_%(parent_buildnumber)s' % build_properties - else: - version_suffix = '_%(buildnumber)s' % build_properties - elif webkit_revision: - version_suffix = '_wk%s_%s' % (webkit_revision, build_revision) - else: - version_suffix = '_%s' % build_revision - - return base_name, version_suffix - - def SlaveBuildName(chrome_dir): """Extracts the build name of this slave (e.g., 'chrome-release') from the leaf subdir of its build directory. @@ -221,96 +54,6 @@ def SlaveBaseDir(chrome_dir): return result -def GetStagingDir(start_dir): - """Creates a chrome_staging dir in the starting directory. and returns its - full path. - """ - start_dir = os.path.abspath(start_dir) - staging_dir = os.path.join(SlaveBaseDir(start_dir), 'chrome_staging') - chromium_utils.MaybeMakeDirectory(staging_dir) - return staging_dir - - -def SetPageHeap(chrome_dir, exe, enable): - """Enables or disables page-heap checking in the given executable, depending - on the 'enable' parameter. gflags_exe should be the full path to gflags.exe. - """ - global _gflags_exe - if _gflags_exe is None: - _gflags_exe = chromium_utils.FindUpward(chrome_dir, - 'tools', 'memory', 'gflags.exe') - command = [_gflags_exe] - if enable: - command.extend(['/p', '/enable', exe, '/full']) - else: - command.extend(['/p', '/disable', exe]) - result = chromium_utils.RunCommand(command) - if result: - description = {True: 'enable', False: 'disable'} - raise PageHeapError('Unable to %s page heap for %s.' % - (description[enable], exe)) - - -def LongSleep(secs): - """A sleep utility for long durations that avoids appearing hung. - - Sleeps for the specified duration. Prints output periodically so as not to - look hung in order to avoid being timed out. Since this function is meant - for long durations, it assumes that the caller does not care about losing a - small amount of precision. - - Args: - secs: The time to sleep, in seconds. - """ - secs_per_iteration = 60 - time_slept = 0 - - # Make sure we are dealing with an integral duration, since this function is - # meant for long-lived sleeps we don't mind losing floating point precision. - secs = int(round(secs)) - - remainder = secs % secs_per_iteration - if remainder > 0: - time.sleep(remainder) - time_slept += remainder - sys.stdout.write('.') - sys.stdout.flush() - - while time_slept < secs: - time.sleep(secs_per_iteration) - time_slept += secs_per_iteration - sys.stdout.write('.') - sys.stdout.flush() - - sys.stdout.write('\n') - - -def RunPythonCommandInBuildDir(build_dir, target, command_line_args, - server_dir=None, filter_obj=None): - if sys.platform == 'win32': - python_exe = 'python.exe' - else: - os.environ['PYTHONPATH'] = (chromium_utils.FindUpward(build_dir, 'tools', - 'python') - + ':' +os.environ.get('PYTHONPATH', '')) - python_exe = 'python' - - command = [python_exe] + command_line_args - return chromium_utils.RunCommand(command, filter_obj=filter_obj) - - -class RunCommandCaptureFilter(object): - lines = [] - - def FilterLine(self, in_line): - self.lines.append(in_line) - return None - - def FilterDone(self, last_bits): - self.lines.append(last_bits) - return None - - def GypFlagIsOn(options, flag): value = GetGypFlag(options, flag, False) # The values we understand as Off are False and a text zero. @@ -329,220 +72,6 @@ def GetGypFlag(options, flag, default=None): return gypflags[flag] -def GSUtilSetup(): - # Get the path to the gsutil script. - gsutil = os.path.join(os.path.dirname(__file__), 'gsutil') - gsutil = os.path.normpath(gsutil) - if chromium_utils.IsWindows(): - gsutil += '.bat' - - # Get the path to the boto file containing the password. - boto_file = os.path.join(os.path.dirname(__file__), '..', '..', 'site_config', - '.boto') - - # Make sure gsutil uses this boto file if it exists. - if os.path.exists(boto_file): - os.environ['AWS_CREDENTIAL_FILE'] = boto_file - os.environ['BOTO_CONFIG'] = boto_file - return gsutil - - -def GSUtilGetMetadataField(name, provider_prefix=None): - """Returns: (str) the metadata field to use with Google Storage - - The Google Storage specification for metadata can be found at: - https://developers.google.com/storage/docs/gsutil/addlhelp/WorkingWithObjectMetadata - """ - # Already contains custom provider prefix - if name.lower().startswith('x-'): - return name - - # See if it's innately supported by Google Storage - if name in ( - 'Cache-Control', - 'Content-Disposition', - 'Content-Encoding', - 'Content-Language', - 'Content-MD5', - 'Content-Type', - ): - return name - - # Add provider prefix - if not provider_prefix: - provider_prefix = 'x-goog-meta' - return '%s-%s' % (provider_prefix, name) - - -def GSUtilCopy(source, dest, mimetype=None, gs_acl=None, cache_control=None, - metadata=None): - """Copy a file to Google Storage. - - Runs the following command: - gsutil -h Content-Type:<mimetype> \ - -h Cache-Control:<cache_control> \ - cp -a <gs_acl> file://<filename> <gs_base>/<subdir>/<filename w/o path> - - Args: - source: the source URI - dest: the destination URI - mimetype: optional value to add as a Content-Type header - gs_acl: optional value to add as a canned-acl - cache_control: optional value to set Cache-Control header - metadata: (dict) A dictionary of string key/value metadata entries to set - (see `gsutil cp' '-h' option) - Returns: - The status code returned from running the generated gsutil command. - """ - - if not source.startswith('gs://') and not source.startswith('file://'): - source = 'file://' + source - if not dest.startswith('gs://') and not dest.startswith('file://'): - dest = 'file://' + dest - gsutil = GSUtilSetup() - # Run the gsutil command. gsutil internally calls command_wrapper, which - # will try to run the command 10 times if it fails. - command = [gsutil] - - if not metadata: - metadata = {} - if mimetype: - metadata['Content-Type'] = mimetype - if cache_control: - metadata['Cache-Control'] = cache_control - for k, v in sorted(metadata.iteritems(), key=lambda (k, _): k): - field = GSUtilGetMetadataField(k) - param = (field) if v is None else ('%s:%s' % (field, v)) - command += ['-h', param] - command.extend(['cp']) - if gs_acl: - command.extend(['-a', gs_acl]) - command.extend([source, dest]) - return chromium_utils.RunCommand(command) - - -def GSUtilCopyFile(filename, gs_base, subdir=None, mimetype=None, gs_acl=None, - cache_control=None, metadata=None): - """Copy a file to Google Storage. - - Runs the following command: - gsutil -h Content-Type:<mimetype> \ - -h Cache-Control:<cache_control> \ - cp -a <gs_acl> file://<filename> <gs_base>/<subdir>/<filename w/o path> - - Args: - filename: the file to upload - gs_base: the bucket to upload the file to - subdir: optional subdirectory withing the bucket - mimetype: optional value to add as a Content-Type header - gs_acl: optional value to add as a canned-acl - Returns: - The status code returned from running the generated gsutil command. - """ - - source = 'file://' + filename - dest = gs_base - if subdir: - # HACK(nsylvain): We can't use normpath here because it will break the - # slashes on Windows. - if subdir == '..': - dest = os.path.dirname(gs_base) - else: - dest = '/'.join([gs_base, subdir]) - dest = '/'.join([dest, os.path.basename(filename)]) - return GSUtilCopy(source, dest, mimetype, gs_acl, cache_control, - metadata=metadata) - - -def GSUtilCopyDir(src_dir, gs_base, dest_dir=None, gs_acl=None, - cache_control=None): - """Upload the directory and its contents to Google Storage.""" - - if os.path.isfile(src_dir): - assert os.path.isdir(src_dir), '%s must be a directory' % src_dir - - gsutil = GSUtilSetup() - command = [gsutil, '-m'] - if cache_control: - command.extend(['-h', 'Cache-Control:%s' % cache_control]) - command.extend(['cp', '-R']) - if gs_acl: - command.extend(['-a', gs_acl]) - if dest_dir: - command.extend([src_dir, gs_base + '/' + dest_dir]) - else: - command.extend([src_dir, gs_base]) - return chromium_utils.RunCommand(command) - -def GSUtilDownloadFile(src, dst): - """Copy a file from Google Storage.""" - gsutil = GSUtilSetup() - - # Run the gsutil command. gsutil internally calls command_wrapper, which - # will try to run the command 10 times if it fails. - command = [gsutil] - command.extend(['cp', src, dst]) - return chromium_utils.RunCommand(command) - - -def GSUtilMoveFile(source, dest, gs_acl=None): - """Move a file on Google Storage.""" - - gsutil = GSUtilSetup() - - # Run the gsutil command. gsutil internally calls command_wrapper, which - # will try to run the command 10 times if it fails. - command = [gsutil] - command.extend(['mv', source, dest]) - status = chromium_utils.RunCommand(command) - - if status: - return status - - if gs_acl: - command = [gsutil] - command.extend(['setacl', gs_acl, dest]) - status = chromium_utils.RunCommand(command) - - return status - - -def GSUtilDeleteFile(filename): - """Delete a file on Google Storage.""" - - gsutil = GSUtilSetup() - - # Run the gsutil command. gsutil internally calls command_wrapper, which - # will try to run the command 10 times if it fails. - command = [gsutil] - command.extend(['rm', filename]) - return chromium_utils.RunCommand(command) - - -# Python doesn't support the type of variable scope in nested methods needed -# to avoid the global output variable. This variable should only ever be used -# by GSUtilListBucket. -command_output = '' - - -def GSUtilListBucket(gs_base, args): - """List the contents of a Google Storage bucket.""" - - gsutil = GSUtilSetup() - - # Run the gsutil command. gsutil internally calls command_wrapper, which - # will try to run the command 10 times if it fails. - global command_output - command_output = '' - - def GatherOutput(line): - global command_output - command_output += line + '\n' - command = [gsutil, 'ls'] + args + [gs_base] - status = chromium_utils.RunCommand(command, parser_func=GatherOutput) - return (status, command_output) - - def LogAndRemoveFiles(temp_dir, regex_pattern): """Removes files in |temp_dir| that match |regex_pattern|. This function prints out the name of each directory or filename before @@ -706,30 +235,3 @@ def WriteLogLines(logname, lines, perf=None): print '@@@STEP_LOG_END_PERF@%s@%s@@@' % (logname, perf) else: print '@@@STEP_LOG_END@%s@@@' % logname - - -def ZipAndUpload(bucket, archive, *targets): - """Uploads a zipped archive to the specified Google Storage bucket. - - Args: - bucket: Google Storage bucket to upload to. - archive: Name of the .zip archive. - *targets: List of targets that should be included in the archive. - - Returns: - Path to the uploaded archive on Google Storage. - """ - local_archive = os.path.join(tempfile.mkdtemp(archive), archive) - zip_cmd = [ - 'zip', - '-9', - '--filesync', - '--recurse-paths', - '--symlinks', - local_archive, - ] - zip_cmd.extend(targets) - - chromium_utils.RunCommand(zip_cmd) - GSUtilCopy(local_archive, 'gs://%s/%s' % (bucket, archive)) - return 'https://storage.cloud.google.com/%s/%s' % (bucket, archive) |