Revert of [android] Stop using isolate.py for data dependency management. (patchset #9 id:160001 of https://codereview.chromium.org/2492123002/ )

Reason for revert:
Breaks the blink bot, e.g. https://build.chromium.org/p/chromium.webkit/builders/WebKit%20Android%20%28Nexus4%29/builds/57103

Original issue's description:
> [android] Stop using isolate.py for data dependency management.
>
> BUG=663110
>
> Committed: https://crrev.com/634c87cf4af7ac11f2dbe7687a6d7904c2217ee3
> Cr-Commit-Position: refs/heads/master@{#432940}

TBR=agrieve@chromium.org,dpranke@chromium.org
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=663110

Review-Url: https://codereview.chromium.org/2514453003
Cr-Original-Commit-Position: refs/heads/master@{#433020}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: 67002b0fdaa3123f10f96fa2f7965677d531db74
This commit is contained in:
jbudorick 2016-11-17 18:12:19 -08:00 коммит произвёл Commit bot
Родитель d7507a0fce
Коммит faaecf8a5e
20 изменённых файлов: 780 добавлений и 287 удалений

Просмотреть файл

@ -121,6 +121,9 @@ group("test_runner_py") {
"//third_party/catapult/devil/devil/devil_dependencies.json",
"//third_party/proguard/lib/proguard.jar",
]
data_deps = [
"//tools/swarming_client:isolate_py",
]
}
# Create wrapper scripts in out/bin that takes care of setting the

Просмотреть файл

@ -62,7 +62,6 @@ def CommonChecks(input_api, output_api):
J('pylib', 'local', 'device', 'local_device_test_run_test.py'),
J('pylib', 'results', 'json_results_test.py'),
J('pylib', 'symbols', 'elf_symbolizer_unittest.py'),
J('pylib', 'utils', 'device_dependencies_test.py'),
],
env=pylib_test_env))

120
android/gn/generate_isolate.py Executable file
Просмотреть файл

@ -0,0 +1,120 @@
#!/usr/bin/env python
# Copyright 2016 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.
"""Creates an .isolate given a list of files.
"""
import argparse
import os
import pprint
import re
import sys
_UNIVERSAL_BLACKLIST = (
r'.*OWNERS', # Should never be included.
)
_ANDROID_BLACKLIST = (
r'.*\.crx', # Chrome extension zip files.
r'.*external_extensions\.json', # Chrome external extensions config file.
r'.*\.so', # Libraries packed into .apk.
r'.*\.mojom\.js', # Some test_support targets include python deps.
r'.*Mojo.*manifest\.json', # Some source_set()s pull these in.
r'.*jni_generator_tests', # Exists just to test the compile, not to be run.
)
_DEVICE_BLACKLIST = (
r'.*\.py', # Some test_support targets include python deps.
# v8's blobs get packaged into APKs.
r'.*natives_blob.*\.bin',
r'.*snapshot_blob.*\.bin',
)
_ASSERT_WHITELIST = (
r'.*\.pak',
r'.*/', # Assume directories are always included on purpose.
)
def _IsExecutable(path):
return os.path.isfile(path) and os.access(path, os.X_OK)
def _MatchesAny(path, patterns):
return any(re.match(p, path) for p in patterns)
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--command',
help='The command to put in the .isolate (optional)')
parser.add_argument('--runtime-deps-file', required=True,
help='Input .runtime_deps file.')
parser.add_argument('--output-directory', required=True,
help='Location of the ninja output directory')
parser.add_argument('--out-file', help='Write to file rather than stdout.')
parser.add_argument('--apply-android-filters', action='store_true',
help='Filter files not required for Android.')
parser.add_argument('--apply-device-filters', action='store_true',
help='Filter files not required in *.device.isolate.')
parser.add_argument('--assert-no-odd-data', action='store_true',
help='Fail if any data deps exist (after filtering) '
'that are not a part of the _ASSERT_WHITELIST. Use '
'this to prevent unexpected runtime_deps from '
'creeping in')
options = parser.parse_args()
deps = []
with open(options.runtime_deps_file) as deps_file:
for path in deps_file:
if path.startswith('./'):
path = path[2:]
deps.append(path.rstrip())
deps = (d for d in deps if not _MatchesAny(d, _UNIVERSAL_BLACKLIST))
if options.apply_android_filters:
deps = (d for d in deps if not _MatchesAny(d, _ANDROID_BLACKLIST))
if options.apply_device_filters:
deps = (d for d in deps if not _MatchesAny(d, _DEVICE_BLACKLIST))
# Breakpad tests have a helper exe, which is packaged in the _dist.
deps = (d for d in deps if not _IsExecutable(d))
# Make them relative to out-file.
if options.out_file:
subdir = os.path.relpath(options.output_directory,
os.path.dirname(options.out_file))
deps = (os.path.join(subdir, d) for d in deps)
deps = sorted(deps)
if options.assert_no_odd_data:
odd_files = [d for d in deps if not _MatchesAny(d, _ASSERT_WHITELIST)]
assert not odd_files, ('Found possibly undesired file in runtime_deps: %s' %
odd_files)
isolate_dict = {
'variables': {
'files': deps,
}
}
if options.command:
isolate_dict['variables']['command'] = [options.command]
isolate_data = pprint.pformat(isolate_dict)
if options.out_file:
with open(options.out_file, 'w') as f:
f.write(isolate_data + '\n')
else:
print isolate_data
if __name__ == '__main__':
sys.exit(main())

Просмотреть файл

@ -62,7 +62,6 @@ def main(args):
group.add_argument('--executable-dist-dir')
group.add_argument('--isolate-file-path')
group.add_argument('--output-directory')
group.add_argument('--runtime-deps-path')
group.add_argument('--test-apk')
group.add_argument('--test-apk-incremental-install-script')
group.add_argument('--coverage-dir')
@ -103,9 +102,6 @@ def main(args):
if args.output_directory:
test_runner_path_args.append(
('--output-directory', RelativizePathToScript(args.output_directory)))
if args.runtime_deps_path:
test_runner_path_args.append(
('--runtime-deps-path', RelativizePathToScript(args.runtime_deps_path)))
if args.test_apk:
test_runner_path_args.append(
('--test-apk', RelativizePathToScript(args.test_apk)))

Просмотреть файл

