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

Unified Diff: tools/mb/mb.py

Issue 2298403002: Update MB to use `gn analyze`. (Closed)
Patch Set: back out test-related changes Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « testing/buildbot/manage.py ('k') | tools/mb/mb_unittest.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/mb/mb.py
diff --git a/tools/mb/mb.py b/tools/mb/mb.py
index e54763c0370b0c2fff818d36e2fdb9550c539a91..2711bf1fc89b795f97ab0264225a5b8cb7f6772f 100755
--- a/tools/mb/mb.py
+++ b/tools/mb/mb.py
@@ -61,7 +61,7 @@ class MetaBuildWrapper(object):
self.DumpInputFiles()
return ret
except KeyboardInterrupt:
- self.Print('interrupted, exiting', stream=sys.stderr)
+ self.Print('interrupted, exiting')
return 130
except Exception:
self.DumpInputFiles()
@@ -229,7 +229,7 @@ class MetaBuildWrapper(object):
def DumpContentsOfFilePassedTo(arg_name, path):
if path and self.Exists(path):
self.Print("\n# To recreate the file passed to %s:" % arg_name)
- self.Print("%% cat > %s <<EOF)" % path)
+ self.Print("%% cat > %s <<EOF" % path)
contents = self.ReadFile(path)
self.Print(contents)
self.Print("EOF\n%\n")
@@ -643,6 +643,10 @@ class MetaBuildWrapper(object):
self.masters = contents['masters']
self.mixins = contents['mixins']
+ def ReadIsolateMap(self):
+ return ast.literal_eval(self.ReadFile(self.PathJoin(
+ self.chromium_src_dir, 'testing', 'buildbot', 'gn_isolate_map.pyl')))
+
def ConfigFromArgs(self):
if self.args.config:
if self.args.master or self.args.builder:
@@ -770,7 +774,7 @@ class MetaBuildWrapper(object):
if getattr(self.args, 'swarming_targets_file', None):
# We need GN to generate the list of runtime dependencies for
# the compile targets listed (one per line) in the file so
- # we can run them via swarming. We use ninja_to_gn.pyl to convert
+ # we can run them via swarming. We use gn_isolate_map.pyl to convert
# the compile targets to the matching GN labels.
path = self.args.swarming_targets_file
if not self.Exists(path):
@@ -778,25 +782,14 @@ class MetaBuildWrapper(object):
output_path=None)
contents = self.ReadFile(path)
swarming_targets = set(contents.splitlines())
- gn_isolate_map = ast.literal_eval(self.ReadFile(self.PathJoin(
- self.chromium_src_dir, 'testing', 'buildbot', 'gn_isolate_map.pyl')))
- gn_labels = []
- err = ''
- for target in swarming_targets:
- target_name = self.GNTargetName(target)
- if not target_name in gn_isolate_map:
- err += ('test target "%s" not found\n' % target_name)
- elif gn_isolate_map[target_name]['type'] == 'unknown':
- err += ('test target "%s" type is unknown\n' % target_name)
- else:
- gn_labels.append(gn_isolate_map[target_name]['label'])
+ isolate_map = self.ReadIsolateMap()
+ err, labels = self.MapTargetsToLabels(isolate_map, swarming_targets)
if err:
- raise MBErr('Error: Failed to match swarming targets to %s:\n%s' %
- ('//testing/buildbot/gn_isolate_map.pyl', err))
+ raise MBErr(err)
gn_runtime_deps_path = self.ToAbsPath(build_dir, 'runtime_deps')
- self.WriteFile(gn_runtime_deps_path, '\n'.join(gn_labels) + '\n')
+ self.WriteFile(gn_runtime_deps_path, '\n'.join(labels) + '\n')
cmd.append('--runtime-deps-list-file=%s' % gn_runtime_deps_path)
ret, _, _ = self.Run(cmd)
@@ -812,22 +805,21 @@ class MetaBuildWrapper(object):
# Android targets may be either android_apk or executable. The former
# will result in runtime_deps associated with the stamp file, while the
# latter will result in runtime_deps associated with the executable.
- target_name = self.GNTargetName(target)
- label = gn_isolate_map[target_name]['label']
+ label = isolate_map[target]['label']
runtime_deps_targets = [
- target_name + '.runtime_deps',
+ target + '.runtime_deps',
'obj/%s.stamp.runtime_deps' % label.replace(':', '/')]
- elif gn_isolate_map[target]['type'] == 'gpu_browser_test':
+ elif isolate_map[target]['type'] == 'gpu_browser_test':
if self.platform == 'win32':
runtime_deps_targets = ['browser_tests.exe.runtime_deps']
else:
runtime_deps_targets = ['browser_tests.runtime_deps']
- elif (gn_isolate_map[target]['type'] == 'script' or
- gn_isolate_map[target].get('label_type') == 'group'):
+ elif (isolate_map[target]['type'] == 'script' or
+ isolate_map[target].get('label_type') == 'group'):
# For script targets, the build target is usually a group,
# for which gn generates the runtime_deps next to the stamp file
# for the label, which lives under the obj/ directory.
- label = gn_isolate_map[target]['label']
+ label = isolate_map[target]['label']
runtime_deps_targets = [
'obj/%s.stamp.runtime_deps' % label.replace(':', '/')]
elif self.platform == 'win32':
@@ -843,8 +835,7 @@ class MetaBuildWrapper(object):
raise MBErr('did not generate any of %s' %
', '.join(runtime_deps_targets))
- command, extra_files = self.GetIsolateCommand(target, vals,
- gn_isolate_map)
+ command, extra_files = self.GetIsolateCommand(target, vals)
runtime_deps = self.ReadFile(runtime_deps_path).splitlines()
@@ -854,15 +845,16 @@ class MetaBuildWrapper(object):
return 0
def RunGNIsolate(self, vals):
- gn_isolate_map = ast.literal_eval(self.ReadFile(self.PathJoin(
- self.chromium_src_dir, 'testing', 'buildbot', 'gn_isolate_map.pyl')))
+ target = self.args.target[0]
+ isolate_map = self.ReadIsolateMap()
+ err, labels = self.MapTargetsToLabels(isolate_map, [target])
+ if err:
+ raise MBErr(err)
+ label = labels[0]
build_dir = self.args.path[0]
- target = self.args.target[0]
- target_name = self.GNTargetName(target)
- command, extra_files = self.GetIsolateCommand(target, vals, gn_isolate_map)
+ command, extra_files = self.GetIsolateCommand(target, vals)
- label = gn_isolate_map[target_name]['label']
cmd = self.GNCmd('desc', build_dir, label, 'runtime_deps')
ret, out, _ = self.Call(cmd)
if ret:
@@ -912,6 +904,27 @@ class MetaBuildWrapper(object):
isolate_path + 'd.gen.json',
)
+ def MapTargetsToLabels(self, isolate_map, targets):
+ labels = []
+ err = ''
+ for target in targets:
+ if target == 'all':
+ labels.append(target)
+ elif not target in isolate_map:
+ non_run_target = target[:-4]
+ if target.endswith('_run') and non_run_target in isolate_map:
+ labels.append(isolate_map[non_run_target]['label'] + '_run')
+ elif target.startswith('//'):
+ labels.append(target)
+ else:
+ err += ('target "%s" not found in '
+ '//testing/buildbot/gn_isolate_map.pyl\n' % target)
+ elif isolate_map[target]['type'] == 'unknown':
+ err += ('test target "%s" type is unknown\n' % target)
+ else:
+ labels.append(isolate_map[target]['label'])
+ return err, labels
+
def GNCmd(self, subcommand, path, *args):
if self.platform == 'linux2':
subdir, exe = 'linux64', 'gn'
@@ -921,9 +934,9 @@ class MetaBuildWrapper(object):
subdir, exe = 'win', 'gn.exe'
gn_path = self.PathJoin(self.chromium_src_dir, 'buildtools', subdir, exe)
-
return [gn_path, subcommand, path] + list(args)
+
def GNArgs(self, vals):
if vals['cros_passthrough']:
if not 'GN_ARGS' in os.environ:
@@ -988,7 +1001,7 @@ class MetaBuildWrapper(object):
return ret
- def GetIsolateCommand(self, target, vals, gn_isolate_map):
+ def GetIsolateCommand(self, target, vals):
android = 'target_os="android"' in vals['gn_args']
# This needs to mirror the settings in //build/config/ui.gni:
@@ -1001,15 +1014,19 @@ class MetaBuildWrapper(object):
msan = 'is_msan=true' in vals['gn_args']
tsan = 'is_tsan=true' in vals['gn_args']
- target_name = self.GNTargetName(target)
- test_type = gn_isolate_map[target_name]['type']
+ isolate_map = self.ReadIsolateMap()
+ test_type = isolate_map[target]['type']
- executable = gn_isolate_map[target_name].get('executable', target_name)
+ executable = isolate_map[target].get('executable', target)
executable_suffix = '.exe' if self.platform == 'win32' else ''
cmdline = []
extra_files = []
+ if test_type == 'nontest':
+ self.WriteFailureAndRaise('We should not be isolating %s.' % target,
+ output_path=None)
+
if android and test_type != "script":
logdog_command = [
'--logdog-bin-cmd', './../../bin/logdog_butler',
@@ -1021,7 +1038,7 @@ class MetaBuildWrapper(object):
'--name', 'unified_logcats',
]
test_cmdline = [
- self.PathJoin('bin', 'run_%s' % target_name),
+ self.PathJoin('bin', 'run_%s' % target),
'--logcat-output-file', '${ISOLATED_OUTDIR}/logcats',
'--target-devices-file', '${SWARMING_BOT_FILE}',
'-v'
@@ -1061,7 +1078,7 @@ class MetaBuildWrapper(object):
extra_files = [
'../../testing/test_env.py'
]
- gtest_filter = gn_isolate_map[target]['gtest_filter']
+ gtest_filter = isolate_map[target]['gtest_filter']
cmdline = [
'../../testing/test_env.py',
'./browser_tests' + executable_suffix,
@@ -1076,7 +1093,7 @@ class MetaBuildWrapper(object):
]
cmdline = [
'../../testing/test_env.py',
- '../../' + self.ToSrcRelPath(gn_isolate_map[target]['script'])
+ '../../' + self.ToSrcRelPath(isolate_map[target]['script'])
]
elif test_type in ('raw'):
extra_files = []
@@ -1088,7 +1105,7 @@ class MetaBuildWrapper(object):
self.WriteFailureAndRaise('No command line for %s found (test type %s).'
% (target, test_type), output_path=None)
- cmdline += gn_isolate_map[target_name].get('args', [])
+ cmdline += isolate_map[target].get('args', [])
return cmdline, extra_files
@@ -1184,12 +1201,18 @@ class MetaBuildWrapper(object):
return cmd, env
def RunGNAnalyze(self, vals):
- # analyze runs before 'gn gen' now, so we need to run gn gen
+ # Analyze runs before 'gn gen' now, so we need to run gn gen
# in order to ensure that we have a build directory.
ret = self.RunGNGen(vals)
if ret:
return ret
+ build_path = self.args.path[0]
+ input_path = self.args.input_path[0]
+ gn_input_path = input_path + '.gn'
+ output_path = self.args.output_path[0]
+ gn_output_path = output_path + '.gn'
+
inp = self.ReadInputJSON(['files', 'test_targets',
'additional_compile_targets'])
if self.args.verbose:
@@ -1198,26 +1221,6 @@ class MetaBuildWrapper(object):
self.PrintJSON(inp)
self.Print()
- # TODO(crbug.com/555273) - currently GN treats targets and
- # additional_compile_targets identically since we can't tell the
- # difference between a target that is a group in GN and one that isn't.
- # We should eventually fix this and treat the two types differently.
- targets = (set(inp['test_targets']) |
- set(inp['additional_compile_targets']))
-
- output_path = self.args.output_path[0]
-
- # Bail out early if a GN file was modified, since 'gn refs' won't know
- # what to do about it. Also, bail out early if 'all' was asked for,
- # since we can't deal with it yet.
- if (any(f.endswith('.gn') or f.endswith('.gni') for f in inp['files']) or
- 'all' in targets):
- self.WriteJSON({
- 'status': 'Found dependency (all)',
- 'compile_targets': sorted(targets),
- 'test_targets': sorted(targets & set(inp['test_targets'])),
- }, output_path)
- return 0
# This shouldn't normally happen, but could due to unusual race conditions,
# like a try job that gets scheduled before a patch lands but runs after
@@ -1231,68 +1234,65 @@ class MetaBuildWrapper(object):
}, output_path)
return 0
- ret = 0
- response_file = self.TempFile()
- response_file.write('\n'.join(inp['files']) + '\n')
- response_file.close()
+ gn_inp = {}
+ gn_inp['files'] = ['//' + f for f in inp['files'] if not f.startswith('//')]
+
+ isolate_map = self.ReadIsolateMap()
+ err, gn_inp['additional_compile_targets'] = self.MapTargetsToLabels(
+ isolate_map, inp['additional_compile_targets'])
+ if err:
+ raise MBErr(err)
+
+ err, gn_inp['test_targets'] = self.MapTargetsToLabels(
+ isolate_map, inp['test_targets'])
+ if err:
+ raise MBErr(err)
+ labels_to_targets = {}
+ for i, label in enumerate(gn_inp['test_targets']):
+ labels_to_targets[label] = inp['test_targets'][i]
- matching_targets = set()
try:
- cmd = self.GNCmd('refs',
- self.args.path[0],
- '@%s' % response_file.name,
- '--all',
- '--as=output')
- ret, out, _ = self.Run(cmd, force_verbose=False)
- if ret and not 'The input matches no targets' in out:
- self.WriteFailureAndRaise('gn refs returned %d: %s' % (ret, out),
- output_path)
- build_dir = self.ToSrcRelPath(self.args.path[0]) + self.sep
- for output in out.splitlines():
- build_output = output.replace(build_dir, '')
- if build_output in targets:
- matching_targets.add(build_output)
-
- cmd = self.GNCmd('refs',
- self.args.path[0],
- '@%s' % response_file.name,
- '--all')
- ret, out, _ = self.Run(cmd, force_verbose=False)
- if ret and not 'The input matches no targets' in out:
- self.WriteFailureAndRaise('gn refs returned %d: %s' % (ret, out),
- output_path)
- for label in out.splitlines():
- build_target = label[2:]
- # We want to accept 'chrome/android:chrome_public_apk' and
- # just 'chrome_public_apk'. This may result in too many targets
- # getting built, but we can adjust that later if need be.
- for input_target in targets:
- if (input_target == build_target or
- build_target.endswith(':' + input_target)):
- matching_targets.add(input_target)
- finally:
- self.RemoveFile(response_file.name)
+ self.WriteJSON(gn_inp, gn_input_path)
+ cmd = self.GNCmd('analyze', build_path, gn_input_path, gn_output_path)
+ ret, _, _ = self.Run(cmd, force_verbose=True)
+ if ret:
+ return ret
- if matching_targets:
- self.WriteJSON({
- 'status': 'Found dependency',
- 'compile_targets': sorted(matching_targets),
- 'test_targets': sorted(matching_targets &
- set(inp['test_targets'])),
- }, output_path)
- else:
- self.WriteJSON({
- 'status': 'No dependency',
- 'compile_targets': [],
- 'test_targets': [],
- }, output_path)
+ gn_outp_str = self.ReadFile(gn_output_path)
+ try:
+ gn_outp = json.loads(gn_outp_str)
+ except Exception as e:
+ self.Print("Failed to parse the JSON string GN returned: %s\n%s"
+ % (repr(gn_outp_str), str(e)))
+ raise
- if self.args.verbose:
- outp = json.loads(self.ReadFile(output_path))
- self.Print()
- self.Print('analyze output:')
- self.PrintJSON(outp)
- self.Print()
+ outp = {}
+ if 'status' in gn_outp:
+ outp['status'] = gn_outp['status']
+ if 'error' in gn_outp:
+ outp['error'] = gn_outp['error']
+ if 'invalid_targets' in gn_outp:
+ outp['invalid_targets'] = gn_outp['invalid_targets']
+ if 'compile_targets' in gn_outp:
+ outp['compile_targets'] = [
+ label.replace('//', '') for label in gn_outp['compile_targets']]
+ if 'test_targets' in gn_outp:
+ outp['test_targets'] = [
+ labels_to_targets[label] for label in gn_outp['test_targets']]
+
+ if self.args.verbose:
+ self.Print()
+ self.Print('analyze output:')
+ self.PrintJSON(outp)
+ self.Print()
+
+ self.WriteJSON(outp, output_path)
+
+ finally:
+ if self.Exists(gn_input_path):
+ self.RemoveFile(gn_input_path)
+ if self.Exists(gn_output_path):
+ self.RemoveFile(gn_output_path)
return 0
@@ -1375,9 +1375,6 @@ class MetaBuildWrapper(object):
def PrintJSON(self, obj):
self.Print(json.dumps(obj, indent=2, sort_keys=True))
- def GNTargetName(self, target):
- return target
-
def Build(self, target):
build_dir = self.ToSrcRelPath(self.args.path[0])
ninja_cmd = ['ninja', '-C', build_dir]
« no previous file with comments | « testing/buildbot/manage.py ('k') | tools/mb/mb_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698