Chromium Code Reviews| Index: tools/mb/mb.py |
| diff --git a/tools/mb/mb.py b/tools/mb/mb.py |
| index 347fc8cd5d558d2e26bfd0f288b022e7e9629cf4..e40d2cecf18cfcc19f27413aef1a853a7b21b99c 100755 |
| --- a/tools/mb/mb.py |
| +++ b/tools/mb/mb.py |
| @@ -98,12 +98,37 @@ class MetaBuildWrapper(object): |
| help='path to generate build into') |
| subp.set_defaults(func=self.CmdGen) |
| + subp = subps.add_parser('isolate', |
| + help='generate the .isolate files for a given' |
| + 'binary') |
| + AddCommonOptions(subp) |
| + subp.add_argument('path', nargs=1, |
| + help='path build was generated into') |
| + subp.add_argument('target', nargs=1, |
| + help='ninja target to generate the isolate for') |
| + subp.set_defaults(func=self.CmdIsolate) |
| + |
| subp = subps.add_parser('lookup', |
| help='look up the command for a given config or ' |
| 'builder') |
| AddCommonOptions(subp) |
| subp.set_defaults(func=self.CmdLookup) |
| + subp = subps.add_parser('run', |
| + help='build and run the isolated version of a ' |
| + 'binary') |
| + AddCommonOptions(subp) |
| + subp.add_argument('-j', '--jobs', dest='jobs', type=int, |
| + help='Number of jobs to pass to ninja') |
| + subp.add_argument('--no-build', dest='build', default=True, |
| + action='store_false', |
| + help='Do not build, just isolate and run') |
| + subp.add_argument('path', nargs=1, |
| + help='path to generate build into') |
| + subp.add_argument('target', nargs=1, |
| + help='ninja target to build and run') |
| + subp.set_defaults(func=self.CmdRun) |
| + |
| subp = subps.add_parser('validate', |
| help='validate the config file') |
| subp.add_argument('-f', '--config-file', metavar='PATH', |
| @@ -121,44 +146,79 @@ class MetaBuildWrapper(object): |
| self.args = parser.parse_args(argv) |
| def CmdAnalyze(self): |
| - vals = self.GetConfig() |
| + vals = self.Lookup() |
| if vals['type'] == 'gn': |
| return self.RunGNAnalyze(vals) |
| - elif vals['type'] == 'gyp': |
| - return self.RunGYPAnalyze(vals) |
| else: |
| - raise MBErr('Unknown meta-build type "%s"' % vals['type']) |
| + return self.RunGYPAnalyze(vals) |
| def CmdGen(self): |
| - vals = self.GetConfig() |
| - |
| + vals = self.Lookup() |
| self.ClobberIfNeeded(vals) |
| if vals['type'] == 'gn': |
| return self.RunGNGen(vals) |
| - if vals['type'] == 'gyp': |
| + else: |
| return self.RunGYPGen(vals) |
| - raise MBErr('Unknown meta-build type "%s"' % vals['type']) |
| + def CmdHelp(self): |
| + if self.args.subcommand: |
| + self.ParseArgs([self.args.subcommand, '--help']) |
| + else: |
| + self.ParseArgs(['--help']) |
| + |
| + def CmdIsolate(self): |
| + vals = self.GetConfig() |
| + if vals['type'] == 'gn': |
| + return self.RunGNIsolate(vals) |
| + else: |
| + return self.Build('%s_run' % self.args.target[0]) |
| def CmdLookup(self): |
| - vals = self.GetConfig() |
| + vals = self.Lookup() |
| if vals['type'] == 'gn': |
| cmd = self.GNCmd('gen', '_path_', vals['gn_args']) |
| env = None |
| - elif vals['type'] == 'gyp': |
| - cmd, env = self.GYPCmd('_path_', vals) |
| else: |
| - raise MBErr('Unknown meta-build type "%s"' % vals['type']) |
| + cmd, env = self.GYPCmd('_path_', vals) |
| self.PrintCmd(cmd, env) |
| return 0 |
| - def CmdHelp(self): |
| - if self.args.subcommand: |
| - self.ParseArgs([self.args.subcommand, '--help']) |
| + def CmdRun(self): |
| + vals = self.GetConfig() |
| + build_dir = self.args.path[0] |
| + target = self.args.target[0] |
| + |
| + if vals['type'] == 'gn': |
| + ret = self.RunGNIsolate(vals) |
| + if ret: |
| + return ret |
| + if self.args.build: |
| + ret = self.Build(target) |
| + if ret: |
| + return ret |
| else: |
| - self.ParseArgs(['--help']) |
| + ret = self.Build('%s_run' % target) |
| + if ret: |
| + return ret |
| + |
| + ret, out, err = self.Run([ |
| + self.executable, |
| + self.PathJoin('tools', 'swarming_client', 'isolate.py'), |
| + 'run', |
| + '-s', |
| + self.ToSrcRelPath('%s/%s.isolated' % (build_dir, target))]) |
|
Ken Russell (switch to Gerrit)
2015/10/02 18:08:19
Will this '/' cause problems on Windows? I'm guess
|
| + |
| + # Always log the output of the test run, but we only log the |
| + # commend when self.args.verbose=True. |
| + if not self.args.verbose: |
| + if out: |
| + self.Print(out) |
| + if err: |
| + self.Print(err, file=sys.stderr) |
| + |
| + return ret |
| def CmdValidate(self): |
| errs = [] |
| @@ -234,13 +294,70 @@ class MetaBuildWrapper(object): |
| return 0 |
| def GetConfig(self): |
| + build_dir = self.args.path[0] |
| + |
| + vals = {} |
| + if self.args.builder or self.args.master or self.args.config: |
| + vals = self.Lookup() |
| + if vals['type'] == 'gn': |
| + # Re-run gn gen in order to ensure the config is consistent with the |
| + # build dir. |
| + self.RunGNGen(vals) |
| + return vals |
| + |
| + # TODO: We can only get the config for GN build dirs, not GYP build dirs. |
| + # GN stores the args that were used in args.gn in the build dir, |
| + # but GYP doesn't store them anywhere. We should consider modifying |
| + # gyp_chromium to record the arguments it runs with in a similar |
| + # manner. |
| + |
| + mb_type_path = self.PathJoin(self.ToAbsPath(build_dir), 'mb_type') |
| + if not self.Exists(mb_type_path): |
| + self.Print('Must either specify a path to an existing GN build dir ' |
| + 'or pass in a -m/-b pair or a -c flag to specify the ' |
| + 'configuration') |
| + return 1 |
| + |
| + mb_type = self.ReadFile(mb_type_path).strip() |
| + if mb_type == 'gn': |
| + vals = self.GNValsFromDir(build_dir) |
| + else: |
| + vals = {} |
| + vals['type'] = mb_type |
| + |
| + return vals |
| + |
| + def GNValsFromDir(self, build_dir): |
| + args_contents = self.ReadFile( |
| + self.PathJoin(self.ToAbsPath(build_dir), 'args.gn')) |
| + gn_args = [] |
| + for l in args_contents.splitlines(): |
| + fields = l.split(' ') |
| + name = fields[0] |
| + val = ' '.join(fields[2:]) |
| + gn_args.append('%s=%s' % (name, val)) |
| + |
| + return { |
| + 'gn_args': ' '.join(gn_args), |
| + 'type': 'gn', |
| + } |
| + |
| + def Lookup(self): |
| self.ReadConfigFile() |
| config = self.ConfigFromArgs() |
| if not config in self.configs: |
| raise MBErr('Config "%s" not found in %s' % |
| (config, self.args.config_file)) |
| - return self.FlattenConfig(config) |
| + vals = self.FlattenConfig(config) |
| + |
| + # Do some basic sanity checking on the config so that we |
| + # don't have to do this in every caller. |
| + assert 'type' in vals, 'No meta-build type specified in the config' |
| + assert vals['type'] in ('gn', 'gyp'), ( |
| + 'Unknown meta-build type "%s"' % vals['gn_args']) |
| + |
| + return vals |
| def ReadConfigFile(self): |
| if not self.Exists(self.args.config_file): |
| @@ -353,9 +470,9 @@ class MetaBuildWrapper(object): |
| self.WriteFile(mb_type_path, new_mb_type) |
| def RunGNGen(self, vals): |
| - path = self.args.path[0] |
| + build_dir = self.args.path[0] |
| - cmd = self.GNCmd('gen', path, vals['gn_args'], extra_args=['--check']) |
| + cmd = self.GNCmd('gen', build_dir, vals['gn_args'], extra_args=['--check']) |
| swarming_targets = [] |
| if self.args.swarming_targets_file: |
| @@ -374,10 +491,10 @@ class MetaBuildWrapper(object): |
| (target, '//testing/buildbot/gn_isolate_map.pyl')) |
| gn_labels.append(gn_isolate_map[target]['label']) |
| - gn_runtime_deps_path = self.ToAbsPath(path, 'runtime_deps') |
| + gn_runtime_deps_path = self.ToAbsPath(build_dir, 'runtime_deps') |
| # Since GN hasn't run yet, the build directory may not even exist. |
| - self.MaybeMakeDirectory(self.ToAbsPath(path)) |
| + self.MaybeMakeDirectory(self.ToAbsPath(build_dir)) |
| self.WriteFile(gn_runtime_deps_path, '\n'.join(gn_labels) + '\n') |
| cmd.append('--runtime-deps-list-file=%s' % gn_runtime_deps_path) |
| @@ -401,10 +518,10 @@ class MetaBuildWrapper(object): |
| else: |
| runtime_deps_target = target |
| if self.platform == 'win32': |
| - deps_path = self.ToAbsPath(path, |
| + deps_path = self.ToAbsPath(build_dir, |
| runtime_deps_target + '.exe.runtime_deps') |
| else: |
| - deps_path = self.ToAbsPath(path, |
| + deps_path = self.ToAbsPath(build_dir, |
| runtime_deps_target + '.runtime_deps') |
| if not self.Exists(deps_path): |
| raise MBErr('did not generate %s' % deps_path) |
| @@ -414,31 +531,65 @@ class MetaBuildWrapper(object): |
| runtime_deps = self.ReadFile(deps_path).splitlines() |
| - isolate_path = self.ToAbsPath(path, target + '.isolate') |
| - self.WriteFile(isolate_path, |
| - pprint.pformat({ |
| - 'variables': { |
| - 'command': command, |
| - 'files': sorted(runtime_deps + extra_files), |
| - } |
| - }) + '\n') |
| - |
| - self.WriteJSON( |
| - { |
| - 'args': [ |
| - '--isolated', |
| - self.ToSrcRelPath('%s%s%s.isolated' % (path, self.sep, target)), |
| - '--isolate', |
| - self.ToSrcRelPath('%s%s%s.isolate' % (path, self.sep, target)), |
| - ], |
| - 'dir': self.chromium_src_dir, |
| - 'version': 1, |
| - }, |
| - isolate_path + 'd.gen.json', |
| - ) |
| + self.WriteIsolateFiles(build_dir, command, target, runtime_deps, |
| + extra_files) |
| + |
| + 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'))) |
| + |
| + build_dir = self.args.path[0] |
| + target = self.args.target[0] |
| + command, extra_files = self.GetIsolateCommand(target, vals, gn_isolate_map) |
| + |
| + label = gn_isolate_map[target]['label'] |
| + ret, out, _ = self.Call(['gn', 'desc', build_dir, label, 'runtime_deps']) |
| + if ret: |
| + return ret |
| + |
| + runtime_deps = out.splitlines() |
| + |
| + self.WriteIsolateFiles(build_dir, command, target, runtime_deps, |
| + extra_files) |
| + |
| + ret, _, _ = self.Run([ |
| + self.executable, |
| + self.PathJoin('tools', 'swarming_client', 'isolate.py'), |
| + 'check', |
| + '-i', |
| + self.ToSrcRelPath('%s/%s.isolate' % (build_dir, target)), |
| + '-s', |
| + self.ToSrcRelPath('%s/%s.isolated' % (build_dir, target))]) |
| return ret |
| + def WriteIsolateFiles(self, build_dir, command, target, runtime_deps, |
| + extra_files): |
| + isolate_path = self.ToAbsPath(build_dir, target + '.isolate') |
| + self.WriteFile(isolate_path, |
| + pprint.pformat({ |
| + 'variables': { |
| + 'command': command, |
| + 'files': sorted(runtime_deps + extra_files), |
| + } |
| + }) + '\n') |
| + |
| + self.WriteJSON( |
| + { |
| + 'args': [ |
| + '--isolated', |
| + self.ToSrcRelPath('%s/%s.isolated' % (build_dir, target)), |
| + '--isolate', |
| + self.ToSrcRelPath('%s/%s.isolate' % (build_dir, target)), |
| + ], |
| + 'dir': self.chromium_src_dir, |
| + 'version': 1, |
| + }, |
| + isolate_path + 'd.gen.json', |
| + ) |
| + |
| def GNCmd(self, subcommand, path, gn_args='', extra_args=None): |
| if self.platform == 'linux2': |
| subdir = 'linux64' |
| @@ -769,6 +920,14 @@ class MetaBuildWrapper(object): |
| # This function largely exists so it can be overridden for testing. |
| print(*args, **kwargs) |
| + def Build(self, target): |
| + build_dir = self.ToSrcRelPath(self.args.path[0]) |
| + ninja_cmd = ['ninja', '-C', build_dir] |
| + if self.args.jobs: |
| + ninja_cmd.extend(['-j', '%d' % self.args.jobs]) |
| + ninja_cmd.append(target) |
| + return self.Run(ninja_cmd) |
| + |
| def Run(self, cmd, env=None, force_verbose=True): |
| # This function largely exists so it can be overridden for testing. |
| if self.args.dryrun or self.args.verbose or force_verbose: |