[Reland #1] Add isolate-everything command to mb.py
The first attempt to land this CL failed because there is one consumer that
attempts to isolate a target called 'official_tests' which also has type
'additional_compile_target'. This is a misuse of the unofficial API for mb.py,
but is not trivial to fix.
This CL simply marks this as a special case a leaves a TODO for the responsible
party.
> The command generates a .isolate for every compatible gn target.
>
> This will be used by the deterministic builder to check that all isolates
> dependencies are identical between two builds.
>
> Bug: 870731
> Reviewed-on: https://chromium-review.googlesource.com/1176441
> Commit-Queue: Erik Chen <[email protected]>
> Reviewed-by: Marc-Antoine Ruel <[email protected]>
> Reviewed-by: Dirk Pranke <[email protected]>
> Cr-Commit-Position: refs/heads/master@{#584268}
Change-Id: I1d31870b0513911b3ff6ebe296ea812b9036bf45
Bug: 876065, 875713, 870731
Reviewed-on: https://chromium-review.googlesource.com/1182621
Commit-Queue: Erik Chen <[email protected]>
Reviewed-by: Dirk Pranke <[email protected]>
Cr-Commit-Position: refs/heads/master@{#584800}
diff --git a/tools/mb/mb.py b/tools/mb/mb.py
index dfd2608..0c2848d 100755
--- a/tools/mb/mb.py
+++ b/tools/mb/mb.py
@@ -145,6 +145,15 @@
help='path to generate build into')
subp.set_defaults(func=self.CmdGen)
+ subp = subps.add_parser('isolate-everything',
+ help='generates a .isolate for all targets. '
+ 'Requires that mb.py gen has already been '
+ 'run.')
+ AddCommonOptions(subp)
+ subp.set_defaults(func=self.CmdIsolateEverything)
+ subp.add_argument('path',
+ help='path build was generated into')
+
subp = subps.add_parser('isolate',
help='generate the .isolate files for a given'
'binary')
@@ -299,6 +308,10 @@
vals = self.Lookup()
return self.RunGNGen(vals)
+ def CmdIsolateEverything(self):
+ vals = self.Lookup()
+ return self.RunGNGenAllIsolates(vals)
+
def CmdHelp(self):
if self.args.subcommand:
self.ParseArgs([self.args.subcommand, '--help'])
@@ -753,7 +766,6 @@
gn_args_path = self.ToAbsPath(build_dir, 'args.gn')
self.WriteFile(gn_args_path, gn_args, force_verbose=True)
- swarming_targets = []
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
@@ -764,10 +776,10 @@
self.WriteFailureAndRaise('"%s" does not exist' % path,
output_path=None)
contents = self.ReadFile(path)
- swarming_targets = set(contents.splitlines())
+ isolate_targets = set(contents.splitlines())
isolate_map = self.ReadIsolateMap()
- err, labels = self.MapTargetsToLabels(isolate_map, swarming_targets)
+ err, labels = self.MapTargetsToLabels(isolate_map, isolate_targets)
if err:
raise MBErr(err)
@@ -782,11 +794,85 @@
self.Print('GN gen failed: %d' % ret)
return ret
+ if getattr(self.args, 'swarming_targets_file', None):
+ return self.GenerateIsolates(vals, isolate_targets, isolate_map,
+ build_dir)
+
+ return 0
+
+ def RunGNGenAllIsolates(self, vals):
+ """
+ This command generates all .isolate files.
+
+ This command assumes that "mb.py gen" has already been run, as it relies on
+ "gn ls" to fetch all gn targets. If uses that output, combined with the
+ isolate_map, to determine all isolates that can be generated for the current
+ gn configuration.
+ """
+ build_dir = self.args.path
+ ret, output, _ = self.Run(self.GNCmd('ls', build_dir),
+ force_verbose=False)
+ if ret:
+ # If `gn ls` failed, we should exit early rather than trying to
+ # generate isolates.
+ self.Print('GN ls failed: %d' % ret)
+ return ret
+
+ # Create a reverse map from isolate label to isolate dict.
+ isolate_map = self.ReadIsolateMap()
+ isolate_dict_map = {}
+ for key, isolate_dict in isolate_map.iteritems():
+ isolate_dict_map[isolate_dict['label']] = isolate_dict
+ isolate_dict_map[isolate_dict['label']]['isolate_key'] = key
+
+ runtime_deps = []
+
+ isolate_targets = []
+ # For every GN target, look up the isolate dict.
+ for line in output.splitlines():
+ target = line.strip()
+ if target in isolate_dict_map:
+ if isolate_dict_map[target]['type'] == 'additional_compile_target':
+ # By definition, additional_compile_targets are not tests, so we
+ # shouldn't generate isolates for them.
+ continue
+
+ isolate_targets.append(isolate_dict_map[target]['isolate_key'])
+ runtime_deps.append(target)
+
+ # Now we need to run "gn gen" again with --runtime-deps-list-file
+ gn_runtime_deps_path = self.ToAbsPath(build_dir, 'runtime_deps')
+ self.WriteFile(gn_runtime_deps_path, '\n'.join(runtime_deps) + '\n')
+ cmd = self.GNCmd('gen', build_dir)
+ cmd.append('--runtime-deps-list-file=%s' % gn_runtime_deps_path)
+ self.Run(cmd)
+
+ return self.GenerateIsolates(vals, isolate_targets, isolate_map, build_dir)
+
+ def GenerateIsolates(self, vals, ninja_targets, isolate_map, build_dir):
+ """
+ Generates isolates for a list of ninja targets.
+
+ Ninja targets are transformed to GN targets via isolate_map.
+
+ This function assumes that a previous invocation of "mb.py gen" has
+ generated runtime deps for all targets.
+ """
android = 'target_os="android"' in vals['gn_args']
fuchsia = 'target_os="fuchsia"' in vals['gn_args']
win = self.platform == 'win32' or 'target_os="win"' in vals['gn_args']
- for target in swarming_targets:
- if android:
+ for target in ninja_targets:
+ # TODO(https://crbug.com/876065): 'official_tests' use
+ # type='additional_compile_target' to isolate tests. This is not the
+ # intended use for 'additional_compile_target'.
+ if (isolate_map[target]['type'] == 'additional_compile_target' and
+ target != 'official_tests'):
+ # By definition, additional_compile_targets are not tests, so we
+ # shouldn't generate isolates for them.
+ self.Print('Cannot generate isolate for %s since it is an '
+ 'additional_compile_target.' % target)
+ return 1
+ elif android:
# 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.
@@ -826,10 +912,10 @@
', '.join(runtime_deps_targets))
command, extra_files = self.GetIsolateCommand(target, vals)
-
runtime_deps = self.ReadFile(runtime_deps_path).splitlines()
- self.WriteIsolateFiles(build_dir, command, target, runtime_deps,
+ canonical_target = target.replace(':','_').replace('/','_')
+ self.WriteIsolateFiles(build_dir, command, canonical_target, runtime_deps,
extra_files)
return 0