Index: tools/mb/mb.py |
diff --git a/tools/mb/mb.py b/tools/mb/mb.py |
index 3ba59438984fbb8a0364102cf1957a3f563e8ec0..0c3d446913a6a6d972ebbd675afb70f81395248b 100755 |
--- a/tools/mb/mb.py |
+++ b/tools/mb/mb.py |
@@ -16,6 +16,7 @@ import ast |
import json |
import os |
import pipes |
+import pprint |
import shlex |
import shutil |
import sys |
@@ -75,7 +76,7 @@ class MetaBuildWrapper(object): |
help='analyze whether changes to a set of files ' |
'will cause a set of binaries to be rebuilt.') |
AddCommonOptions(subp) |
- subp.add_argument('path', type=str, nargs=1, |
+ subp.add_argument('path', nargs=1, |
help='path build was generated into.') |
subp.add_argument('input_path', nargs=1, |
help='path to a file containing the input arguments ' |
@@ -88,10 +89,23 @@ class MetaBuildWrapper(object): |
subp = subps.add_parser('gen', |
help='generate a new set of build files') |
AddCommonOptions(subp) |
- subp.add_argument('path', type=str, nargs=1, |
+ subp.add_argument('path', nargs=1, |
help='path to generate build into') |
subp.set_defaults(func=self.CmdGen) |
+ subp = subps.add_parser('isolate', |
+ help='build isolates') |
+ AddCommonOptions(subp) |
+ subp.add_argument('path', nargs=1, |
+ help='path build was generated into.') |
+ subp.add_argument('input_path', nargs=1, |
+ help='path to a file containing the input arguments ' |
+ 'as a JSON object.') |
+ subp.add_argument('output_path', nargs=1, |
+ help='path to a file containing the output arguments ' |
+ 'as a JSON object.') |
+ subp.set_defaults(func=self.CmdIsolate) |
+ |
subp = subps.add_parser('lookup', |
help='look up the command for a given config or ' |
'builder') |
@@ -129,6 +143,17 @@ class MetaBuildWrapper(object): |
raise MBErr('Unknown meta-build type "%s"' % vals['type']) |
+ def CmdIsolate(self): |
+ vals = self.GetConfig() |
+ if vals['type'] == 'gn': |
+ return self.RunGNIsolate(vals) |
+ if vals['type'] == 'gyp': |
+ # For GYP builds the .isolate files are checked in and the |
+ # .isolate.gen.json files are generated during the compile, |
+ # so there is no work to do here. |
+ return 0 |
+ raise MBErr('Unknown meta-build type "%s"' % vals['type']) |
+ |
def CmdLookup(self): |
vals = self.GetConfig() |
if vals['type'] == 'gn': |
@@ -367,6 +392,123 @@ class MetaBuildWrapper(object): |
return ret |
+ def RunGNIsolate(self, vals): |
+ build_path = self.args.path[0] |
+ inp = self.ReadInputJSON(['targets']) |
+ if self.args.verbose: |
+ self.Print() |
+ self.Print('isolate input:') |
+ self.PrintJSON(inp) |
+ self.Print() |
+ output_path = self.args.output_path[0] |
+ |
+ for target in inp['targets']: |
+ runtime_deps_path = self.ToAbsPath(build_path, target + '.runtime_deps') |
+ |
+ if not self.Exists(runtime_deps_path): |
+ self.WriteFailureAndRaise('"%s" does not exist' % runtime_deps_path, |
+ output_path) |
+ |
+ command, extra_files = self.GetIsolateCommand(target, vals) |
+ |
+ runtime_deps = self.ReadFile(runtime_deps_path).splitlines() |
+ |
+ |
+ isolate_path = self.ToAbsPath(build_path, target + '.isolate') |
+ self.WriteFile(isolate_path, |
+ pprint.pformat({ |
+ 'variables': { |
+ 'command': command, |
+ 'files': sorted(runtime_deps + extra_files), |
+ 'read_only': 1, |
+ } |
+ }) + '\n') |
+ |
+ self.WriteJSON( |
+ { |
+ 'args': [ |
+ '--isolated', |
+ self.ToSrcRelPath('%s/%s.isolated' % (build_path, target)), |
+ '--isolate', |
+ self.ToSrcRelPath('%s/%s.isolate' % (build_path, target)), |
+ ], |
+ 'dir': self.chromium_src_dir, |
+ 'version': 1, |
+ }, |
+ isolate_path + '.gen.json', |
+ ) |
+ |
+ return 0 |
+ |
+ def GetIsolateCommand(self, target, vals): |
+ output_path = self.args.output_path[0] |
+ |
+ extra_files = [] |
+ |
+ # TODO(dpranke): We should probably pull this from |
+ # the test list info in //testing/buildbot/*.json, |
+ # and assert that the test has can_use_on_swarming_builders: True, |
+ # but we hardcode it here for now. |
+ test_type = {}.get(target, 'gtest_test') |
+ |
+ # This needs to mirror the settings in //build/config/ui.gni: |
+ # use_x11 = is_linux && !use_ozone. |
+ # TODO(dpranke): Figure out how to keep this in sync better. |
+ use_x11 = (sys.platform == 'linux2' and |
+ not 'target_os="android"' in vals['gn_args'] and |
+ not 'use_ozone=true' in vals['gn_args']) |
+ |
+ asan = 'is_asan=true' in vals['gn_args'] |
+ msan = 'is_msan=true' in vals['gn_args'] |
+ tsan = 'is_tsan=true' in vals['gn_args'] |
+ |
+ executable_suffix = '.exe' if sys.platform == 'win32' else '' |
+ |
+ if test_type == 'gtest_test': |
+ extra_files.append('../../testing/test_env.py') |
+ |
+ if use_x11: |
+ # TODO(dpranke): Figure out some way to figure out which |
+ # test steps really need xvfb. |
+ extra_files.append('xdisplaycheck') |
+ extra_files.append('../../testing/xvfb.py') |
+ |
+ cmdline = [ |
+ '../../testing/xvfb.py', |
+ '.', |
+ './' + str(target), |
+ '--brave-new-test-launcher', |
+ '--test-launcher-bot-mode', |
+ '--asan=%d' % asan, |
+ '--msan=%d' % msan, |
+ '--tsan=%d' % tsan, |
+ ] |
+ else: |
+ cmdline = [ |
+ '../../testing/test_env.py', |
+ '.', |
+ './' + str(target) + executable_suffix, |
+ '--brave-new-test-launcher', |
+ '--test-launcher-bot-mode', |
+ '--asan=%d' % asan, |
+ '--msan=%d' % msan, |
+ '--tsan=%d' % tsan, |
+ ] |
+ else: |
+ # TODO(dpranke): Handle script_tests and other types of |
+ # swarmed tests. |
+ self.WriteFailureAndRaise('unknown test type "%s" for %s' % |
+ (test_type, target), |
+ output_path) |
+ |
+ |
+ return cmdline, extra_files |
+ |
+ def ToAbsPath(self, build_path, relpath): |
+ return os.path.join(self.chromium_src_dir, |
+ self.ToSrcRelPath(build_path), |
+ relpath) |
+ |
def ToSrcRelPath(self, path): |
"""Returns a relative path from the top of the repo.""" |
# TODO: Support normal paths in addition to source-absolute paths. |
@@ -401,7 +543,7 @@ class MetaBuildWrapper(object): |
return cmd |
def RunGNAnalyze(self, _vals): |
- inp = self.GetAnalyzeInput() |
+ inp = self.ReadInputJSON(['files', 'targets']) |
if self.args.verbose: |
self.Print() |
self.Print('analyze input:') |
@@ -480,7 +622,7 @@ class MetaBuildWrapper(object): |
return 0 |
- def GetAnalyzeInput(self): |
+ def ReadInputJSON(self, required_keys): |
path = self.args.input_path[0] |
output_path = self.args.output_path[0] |
if not self.Exists(path): |
@@ -491,12 +633,11 @@ class MetaBuildWrapper(object): |
except Exception as e: |
self.WriteFailureAndRaise('Failed to read JSON input from "%s": %s' % |
(path, e), output_path) |
- if not 'files' in inp: |
- self.WriteFailureAndRaise('input file is missing a "files" key', |
- output_path) |
- if not 'targets' in inp: |
- self.WriteFailureAndRaise('input file is missing a "targets" key', |
- output_path) |
+ |
+ for k in required_keys: |
+ if not k in inp: |
+ self.WriteFailureAndRaise('input file is missing a "%s" key' % k, |
+ output_path) |
return inp |