@ -7,17 +7,17 @@ from pylib.instrumentation import instrumentation_test_instance
from pylib.junit import junit_test_instance
from pylib.monkey import monkey_test_instance
from pylib.perf import perf_test_instance
from pylib.utils import device_dependencies
from pylib.utils import isolator
def CreateTestInstance(args, error_func):
if args.command == 'gtest':
return gtest_test_instance.GtestTestInstance(
args, device_dependencies.GetDataDependencies, error_func)
args, isolator.Isolator(), error_func)
elif args.command == 'instrumentation':
return instrumentation_test_instance.InstrumentationTestInstance(
args, device_dependencies.GetDataDependencies, error_func)
args, isolator.Isolator(), error_func)
elif args.command == 'junit':
return junit_test_instance.JunitTestInstance(args, error_func)
elif args.command == 'monkey':

Просмотреть файл

@ -14,6 +14,7 @@ from pylib import constants
from pylib.constants import host_paths
from pylib.base import base_test_result
from pylib.base import test_instance
from pylib.utils import isolator
with host_paths.SysPath(host_paths.BUILD_COMMON_PATH):
import unittest_util # pylint: disable=import-error
@ -229,7 +230,7 @@ def ConvertTestFilterFileIntoGTestFilterArgument(input_lines):
class GtestTestInstance(test_instance.TestInstance):
def __init__(self, args, data_deps_delegate, error_func):
def __init__(self, args, isolate_delegate, error_func):
super(GtestTestInstance, self).__init__()
# TODO(jbudorick): Support multiple test suites.
if len(args.suite_name) > 1:
@ -286,10 +287,16 @@ class GtestTestInstance(test_instance.TestInstance):
else:
self._gtest_filter = None
self._data_deps_delegate = data_deps_delegate
self._runtime_deps_path = args.runtime_deps_path
if not self._runtime_deps_path:
logging.warning('No data dependencies will be pushed.')
if (args.isolate_file_path and
not isolator.IsIsolateEmpty(args.isolate_file_path)):
self._isolate_abs_path = os.path.abspath(args.isolate_file_path)
self._isolate_delegate = isolate_delegate
self._isolated_abs_path = os.path.join(
constants.GetOutDirectory(), '%s.isolated' % self._suite)
else:
logging.warning('%s isolate file provided. No data deps will be pushed.',
'Empty' if args.isolate_file_path else 'No')
self._isolate_delegate = None
if args.app_data_files:
self._app_data_files = args.app_data_files
@ -386,8 +393,15 @@ class GtestTestInstance(test_instance.TestInstance):
#override
def SetUp(self):
"""Map data dependencies via isolate."""
self._data_deps.extend(
self._data_deps_delegate(self._runtime_deps_path))
if self._isolate_delegate:
self._isolate_delegate.Remap(
self._isolate_abs_path, self._isolated_abs_path)
self._isolate_delegate.PurgeExcluded(_DEPS_EXCLUSION_LIST)
self._isolate_delegate.MoveOutputDeps()
dest_dir = None
self._data_deps.extend([
(self._isolate_delegate.isolate_deps_dir, dest_dir)])
def GetDataDependencies(self):
"""Returns the test suite's data dependencies.
@ -441,6 +455,7 @@ class GtestTestInstance(test_instance.TestInstance):
#override
def TearDown(self):
"""Do nothing."""
pass
"""Clear the mappings created by SetUp."""
if self._isolate_delegate:
self._isolate_delegate.Clear()

Просмотреть файл

@ -18,6 +18,7 @@ from pylib.base import test_instance
from pylib.constants import host_paths
from pylib.instrumentation import test_result
from pylib.instrumentation import instrumentation_parser
from pylib.utils import isolator
from pylib.utils import proguard
with host_paths.SysPath(host_paths.BUILD_COMMON_PATH):
@ -399,7 +400,7 @@ def GetUniqueTestName(test, sep='#'):
class InstrumentationTestInstance(test_instance.TestInstance):
def __init__(self, args, data_deps_delegate, error_func):
def __init__(self, args, isolate_delegate, error_func):
super(InstrumentationTestInstance, self).__init__()
self._additional_apks = []
@ -416,9 +417,10 @@ class InstrumentationTestInstance(test_instance.TestInstance):
self._initializeApkAttributes(args, error_func)
self._data_deps = None
self._data_deps_delegate = None
self._runtime_deps_path = None
self._initializeDataDependencyAttributes(args, data_deps_delegate)
self._isolate_abs_path = None
self._isolate_delegate = None
self._isolated_abs_path = None
self._initializeDataDependencyAttributes(args, isolate_delegate)
self._annotations = None
self._excluded_annotations = None
@ -518,12 +520,22 @@ class InstrumentationTestInstance(test_instance.TestInstance):
self._additional_apks = (
[apk_helper.ToHelper(x) for x in args.additional_apks])
def _initializeDataDependencyAttributes(self, args, data_deps_delegate):
def _initializeDataDependencyAttributes(self, args, isolate_delegate):
self._data_deps = []
self._data_deps_delegate = data_deps_delegate
self._runtime_deps_path = args.runtime_deps_path
if (args.isolate_file_path and
not isolator.IsIsolateEmpty(args.isolate_file_path)):
if os.path.isabs(args.isolate_file_path):
self._isolate_abs_path = args.isolate_file_path
else:
self._isolate_abs_path = os.path.join(
constants.DIR_SOURCE_ROOT, args.isolate_file_path)
self._isolate_delegate = isolate_delegate
self._isolated_abs_path = os.path.join(
constants.GetOutDirectory(), '%s.isolated' % self._test_package)
else:
self._isolate_delegate = None
if not self._runtime_deps_path:
if not self._isolate_delegate:
logging.warning('No data dependencies will be pushed.')
def _initializeTestFilterAttributes(self, args):
@ -676,8 +688,11 @@ class InstrumentationTestInstance(test_instance.TestInstance):
#override
def SetUp(self):
self._data_deps.extend(
self._data_deps_delegate(self._runtime_deps_path))
if self._isolate_delegate:
self._isolate_delegate.Remap(
self._isolate_abs_path, self._isolated_abs_path)
self._isolate_delegate.MoveOutputDeps()
self._data_deps.extend([(self._isolate_delegate.isolate_deps_dir, None)])
def GetDataDependencies(self):
return self._data_deps
@ -749,4 +764,5 @@ class InstrumentationTestInstance(test_instance.TestInstance):
#override
def TearDown(self):
pass
if self._isolate_delegate:
self._isolate_delegate.Clear()

Просмотреть файл

@ -251,7 +251,7 @@ class LocalDeviceGtestRun(local_device_test_run.LocalDeviceTestRun):
def SetUp(self):
@local_device_environment.handle_shard_failures_with(
on_failure=self._env.BlacklistDevice)
def individual_device_set_up(dev, host_device_tuples):
def individual_device_set_up(dev):
def install_apk():
# Install test APK.
self._delegate.Install(dev)
@ -259,12 +259,11 @@ class LocalDeviceGtestRun(local_device_test_run.LocalDeviceTestRun):
def push_test_data():
# Push data dependencies.
device_root = self._delegate.GetTestDataRoot(dev)
host_device_tuples_substituted = [
(h, local_device_test_run.SubstituteDeviceRoot(d, device_root))
for h, d in host_device_tuples]
dev.PushChangedFiles(
host_device_tuples_substituted,
delete_device_stale=True)
data_deps = self._test_instance.GetDataDependencies()
host_device_tuples = [
(h, d if d is not None else device_root)
for h, d in data_deps]
dev.PushChangedFiles(host_device_tuples, delete_device_stale=True)
if not host_device_tuples:
dev.RunShellCommand(['rm', '-rf', device_root], check_return=True)
dev.RunShellCommand(['mkdir', '-p', device_root], check_return=True)
@ -290,9 +289,7 @@ class LocalDeviceGtestRun(local_device_test_run.LocalDeviceTestRun):
for step in steps:
step()
self._env.parallel_devices.pMap(
individual_device_set_up,
self._test_instance.GetDataDependencies())
self._env.parallel_devices.pMap(individual_device_set_up)
#override
def _ShouldShard(self):

Просмотреть файл

@ -66,6 +66,14 @@ class LocalDeviceInstrumentationTestRun(
#override
def SetUp(self):
def substitute_device_root(d, device_root):
if not d:
return device_root
elif isinstance(d, list):
return posixpath.join(*(p if p else device_root for p in d))
else:
return d
@local_device_environment.handle_shard_failures_with(
self._env.BlacklistDevice)
def individual_device_set_up(dev, host_device_tuples):
@ -109,7 +117,7 @@ class LocalDeviceInstrumentationTestRun(
device_root = posixpath.join(dev.GetExternalStoragePath(),
'chromium_tests_root')
host_device_tuples_substituted = [
(h, local_device_test_run.SubstituteDeviceRoot(d, device_root))
(h, substitute_device_root(d, device_root))
for h, d in host_device_tuples]
logging.info('instrumentation data deps:')
for h, d in host_device_tuples_substituted:

Просмотреть файл

@ -5,7 +5,6 @@
import fnmatch
import imp
import logging
import posixpath
import signal
import thread
import threading
@ -45,15 +44,6 @@ def IncrementalInstall(device, apk_helper, installer_script):
permissions=None) # Auto-grant permissions from manifest.
def SubstituteDeviceRoot(device_path, device_root):
if not device_path:
return device_root
elif isinstance(device_path, list):
return posixpath.join(*(p if p else device_root for p in device_path))
else:
return device_path
class LocalDeviceTestRun(test_run.TestRun):
def __init__(self, env, test_instance):

Просмотреть файл

@ -15,34 +15,6 @@ with host_paths.SysPath(host_paths.PYMOCK_PATH):
import mock # pylint: disable=import-error
class SubstituteDeviceRootTest(unittest.TestCase):
def testNoneDevicePath(self):
self.assertEquals(
'/fake/device/root',
local_device_test_run.SubstituteDeviceRoot(
None, '/fake/device/root'))
def testStringDevicePath(self):
self.assertEquals(
'/another/fake/device/path',
local_device_test_run.SubstituteDeviceRoot(
'/another/fake/device/path', '/fake/device/root'))
def testListWithNoneDevicePath(self):
self.assertEquals(
'/fake/device/root/subpath',
local_device_test_run.SubstituteDeviceRoot(
[None, 'subpath'], '/fake/device/root'))
def testListWithoutNoneDevicePath(self):
self.assertEquals(
'/another/fake/device/path',
local_device_test_run.SubstituteDeviceRoot(
['/', 'another', 'fake', 'device', 'path'],
'/fake/device/root'))
class TestLocalDeviceTestRun(local_device_test_run.LocalDeviceTestRun):
# pylint: disable=abstract-method

Просмотреть файл

@ -1,104 +0,0 @@
# Copyright 2016 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.
import os
import re
from pylib import constants
_BLACKLIST = [
re.compile(r'.*OWNERS'), # Should never be included.
re.compile(r'.*\.crx'), # Chrome extension zip files.
re.compile(r'.*\.so'), # Libraries packed into .apk.
re.compile(r'.*Mojo.*manifest\.json'), # Some source_set()s pull these in.
re.compile(r'.*\.py'), # Some test_support targets include python deps.
# Some test_support targets include python deps.
re.compile(r'.*\.mojom\.js'),
# Chrome external extensions config file.
re.compile(r'.*external_extensions\.json'),
# Exists just to test the compile, not to be run.
re.compile(r'.*jni_generator_tests'),
# v8's blobs get packaged into APKs.
re.compile(r'.*natives_blob.*\.bin'),
re.compile(r'.*snapshot_blob.*\.bin'),
]
def DevicePathComponentsFor(host_path, output_directory):
"""Returns the device path components for a given host path.
This returns the device path as a list of joinable path components,
with None as the first element to indicate that the path should be
rooted at $EXTERNAL_STORAGE.
e.g., given
'$CHROMIUM_SRC/foo/bar/baz.txt'
this would return
[None, 'foo', 'bar', 'baz.txt']
This handles a couple classes of paths differently than it otherwise would:
- All .pak files get mapped to top-level paks/
- Anything in the output directory gets mapped relative to the output
directory rather than the source directory.
e.g. given
'$CHROMIUM_SRC/out/Release/icu_fake_dir/icudtl.dat'
this would return
[None, 'icu_fake_dir', 'icudtl.dat']
Args:
host_path: The absolute path to the host file.
Returns:
A list of device path components.
"""
if host_path.startswith(output_directory):
if os.path.splitext(host_path)[1] == '.pak':
return [None, 'paks', os.path.basename(host_path)]
rel_host_path = os.path.relpath(host_path, output_directory)
else:
rel_host_path = os.path.relpath(host_path, constants.DIR_SOURCE_ROOT)
device_path_components = [None]
p = rel_host_path
while p:
p, d = os.path.split(p)
if d:
device_path_components.insert(1, d)
return device_path_components
def GetDataDependencies(runtime_deps_path):
"""Returns a list of device data dependencies.
Args:
runtime_deps_path: A str path to the .runtime_deps file.
Returns:
A list of (host_path, device_path) tuples.
"""
if not runtime_deps_path:
return []
with open(runtime_deps_path, 'r') as runtime_deps_file:
rel_host_files = [l.strip() for l in runtime_deps_file if l]
output_directory = constants.GetOutDirectory()
abs_host_files = [
os.path.abspath(os.path.join(output_directory, r))
for r in rel_host_files]
filtered_abs_host_files = [
host_file for host_file in abs_host_files
if not any(blacklist_re.match(host_file) for blacklist_re in _BLACKLIST)]
return [(f, DevicePathComponentsFor(f, output_directory))
for f in filtered_abs_host_files]

Просмотреть файл

@ -1,56 +0,0 @@
#! /usr/bin/env python
# Copyright 2016 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.
import os
import unittest
from pylib import constants
from pylib.utils import device_dependencies
class DevicePathComponentsForTest(unittest.TestCase):
def testCheckedInFile(self):
test_path = os.path.join(constants.DIR_SOURCE_ROOT, 'foo', 'bar', 'baz.txt')
output_directory = os.path.join(
constants.DIR_SOURCE_ROOT, 'out-foo', 'Release')
self.assertEquals(
[None, 'foo', 'bar', 'baz.txt'],
device_dependencies.DevicePathComponentsFor(
test_path, output_directory))
def testOutputDirectoryFile(self):
test_path = os.path.join(constants.DIR_SOURCE_ROOT, 'out-foo', 'Release',
'icudtl.dat')
output_directory = os.path.join(
constants.DIR_SOURCE_ROOT, 'out-foo', 'Release')
self.assertEquals(
[None, 'icudtl.dat'],
device_dependencies.DevicePathComponentsFor(
test_path, output_directory))
def testOutputDirectorySubdirFile(self):
test_path = os.path.join(constants.DIR_SOURCE_ROOT, 'out-foo', 'Release',
'test_dir', 'icudtl.dat')
output_directory = os.path.join(
constants.DIR_SOURCE_ROOT, 'out-foo', 'Release')
self.assertEquals(
[None, 'test_dir', 'icudtl.dat'],
device_dependencies.DevicePathComponentsFor(
test_path, output_directory))
def testOutputDirectoryPakFile(self):
test_path = os.path.join(constants.DIR_SOURCE_ROOT, 'out-foo', 'Release',
'foo.pak')
output_directory = os.path.join(
constants.DIR_SOURCE_ROOT, 'out-foo', 'Release')
self.assertEquals(
[None, 'paks', 'foo.pak'],
device_dependencies.DevicePathComponentsFor(
test_path, output_directory))
if __name__ == '__main__':
unittest.main()

Просмотреть файл

@ -0,0 +1,192 @@
# Copyright 2014 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.
import fnmatch
import glob
import os
import shutil
import sys
import tempfile
from devil.utils import cmd_helper
from pylib import constants
from pylib.constants import host_paths
_ISOLATE_SCRIPT = os.path.join(
host_paths.DIR_SOURCE_ROOT, 'tools', 'swarming_client', 'isolate.py')
def DefaultPathVariables():
return {
'DEPTH': host_paths.DIR_SOURCE_ROOT,
'PRODUCT_DIR': constants.GetOutDirectory(),
}
def DefaultConfigVariables():
# Note: This list must match the --config-vars in build/isolate.gypi
return {
'CONFIGURATION_NAME': constants.GetBuildType(),
'OS': 'android',
'asan': '0',
'branding': 'Chromium',
'chromeos': '0',
'component': 'static_library',
'enable_pepper_cdms': '0',
'enable_plugins': '0',
'fastbuild': '0',
'icu_use_data_file_flag': '1',
'kasko': '0',
'lsan': '0',
'msan': '0',
# TODO(maruel): This may not always be true.
'target_arch': 'arm',
'tsan': '0',
'use_custom_libcxx': '0',
'use_instrumented_libraries': '0',
'use_prebuilt_instrumented_libraries': '0',
'use_ozone': '0',
'use_x11': '0',
'v8_use_external_startup_data': '1',
'msvs_version': '0',
}
def IsIsolateEmpty(isolate_path):
"""Returns whether there are no files in the .isolate."""
with open(isolate_path) as f:
return "'files': []" in f.read()
class Isolator(object):
"""Manages calls to isolate.py for the android test runner scripts."""
def __init__(self):
self._isolate_deps_dir = tempfile.mkdtemp()
@property
def isolate_deps_dir(self):
return self._isolate_deps_dir
def Clear(self):
"""Deletes the isolate dependency directory."""
if os.path.exists(self._isolate_deps_dir):
shutil.rmtree(self._isolate_deps_dir)
def Remap(self, isolate_abs_path, isolated_abs_path,
path_variables=None, config_variables=None):
"""Remaps data dependencies into |self._isolate_deps_dir|.
Args:
isolate_abs_path: The absolute path to the .isolate file, which specifies
data dependencies in the source tree.
isolated_abs_path: The absolute path to the .isolated file, which is
generated by isolate.py and specifies data dependencies in
|self._isolate_deps_dir| and their digests.
path_variables: A dict containing everything that should be passed
as a |--path-variable| to the isolate script. Defaults to the return
value of |DefaultPathVariables()|.
config_variables: A dict containing everything that should be passed
as a |--config-variable| to the isolate script. Defaults to the return
value of |DefaultConfigVariables()|.
Raises:
Exception if the isolate command fails for some reason.
"""
if not path_variables:
path_variables = DefaultPathVariables()
if not config_variables:
config_variables = DefaultConfigVariables()
isolate_cmd = [
sys.executable, _ISOLATE_SCRIPT, 'remap',
'--isolate', isolate_abs_path,
'--isolated', isolated_abs_path,
'--outdir', self._isolate_deps_dir,
]
for k, v in path_variables.iteritems():
isolate_cmd.extend(['--path-variable', k, v])
for k, v in config_variables.iteritems():
isolate_cmd.extend(['--config-variable', k, v])
exit_code, _ = cmd_helper.GetCmdStatusAndOutput(isolate_cmd)
if exit_code:
raise Exception('isolate command failed: %s' % ' '.join(isolate_cmd))
def VerifyHardlinks(self):
"""Checks |isolate_deps_dir| for a hardlink.
Returns:
True if a hardlink is found.
False if nothing is found.
Raises:
Exception if a non-hardlink is found.
"""
for root, _, filenames in os.walk(self._isolate_deps_dir):
if filenames:
linked_file = os.path.join(root, filenames[0])
orig_file = os.path.join(
self._isolate_deps_dir,
os.path.relpath(linked_file, self._isolate_deps_dir))
if os.stat(linked_file).st_ino == os.stat(orig_file).st_ino:
return True
else:
raise Exception('isolate remap command did not use hardlinks.')
return False
def PurgeExcluded(self, deps_exclusion_list):
"""Deletes anything on |deps_exclusion_list| from |self._isolate_deps_dir|.
Args:
deps_exclusion_list: A list of globs to exclude from the isolate
dependency directory.
"""
excluded_paths = (
x for y in deps_exclusion_list
for x in glob.glob(
os.path.abspath(os.path.join(self._isolate_deps_dir, y))))
for p in excluded_paths:
if os.path.isdir(p):
shutil.rmtree(p)
else:
os.remove(p)
@classmethod
def _DestructiveMerge(cls, src, dest):
if os.path.exists(dest) and os.path.isdir(dest):
for p in os.listdir(src):
cls._DestructiveMerge(os.path.join(src, p), os.path.join(dest, p))
os.rmdir(src)
else:
shutil.move(src, dest)
def MoveOutputDeps(self):
"""Moves files from the output directory to the top level of
|self._isolate_deps_dir|.
Moves pak files from the output directory to to <isolate_deps_dir>/paks
Moves files from the product directory to <isolate_deps_dir>
"""
# On Android, all pak files need to be in the top-level 'paks' directory.
paks_dir = os.path.join(self._isolate_deps_dir, 'paks')
os.mkdir(paks_dir)
deps_out_dir = os.path.join(
self._isolate_deps_dir,
os.path.relpath(os.path.join(constants.GetOutDirectory(), os.pardir),
host_paths.DIR_SOURCE_ROOT))
for root, _, filenames in os.walk(deps_out_dir):
for filename in fnmatch.filter(filenames, '*.pak'):
shutil.move(os.path.join(root, filename), paks_dir)
# Move everything in PRODUCT_DIR to top level.
deps_product_dir = os.path.join(
deps_out_dir, os.path.basename(constants.GetOutDirectory()))
if os.path.isdir(deps_product_dir):
for p in os.listdir(deps_product_dir):
Isolator._DestructiveMerge(os.path.join(deps_product_dir, p),
os.path.join(self._isolate_deps_dir, p))
os.rmdir(deps_product_dir)
os.rmdir(deps_out_dir)

Просмотреть файл

@ -196,16 +196,12 @@ def AddGTestOptions(parser):
dest='shard_timeout', type=int, default=120,
help='Timeout to wait for each test '
'(default: %(default)s).')
# TODO(jbudorick): Remove this after ensuring nothing else uses it.
group.add_argument('--isolate_file_path',
'--isolate-file-path',
dest='isolate_file_path',
type=os.path.realpath,
help=argparse.SUPPRESS)
group.add_argument('--runtime-deps-path',
dest='runtime_deps_path',
type=os.path.realpath,
help='Runtime data dependency file from GN.')
help='.isolate file path to override the default '
'path')
group.add_argument('--app-data-file', action='append', dest='app_data_files',
help='A file path relative to the app data directory '
'that should be saved to the host.')
@ -364,16 +360,12 @@ def AddInstrumentationTestOptions(parser):
group.add_argument('--device-flags-file', type=os.path.realpath,
help='The relative filepath to a file containing '
'command-line flags to set on the device')
# TODO(jbudorick): Remove this after ensuring nothing else uses it.
group.add_argument('--isolate_file_path',
'--isolate-file-path',
dest='isolate_file_path',
type=os.path.realpath,
help=argparse.SUPPRESS)
group.add_argument('--runtime-deps-path',
dest='runtime_deps_path',
type=os.path.realpath,
help='Runtime data dependency file from GN.')
help='.isolate file path to override the default '
'path')
group.add_argument('--delete-stale-data', dest='delete_stale_data',
action='store_true',
help='Delete stale test data on the device.')

Просмотреть файл

@ -124,7 +124,7 @@ pylib/results/flakiness_dashboard/results_uploader.py
pylib/results/json_results.py
pylib/results/report_results.py
pylib/utils/__init__.py
pylib/utils/device_dependencies.py
pylib/utils/isolator.py
pylib/utils/proguard.py
pylib/utils/repo_utils.py
pylib/valgrind_tools.py

Просмотреть файл

@ -417,6 +417,55 @@ template("copy_ex") {
}
}
template("device_isolate") {
testonly = true
_runtime_deps_file = "$target_gen_dir/$target_name.runtime_deps"
group("${target_name}__write_deps") {
forward_variables_from(invoker,
[
"data",
"data_deps",
"deps",
"public_deps",
])
write_runtime_deps = _runtime_deps_file
}
action(target_name) {
script = "//build/android/gn/generate_isolate.py"
inputs = [
_runtime_deps_file,
]
outputs = [
invoker.output,
]
args = [
"--output-directory=.",
"--out-file",
rebase_path(invoker.output, root_build_dir),
"--runtime-deps-file",
rebase_path(_runtime_deps_file, root_build_dir),
"--apply-android-filters",
"--apply-device-filters",
]
_assert_no_odd_data =
defined(invoker.assert_no_odd_data) && invoker.assert_no_odd_data
if (_assert_no_odd_data) {
args += [ "--assert-no-odd-data" ]
}
if (defined(invoker.command)) {
_isolate_dir = get_path_info(invoker.output, "dir")
args += [
"--command",
rebase_path(invoker.command, _isolate_dir),
]
}
deps = [
":${invoker.target_name}__write_deps",
]
}
}
# Generates a script in the output bin directory which runs the test
# target using the test runner script in build/android/test_runner.py.
template("test_runner_script") {
@ -426,24 +475,6 @@ template("test_runner_script") {
_incremental_install =
defined(invoker.incremental_install) && invoker.incremental_install
_runtime_deps =
!defined(invoker.ignore_all_data_deps) || !invoker.ignore_all_data_deps
if (_runtime_deps) {
_runtime_deps_file = "$target_gen_dir/$target_name.runtime_deps"
_runtime_deps_target = "${target_name}__write_deps"
group(_runtime_deps_target) {
forward_variables_from(invoker,
[
"data",
"data_deps",
"deps",
"public_deps",
])
write_runtime_deps = _runtime_deps_file
}
}
action(target_name) {
forward_variables_from(invoker,
[
@ -469,15 +500,6 @@ template("test_runner_script") {
rebase_path(root_build_dir, root_build_dir),
]
if (_runtime_deps) {
deps += [ ":$_runtime_deps_target" ]
data += [ _runtime_deps_file ]
test_runner_args += [
"--runtime-deps-path",
rebase_path(_runtime_deps_file, root_build_dir),
]
}
# apk_target is not used for native executable tests
# (e.g. breakpad_unittests).
if (defined(invoker.apk_target)) {
@ -554,6 +576,13 @@ template("test_runner_script") {
]
}
}
if (defined(invoker.isolate_file)) {
data += [ invoker.isolate_file ]
test_runner_args += [
"--isolate-file-path",
rebase_path(invoker.isolate_file, root_build_dir),
]
}
if (defined(invoker.shard_timeout)) {
test_runner_args += [ "--shard-timeout=${invoker.shard_timeout}" ]
}

Просмотреть файл

@ -2260,23 +2260,36 @@ if (enable_java_templates) {
template("instrumentation_test_apk") {
testonly = true
_apk_target_name = "${target_name}__apk"
_gen_isolate_target_name = "${target_name}__isolate"
_test_runner_target_name = "${target_name}__test_runner_script"
_install_script_name = "install_$target_name"
_target_dir_name = get_label_info(":$target_name", "dir")
_device_isolate_path = "$root_out_dir/gen.runtime/$_target_dir_name/$target_name.device.isolate"
device_isolate(_gen_isolate_target_name) {
forward_variables_from(invoker,
[
"data",
"data_deps",
"deps",
"public_deps",
])
output = _device_isolate_path
}
test_runner_script(_test_runner_target_name) {
forward_variables_from(invoker,
[
"additional_apks",
"apk_under_test",
"data",
"data_deps",
"deps",
"ignore_all_data_deps",
"public_deps",
])
test_name = invoker.target_name
test_type = "instrumentation"
apk_target = ":$_apk_target_name"
isolate_file = _device_isolate_path
deps = [
":$_gen_isolate_target_name",
]
}
test_runner_script("${_test_runner_target_name}_incremental") {
@ -2284,16 +2297,15 @@ if (enable_java_templates) {
[
"additional_apks",
"apk_under_test",
"data",
"data_deps",
"deps",
"ignore_all_data_deps",
"public_deps",
])
test_name = "${invoker.target_name}_incremental"
test_type = "instrumentation"
apk_target = ":$_apk_target_name"
incremental_install = true
isolate_file = _device_isolate_path
deps = [
":$_gen_isolate_target_name",
]
}
android_apk(_apk_target_name) {

Просмотреть файл

@ -0,0 +1,14 @@
# Copyright 2016 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.
group("isolate_py") {
_py_files =
read_file("//build/secondary/tools/swarming_client/isolate.pydeps",
"list lines")
# Filter out comments.
set_sources_assignment_filter([ "#*" ])
sources = _py_files
data = sources
}

Просмотреть файл

@ -0,0 +1,298 @@
# Generated by running:
# build/print_python_deps.py --root tools/swarming_client --output build/secondary/tools/swarming_client/isolate.pydeps --whitelist tools/swarming_client/libs --whitelist tools/swarming_client/third_party tools/swarming_client/isolate.py
auth.py
cipd.py
isolate.py
isolate_format.py
isolated_format.py
isolateserver.py
libs/__init__.py
libs/arfile/__init__.py
libs/arfile/arfile.py
libs/arfile/cli.py
libs/logdog/__init__.py
libs/logdog/bootstrap.py
libs/logdog/stream.py
libs/logdog/streamname.py
libs/logdog/varint.py
libs/luci_context/__init__.py
libs/luci_context/luci_context.py
named_cache.py
run_isolated.py
third_party/__init__.py
third_party/chromium/__init__.py
third_party/chromium/natsort.py
third_party/colorama/__init__.py
third_party/colorama/ansi.py
third_party/colorama/ansitowin32.py
third_party/colorama/initialise.py
third_party/colorama/win32.py
third_party/colorama/winterm.py
third_party/depot_tools/__init__.py
third_party/depot_tools/auto_stub.py
third_party/depot_tools/fix_encoding.py
third_party/depot_tools/subcommand.py
third_party/google/__init__.py
third_party/google/protobuf/__init__.py
third_party/google/protobuf/any_pb2.py
third_party/google/protobuf/any_test_pb2.py
third_party/google/protobuf/api_pb2.py
third_party/google/protobuf/compiler/__init__.py
third_party/google/protobuf/compiler/plugin_pb2.py
third_party/google/protobuf/descriptor.py
third_party/google/protobuf/descriptor_database.py
third_party/google/protobuf/descriptor_pb2.py
third_party/google/protobuf/descriptor_pool.py
third_party/google/protobuf/duration_pb2.py
third_party/google/protobuf/empty_pb2.py
third_party/google/protobuf/field_mask_pb2.py
third_party/google/protobuf/internal/__init__.py
third_party/google/protobuf/internal/_parameterized.py
third_party/google/protobuf/internal/any_test_pb2.py
third_party/google/protobuf/internal/api_implementation.py
third_party/google/protobuf/internal/containers.py
third_party/google/protobuf/internal/decoder.py
third_party/google/protobuf/internal/descriptor_pool_test1_pb2.py
third_party/google/protobuf/internal/descriptor_pool_test2_pb2.py
third_party/google/protobuf/internal/encoder.py
third_party/google/protobuf/internal/enum_type_wrapper.py
third_party/google/protobuf/internal/factory_test1_pb2.py
third_party/google/protobuf/internal/factory_test2_pb2.py
third_party/google/protobuf/internal/file_options_test_pb2.py
third_party/google/protobuf/internal/import_test_package/__init__.py
third_party/google/protobuf/internal/import_test_package/inner_pb2.py
third_party/google/protobuf/internal/import_test_package/outer_pb2.py
third_party/google/protobuf/internal/message_listener.py
third_party/google/protobuf/internal/message_set_extensions_pb2.py
third_party/google/protobuf/internal/missing_enum_values_pb2.py
third_party/google/protobuf/internal/more_extensions_dynamic_pb2.py
third_party/google/protobuf/internal/more_extensions_pb2.py
third_party/google/protobuf/internal/more_messages_pb2.py
third_party/google/protobuf/internal/packed_field_test_pb2.py
third_party/google/protobuf/internal/python_message.py
third_party/google/protobuf/internal/test_bad_identifiers_pb2.py
third_party/google/protobuf/internal/test_util.py
third_party/google/protobuf/internal/type_checkers.py
third_party/google/protobuf/internal/well_known_types.py
third_party/google/protobuf/internal/wire_format.py
third_party/google/protobuf/json_format.py
third_party/google/protobuf/map_unittest_pb2.py
third_party/google/protobuf/message.py
third_party/google/protobuf/message_factory.py
third_party/google/protobuf/proto_builder.py
third_party/google/protobuf/pyext/__init__.py
third_party/google/protobuf/pyext/cpp_message.py
third_party/google/protobuf/pyext/python_pb2.py
third_party/google/protobuf/reflection.py
third_party/google/protobuf/service.py
third_party/google/protobuf/service_reflection.py
third_party/google/protobuf/source_context_pb2.py
third_party/google/protobuf/struct_pb2.py
third_party/google/protobuf/symbol_database.py
third_party/google/protobuf/text_encoding.py
third_party/google/protobuf/text_format.py
third_party/google/protobuf/timestamp_pb2.py
third_party/google/protobuf/type_pb2.py
third_party/google/protobuf/unittest_arena_pb2.py
third_party/google/protobuf/unittest_custom_options_pb2.py
third_party/google/protobuf/unittest_import_pb2.py
third_party/google/protobuf/unittest_import_public_pb2.py
third_party/google/protobuf/unittest_mset_pb2.py
third_party/google/protobuf/unittest_mset_wire_format_pb2.py
third_party/google/protobuf/unittest_no_arena_import_pb2.py
third_party/google/protobuf/unittest_no_arena_pb2.py
third_party/google/protobuf/unittest_no_generic_services_pb2.py
third_party/google/protobuf/unittest_pb2.py
third_party/google/protobuf/unittest_proto3_arena_pb2.py
third_party/google/protobuf/util/__init__.py
third_party/google/protobuf/util/json_format_proto3_pb2.py
third_party/google/protobuf/wrappers_pb2.py
third_party/googleapiclient/__init__.py
third_party/googleapiclient/channel.py
third_party/googleapiclient/discovery.py
third_party/googleapiclient/discovery_cache/__init__.py
third_party/googleapiclient/discovery_cache/appengine_memcache.py
third_party/googleapiclient/discovery_cache/base.py
third_party/googleapiclient/discovery_cache/file_cache.py
third_party/googleapiclient/errors.py
third_party/googleapiclient/http.py
third_party/googleapiclient/mimeparse.py
third_party/googleapiclient/model.py
third_party/googleapiclient/sample_tools.py
third_party/googleapiclient/schema.py
third_party/httplib2/__init__.py
third_party/httplib2/iri2uri.py
third_party/httplib2/socks.py
third_party/infra_libs/__init__.py
third_party/infra_libs/_command_line_linux.py
third_party/infra_libs/_command_line_stub.py
third_party/infra_libs/app.py
third_party/infra_libs/authentication.py
third_party/infra_libs/event_mon/__init__.py
third_party/infra_libs/event_mon/checkouts.py
third_party/infra_libs/event_mon/config.py
third_party/infra_libs/event_mon/monitoring.py
third_party/infra_libs/event_mon/protos/__init__.py
third_party/infra_libs/event_mon/protos/chrome_infra_log_pb2.py
third_party/infra_libs/event_mon/protos/goma_stats_pb2.py
third_party/infra_libs/event_mon/protos/log_request_lite_pb2.py
third_party/infra_libs/event_mon/router.py
third_party/infra_libs/experiments.py
third_party/infra_libs/httplib2_utils.py
third_party/infra_libs/infra_types/__init__.py
third_party/infra_libs/infra_types/infra_types.py
third_party/infra_libs/instrumented_requests.py
third_party/infra_libs/logs/__init__.py
third_party/infra_libs/logs/logs.py
third_party/infra_libs/memoize.py
third_party/infra_libs/time_functions/__init__.py
third_party/infra_libs/time_functions/parser.py
third_party/infra_libs/time_functions/testing.py
third_party/infra_libs/time_functions/timestamp.py
third_party/infra_libs/time_functions/zulu.py
third_party/infra_libs/ts_mon/__init__.py
third_party/infra_libs/ts_mon/common/__init__.py
third_party/infra_libs/ts_mon/common/distribution.py
third_party/infra_libs/ts_mon/common/errors.py
third_party/infra_libs/ts_mon/common/helpers.py
third_party/infra_libs/ts_mon/common/http_metrics.py
third_party/infra_libs/ts_mon/common/interface.py
third_party/infra_libs/ts_mon/common/metric_store.py
third_party/infra_libs/ts_mon/common/metrics.py
third_party/infra_libs/ts_mon/common/monitors.py
third_party/infra_libs/ts_mon/common/pb_to_popo.py
third_party/infra_libs/ts_mon/common/standard_metrics.py
third_party/infra_libs/ts_mon/common/targets.py
third_party/infra_libs/ts_mon/config.py
third_party/infra_libs/ts_mon/protos/__init__.py
third_party/infra_libs/ts_mon/protos/current/__init__.py
third_party/infra_libs/ts_mon/protos/current/acquisition_network_device_pb2.py
third_party/infra_libs/ts_mon/protos/current/acquisition_task_pb2.py
third_party/infra_libs/ts_mon/protos/current/metrics_pb2.py
third_party/infra_libs/ts_mon/protos/new/__init__.py
third_party/infra_libs/ts_mon/protos/new/acquisition_network_device_pb2.py
third_party/infra_libs/ts_mon/protos/new/acquisition_task_pb2.py
third_party/infra_libs/ts_mon/protos/new/any_pb2.py
third_party/infra_libs/ts_mon/protos/new/metrics_pb2.py
third_party/infra_libs/ts_mon/protos/new/timestamp_pb2.py
third_party/infra_libs/utils.py
third_party/oauth2client/__init__.py
third_party/oauth2client/_helpers.py
third_party/oauth2client/_openssl_crypt.py
third_party/oauth2client/_pycrypto_crypt.py
third_party/oauth2client/client.py
third_party/oauth2client/clientsecrets.py
third_party/oauth2client/crypt.py
third_party/oauth2client/file.py
third_party/oauth2client/gce.py
third_party/oauth2client/keyring_storage.py
third_party/oauth2client/locked_file.py
third_party/oauth2client/multistore_file.py
third_party/oauth2client/service_account.py
third_party/oauth2client/tools.py
third_party/oauth2client/util.py
third_party/oauth2client/xsrfutil.py
third_party/pyasn1/pyasn1/__init__.py
third_party/pyasn1/pyasn1/codec/__init__.py
third_party/pyasn1/pyasn1/codec/ber/__init__.py
third_party/pyasn1/pyasn1/codec/ber/decoder.py
third_party/pyasn1/pyasn1/codec/ber/encoder.py
third_party/pyasn1/pyasn1/codec/ber/eoo.py
third_party/pyasn1/pyasn1/codec/cer/__init__.py
third_party/pyasn1/pyasn1/codec/cer/decoder.py
third_party/pyasn1/pyasn1/codec/cer/encoder.py
third_party/pyasn1/pyasn1/codec/der/__init__.py
third_party/pyasn1/pyasn1/codec/der/decoder.py
third_party/pyasn1/pyasn1/codec/der/encoder.py
third_party/pyasn1/pyasn1/compat/__init__.py
third_party/pyasn1/pyasn1/compat/binary.py
third_party/pyasn1/pyasn1/compat/octets.py
third_party/pyasn1/pyasn1/debug.py
third_party/pyasn1/pyasn1/error.py
third_party/pyasn1/pyasn1/type/__init__.py
third_party/pyasn1/pyasn1/type/base.py
third_party/pyasn1/pyasn1/type/char.py
third_party/pyasn1/pyasn1/type/constraint.py
third_party/pyasn1/pyasn1/type/error.py
third_party/pyasn1/pyasn1/type/namedtype.py
third_party/pyasn1/pyasn1/type/namedval.py
third_party/pyasn1/pyasn1/type/tag.py
third_party/pyasn1/pyasn1/type/tagmap.py
third_party/pyasn1/pyasn1/type/univ.py
third_party/pyasn1/pyasn1/type/useful.py
third_party/requests/__init__.py
third_party/requests/adapters.py
third_party/requests/api.py
third_party/requests/auth.py
third_party/requests/certs.py
third_party/requests/compat.py
third_party/requests/cookies.py
third_party/requests/exceptions.py
third_party/requests/hooks.py
third_party/requests/models.py
third_party/requests/packages/__init__.py
third_party/requests/packages/urllib3/__init__.py
third_party/requests/packages/urllib3/_collections.py
third_party/requests/packages/urllib3/connection.py
third_party/requests/packages/urllib3/connectionpool.py
third_party/requests/packages/urllib3/contrib/__init__.py
third_party/requests/packages/urllib3/contrib/appengine.py
third_party/requests/packages/urllib3/contrib/ntlmpool.py
third_party/requests/packages/urllib3/contrib/pyopenssl.py
third_party/requests/packages/urllib3/exceptions.py
third_party/requests/packages/urllib3/fields.py
third_party/requests/packages/urllib3/filepost.py
third_party/requests/packages/urllib3/packages/__init__.py
third_party/requests/packages/urllib3/packages/ordered_dict.py
third_party/requests/packages/urllib3/packages/six.py
third_party/requests/packages/urllib3/packages/ssl_match_hostname/__init__.py
third_party/requests/packages/urllib3/packages/ssl_match_hostname/_implementation.py
third_party/requests/packages/urllib3/poolmanager.py
third_party/requests/packages/urllib3/request.py
third_party/requests/packages/urllib3/response.py
third_party/requests/packages/urllib3/util/__init__.py
third_party/requests/packages/urllib3/util/connection.py
third_party/requests/packages/urllib3/util/request.py
third_party/requests/packages/urllib3/util/response.py
third_party/requests/packages/urllib3/util/retry.py
third_party/requests/packages/urllib3/util/ssl_.py
third_party/requests/packages/urllib3/util/timeout.py
third_party/requests/packages/urllib3/util/url.py
third_party/requests/sessions.py
third_party/requests/status_codes.py
third_party/requests/structures.py
third_party/requests/utils.py
third_party/rsa/rsa/__init__.py
third_party/rsa/rsa/_compat.py
third_party/rsa/rsa/_version133.py
third_party/rsa/rsa/_version200.py
third_party/rsa/rsa/asn1.py
third_party/rsa/rsa/bigfile.py
third_party/rsa/rsa/cli.py
third_party/rsa/rsa/common.py
third_party/rsa/rsa/core.py
third_party/rsa/rsa/key.py
third_party/rsa/rsa/parallel.py
third_party/rsa/rsa/pem.py
third_party/rsa/rsa/pkcs1.py
third_party/rsa/rsa/prime.py
third_party/rsa/rsa/randnum.py
third_party/rsa/rsa/transform.py
third_party/rsa/rsa/util.py
third_party/rsa/rsa/varblock.py
third_party/six/__init__.py
third_party/uritemplate/__init__.py
utils/__init__.py
utils/authenticators.py
utils/file_path.py
utils/fs.py
utils/large.py
utils/logging_utils.py
utils/lru.py
utils/net.py
utils/oauth.py
utils/on_error.py
utils/subprocess42.py
utils/threading_utils.py
utils/tools.py
utils/zip_package.py