deps: patch V8 to 6.2.414.46

Refs: https://github.com/v8/v8/compare/6.2.414.44...6.2.414.46
PR-URL: https://github.com/nodejs/node/pull/17206
Reviewed-By: Michaël Zasso <targos@protonmail.com>
This commit is contained in:
Myles Borins 2017-11-22 11:09:53 +08:00
parent babcac386c
commit 04aab13ce0
No known key found for this signature in database
GPG Key ID: 933B01F40B5CA946
6 changed files with 500 additions and 416 deletions

View File

@ -11,7 +11,7 @@
#define V8_MAJOR_VERSION 6 #define V8_MAJOR_VERSION 6
#define V8_MINOR_VERSION 2 #define V8_MINOR_VERSION 2
#define V8_BUILD_NUMBER 414 #define V8_BUILD_NUMBER 414
#define V8_PATCH_LEVEL 44 #define V8_PATCH_LEVEL 46
// Use 1 for candidates and 0 otherwise. // Use 1 for candidates and 0 otherwise.
// (Boolean macro values are not supported by all preprocessors.) // (Boolean macro values are not supported by all preprocessors.)

View File

@ -1895,6 +1895,7 @@ void Debug::ProcessCompileEvent(v8::DebugEvent event, Handle<Script> script) {
HandleScope scope(isolate_); HandleScope scope(isolate_);
PostponeInterruptsScope postpone(isolate_); PostponeInterruptsScope postpone(isolate_);
DisableBreak no_recursive_break(this); DisableBreak no_recursive_break(this);
AllowJavascriptExecution allow_script(isolate_);
debug_delegate_->ScriptCompiled(ToApiHandle<debug::Script>(script), debug_delegate_->ScriptCompiled(ToApiHandle<debug::Script>(script),
live_edit_enabled(), live_edit_enabled(),
event != v8::AfterCompile); event != v8::AfterCompile);

View File

@ -411,7 +411,7 @@ config file change, however.
### Non-goals ### Non-goals
* MB is not intended to replace direct invocation of GN or GYP for * 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 to be set to user-defined paths for specific toolchains (e.g., where
Chrome OS needs to specify specific board types and compilers). Chrome OS needs to specify specific board types and compilers).

View File

