Commit d811358b authored by dpranke's avatar dpranke Committed by Commit bot

Make swarming work w/ GN (kinda).

This patch implements the basic functionality needed to make swarming
and isolates work w/ GN. It relies on GN's existing functionality
for dumping the runtime dependencies needed for a target, and adds
a new command to MB that will take the runtime deps for a target,
compute the command line needed for the target, and then generate
the .isolate and .isolate.gen.json files needed for the
'isolate.py batcharchive' command.

We still need recipe-side work for swarming to actually work, however.

In addition, the way to manage the command lines for a target is a total
hack that will need to be cleaned up in subsequent patches.

R=maruel@chromium.org, brettw@chromium.org
BUG=480053

Review URL: https://codereview.chromium.org/1168513006

Cr-Commit-Position: refs/heads/master@{#333114}
parent b032a08d
...@@ -1316,6 +1316,13 @@ test("base_unittests") { ...@@ -1316,6 +1316,13 @@ test("base_unittests") {
"//third_party/icu", "//third_party/icu",
] ]
data = [
"test/data/",
# TODO(dpranke): Remove when icu declares this directly.
"$root_out_dir/icudtl.dat",
]
# Allow more direct string conversions on platforms with native utf8 # Allow more direct string conversions on platforms with native utf8
# strings # strings
if (is_mac || is_ios || is_chromeos) { if (is_mac || is_ios || is_chromeos) {
......
...@@ -16,6 +16,7 @@ import ast ...@@ -16,6 +16,7 @@ import ast
import json import json
import os import os
import pipes import pipes
import pprint
import shlex import shlex
import shutil import shutil
import sys import sys
...@@ -75,7 +76,7 @@ class MetaBuildWrapper(object): ...@@ -75,7 +76,7 @@ class MetaBuildWrapper(object):
help='analyze whether changes to a set of files ' help='analyze whether changes to a set of files '
'will cause a set of binaries to be rebuilt.') 'will cause a set of binaries to be rebuilt.')
AddCommonOptions(subp) AddCommonOptions(subp)
subp.add_argument('path', type=str, nargs=1, subp.add_argument('path', nargs=1,
help='path build was generated into.') help='path build was generated into.')
subp.add_argument('input_path', nargs=1, subp.add_argument('input_path', nargs=1,
help='path to a file containing the input arguments ' help='path to a file containing the input arguments '
...@@ -88,10 +89,23 @@ class MetaBuildWrapper(object): ...@@ -88,10 +89,23 @@ class MetaBuildWrapper(object):
subp = subps.add_parser('gen', subp = subps.add_parser('gen',
help='generate a new set of build files') help='generate a new set of build files')
AddCommonOptions(subp) AddCommonOptions(subp)
subp.add_argument('path', type=str, nargs=1, subp.add_argument('path', nargs=1,
help='path to generate build into') help='path to generate build into')
subp.set_defaults(func=self.CmdGen) 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', subp = subps.add_parser('lookup',
help='look up the command for a given config or ' help='look up the command for a given config or '
'builder') 'builder')
...@@ -129,6 +143,17 @@ class MetaBuildWrapper(object): ...@@ -129,6 +143,17 @@ class MetaBuildWrapper(object):
raise MBErr('Unknown meta-build type "%s"' % vals['type']) 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): def CmdLookup(self):
vals = self.GetConfig() vals = self.GetConfig()
if vals['type'] == 'gn': if vals['type'] == 'gn':
...@@ -367,6 +392,123 @@ class MetaBuildWrapper(object): ...@@ -367,6 +392,123 @@ class MetaBuildWrapper(object):
return ret 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): def ToSrcRelPath(self, path):
"""Returns a relative path from the top of the repo.""" """Returns a relative path from the top of the repo."""
# TODO: Support normal paths in addition to source-absolute paths. # TODO: Support normal paths in addition to source-absolute paths.
...@@ -401,7 +543,7 @@ class MetaBuildWrapper(object): ...@@ -401,7 +543,7 @@ class MetaBuildWrapper(object):
return cmd return cmd
def RunGNAnalyze(self, _vals): def RunGNAnalyze(self, _vals):
inp = self.GetAnalyzeInput() inp = self.ReadInputJSON(['files', 'targets'])
if self.args.verbose: if self.args.verbose:
self.Print() self.Print()
self.Print('analyze input:') self.Print('analyze input:')
...@@ -480,7 +622,7 @@ class MetaBuildWrapper(object): ...@@ -480,7 +622,7 @@ class MetaBuildWrapper(object):
return 0 return 0
def GetAnalyzeInput(self): def ReadInputJSON(self, required_keys):
path = self.args.input_path[0] path = self.args.input_path[0]
output_path = self.args.output_path[0] output_path = self.args.output_path[0]
if not self.Exists(path): if not self.Exists(path):
...@@ -491,12 +633,11 @@ class MetaBuildWrapper(object): ...@@ -491,12 +633,11 @@ class MetaBuildWrapper(object):
except Exception as e: except Exception as e:
self.WriteFailureAndRaise('Failed to read JSON input from "%s": %s' % self.WriteFailureAndRaise('Failed to read JSON input from "%s": %s' %
(path, e), output_path) (path, e), output_path)
if not 'files' in inp:
self.WriteFailureAndRaise('input file is missing a "files" key', for k in required_keys:
output_path) if not k in inp:
if not 'targets' in inp: self.WriteFailureAndRaise('input file is missing a "%s" key' % k,
self.WriteFailureAndRaise('input file is missing a "targets" key', output_path)
output_path)
return inp return inp
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment