1449 строки
55 KiB
Diff
1449 строки
55 KiB
Diff
diff --git a/include/v8-version.h b/include/v8-version.h
|
|
index a812b241e4..2760fe3f29 100644
|
|
--- a/include/v8-version.h
|
|
+++ b/include/v8-version.h
|
|
@@ -11,7 +11,7 @@
|
|
#define V8_MAJOR_VERSION 6
|
|
#define V8_MINOR_VERSION 2
|
|
#define V8_BUILD_NUMBER 414
|
|
-#define V8_PATCH_LEVEL 42
|
|
+#define V8_PATCH_LEVEL 46
|
|
|
|
// Use 1 for candidates and 0 otherwise.
|
|
// (Boolean macro values are not supported by all preprocessors.)
|
|
diff --git a/src/debug/debug.cc b/src/debug/debug.cc
|
|
index 8d5adfbaea..e2beaed6ab 100644
|
|
--- a/src/debug/debug.cc
|
|
+++ b/src/debug/debug.cc
|
|
@@ -1895,6 +1895,7 @@ void Debug::ProcessCompileEvent(v8::DebugEvent event, Handle<Script> script) {
|
|
HandleScope scope(isolate_);
|
|
PostponeInterruptsScope postpone(isolate_);
|
|
DisableBreak no_recursive_break(this);
|
|
+ AllowJavascriptExecution allow_script(isolate_);
|
|
debug_delegate_->ScriptCompiled(ToApiHandle<debug::Script>(script),
|
|
live_edit_enabled(),
|
|
event != v8::AfterCompile);
|
|
diff --git a/tools/mb/docs/design_spec.md b/tools/mb/docs/design_spec.md
|
|
index 33fda806e8..fb202da74e 100644
|
|
--- a/tools/mb/docs/design_spec.md
|
|
+++ b/tools/mb/docs/design_spec.md
|
|
@@ -411,9 +411,9 @@ config file change, however.
|
|
### Non-goals
|
|
|
|
* MB is not intended to replace direct invocation of GN or GYP for
|
|
- complicated build scenarios (aka ChromeOS), where multiple flags need
|
|
+ complicated build scenarios (a.k.a. Chrome OS), where multiple flags need
|
|
to be set to user-defined paths for specific toolchains (e.g., where
|
|
- ChromeOS needs to specify specific board types and compilers).
|
|
+ Chrome OS needs to specify specific board types and compilers).
|
|
|
|
* MB is not intended at this time to be something developers use frequently,
|
|
or to add a lot of features to. We hope to be able to get rid of it once
|
|
diff --git a/tools/mb/docs/user_guide.md b/tools/mb/docs/user_guide.md
|
|
index b897fa4d67..a7d72c8839 100644
|
|
--- a/tools/mb/docs/user_guide.md
|
|
+++ b/tools/mb/docs/user_guide.md
|
|
@@ -20,7 +20,7 @@ For more discussion of MB, see also [the design spec](design_spec.md).
|
|
|
|
### `mb analyze`
|
|
|
|
-`mb analyze` is responsible for determining what targets are affected by
|
|
+`mb analyze` is reponsible for determining what targets are affected by
|
|
a list of files (e.g., the list of files in a patch on a trybot):
|
|
|
|
```
|
|
@@ -45,12 +45,12 @@ a single object with the following fields:
|
|
reflect the stuff we might want to build *in addition to* the list
|
|
passed in `test_targets`. Targets in this list will be treated
|
|
specially, in the following way: if a given target is a "meta"
|
|
- (GN: group, GYP: none) target like 'blink_tests' or
|
|
- 'chromium_builder_tests', or even the ninja-specific 'all' target,
|
|
- then only the *dependencies* of the target that are affected by
|
|
- the modified files will be rebuilt (not the target itself, which
|
|
- might also cause unaffected dependencies to be rebuilt). An empty
|
|
- list will be treated as if there are no additional targets to build.
|
|
+ (GN: group, GYP: none) target like 'blink_tests' or or even the
|
|
+ ninja-specific 'all' target, then only the *dependencies* of the
|
|
+ target that are affected by the modified files will be rebuilt
|
|
+ (not the target itself, which might also cause unaffected dependencies
|
|
+ to be rebuilt). An empty list will be treated as if there are no additional
|
|
+ targets to build.
|
|
Empty lists for both `test_targets` and `additional_compile_targets`
|
|
would cause no work to be done, so will result in an error.
|
|
* `targets`: a legacy field that resembled a union of `compile_targets`
|
|
@@ -167,6 +167,21 @@ The `-f/--config-file` and `-q/--quiet` flags work as documented for
|
|
This is mostly useful as a presubmit check and for verifying changes to
|
|
the config file.
|
|
|
|
+### `mb gerrit-buildbucket-config`
|
|
+
|
|
+Generates a gerrit buildbucket configuration file and prints it to
|
|
+stdout. This file contains the list of trybots shown in gerrit's UI.
|
|
+
|
|
+The master copy of the buildbucket.config file lives
|
|
+in a separate branch of the chromium repository. Run `mb
|
|
+gerrit-buildbucket-config > buildbucket.config.new && git fetch origin
|
|
+refs/meta/config:refs/remotes/origin/meta/config && git checkout
|
|
+-t -b meta_config origin/meta/config && mv buildbucket.config.new
|
|
+buildbucket.config` to update the file.
|
|
+
|
|
+Note that after committing, `git cl upload` will not work. Instead, use `git
|
|
+push origin HEAD:refs/for/refs/meta/config` to upload the CL for review.
|
|
+
|
|
## Isolates and Swarming
|
|
|
|
`mb gen` is also responsible for generating the `.isolate` and
|
|
diff --git a/tools/mb/mb.py b/tools/mb/mb.py
|
|
index b37c9dde7d..9a6600225b 100755
|
|
--- a/tools/mb/mb.py
|
|
+++ b/tools/mb/mb.py
|
|
@@ -10,6 +10,10 @@ MB is a wrapper script for GYP and GN that can be used to generate build files
|
|
for sets of canned configurations and analyze them.
|
|
"""
|
|
|
|
+# TODO(thomasanderson): Remove this comment. It is added to
|
|
+# workaround https://crbug.com/736215 for CL
|
|
+# https://codereview.chromium.org/2974603002/
|
|
+
|
|
from __future__ import print_function
|
|
|
|
import argparse
|
|
@@ -46,11 +50,14 @@ class MetaBuildWrapper(object):
|
|
self.chromium_src_dir = CHROMIUM_SRC_DIR
|
|
self.default_config = os.path.join(self.chromium_src_dir, 'infra', 'mb',
|
|
'mb_config.pyl')
|
|
+ self.default_isolate_map = os.path.join(self.chromium_src_dir, 'infra',
|
|
+ 'mb', 'gn_isolate_map.pyl')
|
|
self.executable = sys.executable
|
|
self.platform = sys.platform
|
|
self.sep = os.sep
|
|
self.args = argparse.Namespace()
|
|
self.configs = {}
|
|
+ self.luci_tryservers = {}
|
|
self.masters = {}
|
|
self.mixins = {}
|
|
|
|
@@ -62,7 +69,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()
|
|
@@ -79,13 +86,18 @@ class MetaBuildWrapper(object):
|
|
help='master name to look up config from')
|
|
subp.add_argument('-c', '--config',
|
|
help='configuration to analyze')
|
|
- subp.add_argument('--phase', type=int,
|
|
- help=('build phase for a given build '
|
|
- '(int in [1, 2, ...))'))
|
|
+ subp.add_argument('--phase',
|
|
+ help='optional phase name (used when builders '
|
|
+ 'do multiple compiles with different '
|
|
+ 'arguments in a single build)')
|
|
subp.add_argument('-f', '--config-file', metavar='PATH',
|
|
default=self.default_config,
|
|
help='path to config file '
|
|
- '(default is //tools/mb/mb_config.pyl)')
|
|
+ '(default is %(default)s)')
|
|
+ subp.add_argument('-i', '--isolate-map-file', metavar='PATH',
|
|
+ default=self.default_isolate_map,
|
|
+ help='path to isolate map file '
|
|
+ '(default is %(default)s)')
|
|
subp.add_argument('-g', '--goma-dir',
|
|
help='path to goma directory')
|
|
subp.add_argument('--gyp-script', metavar='PATH',
|
|
@@ -121,6 +133,16 @@ class MetaBuildWrapper(object):
|
|
'as a JSON object.')
|
|
subp.set_defaults(func=self.CmdAnalyze)
|
|
|
|
+ subp = subps.add_parser('export',
|
|
+ help='print out the expanded configuration for'
|
|
+ 'each builder as a JSON object')
|
|
+ subp.add_argument('-f', '--config-file', metavar='PATH',
|
|
+ default=self.default_config,
|
|
+ help='path to config file (default is %(default)s)')
|
|
+ subp.add_argument('-g', '--goma-dir',
|
|
+ help='path to goma directory')
|
|
+ subp.set_defaults(func=self.CmdExport)
|
|
+
|
|
subp = subps.add_parser('gen',
|
|
help='generate a new set of build files')
|
|
AddCommonOptions(subp)
|
|
@@ -192,16 +214,14 @@ class MetaBuildWrapper(object):
|
|
help='validate the config file')
|
|
subp.add_argument('-f', '--config-file', metavar='PATH',
|
|
default=self.default_config,
|
|
- help='path to config file '
|
|
- '(default is //infra/mb/mb_config.pyl)')
|
|
+ help='path to config file (default is %(default)s)')
|
|
subp.set_defaults(func=self.CmdValidate)
|
|
|
|
subp = subps.add_parser('audit',
|
|
help='Audit the config file to track progress')
|
|
subp.add_argument('-f', '--config-file', metavar='PATH',
|
|
default=self.default_config,
|
|
- help='path to config file '
|
|
- '(default is //infra/mb/mb_config.pyl)')
|
|
+ help='path to config file (default is %(default)s)')
|
|
subp.add_argument('-i', '--internal', action='store_true',
|
|
help='check internal masters also')
|
|
subp.add_argument('-m', '--master', action='append',
|
|
@@ -217,6 +237,14 @@ class MetaBuildWrapper(object):
|
|
' do compiles')
|
|
subp.set_defaults(func=self.CmdAudit)
|
|
|
|
+ subp = subps.add_parser('gerrit-buildbucket-config',
|
|
+ help='Print buildbucket.config for gerrit '
|
|
+ '(see MB user guide)')
|
|
+ subp.add_argument('-f', '--config-file', metavar='PATH',
|
|
+ default=self.default_config,
|
|
+ help='path to config file (default is %(default)s)')
|
|
+ subp.set_defaults(func=self.CmdBuildbucket)
|
|
+
|
|
subp = subps.add_parser('help',
|
|
help='Get help on a subcommand.')
|
|
subp.add_argument(nargs='?', action='store', dest='subcommand',
|
|
@@ -225,12 +253,16 @@ class MetaBuildWrapper(object):
|
|
|
|
self.args = parser.parse_args(argv)
|
|
|
|
+ # TODO(machenbach): This prepares passing swarming targets to isolate on the
|
|
+ # infra side.
|
|
+ self.args.swarming_targets_file = None
|
|
+
|
|
def DumpInputFiles(self):
|
|
|
|
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")
|
|
@@ -250,6 +282,34 @@ class MetaBuildWrapper(object):
|
|
else:
|
|
return self.RunGYPAnalyze(vals)
|
|
|
|
+ def CmdExport(self):
|
|
+ self.ReadConfigFile()
|
|
+ obj = {}
|
|
+ for master, builders in self.masters.items():
|
|
+ obj[master] = {}
|
|
+ for builder in builders:
|
|
+ config = self.masters[master][builder]
|
|
+ if not config:
|
|
+ continue
|
|
+
|
|
+ if isinstance(config, dict):
|
|
+ args = {k: self.FlattenConfig(v)['gn_args']
|
|
+ for k, v in config.items()}
|
|
+ elif config.startswith('//'):
|
|
+ args = config
|
|
+ else:
|
|
+ args = self.FlattenConfig(config)['gn_args']
|
|
+ if 'error' in args:
|
|
+ continue
|
|
+
|
|
+ obj[master][builder] = args
|
|
+
|
|
+ # Dump object and trim trailing whitespace.
|
|
+ s = '\n'.join(l.rstrip() for l in
|
|
+ json.dumps(obj, sort_keys=True, indent=2).splitlines())
|
|
+ self.Print(s)
|
|
+ return 0
|
|
+
|
|
def CmdGen(self):
|
|
vals = self.Lookup()
|
|
self.ClobberIfNeeded(vals)
|
|
@@ -270,7 +330,7 @@ class MetaBuildWrapper(object):
|
|
return 1
|
|
|
|
if vals['type'] == 'gn':
|
|
- return self.RunGNIsolate(vals)
|
|
+ return self.RunGNIsolate()
|
|
else:
|
|
return self.Build('%s_run' % self.args.target[0])
|
|
|
|
@@ -300,7 +360,7 @@ class MetaBuildWrapper(object):
|
|
ret = self.Build(target)
|
|
if ret:
|
|
return ret
|
|
- ret = self.RunGNIsolate(vals)
|
|
+ ret = self.RunGNIsolate()
|
|
if ret:
|
|
return ret
|
|
else:
|
|
@@ -322,6 +382,25 @@ class MetaBuildWrapper(object):
|
|
|
|
return ret
|
|
|
|
+ def CmdBuildbucket(self):
|
|
+ self.ReadConfigFile()
|
|
+
|
|
+ self.Print('# This file was generated using '
|
|
+ '"tools/mb/mb.py gerrit-buildbucket-config".')
|
|
+
|
|
+ for luci_tryserver in sorted(self.luci_tryservers):
|
|
+ self.Print('[bucket "luci.%s"]' % luci_tryserver)
|
|
+ for bot in sorted(self.luci_tryservers[luci_tryserver]):
|
|
+ self.Print('\tbuilder = %s' % bot)
|
|
+
|
|
+ for master in sorted(self.masters):
|
|
+ if master.startswith('tryserver.'):
|
|
+ self.Print('[bucket "master.%s"]' % master)
|
|
+ for bot in sorted(self.masters[master]):
|
|
+ self.Print('\tbuilder = %s' % bot)
|
|
+
|
|
+ return 0
|
|
+
|
|
def CmdValidate(self, print_ok=True):
|
|
errs = []
|
|
|
|
@@ -332,8 +411,8 @@ class MetaBuildWrapper(object):
|
|
all_configs = {}
|
|
for master in self.masters:
|
|
for config in self.masters[master].values():
|
|
- if isinstance(config, list):
|
|
- for c in config:
|
|
+ if isinstance(config, dict):
|
|
+ for c in config.values():
|
|
all_configs[c] = master
|
|
else:
|
|
all_configs[config] = master
|
|
@@ -461,8 +540,8 @@ class MetaBuildWrapper(object):
|
|
config = self.masters[master][builder]
|
|
if config == 'tbd':
|
|
tbd.add(builder)
|
|
- elif isinstance(config, list):
|
|
- vals = self.FlattenConfig(config[0])
|
|
+ elif isinstance(config, dict):
|
|
+ vals = self.FlattenConfig(config.values()[0])
|
|
if vals['type'] == 'gyp':
|
|
gyp.add(builder)
|
|
else:
|
|
@@ -504,7 +583,7 @@ class MetaBuildWrapper(object):
|
|
def GetConfig(self):
|
|
build_dir = self.args.path[0]
|
|
|
|
- vals = {}
|
|
+ vals = self.DefaultVals()
|
|
if self.args.builder or self.args.master or self.args.config:
|
|
vals = self.Lookup()
|
|
if vals['type'] == 'gn':
|
|
@@ -528,14 +607,12 @@ class MetaBuildWrapper(object):
|
|
mb_type = self.ReadFile(mb_type_path).strip()
|
|
|
|
if mb_type == 'gn':
|
|
- vals = self.GNValsFromDir(build_dir)
|
|
- else:
|
|
- vals = {}
|
|
+ vals['gn_args'] = self.GNArgsFromDir(build_dir)
|
|
vals['type'] = mb_type
|
|
|
|
return vals
|
|
|
|
- def GNValsFromDir(self, build_dir):
|
|
+ def GNArgsFromDir(self, build_dir):
|
|
args_contents = ""
|
|
gn_args_path = self.PathJoin(self.ToAbsPath(build_dir), 'args.gn')
|
|
if self.Exists(gn_args_path):
|
|
@@ -547,27 +624,18 @@ class MetaBuildWrapper(object):
|
|
val = ' '.join(fields[2:])
|
|
gn_args.append('%s=%s' % (name, val))
|
|
|
|
- return {
|
|
- 'gn_args': ' '.join(gn_args),
|
|
- 'type': 'gn',
|
|
- }
|
|
+ return ' '.join(gn_args)
|
|
|
|
def Lookup(self):
|
|
- vals = self.ReadBotConfig()
|
|
+ vals = self.ReadIOSBotConfig()
|
|
if not vals:
|
|
self.ReadConfigFile()
|
|
config = self.ConfigFromArgs()
|
|
if config.startswith('//'):
|
|
if not self.Exists(self.ToAbsPath(config)):
|
|
raise MBErr('args file "%s" not found' % config)
|
|
- vals = {
|
|
- 'args_file': config,
|
|
- 'cros_passthrough': False,
|
|
- 'gn_args': '',
|
|
- 'gyp_crosscompile': False,
|
|
- 'gyp_defines': '',
|
|
- 'type': 'gn',
|
|
- }
|
|
+ vals = self.DefaultVals()
|
|
+ vals['args_file'] = config
|
|
else:
|
|
if not config in self.configs:
|
|
raise MBErr('Config "%s" not found in %s' %
|
|
@@ -576,13 +644,14 @@ class MetaBuildWrapper(object):
|
|
|
|
# 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'
|
|
+ if 'type' not in vals:
|
|
+ vals['type'] = 'gn'
|
|
assert vals['type'] in ('gn', 'gyp'), (
|
|
'Unknown meta-build type "%s"' % vals['gn_args'])
|
|
|
|
return vals
|
|
|
|
- def ReadBotConfig(self):
|
|
+ def ReadIOSBotConfig(self):
|
|
if not self.args.master or not self.args.builder:
|
|
return {}
|
|
path = self.PathJoin(self.chromium_src_dir, 'ios', 'build', 'bots',
|
|
@@ -598,14 +667,11 @@ class MetaBuildWrapper(object):
|
|
gyp_defines = ' '.join(gyp_vals)
|
|
gn_args = ' '.join(contents.get('gn_args', []))
|
|
|
|
- return {
|
|
- 'args_file': '',
|
|
- 'cros_passthrough': False,
|
|
- 'gn_args': gn_args,
|
|
- 'gyp_crosscompile': False,
|
|
- 'gyp_defines': gyp_defines,
|
|
- 'type': contents.get('mb_type', ''),
|
|
- }
|
|
+ vals = self.DefaultVals()
|
|
+ vals['gn_args'] = gn_args
|
|
+ vals['gyp_defines'] = gyp_defines
|
|
+ vals['type'] = contents.get('mb_type', 'gn')
|
|
+ return vals
|
|
|
|
def ReadConfigFile(self):
|
|
if not self.Exists(self.args.config_file):
|
|
@@ -618,9 +684,20 @@ class MetaBuildWrapper(object):
|
|
(self.args.config_file, e))
|
|
|
|
self.configs = contents['configs']
|
|
+ self.luci_tryservers = contents.get('luci_tryservers', {})
|
|
self.masters = contents['masters']
|
|
self.mixins = contents['mixins']
|
|
|
|
+ def ReadIsolateMap(self):
|
|
+ if not self.Exists(self.args.isolate_map_file):
|
|
+ raise MBErr('isolate map file not found at %s' %
|
|
+ self.args.isolate_map_file)
|
|
+ try:
|
|
+ return ast.literal_eval(self.ReadFile(self.args.isolate_map_file))
|
|
+ except SyntaxError as e:
|
|
+ raise MBErr('Failed to parse isolate map file "%s": %s' %
|
|
+ (self.args.isolate_map_file, e))
|
|
+
|
|
def ConfigFromArgs(self):
|
|
if self.args.config:
|
|
if self.args.master or self.args.builder:
|
|
@@ -642,15 +719,15 @@ class MetaBuildWrapper(object):
|
|
(self.args.builder, self.args.master, self.args.config_file))
|
|
|
|
config = self.masters[self.args.master][self.args.builder]
|
|
- if isinstance(config, list):
|
|
+ if isinstance(config, dict):
|
|
if self.args.phase is None:
|
|
raise MBErr('Must specify a build --phase for %s on %s' %
|
|
(self.args.builder, self.args.master))
|
|
- phase = int(self.args.phase)
|
|
- if phase < 1 or phase > len(config):
|
|
- raise MBErr('Phase %d out of bounds for %s on %s' %
|
|
+ phase = str(self.args.phase)
|
|
+ if phase not in config:
|
|
+ raise MBErr('Phase %s doesn\'t exist for %s on %s' %
|
|
(phase, self.args.builder, self.args.master))
|
|
- return config[phase-1]
|
|
+ return config[phase]
|
|
|
|
if self.args.phase is not None:
|
|
raise MBErr('Must not specify a build --phase for %s on %s' %
|
|
@@ -659,19 +736,22 @@ class MetaBuildWrapper(object):
|
|
|
|
def FlattenConfig(self, config):
|
|
mixins = self.configs[config]
|
|
- vals = {
|
|
+ vals = self.DefaultVals()
|
|
+
|
|
+ visited = []
|
|
+ self.FlattenMixins(mixins, vals, visited)
|
|
+ return vals
|
|
+
|
|
+ def DefaultVals(self):
|
|
+ return {
|
|
'args_file': '',
|
|
'cros_passthrough': False,
|
|
- 'gn_args': [],
|
|
+ 'gn_args': '',
|
|
'gyp_defines': '',
|
|
'gyp_crosscompile': False,
|
|
- 'type': None,
|
|
+ 'type': 'gn',
|
|
}
|
|
|
|
- visited = []
|
|
- self.FlattenMixins(mixins, vals, visited)
|
|
- return vals
|
|
-
|
|
def FlattenMixins(self, mixins, vals, visited):
|
|
for m in mixins:
|
|
if m not in self.mixins:
|
|
@@ -683,6 +763,11 @@ class MetaBuildWrapper(object):
|
|
|
|
if 'cros_passthrough' in mixin_vals:
|
|
vals['cros_passthrough'] = mixin_vals['cros_passthrough']
|
|
+ if 'args_file' in mixin_vals:
|
|
+ if vals['args_file']:
|
|
+ raise MBErr('args_file specified multiple times in mixins '
|
|
+ 'for %s on %s' % (self.args.builder, self.args.master))
|
|
+ vals['args_file'] = mixin_vals['args_file']
|
|
if 'gn_args' in mixin_vals:
|
|
if vals['gn_args']:
|
|
vals['gn_args'] += ' ' + mixin_vals['gn_args']
|
|
@@ -732,11 +817,13 @@ class MetaBuildWrapper(object):
|
|
self.MaybeMakeDirectory(build_dir)
|
|
self.WriteFile(mb_type_path, new_mb_type)
|
|
|
|
- def RunGNGen(self, vals):
|
|
+ def RunGNGen(self, vals, compute_grit_inputs_for_analyze=False):
|
|
build_dir = self.args.path[0]
|
|
|
|
cmd = self.GNCmd('gen', build_dir, '--check')
|
|
gn_args = self.GNArgs(vals)
|
|
+ if compute_grit_inputs_for_analyze:
|
|
+ gn_args += ' compute_grit_inputs_for_analyze=true'
|
|
|
|
# Since GN hasn't run yet, the build directory may not even exist.
|
|
self.MaybeMakeDirectory(self.ToAbsPath(build_dir))
|
|
@@ -748,7 +835,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):
|
|
@@ -756,34 +843,17 @@ 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)
|
|
|
|
- # Override msvs infra environment variables.
|
|
- # TODO(machenbach): Remove after GYP_MSVS_VERSION is removed on infra side.
|
|
- env = {}
|
|
- env.update(os.environ)
|
|
- env['GYP_MSVS_VERSION'] = '2015'
|
|
-
|
|
- ret, _, _ = self.Run(cmd, env=env)
|
|
+ ret, _, _ = self.Run(cmd)
|
|
if ret:
|
|
# If `gn gen` failed, we should exit early rather than trying to
|
|
# generate isolates. Run() will have already logged any error output.
|
|
@@ -796,24 +866,23 @@ 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':
|
|
- 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']
|
|
+ # for the label, which lives under the obj/ directory, but it may
|
|
+ # also be an executable.
|
|
+ label = isolate_map[target]['label']
|
|
runtime_deps_targets = [
|
|
'obj/%s.stamp.runtime_deps' % label.replace(':', '/')]
|
|
+ if self.platform == 'win32':
|
|
+ runtime_deps_targets += [ target + '.exe.runtime_deps' ]
|
|
+ else:
|
|
+ runtime_deps_targets += [ target + '.runtime_deps' ]
|
|
elif self.platform == 'win32':
|
|
runtime_deps_targets = [target + '.exe.runtime_deps']
|
|
else:
|
|
@@ -827,26 +896,22 @@ 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)
|
|
-
|
|
runtime_deps = self.ReadFile(runtime_deps_path).splitlines()
|
|
|
|
- self.WriteIsolateFiles(build_dir, command, target, runtime_deps,
|
|
- extra_files)
|
|
+ self.WriteIsolateFiles(build_dir, target, runtime_deps)
|
|
|
|
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')))
|
|
+ def RunGNIsolate(self):
|
|
+ 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)
|
|
|
|
- label = gn_isolate_map[target_name]['label']
|
|
cmd = self.GNCmd('desc', build_dir, label, 'runtime_deps')
|
|
ret, out, _ = self.Call(cmd)
|
|
if ret:
|
|
@@ -856,8 +921,7 @@ class MetaBuildWrapper(object):
|
|
|
|
runtime_deps = out.splitlines()
|
|
|
|
- self.WriteIsolateFiles(build_dir, command, target, runtime_deps,
|
|
- extra_files)
|
|
+ self.WriteIsolateFiles(build_dir, target, runtime_deps)
|
|
|
|
ret, _, _ = self.Run([
|
|
self.executable,
|
|
@@ -871,14 +935,12 @@ class MetaBuildWrapper(object):
|
|
|
|
return ret
|
|
|
|
- def WriteIsolateFiles(self, build_dir, command, target, runtime_deps,
|
|
- extra_files):
|
|
+ def WriteIsolateFiles(self, build_dir, target, runtime_deps):
|
|
isolate_path = self.ToAbsPath(build_dir, target + '.isolate')
|
|
self.WriteFile(isolate_path,
|
|
pprint.pformat({
|
|
'variables': {
|
|
- 'command': command,
|
|
- 'files': sorted(runtime_deps + extra_files),
|
|
+ 'files': sorted(runtime_deps),
|
|
}
|
|
}) + '\n')
|
|
|
|
@@ -896,6 +958,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 target.startswith('//'):
|
|
+ labels.append(target)
|
|
+ else:
|
|
+ if target in isolate_map:
|
|
+ if isolate_map[target]['type'] == 'unknown':
|
|
+ err += ('test target "%s" type is unknown\n' % target)
|
|
+ else:
|
|
+ labels.append(isolate_map[target]['label'])
|
|
+ else:
|
|
+ err += ('target "%s" not found in '
|
|
+ '//infra/mb/gn_isolate_map.pyl\n' % target)
|
|
+
|
|
+ return err, labels
|
|
+
|
|
def GNCmd(self, subcommand, path, *args):
|
|
if self.platform == 'linux2':
|
|
subdir, exe = 'linux64', 'gn'
|
|
@@ -905,9 +988,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:
|
|
@@ -972,109 +1055,6 @@ class MetaBuildWrapper(object):
|
|
|
|
return ret
|
|
|
|
- def GetIsolateCommand(self, target, vals, gn_isolate_map):
|
|
- android = 'target_os="android"' in vals['gn_args']
|
|
-
|
|
- # This needs to mirror the settings in //build/config/ui.gni:
|
|
- # use_x11 = is_linux && !use_ozone.
|
|
- use_x11 = (self.platform == 'linux2' and
|
|
- not android 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']
|
|
-
|
|
- target_name = self.GNTargetName(target)
|
|
- test_type = gn_isolate_map[target_name]['type']
|
|
-
|
|
- executable = gn_isolate_map[target_name].get('executable', target_name)
|
|
- executable_suffix = '.exe' if self.platform == 'win32' else ''
|
|
-
|
|
- cmdline = []
|
|
- extra_files = []
|
|
-
|
|
- if android and test_type != "script":
|
|
- logdog_command = [
|
|
- '--logdog-bin-cmd', './../../bin/logdog_butler',
|
|
- '--project', 'chromium',
|
|
- '--service-account-json',
|
|
- '/creds/service_accounts/service-account-luci-logdog-publisher.json',
|
|
- '--prefix', 'android/swarming/logcats/${SWARMING_TASK_ID}',
|
|
- '--source', '${ISOLATED_OUTDIR}/logcats',
|
|
- '--name', 'unified_logcats',
|
|
- ]
|
|
- test_cmdline = [
|
|
- self.PathJoin('bin', 'run_%s' % target_name),
|
|
- '--logcat-output-file', '${ISOLATED_OUTDIR}/logcats',
|
|
- '--target-devices-file', '${SWARMING_BOT_FILE}',
|
|
- '-v'
|
|
- ]
|
|
- cmdline = (['./../../build/android/test_wrapper/logdog_wrapper.py']
|
|
- + logdog_command + test_cmdline)
|
|
- elif use_x11 and test_type == 'windowed_test_launcher':
|
|
- extra_files = [
|
|
- '../../testing/test_env.py',
|
|
- '../../testing/xvfb.py',
|
|
- ]
|
|
- cmdline = [
|
|
- '../../testing/xvfb.py',
|
|
- '.',
|
|
- './' + str(executable) + executable_suffix,
|
|
- '--brave-new-test-launcher',
|
|
- '--test-launcher-bot-mode',
|
|
- '--asan=%d' % asan,
|
|
- '--msan=%d' % msan,
|
|
- '--tsan=%d' % tsan,
|
|
- ]
|
|
- elif test_type in ('windowed_test_launcher', 'console_test_launcher'):
|
|
- extra_files = [
|
|
- '../../testing/test_env.py'
|
|
- ]
|
|
- cmdline = [
|
|
- '../../testing/test_env.py',
|
|
- './' + str(executable) + executable_suffix,
|
|
- '--brave-new-test-launcher',
|
|
- '--test-launcher-bot-mode',
|
|
- '--asan=%d' % asan,
|
|
- '--msan=%d' % msan,
|
|
- '--tsan=%d' % tsan,
|
|
- ]
|
|
- elif test_type == 'gpu_browser_test':
|
|
- extra_files = [
|
|
- '../../testing/test_env.py'
|
|
- ]
|
|
- gtest_filter = gn_isolate_map[target]['gtest_filter']
|
|
- cmdline = [
|
|
- '../../testing/test_env.py',
|
|
- './browser_tests' + executable_suffix,
|
|
- '--test-launcher-bot-mode',
|
|
- '--enable-gpu',
|
|
- '--test-launcher-jobs=1',
|
|
- '--gtest_filter=%s' % gtest_filter,
|
|
- ]
|
|
- elif test_type == 'script':
|
|
- extra_files = [
|
|
- '../../testing/test_env.py'
|
|
- ]
|
|
- cmdline = [
|
|
- '../../testing/test_env.py',
|
|
- '../../' + self.ToSrcRelPath(gn_isolate_map[target]['script'])
|
|
- ]
|
|
- elif test_type in ('raw'):
|
|
- extra_files = []
|
|
- cmdline = [
|
|
- './' + str(target) + executable_suffix,
|
|
- ]
|
|
-
|
|
- else:
|
|
- 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', [])
|
|
-
|
|
- return cmdline, extra_files
|
|
-
|
|
def ToAbsPath(self, build_path, *comps):
|
|
return self.PathJoin(self.chromium_src_dir,
|
|
self.ToSrcRelPath(build_path),
|
|
@@ -1167,12 +1147,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)
|
|
+ ret = self.RunGNGen(vals, compute_grit_inputs_for_analyze=True)
|
|
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:
|
|
@@ -1181,26 +1167,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
|
|
@@ -1214,68 +1180,103 @@ 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:
|
|
+ all_input_compile_targets = sorted(
|
|
+ set(inp['test_targets'] + inp['additional_compile_targets']))
|
|
+
|
|
+ # If we're building 'all', we can throw away the rest of the targets
|
|
+ # since they're redundant.
|
|
+ if 'all' in gn_outp['compile_targets']:
|
|
+ outp['compile_targets'] = ['all']
|
|
+ else:
|
|
+ outp['compile_targets'] = gn_outp['compile_targets']
|
|
+
|
|
+ # crbug.com/736215: When GN returns targets back, for targets in
|
|
+ # the default toolchain, GN will have generated a phony ninja
|
|
+ # target matching the label, and so we can safely (and easily)
|
|
+ # transform any GN label into the matching ninja target. For
|
|
+ # targets in other toolchains, though, GN doesn't generate the
|
|
+ # phony targets, and we don't know how to turn the labels into
|
|
+ # compile targets. In this case, we also conservatively give up
|
|
+ # and build everything. Probably the right thing to do here is
|
|
+ # to have GN return the compile targets directly.
|
|
+ if any("(" in target for target in outp['compile_targets']):
|
|
+ self.Print('WARNING: targets with non-default toolchains were '
|
|
+ 'found, building everything instead.')
|
|
+ outp['compile_targets'] = all_input_compile_targets
|
|
+ else:
|
|
+ outp['compile_targets'] = [
|
|
+ label.replace('//', '') for label in outp['compile_targets']]
|
|
+
|
|
+ # Windows has a maximum command line length of 8k; even Linux
|
|
+ # maxes out at 128k; if analyze returns a *really long* list of
|
|
+ # targets, we just give up and conservatively build everything instead.
|
|
+ # Probably the right thing here is for ninja to support response
|
|
+ # files as input on the command line
|
|
+ # (see https://github.com/ninja-build/ninja/issues/1355).
|
|
+ if len(' '.join(outp['compile_targets'])) > 7*1024:
|
|
+ self.Print('WARNING: Too many compile targets were affected.')
|
|
+ self.Print('WARNING: Building everything instead to avoid '
|
|
+ 'command-line length issues.')
|
|
+ outp['compile_targets'] = all_input_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
|
|
|
|
@@ -1358,9 +1359,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]
|
|
diff --git a/tools/mb/mb_unittest.py b/tools/mb/mb_unittest.py
|
|
index ac58c0284f..15763750da 100755
|
|
--- a/tools/mb/mb_unittest.py
|
|
+++ b/tools/mb/mb_unittest.py
|
|
@@ -23,12 +23,15 @@ class FakeMBW(mb.MetaBuildWrapper):
|
|
if win32:
|
|
self.chromium_src_dir = 'c:\\fake_src'
|
|
self.default_config = 'c:\\fake_src\\tools\\mb\\mb_config.pyl'
|
|
+ self.default_isolate_map = ('c:\\fake_src\\testing\\buildbot\\'
|
|
+ 'gn_isolate_map.pyl')
|
|
self.platform = 'win32'
|
|
self.executable = 'c:\\python\\python.exe'
|
|
self.sep = '\\'
|
|
else:
|
|
self.chromium_src_dir = '/fake_src'
|
|
self.default_config = '/fake_src/tools/mb/mb_config.pyl'
|
|
+ self.default_isolate_map = '/fake_src/testing/buildbot/gn_isolate_map.pyl'
|
|
self.executable = '/usr/bin/python'
|
|
self.platform = 'linux2'
|
|
self.sep = '/'
|
|
@@ -115,10 +118,14 @@ TEST_CONFIG = """\
|
|
'fake_gn_debug_builder': 'gn_debug_goma',
|
|
'fake_gyp_builder': 'gyp_debug',
|
|
'fake_gn_args_bot': '//build/args/bots/fake_master/fake_gn_args_bot.gn',
|
|
- 'fake_multi_phase': ['gn_phase_1', 'gn_phase_2'],
|
|
+ 'fake_multi_phase': { 'phase_1': 'gn_phase_1', 'phase_2': 'gn_phase_2'},
|
|
+ 'fake_args_file': 'args_file_goma',
|
|
+ 'fake_args_file_twice': 'args_file_twice',
|
|
},
|
|
},
|
|
'configs': {
|
|
+ 'args_file_goma': ['args_file', 'goma'],
|
|
+ 'args_file_twice': ['args_file', 'args_file'],
|
|
'gyp_rel_bot': ['gyp', 'rel', 'goma'],
|
|
'gn_debug_goma': ['gn', 'debug', 'goma'],
|
|
'gyp_debug': ['gyp', 'debug', 'fake_feature1'],
|
|
@@ -141,6 +148,9 @@ TEST_CONFIG = """\
|
|
'gn_args': 'use_goma=true',
|
|
'gyp_defines': 'goma=1',
|
|
},
|
|
+ 'args_file': {
|
|
+ 'args_file': '//build/args/fake.gn',
|
|
+ },
|
|
'phase_1': {
|
|
'gn_args': 'phase=1',
|
|
'gyp_args': 'phase=1',
|
|
@@ -159,35 +169,6 @@ TEST_CONFIG = """\
|
|
}
|
|
"""
|
|
|
|
-
|
|
-TEST_BAD_CONFIG = """\
|
|
-{
|
|
- 'configs': {
|
|
- 'gn_rel_bot_1': ['gn', 'rel', 'chrome_with_codecs'],
|
|
- 'gn_rel_bot_2': ['gn', 'rel', 'bad_nested_config'],
|
|
- },
|
|
- 'masters': {
|
|
- 'chromium': {
|
|
- 'a': 'gn_rel_bot_1',
|
|
- 'b': 'gn_rel_bot_2',
|
|
- },
|
|
- },
|
|
- 'mixins': {
|
|
- 'gn': {'type': 'gn'},
|
|
- 'chrome_with_codecs': {
|
|
- 'gn_args': 'proprietary_codecs=true',
|
|
- },
|
|
- 'bad_nested_config': {
|
|
- 'mixins': ['chrome_with_codecs'],
|
|
- },
|
|
- 'rel': {
|
|
- 'gn_args': 'is_debug=false',
|
|
- },
|
|
- },
|
|
-}
|
|
-"""
|
|
-
|
|
-
|
|
GYP_HACKS_CONFIG = """\
|
|
{
|
|
'masters': {
|
|
@@ -211,11 +192,42 @@ GYP_HACKS_CONFIG = """\
|
|
}
|
|
"""
|
|
|
|
+TRYSERVER_CONFIG = """\
|
|
+{
|
|
+ 'masters': {
|
|
+ 'not_a_tryserver': {
|
|
+ 'fake_builder': 'fake_config',
|
|
+ },
|
|
+ 'tryserver.chromium.linux': {
|
|
+ 'try_builder': 'fake_config',
|
|
+ },
|
|
+ 'tryserver.chromium.mac': {
|
|
+ 'try_builder2': 'fake_config',
|
|
+ },
|
|
+ },
|
|
+ 'luci_tryservers': {
|
|
+ 'luci_tryserver1': ['luci_builder1'],
|
|
+ 'luci_tryserver2': ['luci_builder2'],
|
|
+ },
|
|
+ 'configs': {},
|
|
+ 'mixins': {},
|
|
+}
|
|
+"""
|
|
+
|
|
|
|
class UnitTest(unittest.TestCase):
|
|
def fake_mbw(self, files=None, win32=False):
|
|
mbw = FakeMBW(win32=win32)
|
|
mbw.files.setdefault(mbw.default_config, TEST_CONFIG)
|
|
+ mbw.files.setdefault(
|
|
+ mbw.ToAbsPath('//testing/buildbot/gn_isolate_map.pyl'),
|
|
+ '''{
|
|
+ "foo_unittests": {
|
|
+ "label": "//foo:foo_unittests",
|
|
+ "type": "console_test_launcher",
|
|
+ "args": [],
|
|
+ },
|
|
+ }''')
|
|
mbw.files.setdefault(
|
|
mbw.ToAbsPath('//build/args/bots/fake_master/fake_gn_args_bot.gn'),
|
|
'is_debug = false\n')
|
|
@@ -268,78 +280,104 @@ class UnitTest(unittest.TestCase):
|
|
['/fake_src/out/Debug', '/fake_src/out/Debug'])
|
|
self.assertEqual(mbw.files['/fake_src/out/Debug/mb_type'], 'gyp')
|
|
|
|
- def test_gn_analyze(self):
|
|
- files = {'/tmp/in.json': """{\
|
|
+ def test_analyze(self):
|
|
+ files = {'/tmp/in.json': '''{\
|
|
"files": ["foo/foo_unittest.cc"],
|
|
- "test_targets": ["foo_unittests", "bar_unittests"],
|
|
- "additional_compile_targets": []
|
|
- }"""}
|
|
+ "test_targets": ["foo_unittests"],
|
|
+ "additional_compile_targets": ["all"]
|
|
+ }''',
|
|
+ '/tmp/out.json.gn': '''{\
|
|
+ "status": "Found dependency",
|
|
+ "compile_targets": ["//foo:foo_unittests"],
|
|
+ "test_targets": ["//foo:foo_unittests"]
|
|
+ }'''}
|
|
|
|
mbw = self.fake_mbw(files)
|
|
- mbw.Call = lambda cmd, env=None, buffer_output=True: (
|
|
- 0, 'out/Default/foo_unittests\n', '')
|
|
+ mbw.Call = lambda cmd, env=None, buffer_output=True: (0, '', '')
|
|
|
|
self.check(['analyze', '-c', 'gn_debug_goma', '//out/Default',
|
|
'/tmp/in.json', '/tmp/out.json'], mbw=mbw, ret=0)
|
|
out = json.loads(mbw.files['/tmp/out.json'])
|
|
self.assertEqual(out, {
|
|
'status': 'Found dependency',
|
|
- 'compile_targets': ['foo_unittests'],
|
|
+ 'compile_targets': ['foo:foo_unittests'],
|
|
'test_targets': ['foo_unittests']
|
|
})
|
|
|
|
- def test_gn_analyze_fails(self):
|
|
- files = {'/tmp/in.json': """{\
|
|
+ def test_analyze_optimizes_compile_for_all(self):
|
|
+ files = {'/tmp/in.json': '''{\
|
|
"files": ["foo/foo_unittest.cc"],
|
|
- "test_targets": ["foo_unittests", "bar_unittests"],
|
|
- "additional_compile_targets": []
|
|
- }"""}
|
|
+ "test_targets": ["foo_unittests"],
|
|
+ "additional_compile_targets": ["all"]
|
|
+ }''',
|
|
+ '/tmp/out.json.gn': '''{\
|
|
+ "status": "Found dependency",
|
|
+ "compile_targets": ["//foo:foo_unittests", "all"],
|
|
+ "test_targets": ["//foo:foo_unittests"]
|
|
+ }'''}
|
|
|
|
mbw = self.fake_mbw(files)
|
|
- mbw.Call = lambda cmd, env=None, buffer_output=True: (1, '', '')
|
|
+ mbw.Call = lambda cmd, env=None, buffer_output=True: (0, '', '')
|
|
|
|
self.check(['analyze', '-c', 'gn_debug_goma', '//out/Default',
|
|
- '/tmp/in.json', '/tmp/out.json'], mbw=mbw, ret=1)
|
|
+ '/tmp/in.json', '/tmp/out.json'], mbw=mbw, ret=0)
|
|
+ out = json.loads(mbw.files['/tmp/out.json'])
|
|
+
|
|
+ # check that 'foo_unittests' is not in the compile_targets
|
|
+ self.assertEqual(['all'], out['compile_targets'])
|
|
|
|
- def test_gn_analyze_all(self):
|
|
- files = {'/tmp/in.json': """{\
|
|
+ def test_analyze_handles_other_toolchains(self):
|
|
+ files = {'/tmp/in.json': '''{\
|
|
"files": ["foo/foo_unittest.cc"],
|
|
- "test_targets": ["bar_unittests"],
|
|
+ "test_targets": ["foo_unittests"],
|
|
"additional_compile_targets": ["all"]
|
|
- }"""}
|
|
+ }''',
|
|
+ '/tmp/out.json.gn': '''{\
|
|
+ "status": "Found dependency",
|
|
+ "compile_targets": ["//foo:foo_unittests",
|
|
+ "//foo:foo_unittests(bar)"],
|
|
+ "test_targets": ["//foo:foo_unittests"]
|
|
+ }'''}
|
|
+
|
|
mbw = self.fake_mbw(files)
|
|
- mbw.Call = lambda cmd, env=None, buffer_output=True: (
|
|
- 0, 'out/Default/foo_unittests\n', '')
|
|
+ mbw.Call = lambda cmd, env=None, buffer_output=True: (0, '', '')
|
|
+
|
|
self.check(['analyze', '-c', 'gn_debug_goma', '//out/Default',
|
|
'/tmp/in.json', '/tmp/out.json'], mbw=mbw, ret=0)
|
|
out = json.loads(mbw.files['/tmp/out.json'])
|
|
- self.assertEqual(out, {
|
|
- 'status': 'Found dependency (all)',
|
|
- 'compile_targets': ['all', 'bar_unittests'],
|
|
- 'test_targets': ['bar_unittests'],
|
|
- })
|
|
|
|
- def test_gn_analyze_missing_file(self):
|
|
- files = {'/tmp/in.json': """{\
|
|
+ # crbug.com/736215: If GN returns a label containing a toolchain,
|
|
+ # MB (and Ninja) don't know how to handle it; to work around this,
|
|
+ # we give up and just build everything we were asked to build. The
|
|
+ # output compile_targets should include all of the input test_targets and
|
|
+ # additional_compile_targets.
|
|
+ self.assertEqual(['all', 'foo_unittests'], out['compile_targets'])
|
|
+
|
|
+ def test_analyze_handles_way_too_many_results(self):
|
|
+ too_many_files = ', '.join(['"//foo:foo%d"' % i for i in xrange(4 * 1024)])
|
|
+ files = {'/tmp/in.json': '''{\
|
|
"files": ["foo/foo_unittest.cc"],
|
|
- "test_targets": ["bar_unittests"],
|
|
- "additional_compile_targets": []
|
|
- }"""}
|
|
+ "test_targets": ["foo_unittests"],
|
|
+ "additional_compile_targets": ["all"]
|
|
+ }''',
|
|
+ '/tmp/out.json.gn': '''{\
|
|
+ "status": "Found dependency",
|
|
+ "compile_targets": [''' + too_many_files + '''],
|
|
+ "test_targets": ["//foo:foo_unittests"]
|
|
+ }'''}
|
|
+
|
|
mbw = self.fake_mbw(files)
|
|
- mbw.cmds = [
|
|
- (0, '', ''),
|
|
- (1, 'The input matches no targets, configs, or files\n', ''),
|
|
- (1, 'The input matches no targets, configs, or files\n', ''),
|
|
- ]
|
|
+ mbw.Call = lambda cmd, env=None, buffer_output=True: (0, '', '')
|
|
|
|
self.check(['analyze', '-c', 'gn_debug_goma', '//out/Default',
|
|
'/tmp/in.json', '/tmp/out.json'], mbw=mbw, ret=0)
|
|
out = json.loads(mbw.files['/tmp/out.json'])
|
|
- self.assertEqual(out, {
|
|
- 'status': 'No dependency',
|
|
- 'compile_targets': [],
|
|
- 'test_targets': [],
|
|
- })
|
|
+
|
|
+ # If GN returns so many compile targets that we might have command-line
|
|
+ # issues, we should give up and just build everything we were asked to
|
|
+ # build. The output compile_targets should include all of the input
|
|
+ # test_targets and additional_compile_targets.
|
|
+ self.assertEqual(['all', 'foo_unittests'], out['compile_targets'])
|
|
|
|
def test_gn_gen(self):
|
|
mbw = self.fake_mbw()
|
|
@@ -373,12 +411,27 @@ class UnitTest(unittest.TestCase):
|
|
mbw.files['/fake_src/out/Debug/args.gn'],
|
|
'import("//build/args/bots/fake_master/fake_gn_args_bot.gn")\n')
|
|
|
|
+ def test_gn_gen_args_file_mixins(self):
|
|
+ mbw = self.fake_mbw()
|
|
+ self.check(['gen', '-m', 'fake_master', '-b', 'fake_args_file',
|
|
+ '//out/Debug'], mbw=mbw, ret=0)
|
|
+
|
|
+ self.assertEqual(
|
|
+ mbw.files['/fake_src/out/Debug/args.gn'],
|
|
+ ('import("//build/args/fake.gn")\n'
|
|
+ 'use_goma = true\n'))
|
|
+
|
|
+ mbw = self.fake_mbw()
|
|
+ self.check(['gen', '-m', 'fake_master', '-b', 'fake_args_file_twice',
|
|
+ '//out/Debug'], mbw=mbw, ret=1)
|
|
|
|
def test_gn_gen_fails(self):
|
|
mbw = self.fake_mbw()
|
|
mbw.Call = lambda cmd, env=None, buffer_output=True: (1, '', '')
|
|
self.check(['gen', '-c', 'gn_debug_goma', '//out/Default'], mbw=mbw, ret=1)
|
|
|
|
+ # TODO(machenbach): Comment back in after swarming file parameter is used.
|
|
+ """
|
|
def test_gn_gen_swarming(self):
|
|
files = {
|
|
'/tmp/swarming_targets': 'base_unittests\n',
|
|
@@ -403,6 +456,34 @@ class UnitTest(unittest.TestCase):
|
|
self.assertIn('/fake_src/out/Default/base_unittests.isolated.gen.json',
|
|
mbw.files)
|
|
|
|
+ def test_gn_gen_swarming_script(self):
|
|
+ files = {
|
|
+ '/tmp/swarming_targets': 'cc_perftests\n',
|
|
+ '/fake_src/testing/buildbot/gn_isolate_map.pyl': (
|
|
+ "{'cc_perftests': {"
|
|
+ " 'label': '//cc:cc_perftests',"
|
|
+ " 'type': 'script',"
|
|
+ " 'script': '/fake_src/out/Default/test_script.py',"
|
|
+ " 'args': [],"
|
|
+ "}}\n"
|
|
+ ),
|
|
+ 'c:\\fake_src\out\Default\cc_perftests.exe.runtime_deps': (
|
|
+ "cc_perftests\n"
|
|
+ ),
|
|
+ }
|
|
+ mbw = self.fake_mbw(files=files, win32=True)
|
|
+ self.check(['gen',
|
|
+ '-c', 'gn_debug_goma',
|
|
+ '--swarming-targets-file', '/tmp/swarming_targets',
|
|
+ '--isolate-map-file',
|
|
+ '/fake_src/testing/buildbot/gn_isolate_map.pyl',
|
|
+ '//out/Default'], mbw=mbw, ret=0)
|
|
+ self.assertIn('c:\\fake_src\\out\\Default\\cc_perftests.isolate',
|
|
+ mbw.files)
|
|
+ self.assertIn('c:\\fake_src\\out\\Default\\cc_perftests.isolated.gen.json',
|
|
+ mbw.files)
|
|
+ """ # pylint: disable=pointless-string-statement
|
|
+
|
|
def test_gn_isolate(self):
|
|
files = {
|
|
'/fake_src/out/Default/toolchain.ninja': "",
|
|
@@ -509,27 +590,23 @@ class UnitTest(unittest.TestCase):
|
|
|
|
# Check that passing a --phase to a single-phase builder fails.
|
|
mbw = self.check(['lookup', '-m', 'fake_master', '-b', 'fake_gn_builder',
|
|
- '--phase', '1'],
|
|
- ret=1)
|
|
+ '--phase', 'phase_1'], ret=1)
|
|
self.assertIn('Must not specify a build --phase', mbw.out)
|
|
|
|
- # Check different ranges; 0 and 3 are out of bounds, 1 and 2 should work.
|
|
+ # Check that passing a wrong phase key to a multi-phase builder fails.
|
|
mbw = self.check(['lookup', '-m', 'fake_master', '-b', 'fake_multi_phase',
|
|
- '--phase', '0'], ret=1)
|
|
- self.assertIn('Phase 0 out of bounds', mbw.out)
|
|
+ '--phase', 'wrong_phase'], ret=1)
|
|
+ self.assertIn('Phase wrong_phase doesn\'t exist', mbw.out)
|
|
|
|
+ # Check that passing a correct phase key to a multi-phase builder passes.
|
|
mbw = self.check(['lookup', '-m', 'fake_master', '-b', 'fake_multi_phase',
|
|
- '--phase', '1'], ret=0)
|
|
+ '--phase', 'phase_1'], ret=0)
|
|
self.assertIn('phase = 1', mbw.out)
|
|
|
|
mbw = self.check(['lookup', '-m', 'fake_master', '-b', 'fake_multi_phase',
|
|
- '--phase', '2'], ret=0)
|
|
+ '--phase', 'phase_2'], ret=0)
|
|
self.assertIn('phase = 2', mbw.out)
|
|
|
|
- mbw = self.check(['lookup', '-m', 'fake_master', '-b', 'fake_multi_phase',
|
|
- '--phase', '3'], ret=1)
|
|
- self.assertIn('Phase 3 out of bounds', mbw.out)
|
|
-
|
|
def test_validate(self):
|
|
mbw = self.fake_mbw()
|
|
self.check(['validate'], mbw=mbw, ret=0)
|
|
@@ -544,28 +621,21 @@ class UnitTest(unittest.TestCase):
|
|
"LLVM_FORCE_HEAD_REVISION=1\n"
|
|
"python build/gyp_chromium -G output_dir=_path_\n"))
|
|
|
|
-
|
|
-if __name__ == '__main__':
|
|
- unittest.main()
|
|
-
|
|
- def test_validate(self):
|
|
- mbw = self.fake_mbw()
|
|
- self.check(['validate'], mbw=mbw, ret=0)
|
|
-
|
|
- def test_bad_validate(self):
|
|
- mbw = self.fake_mbw()
|
|
- mbw.files[mbw.default_config] = TEST_BAD_CONFIG
|
|
- self.check(['validate'], mbw=mbw, ret=1)
|
|
-
|
|
- def test_gyp_env_hacks(self):
|
|
+ def test_buildbucket(self):
|
|
mbw = self.fake_mbw()
|
|
- mbw.files[mbw.default_config] = GYP_HACKS_CONFIG
|
|
- self.check(['lookup', '-c', 'fake_config'], mbw=mbw,
|
|
+ mbw.files[mbw.default_config] = TRYSERVER_CONFIG
|
|
+ self.check(['gerrit-buildbucket-config'], mbw=mbw,
|
|
ret=0,
|
|
- out=("GYP_DEFINES='foo=bar baz=1'\n"
|
|
- "GYP_LINK_CONCURRENCY=1\n"
|
|
- "LLVM_FORCE_HEAD_REVISION=1\n"
|
|
- "python build/gyp_chromium -G output_dir=_path_\n"))
|
|
+ out=('# This file was generated using '
|
|
+ '"tools/mb/mb.py gerrit-buildbucket-config".\n'
|
|
+ '[bucket "luci.luci_tryserver1"]\n'
|
|
+ '\tbuilder = luci_builder1\n'
|
|
+ '[bucket "luci.luci_tryserver2"]\n'
|
|
+ '\tbuilder = luci_builder2\n'
|
|
+ '[bucket "master.tryserver.chromium.linux"]\n'
|
|
+ '\tbuilder = try_builder\n'
|
|
+ '[bucket "master.tryserver.chromium.mac"]\n'
|
|
+ '\tbuilder = try_builder2\n'))
|
|
|
|
|
|
if __name__ == '__main__':
|