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] |