@ -20,7 +20,7 @@ For more discussion of MB, see also [the design spec](design_spec.md).
### `mb analyze` ### `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): 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 reflect the stuff we might want to build *in addition to* the list
passed in `test_targets`. Targets in this list will be treated passed in `test_targets`. Targets in this list will be treated
specially, in the following way: if a given target is a "meta" specially, in the following way: if a given target is a "meta"
(GN: group, GYP: none) target like 'blink_tests' or (GN: group, GYP: none) target like 'blink_tests' or or even the
'chromium_builder_tests', or even the ninja-specific 'all' target, ninja-specific 'all' target, then only the *dependencies* of the
then only the *dependencies* of the target that are affected by target that are affected by the modified files will be rebuilt
the modified files will be rebuilt (not the target itself, which (not the target itself, which might also cause unaffected dependencies
might also cause unaffected dependencies to be rebuilt). An empty to be rebuilt). An empty list will be treated as if there are no additional
list will be treated as if there are no additional targets to build. targets to build.
Empty lists for both `test_targets` and `additional_compile_targets` Empty lists for both `test_targets` and `additional_compile_targets`
would cause no work to be done, so will result in an error. would cause no work to be done, so will result in an error.
* `targets`: a legacy field that resembled a union of `compile_targets` * `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 This is mostly useful as a presubmit check and for verifying changes to
the config file. 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 ## Isolates and Swarming
`mb gen` is also responsible for generating the `.isolate` and `mb gen` is also responsible for generating the `.isolate` and

580
deps/v8/tools/mb/mb.py vendored
View File

@ -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. 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 from __future__ import print_function
import argparse import argparse
@ -46,11 +50,14 @@ class MetaBuildWrapper(object):
self.chromium_src_dir = CHROMIUM_SRC_DIR self.chromium_src_dir = CHROMIUM_SRC_DIR
self.default_config = os.path.join(self.chromium_src_dir, 'infra', 'mb', self.default_config = os.path.join(self.chromium_src_dir, 'infra', 'mb',
'mb_config.pyl') '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.executable = sys.executable
self.platform = sys.platform self.platform = sys.platform
self.sep = os.sep self.sep = os.sep
self.args = argparse.Namespace() self.args = argparse.Namespace()
self.configs = {} self.configs = {}
self.luci_tryservers = {}
self.masters = {} self.masters = {}
self.mixins = {} self.mixins = {}
@ -62,7 +69,7 @@ class MetaBuildWrapper(object):
self.DumpInputFiles() self.DumpInputFiles()
return ret return ret
except KeyboardInterrupt: except KeyboardInterrupt:
self.Print('interrupted, exiting', stream=sys.stderr) self.Print('interrupted, exiting')
return 130 return 130
except Exception: except Exception:
self.DumpInputFiles() self.DumpInputFiles()
@ -79,13 +86,18 @@ class MetaBuildWrapper(object):
help='master name to look up config from') help='master name to look up config from')
subp.add_argument('-c', '--config', subp.add_argument('-c', '--config',
help='configuration to analyze') help='configuration to analyze')
subp.add_argument('--phase', type=int, subp.add_argument('--phase',
help=('build phase for a given build ' help='optional phase name (used when builders '
'(int in [1, 2, ...))')) 'do multiple compiles with different '
'arguments in a single build)')
subp.add_argument('-f', '--config-file', metavar='PATH', subp.add_argument('-f', '--config-file', metavar='PATH',
default=self.default_config, default=self.default_config,
help='path to config file ' 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', subp.add_argument('-g', '--goma-dir',
help='path to goma directory') help='path to goma directory')
subp.add_argument('--gyp-script', metavar='PATH', subp.add_argument('--gyp-script', metavar='PATH',
@ -121,6 +133,16 @@ class MetaBuildWrapper(object):
'as a JSON object.') 'as a JSON object.')
subp.set_defaults(func=self.CmdAnalyze) 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', 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)
@ -192,16 +214,14 @@ class MetaBuildWrapper(object):
help='validate the config file') help='validate the config file')
subp.add_argument('-f', '--config-file', metavar='PATH', subp.add_argument('-f', '--config-file', metavar='PATH',
default=self.default_config, default=self.default_config,
help='path to config file ' help='path to config file (default is %(default)s)')
'(default is //infra/mb/mb_config.pyl)')
subp.set_defaults(func=self.CmdValidate) subp.set_defaults(func=self.CmdValidate)
subp = subps.add_parser('audit', subp = subps.add_parser('audit',
help='Audit the config file to track progress') help='Audit the config file to track progress')
subp.add_argument('-f', '--config-file', metavar='PATH', subp.add_argument('-f', '--config-file', metavar='PATH',
default=self.default_config, default=self.default_config,
help='path to config file ' help='path to config file (default is %(default)s)')
'(default is //infra/mb/mb_config.pyl)')
subp.add_argument('-i', '--internal', action='store_true', subp.add_argument('-i', '--internal', action='store_true',
help='check internal masters also') help='check internal masters also')
subp.add_argument('-m', '--master', action='append', subp.add_argument('-m', '--master', action='append',
@ -217,6 +237,14 @@ class MetaBuildWrapper(object):
' do compiles') ' do compiles')
subp.set_defaults(func=self.CmdAudit) 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', subp = subps.add_parser('help',
help='Get help on a subcommand.') help='Get help on a subcommand.')
subp.add_argument(nargs='?', action='store', dest='subcommand', subp.add_argument(nargs='?', action='store', dest='subcommand',
@ -225,12 +253,16 @@ class MetaBuildWrapper(object):
self.args = parser.parse_args(argv) 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 DumpInputFiles(self):
def DumpContentsOfFilePassedTo(arg_name, path): def DumpContentsOfFilePassedTo(arg_name, path):
if path and self.Exists(path): if path and self.Exists(path):
self.Print("\n# To recreate the file passed to %s:" % arg_name) 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) contents = self.ReadFile(path)
self.Print(contents) self.Print(contents)
self.Print("EOF\n%\n") self.Print("EOF\n%\n")
@ -250,6 +282,34 @@ class MetaBuildWrapper(object):
else: else:
return self.RunGYPAnalyze(vals) 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): def CmdGen(self):
vals = self.Lookup() vals = self.Lookup()
self.ClobberIfNeeded(vals) self.ClobberIfNeeded(vals)
@ -270,7 +330,7 @@ class MetaBuildWrapper(object):
return 1 return 1
if vals['type'] == 'gn': if vals['type'] == 'gn':
return self.RunGNIsolate(vals) return self.RunGNIsolate()
else: else:
return self.Build('%s_run' % self.args.target[0]) return self.Build('%s_run' % self.args.target[0])
@ -300,7 +360,7 @@ class MetaBuildWrapper(object):
ret = self.Build(target) ret = self.Build(target)
if ret: if ret:
return ret return ret
ret = self.RunGNIsolate(vals) ret = self.RunGNIsolate()
if ret: if ret:
return ret return ret
else: else:
@ -322,6 +382,25 @@ class MetaBuildWrapper(object):
return ret 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): def CmdValidate(self, print_ok=True):
errs = [] errs = []
@ -332,8 +411,8 @@ class MetaBuildWrapper(object):
all_configs = {} all_configs = {}
for master in self.masters: for master in self.masters:
for config in self.masters[master].values(): for config in self.masters[master].values():
if isinstance(config, list): if isinstance(config, dict):
for c in config: for c in config.values():
all_configs[c] = master all_configs[c] = master
else: else:
all_configs[config] = master all_configs[config] = master
@ -461,8 +540,8 @@ class MetaBuildWrapper(object):
config = self.masters[master][builder] config = self.masters[master][builder]
if config == 'tbd': if config == 'tbd':
tbd.add(builder) tbd.add(builder)
elif isinstance(config, list): elif isinstance(config, dict):
vals = self.FlattenConfig(config[0]) vals = self.FlattenConfig(config.values()[0])
if vals['type'] == 'gyp': if vals['type'] == 'gyp':
gyp.add(builder) gyp.add(builder)
else: else:
@ -504,7 +583,7 @@ class MetaBuildWrapper(object):
def GetConfig(self): def GetConfig(self):
build_dir = self.args.path[0] build_dir = self.args.path[0]
vals = {} vals = self.DefaultVals()
if self.args.builder or self.args.master or self.args.config: if self.args.builder or self.args.master or self.args.config:
vals = self.Lookup() vals = self.Lookup()
if vals['type'] == 'gn': if vals['type'] == 'gn':
@ -528,14 +607,12 @@ class MetaBuildWrapper(object):
mb_type = self.ReadFile(mb_type_path).strip() mb_type = self.ReadFile(mb_type_path).strip()
if mb_type == 'gn': if mb_type == 'gn':
vals = self.GNValsFromDir(build_dir) vals['gn_args'] = self.GNArgsFromDir(build_dir)
else:
vals = {}
vals['type'] = mb_type vals['type'] = mb_type
return vals return vals
def GNValsFromDir(self, build_dir): def GNArgsFromDir(self, build_dir):
args_contents = "" args_contents = ""
gn_args_path = self.PathJoin(self.ToAbsPath(build_dir), 'args.gn') gn_args_path = self.PathJoin(self.ToAbsPath(build_dir), 'args.gn')
if self.Exists(gn_args_path): if self.Exists(gn_args_path):
@ -547,27 +624,18 @@ class MetaBuildWrapper(object):
val = ' '.join(fields[2:]) val = ' '.join(fields[2:])
gn_args.append('%s=%s' % (name, val)) gn_args.append('%s=%s' % (name, val))
return { return ' '.join(gn_args)
'gn_args': ' '.join(gn_args),
'type': 'gn',
}
def Lookup(self): def Lookup(self):
vals = self.ReadBotConfig() vals = self.ReadIOSBotConfig()
if not vals: if not vals:
self.ReadConfigFile() self.ReadConfigFile()
config = self.ConfigFromArgs() config = self.ConfigFromArgs()
if config.startswith('//'): if config.startswith('//'):
if not self.Exists(self.ToAbsPath(config)): if not self.Exists(self.ToAbsPath(config)):
raise MBErr('args file "%s" not found' % config) raise MBErr('args file "%s" not found' % config)
vals = { vals = self.DefaultVals()
'args_file': config, vals['args_file'] = config
'cros_passthrough': False,
'gn_args': '',
'gyp_crosscompile': False,
'gyp_defines': '',
'type': 'gn',
}
else: else:
if not config in self.configs: if not config in self.configs:
raise MBErr('Config "%s" not found in %s' % 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 # Do some basic sanity checking on the config so that we
# don't have to do this in every caller. # 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'), ( assert vals['type'] in ('gn', 'gyp'), (
'Unknown meta-build type "%s"' % vals['gn_args']) 'Unknown meta-build type "%s"' % vals['gn_args'])
return vals return vals
def ReadBotConfig(self): def ReadIOSBotConfig(self):
if not self.args.master or not self.args.builder: if not self.args.master or not self.args.builder:
return {} return {}
path = self.PathJoin(self.chromium_src_dir, 'ios', 'build', 'bots', path = self.PathJoin(self.chromium_src_dir, 'ios', 'build', 'bots',
@ -598,14 +667,11 @@ class MetaBuildWrapper(object):
gyp_defines = ' '.join(gyp_vals) gyp_defines = ' '.join(gyp_vals)
gn_args = ' '.join(contents.get('gn_args', [])) gn_args = ' '.join(contents.get('gn_args', []))
return { vals = self.DefaultVals()
'args_file': '', vals['gn_args'] = gn_args
'cros_passthrough': False, vals['gyp_defines'] = gyp_defines
'gn_args': gn_args, vals['type'] = contents.get('mb_type', 'gn')
'gyp_crosscompile': False, return vals
'gyp_defines': gyp_defines,
'type': contents.get('mb_type', ''),
}
def ReadConfigFile(self): def ReadConfigFile(self):
if not self.Exists(self.args.config_file): if not self.Exists(self.args.config_file):
@ -618,9 +684,20 @@ class MetaBuildWrapper(object):
(self.args.config_file, e)) (self.args.config_file, e))
self.configs = contents['configs'] self.configs = contents['configs']
self.luci_tryservers = contents.get('luci_tryservers', {})
self.masters = contents['masters'] self.masters = contents['masters']
self.mixins = contents['mixins'] 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): def ConfigFromArgs(self):
if self.args.config: if self.args.config:
if self.args.master or self.args.builder: 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)) (self.args.builder, self.args.master, self.args.config_file))
config = self.masters[self.args.master][self.args.builder] config = self.masters[self.args.master][self.args.builder]
if isinstance(config, list): if isinstance(config, dict):
if self.args.phase is None: if self.args.phase is None:
raise MBErr('Must specify a build --phase for %s on %s' % raise MBErr('Must specify a build --phase for %s on %s' %
(self.args.builder, self.args.master)) (self.args.builder, self.args.master))
phase = int(self.args.phase) phase = str(self.args.phase)
if phase < 1 or phase > len(config): if phase not in config:
raise MBErr('Phase %d out of bounds for %s on %s' % raise MBErr('Phase %s doesn\'t exist for %s on %s' %
(phase, self.args.builder, self.args.master)) (phase, self.args.builder, self.args.master))
return config[phase-1] return config[phase]
if self.args.phase is not None: if self.args.phase is not None:
raise MBErr('Must not specify a build --phase for %s on %s' % raise MBErr('Must not specify a build --phase for %s on %s' %
@ -659,19 +736,22 @@ class MetaBuildWrapper(object):
def FlattenConfig(self, config): def FlattenConfig(self, config):
mixins = self.configs[config] mixins = self.configs[config]
vals = { vals = self.DefaultVals()
'args_file': '',
'cros_passthrough': False,
'gn_args': [],
'gyp_defines': '',
'gyp_crosscompile': False,
'type': None,
}
visited = [] visited = []
self.FlattenMixins(mixins, vals, visited) self.FlattenMixins(mixins, vals, visited)
return vals return vals
def DefaultVals(self):
return {
'args_file': '',
'cros_passthrough': False,
'gn_args': '',
'gyp_defines': '',
'gyp_crosscompile': False,
'type': 'gn',
}
def FlattenMixins(self, mixins, vals, visited): def FlattenMixins(self, mixins, vals, visited):
for m in mixins: for m in mixins:
if m not in self.mixins: if m not in self.mixins:
@ -683,6 +763,11 @@ class MetaBuildWrapper(object):
if 'cros_passthrough' in mixin_vals: if 'cros_passthrough' in mixin_vals:
vals['cros_passthrough'] = mixin_vals['cros_passthrough'] 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 'gn_args' in mixin_vals:
if vals['gn_args']: if vals['gn_args']:
vals['gn_args'] += ' ' + mixin_vals['gn_args'] vals['gn_args'] += ' ' + mixin_vals['gn_args']
@ -732,11 +817,13 @@ class MetaBuildWrapper(object):
self.MaybeMakeDirectory(build_dir) self.MaybeMakeDirectory(build_dir)
self.WriteFile(mb_type_path, new_mb_type) 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] build_dir = self.args.path[0]
cmd = self.GNCmd('gen', build_dir, '--check') cmd = self.GNCmd('gen', build_dir, '--check')
gn_args = self.GNArgs(vals) 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. # Since GN hasn't run yet, the build directory may not even exist.
self.MaybeMakeDirectory(self.ToAbsPath(build_dir)) self.MaybeMakeDirectory(self.ToAbsPath(build_dir))
@ -748,7 +835,7 @@ class MetaBuildWrapper(object):
if getattr(self.args, 'swarming_targets_file', None): if getattr(self.args, 'swarming_targets_file', None):
# We need GN to generate the list of runtime dependencies for # We need GN to generate the list of runtime dependencies for
# the compile targets listed (one per line) in the file so # 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. # the compile targets to the matching GN labels.
path = self.args.swarming_targets_file path = self.args.swarming_targets_file
if not self.Exists(path): if not self.Exists(path):
@ -756,34 +843,17 @@ class MetaBuildWrapper(object):
output_path=None) output_path=None)
contents = self.ReadFile(path) contents = self.ReadFile(path)
swarming_targets = set(contents.splitlines()) 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: if err:
raise MBErr('Error: Failed to match swarming targets to %s:\n%s' % raise MBErr(err)
('//testing/buildbot/gn_isolate_map.pyl', err))
gn_runtime_deps_path = self.ToAbsPath(build_dir, 'runtime_deps') 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) cmd.append('--runtime-deps-list-file=%s' % gn_runtime_deps_path)
# Override msvs infra environment variables. ret, _, _ = self.Run(cmd)
# 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)
if ret: if ret:
# If `gn gen` failed, we should exit early rather than trying to # If `gn gen` failed, we should exit early rather than trying to
# generate isolates. Run() will have already logged any error output. # 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 # Android targets may be either android_apk or executable. The former
# will result in runtime_deps associated with the stamp file, while the # will result in runtime_deps associated with the stamp file, while the
# latter will result in runtime_deps associated with the executable. # latter will result in runtime_deps associated with the executable.
target_name = self.GNTargetName(target) label = isolate_map[target]['label']
label = gn_isolate_map[target_name]['label']
runtime_deps_targets = [ runtime_deps_targets = [
target_name + '.runtime_deps', target + '.runtime_deps',
'obj/%s.stamp.runtime_deps' % label.replace(':', '/')] 'obj/%s.stamp.runtime_deps' % label.replace(':', '/')]
elif gn_isolate_map[target]['type'] == 'gpu_browser_test': elif (isolate_map[target]['type'] == 'script' or
if self.platform == 'win32': isolate_map[target].get('label_type') == 'group'):
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'):
# For script targets, the build target is usually a group, # For script targets, the build target is usually a group,
# for which gn generates the runtime_deps next to the stamp file # for which gn generates the runtime_deps next to the stamp file
# for the label, which lives under the obj/ directory. # for the label, which lives under the obj/ directory, but it may
label = gn_isolate_map[target]['label'] # also be an executable.
label = isolate_map[target]['label']
runtime_deps_targets = [ runtime_deps_targets = [
'obj/%s.stamp.runtime_deps' % label.replace(':', '/')] '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': elif self.platform == 'win32':
runtime_deps_targets = [target + '.exe.runtime_deps'] runtime_deps_targets = [target + '.exe.runtime_deps']
else: else:
@ -827,26 +896,22 @@ class MetaBuildWrapper(object):
raise MBErr('did not generate any of %s' % raise MBErr('did not generate any of %s' %
', '.join(runtime_deps_targets)) ', '.join(runtime_deps_targets))
command, extra_files = self.GetIsolateCommand(target, vals,
gn_isolate_map)
runtime_deps = self.ReadFile(runtime_deps_path).splitlines() runtime_deps = self.ReadFile(runtime_deps_path).splitlines()
self.WriteIsolateFiles(build_dir, command, target, runtime_deps, self.WriteIsolateFiles(build_dir, target, runtime_deps)
extra_files)
return 0 return 0
def RunGNIsolate(self, vals): def RunGNIsolate(self):
gn_isolate_map = ast.literal_eval(self.ReadFile(self.PathJoin( target = self.args.target[0]
self.chromium_src_dir, 'testing', 'buildbot', 'gn_isolate_map.pyl'))) 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] 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') cmd = self.GNCmd('desc', build_dir, label, 'runtime_deps')
ret, out, _ = self.Call(cmd) ret, out, _ = self.Call(cmd)
if ret: if ret:
@ -856,8 +921,7 @@ class MetaBuildWrapper(object):
runtime_deps = out.splitlines() runtime_deps = out.splitlines()
self.WriteIsolateFiles(build_dir, command, target, runtime_deps, self.WriteIsolateFiles(build_dir, target, runtime_deps)
extra_files)
ret, _, _ = self.Run([ ret, _, _ = self.Run([
self.executable, self.executable,
@ -871,14 +935,12 @@ class MetaBuildWrapper(object):
return ret return ret
def WriteIsolateFiles(self, build_dir, command, target, runtime_deps, def WriteIsolateFiles(self, build_dir, target, runtime_deps):
extra_files):
isolate_path = self.ToAbsPath(build_dir, target + '.isolate') isolate_path = self.ToAbsPath(build_dir, target + '.isolate')
self.WriteFile(isolate_path, self.WriteFile(isolate_path,
pprint.pformat({ pprint.pformat({
'variables': { 'variables': {
'command': command, 'files': sorted(runtime_deps),
'files': sorted(runtime_deps + extra_files),
} }
}) + '\n') }) + '\n')
@ -896,6 +958,27 @@ class MetaBuildWrapper(object):
isolate_path + 'd.gen.json', 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): def GNCmd(self, subcommand, path, *args):
if self.platform == 'linux2': if self.platform == 'linux2':
subdir, exe = 'linux64', 'gn' subdir, exe = 'linux64', 'gn'
@ -905,9 +988,9 @@ class MetaBuildWrapper(object):
subdir, exe = 'win', 'gn.exe' subdir, exe = 'win', 'gn.exe'
gn_path = self.PathJoin(self.chromium_src_dir, 'buildtools', subdir, exe) gn_path = self.PathJoin(self.chromium_src_dir, 'buildtools', subdir, exe)
return [gn_path, subcommand, path] + list(args) return [gn_path, subcommand, path] + list(args)
def GNArgs(self, vals): def GNArgs(self, vals):
if vals['cros_passthrough']: if vals['cros_passthrough']:
if not 'GN_ARGS' in os.environ: if not 'GN_ARGS' in os.environ:
@ -972,109 +1055,6 @@ class MetaBuildWrapper(object):
return ret 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): def ToAbsPath(self, build_path, *comps):
return self.PathJoin(self.chromium_src_dir, return self.PathJoin(self.chromium_src_dir,
self.ToSrcRelPath(build_path), self.ToSrcRelPath(build_path),
@ -1167,12 +1147,18 @@ class MetaBuildWrapper(object):
return cmd, env return cmd, env
def RunGNAnalyze(self, vals): 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. # 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: if ret:
return 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', inp = self.ReadInputJSON(['files', 'test_targets',
'additional_compile_targets']) 'additional_compile_targets'])
if self.args.verbose: if self.args.verbose:
@ -1181,26 +1167,6 @@ class MetaBuildWrapper(object):
self.PrintJSON(inp) self.PrintJSON(inp)
self.Print() 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, # 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 # like a try job that gets scheduled before a patch lands but runs after
@ -1214,69 +1180,104 @@ class MetaBuildWrapper(object):
}, output_path) }, output_path)
return 0 return 0
ret = 0 gn_inp = {}
response_file = self.TempFile() gn_inp['files'] = ['//' + f for f in inp['files'] if not f.startswith('//')]
response_file.write('\n'.join(inp['files']) + '\n')
response_file.close() 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: try:
cmd = self.GNCmd('refs', self.WriteJSON(gn_inp, gn_input_path)
self.args.path[0], cmd = self.GNCmd('analyze', build_path, gn_input_path, gn_output_path)
'@%s' % response_file.name, ret, _, _ = self.Run(cmd, force_verbose=True)
'--all', if ret:
'--as=output') return ret
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', gn_outp_str = self.ReadFile(gn_output_path)
self.args.path[0], try:
'@%s' % response_file.name, gn_outp = json.loads(gn_outp_str)
'--all') except Exception as e:
ret, out, _ = self.Run(cmd, force_verbose=False) self.Print("Failed to parse the JSON string GN returned: %s\n%s"
if ret and not 'The input matches no targets' in out: % (repr(gn_outp_str), str(e)))
self.WriteFailureAndRaise('gn refs returned %d: %s' % (ret, out), raise
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)
if matching_targets: outp = {}
self.WriteJSON({ if 'status' in gn_outp:
'status': 'Found dependency', outp['status'] = gn_outp['status']
'compile_targets': sorted(matching_targets), if 'error' in gn_outp:
'test_targets': sorted(matching_targets & outp['error'] = gn_outp['error']
set(inp['test_targets'])), if 'invalid_targets' in gn_outp:
}, output_path) 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: else:
self.WriteJSON({ outp['compile_targets'] = gn_outp['compile_targets']
'status': 'No dependency',
'compile_targets': [], # crbug.com/736215: When GN returns targets back, for targets in
'test_targets': [], # the default toolchain, GN will have generated a phony ninja
}, output_path) # 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: if self.args.verbose:
outp = json.loads(self.ReadFile(output_path))
self.Print() self.Print()
self.Print('analyze output:') self.Print('analyze output:')
self.PrintJSON(outp) self.PrintJSON(outp)
self.Print() 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 return 0
def ReadInputJSON(self, required_keys): def ReadInputJSON(self, required_keys):
@ -1358,9 +1359,6 @@ class MetaBuildWrapper(object):
def PrintJSON(self, obj): def PrintJSON(self, obj):
self.Print(json.dumps(obj, indent=2, sort_keys=True)) self.Print(json.dumps(obj, indent=2, sort_keys=True))
def GNTargetName(self, target):
return target
def Build(self, target): def Build(self, target):
build_dir = self.ToSrcRelPath(self.args.path[0]) build_dir = self.ToSrcRelPath(self.args.path[0])
ninja_cmd = ['ninja', '-C', build_dir] ninja_cmd = ['ninja', '-C', build_dir]

View File

@ -23,12 +23,15 @@ class FakeMBW(mb.MetaBuildWrapper):
if win32: if win32:
self.chromium_src_dir = 'c:\\fake_src' self.chromium_src_dir = 'c:\\fake_src'
self.default_config = 'c:\\fake_src\\tools\\mb\\mb_config.pyl' 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.platform = 'win32'
self.executable = 'c:\\python\\python.exe' self.executable = 'c:\\python\\python.exe'
self.sep = '\\' self.sep = '\\'
else: else:
self.chromium_src_dir = '/fake_src' self.chromium_src_dir = '/fake_src'
self.default_config = '/fake_src/tools/mb/mb_config.pyl' 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.executable = '/usr/bin/python'
self.platform = 'linux2' self.platform = 'linux2'
self.sep = '/' self.sep = '/'
@ -115,10 +118,14 @@ TEST_CONFIG = """\
'fake_gn_debug_builder': 'gn_debug_goma', 'fake_gn_debug_builder': 'gn_debug_goma',
'fake_gyp_builder': 'gyp_debug', 'fake_gyp_builder': 'gyp_debug',
'fake_gn_args_bot': '//build/args/bots/fake_master/fake_gn_args_bot.gn', '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': { 'configs': {
'args_file_goma': ['args_file', 'goma'],
'args_file_twice': ['args_file', 'args_file'],
'gyp_rel_bot': ['gyp', 'rel', 'goma'], 'gyp_rel_bot': ['gyp', 'rel', 'goma'],
'gn_debug_goma': ['gn', 'debug', 'goma'], 'gn_debug_goma': ['gn', 'debug', 'goma'],
'gyp_debug': ['gyp', 'debug', 'fake_feature1'], 'gyp_debug': ['gyp', 'debug', 'fake_feature1'],
@ -141,6 +148,9 @@ TEST_CONFIG = """\
'gn_args': 'use_goma=true', 'gn_args': 'use_goma=true',
'gyp_defines': 'goma=1', 'gyp_defines': 'goma=1',
}, },
'args_file': {
'args_file': '//build/args/fake.gn',
},
'phase_1': { 'phase_1': {
'gn_args': 'phase=1', 'gn_args': 'phase=1',
'gyp_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 = """\ GYP_HACKS_CONFIG = """\
{ {
'masters': { '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): class UnitTest(unittest.TestCase):
def fake_mbw(self, files=None, win32=False): def fake_mbw(self, files=None, win32=False):
mbw = FakeMBW(win32=win32) mbw = FakeMBW(win32=win32)
mbw.files.setdefault(mbw.default_config, TEST_CONFIG) 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.files.setdefault(
mbw.ToAbsPath('//build/args/bots/fake_master/fake_gn_args_bot.gn'), mbw.ToAbsPath('//build/args/bots/fake_master/fake_gn_args_bot.gn'),
'is_debug = false\n') 'is_debug = false\n')
@ -268,78 +280,104 @@ class UnitTest(unittest.TestCase):
['/fake_src/out/Debug', '/fake_src/out/Debug']) ['/fake_src/out/Debug', '/fake_src/out/Debug'])
self.assertEqual(mbw.files['/fake_src/out/Debug/mb_type'], 'gyp') self.assertEqual(mbw.files['/fake_src/out/Debug/mb_type'], 'gyp')
def test_gn_analyze(self): def test_analyze(self):
files = {'/tmp/in.json': """{\ files = {'/tmp/in.json': '''{\
"files": ["foo/foo_unittest.cc"], "files": ["foo/foo_unittest.cc"],
"test_targets": ["foo_unittests", "bar_unittests"], "test_targets": ["foo_unittests"],
"additional_compile_targets": [] "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 = self.fake_mbw(files)
mbw.Call = lambda cmd, env=None, buffer_output=True: ( mbw.Call = lambda cmd, env=None, buffer_output=True: (0, '', '')
0, 'out/Default/foo_unittests\n', '')
self.check(['analyze', '-c', 'gn_debug_goma', '//out/Default', self.check(['analyze', '-c', 'gn_debug_goma', '//out/Default',
'/tmp/in.json', '/tmp/out.json'], mbw=mbw, ret=0) '/tmp/in.json', '/tmp/out.json'], mbw=mbw, ret=0)
out = json.loads(mbw.files['/tmp/out.json']) out = json.loads(mbw.files['/tmp/out.json'])
self.assertEqual(out, { self.assertEqual(out, {
'status': 'Found dependency', 'status': 'Found dependency',
'compile_targets': ['foo_unittests'], 'compile_targets': ['foo:foo_unittests'],
'test_targets': ['foo_unittests'] 'test_targets': ['foo_unittests']
}) })
def test_gn_analyze_fails(self): def test_analyze_optimizes_compile_for_all(self):
files = {'/tmp/in.json': """{\ files = {'/tmp/in.json': '''{\
"files": ["foo/foo_unittest.cc"], "files": ["foo/foo_unittest.cc"],
"test_targets": ["foo_unittests", "bar_unittests"], "test_targets": ["foo_unittests"],
"additional_compile_targets": []
}"""}
mbw = self.fake_mbw(files)
mbw.Call = lambda cmd, env=None, buffer_output=True: (1, '', '')
self.check(['analyze', '-c', 'gn_debug_goma', '//out/Default',
'/tmp/in.json', '/tmp/out.json'], mbw=mbw, ret=1)
def test_gn_analyze_all(self):
files = {'/tmp/in.json': """{\
"files": ["foo/foo_unittest.cc"],
"test_targets": ["bar_unittests"],
"additional_compile_targets": ["all"] "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 = self.fake_mbw(files)
mbw.Call = lambda cmd, env=None, buffer_output=True: ( mbw.Call = lambda cmd, env=None, buffer_output=True: (0, '', '')
0, 'out/Default/foo_unittests\n', '')
self.check(['analyze', '-c', 'gn_debug_goma', '//out/Default', self.check(['analyze', '-c', 'gn_debug_goma', '//out/Default',
'/tmp/in.json', '/tmp/out.json'], mbw=mbw, ret=0) '/tmp/in.json', '/tmp/out.json'], mbw=mbw, ret=0)
out = json.loads(mbw.files['/tmp/out.json']) 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): # check that 'foo_unittests' is not in the compile_targets
files = {'/tmp/in.json': """{\ self.assertEqual(['all'], out['compile_targets'])
def test_analyze_handles_other_toolchains(self):
files = {'/tmp/in.json': '''{\
"files": ["foo/foo_unittest.cc"], "files": ["foo/foo_unittest.cc"],
"test_targets": ["bar_unittests"], "test_targets": ["foo_unittests"],
"additional_compile_targets": [] "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 = self.fake_mbw(files)
mbw.cmds = [ mbw.Call = lambda cmd, env=None, buffer_output=True: (0, '', '')
(0, '', ''),
(1, 'The input matches no targets, configs, or files\n', ''),
(1, 'The input matches no targets, configs, or files\n', ''),
]
self.check(['analyze', '-c', 'gn_debug_goma', '//out/Default', self.check(['analyze', '-c', 'gn_debug_goma', '//out/Default',
'/tmp/in.json', '/tmp/out.json'], mbw=mbw, ret=0) '/tmp/in.json', '/tmp/out.json'], mbw=mbw, ret=0)
out = json.loads(mbw.files['/tmp/out.json']) out = json.loads(mbw.files['/tmp/out.json'])
self.assertEqual(out, {
'status': 'No dependency', # crbug.com/736215: If GN returns a label containing a toolchain,
'compile_targets': [], # MB (and Ninja) don't know how to handle it; to work around this,
'test_targets': [], # 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": ["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.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'])
# 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): def test_gn_gen(self):
mbw = self.fake_mbw() mbw = self.fake_mbw()
@ -373,12 +411,27 @@ class UnitTest(unittest.TestCase):
mbw.files['/fake_src/out/Debug/args.gn'], mbw.files['/fake_src/out/Debug/args.gn'],
'import("//build/args/bots/fake_master/fake_gn_args_bot.gn")\n') '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): def test_gn_gen_fails(self):
mbw = self.fake_mbw() mbw = self.fake_mbw()
mbw.Call = lambda cmd, env=None, buffer_output=True: (1, '', '') mbw.Call = lambda cmd, env=None, buffer_output=True: (1, '', '')
self.check(['gen', '-c', 'gn_debug_goma', '//out/Default'], mbw=mbw, ret=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): def test_gn_gen_swarming(self):
files = { files = {
'/tmp/swarming_targets': 'base_unittests\n', '/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', self.assertIn('/fake_src/out/Default/base_unittests.isolated.gen.json',
mbw.files) 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): def test_gn_isolate(self):
files = { files = {
'/fake_src/out/Default/toolchain.ninja': "", '/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. # Check that passing a --phase to a single-phase builder fails.
mbw = self.check(['lookup', '-m', 'fake_master', '-b', 'fake_gn_builder', mbw = self.check(['lookup', '-m', 'fake_master', '-b', 'fake_gn_builder',
'--phase', '1'], '--phase', 'phase_1'], ret=1)
ret=1)
self.assertIn('Must not specify a build --phase', mbw.out) 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', mbw = self.check(['lookup', '-m', 'fake_master', '-b', 'fake_multi_phase',
'--phase', '0'], ret=1) '--phase', 'wrong_phase'], ret=1)
self.assertIn('Phase 0 out of bounds', mbw.out) 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', 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) self.assertIn('phase = 1', mbw.out)
mbw = self.check(['lookup', '-m', 'fake_master', '-b', 'fake_multi_phase', 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) 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): def test_validate(self):
mbw = self.fake_mbw() mbw = self.fake_mbw()
self.check(['validate'], mbw=mbw, ret=0) self.check(['validate'], mbw=mbw, ret=0)
@ -544,28 +621,21 @@ class UnitTest(unittest.TestCase):
"LLVM_FORCE_HEAD_REVISION=1\n" "LLVM_FORCE_HEAD_REVISION=1\n"
"python build/gyp_chromium -G output_dir=_path_\n")) "python build/gyp_chromium -G output_dir=_path_\n"))
def test_buildbucket(self):
if __name__ == '__main__':
unittest.main()
def test_validate(self):
mbw = self.fake_mbw() mbw = self.fake_mbw()
self.check(['validate'], mbw=mbw, ret=0) mbw.files[mbw.default_config] = TRYSERVER_CONFIG
self.check(['gerrit-buildbucket-config'], mbw=mbw,
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):
mbw = self.fake_mbw()
mbw.files[mbw.default_config] = GYP_HACKS_CONFIG
self.check(['lookup', '-c', 'fake_config'], mbw=mbw,
ret=0, ret=0,
out=("GYP_DEFINES='foo=bar baz=1'\n" out=('# This file was generated using '
"GYP_LINK_CONCURRENCY=1\n" '"tools/mb/mb.py gerrit-buildbucket-config".\n'
"LLVM_FORCE_HEAD_REVISION=1\n" '[bucket "luci.luci_tryserver1"]\n'
"python build/gyp_chromium -G output_dir=_path_\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__': if __name__ == '__main__':