summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/tools_webrtc/mb
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/libwebrtc/tools_webrtc/mb
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/libwebrtc/tools_webrtc/mb')
-rw-r--r--third_party/libwebrtc/tools_webrtc/mb/OWNERS1
-rw-r--r--third_party/libwebrtc/tools_webrtc/mb/PRESUBMIT.py51
-rw-r--r--third_party/libwebrtc/tools_webrtc/mb/README.md22
-rw-r--r--third_party/libwebrtc/tools_webrtc/mb/docs/README.md4
-rw-r--r--third_party/libwebrtc/tools_webrtc/mb/docs/design_spec.md426
-rw-r--r--third_party/libwebrtc/tools_webrtc/mb/docs/user_guide.md298
-rwxr-xr-xthird_party/libwebrtc/tools_webrtc/mb/mb8
-rwxr-xr-xthird_party/libwebrtc/tools_webrtc/mb/mb.bat6
-rwxr-xr-xthird_party/libwebrtc/tools_webrtc/mb/mb.py156
-rw-r--r--third_party/libwebrtc/tools_webrtc/mb/mb_config.pyl501
-rwxr-xr-xthird_party/libwebrtc/tools_webrtc/mb/mb_unittest.py728
11 files changed, 2201 insertions, 0 deletions
diff --git a/third_party/libwebrtc/tools_webrtc/mb/OWNERS b/third_party/libwebrtc/tools_webrtc/mb/OWNERS
new file mode 100644
index 0000000000..48e6927746
--- /dev/null
+++ b/third_party/libwebrtc/tools_webrtc/mb/OWNERS
@@ -0,0 +1 @@
+mbonadei@webrtc.org
diff --git a/third_party/libwebrtc/tools_webrtc/mb/PRESUBMIT.py b/third_party/libwebrtc/tools_webrtc/mb/PRESUBMIT.py
new file mode 100644
index 0000000000..dd957d0482
--- /dev/null
+++ b/third_party/libwebrtc/tools_webrtc/mb/PRESUBMIT.py
@@ -0,0 +1,51 @@
+#!/usr/bin/env vpython3
+
+# Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+#
+# Use of this source code is governed by a BSD-style license
+# that can be found in the LICENSE file in the root of the source
+# tree. An additional intellectual property rights grant can be found
+# in the file PATENTS. All contributing project authors may
+# be found in the AUTHORS file in the root of the source tree.
+
+
+# Runs PRESUBMIT.py in py3 mode by git cl presubmit.
+USE_PYTHON3 = True
+
+
+def _CommonChecks(input_api, output_api):
+ results = []
+
+ # Run the MB unittests.
+ results.extend(
+ input_api.canned_checks.RunUnitTestsInDirectory(input_api,
+ output_api,
+ '.',
+ [r'^.+_unittest\.py$'],
+ skip_shebang_check=False,
+ run_on_python2=False))
+
+ # Validate the format of the mb_config.pyl file.
+ cmd = [input_api.python3_executable, 'mb.py', 'validate']
+ kwargs = {'cwd': input_api.PresubmitLocalPath()}
+ results.extend(input_api.RunTests([
+ input_api.Command(name='mb_validate',
+ cmd=cmd, kwargs=kwargs,
+ message=output_api.PresubmitError)]))
+
+ results.extend(
+ input_api.canned_checks.CheckLongLines(
+ input_api,
+ output_api,
+ maxlen=80,
+ source_file_filter=lambda x: 'mb_config.pyl' in x.LocalPath()))
+
+ return results
+
+
+def CheckChangeOnUpload(input_api, output_api):
+ return _CommonChecks(input_api, output_api)
+
+
+def CheckChangeOnCommit(input_api, output_api):
+ return _CommonChecks(input_api, output_api)
diff --git a/third_party/libwebrtc/tools_webrtc/mb/README.md b/third_party/libwebrtc/tools_webrtc/mb/README.md
new file mode 100644
index 0000000000..4e73a8e9fc
--- /dev/null
+++ b/third_party/libwebrtc/tools_webrtc/mb/README.md
@@ -0,0 +1,22 @@
+# MB - The Meta-Build wrapper
+
+MB is a simple wrapper intended to provide a uniform interface to either
+GYP or GN, such that users and bots can call one script and not need to
+worry about whether a given bot is meant to use GN or GYP.
+
+It supports two main functions:
+
+1. "gen" - the main `gyp_chromium` / `gn gen` invocation that generates the
+ Ninja files needed for the build.
+
+2. "analyze" - the step that takes a list of modified files and a list of
+ desired targets and reports which targets will need to be rebuilt.
+
+We also use MB as a forcing function to collect all of the different
+build configurations that we actually support for Chromium builds into
+one place, in `//tools/mb/mb_config.pyl`.
+
+For more information, see:
+
+* [The User Guide](docs/user_guide.md)
+* [The Design Spec](docs/design_spec.md)
diff --git a/third_party/libwebrtc/tools_webrtc/mb/docs/README.md b/third_party/libwebrtc/tools_webrtc/mb/docs/README.md
new file mode 100644
index 0000000000..f29007d9ed
--- /dev/null
+++ b/third_party/libwebrtc/tools_webrtc/mb/docs/README.md
@@ -0,0 +1,4 @@
+# The MB (Meta-Build wrapper) documentation
+
+* The [User Guide](user_guide.md)
+* The [Design Spec](design_spec.md)
diff --git a/third_party/libwebrtc/tools_webrtc/mb/docs/design_spec.md b/third_party/libwebrtc/tools_webrtc/mb/docs/design_spec.md
new file mode 100644
index 0000000000..0aaaf89f9b
--- /dev/null
+++ b/third_party/libwebrtc/tools_webrtc/mb/docs/design_spec.md
@@ -0,0 +1,426 @@
+# The MB (Meta-Build wrapper) design spec
+
+[TOC]
+
+## Intro
+
+MB is intended to address two major aspects of the GYP -> GN transition
+for Chromium:
+
+1. "bot toggling" - make it so that we can easily flip a given bot
+ back and forth between GN and GYP.
+
+2. "bot configuration" - provide a single source of truth for all of
+ the different configurations (os/arch/`gyp_define` combinations) of
+ Chromium that are supported.
+
+MB must handle at least the `gen` and `analyze` steps on the bots, i.e.,
+we need to wrap both the `gyp_chromium` invocation to generate the
+Ninja files, and the `analyze` step that takes a list of modified files
+and a list of targets to build and returns which targets are affected by
+the files.
+
+For more information on how to actually use MB, see
+[the user guide](user_guide.md).
+
+## Design
+
+MB is intended to be as simple as possible, and to defer as much work as
+possible to GN or GYP. It should live as a very simple Python wrapper
+that offers little in the way of surprises.
+
+### Command line
+
+It is structured as a single binary that supports a list of subcommands:
+
+* `mb gen -c linux_rel_bot //out/Release`
+* `mb analyze -m tryserver.chromium.linux -b linux_rel /tmp/input.json /tmp/output.json`
+
+### Configurations
+
+`mb` will first look for a bot config file in a set of different locations
+(initially just in //ios/build/bots). Bot config files are JSON files that
+contain keys for 'GYP_DEFINES' (a list of strings that will be joined together
+with spaces and passed to GYP, or a dict that will be similarly converted),
+'gn_args' (a list of strings that will be joined together), and an
+'mb_type' field that says whether to use GN or GYP. Bot config files
+require the full list of settings to be given explicitly.
+
+If no matching bot config file is found, `mb` looks in the
+`//tools/mb/mb_config.pyl` config file to determine whether to use GYP or GN
+for a particular build directory, and what set of flags (`GYP_DEFINES` or `gn
+args`) to use.
+
+A config can either be specified directly (useful for testing) or by specifying
+the builder group name and builder name (useful on the bots so that they do not
+need to specify a config directly and can be hidden from the details).
+
+See the [user guide](user_guide.md#mb_config.pyl) for details.
+
+### Handling the analyze step
+
+The interface to `mb analyze` is described in the
+[user\_guide](user_guide.md#mb_analyze).
+
+The way analyze works can be subtle and complicated (see below).
+
+Since the interface basically mirrors the way the "analyze" step on the bots
+invokes `gyp_chromium` today, when the config is found to be a gyp config,
+the arguments are passed straight through.
+
+It implements the equivalent functionality in GN by calling `gn refs
+[list of files] --type=executable --all --as=output` and filtering the
+output to match the list of targets.
+
+## Analyze
+
+The goal of the `analyze` step is to speed up the cycle time of the try servers
+by only building and running the tests affected by the files in a patch, rather
+than everything that might be out of date. Doing this ends up being tricky.
+
+We start with the following requirements and observations:
+
+* In an ideal (un-resource-constrained) world, we would build and test
+ everything that a patch affected on every patch. This does not
+ necessarily mean that we would build 'all' on every patch (see below).
+
+* In the real world, however, we do not have an infinite number of machines,
+ and try jobs are not infinitely fast, so we need to balance the desire
+ to get maximum test coverage against the desire to have reasonable cycle
+ times, given the number of machines we have.
+
+* Also, since we run most try jobs against tip-of-tree Chromium, by
+ the time one job completes on the bot, new patches have probably landed,
+ rendering the build out of date.
+
+* This means that the next try job may have to do a build that is out of
+ date due to a combination of files affected by a given patch, and files
+ affected for unrelated reasons. We want to rebuild and test only the
+ targets affected by the patch, so that we don't blame or punish the
+ patch author for unrelated changes.
+
+So:
+
+1. We need a way to indicate which changed files we care about and which
+ we don't (the affected files of a patch).
+
+2. We need to know which tests we might potentially want to run, and how
+ those are mapped onto build targets. For some kinds of tests (like
+ GTest-based tests), the mapping is 1:1 - if you want to run base_unittests,
+ you need to build base_unittests. For others (like the telemetry and
+ layout tests), you might need to build several executables in order to
+ run the tests, and that mapping might best be captured by a *meta*
+ target (a GN group or a GYP 'none' target like `webkit_tests`) that
+ depends on the right list of files. Because the GN and GYP files know
+ nothing about test steps, we have to have some way of mapping back
+ and forth between test steps and build targets. That mapping
+ is *not* currently available to MB (or GN or GYP), and so we have to
+ enough information to make it possible for the caller to do the mapping.
+
+3. We might also want to know when test targets are affected by data files
+ that aren't compiled (python scripts, or the layout tests themselves).
+ There's no good way to do this in GYP, but GN supports this.
+
+4. We also want to ensure that particular targets still compile even if they
+ are not actually tested; consider testing the installers themselves, or
+ targets that don't yet have good test coverage. We might want to use meta
+ targets for this purpose as well.
+
+5. However, for some meta targets, we don't necessarily want to rebuild the
+ meta target itself, perhaps just the dependencies of the meta target that
+ are affected by the patch. For example, if you have a meta target like
+ `blink_tests` that might depend on ten different test binaries. If a patch
+ only affects one of them (say `wtf_unittests`), you don't want to
+ build `blink_tests`, because that might actually also build the other nine
+ targets. In other words, some meta targets are *prunable*.
+
+6. As noted above, in the ideal case we actually have enough resources and
+ things are fast enough that we can afford to build everything affected by a
+ patch, but listing every possible target explicitly would be painful. The
+ GYP and GN Ninja generators provide an 'all' target that captures (nearly,
+ see [crbug.com/503241](crbug.com/503241)) everything, but unfortunately
+ neither GN nor GYP actually represents 'all' as a meta target in the build
+ graph, so we will need to write code to handle that specially.
+
+7. In some cases, we will not be able to correctly analyze the build graph to
+ determine the impact of a patch, and need to bail out (e.g,. if you change a
+ build file itself, it may not be easy to tell how that affects the graph).
+ In that case we should simply build and run everything.
+
+The interaction between 2) and 5) means that we need to treat meta targets
+two different ways, and so we need to know which targets should be
+pruned in the sense of 5) and which targets should be returned unchanged
+so that we can map them back to the appropriate tests.
+
+So, we need three things as input:
+
+* `files`: the list of files in the patch
+* `test_targets`: the list of ninja targets which, if affected by a patch,
+ should be reported back so that we can map them back to the appropriate
+ tests to run. Any meta targets in this list should *not* be pruned.
+* `additional_compile_targets`: the list of ninja targets we wish to compile
+ *in addition to* the list in `test_targets`. Any meta targets
+ present in this list should be pruned (we don't need to return the
+ meta targets because they aren't mapped back to tests, and we don't want
+ to build them because we might build too much).
+
+We can then return two lists as output:
+
+* `compile_targets`, which is a list of pruned targets to be
+ passed to Ninja to build. It is acceptable to replace a list of
+ pruned targets by a meta target if it turns out that all of the
+ dependendencies of the target are affected by the patch (i.e.,
+ all ten binaries that blink_tests depends on), but doing so is
+ not required.
+* `test_targets`, which is a list of unpruned targets to be mapped
+ back to determine which tests to run.
+
+There may be substantial overlap between the two lists, but there is
+no guarantee that one is a subset of the other and the two cannot be
+used interchangeably or merged together without losing information and
+causing the wrong thing to happen.
+
+The implementation is responsible for recognizing 'all' as a magic string
+and mapping it onto the list of all root nodes in the build graph.
+
+There may be files listed in the input that don't actually exist in the build
+graph: this could be either the result of an error (the file should be in the
+build graph, but isn't), or perfectly fine (the file doesn't affect the build
+graph at all). We can't tell these two apart, so we should ignore missing
+files.
+
+There may be targets listed in the input that don't exist in the build
+graph; unlike missing files, this can only indicate a configuration error,
+and so we should return which targets are missing so the caller can
+treat this as an error, if so desired.
+
+Any of the three inputs may be an empty list:
+
+* It normally doesn't make sense to call analyze at all if no files
+ were modified, but in rare cases we can hit a race where we try to
+ test a patch after it has already been committed, in which case
+ the list of modified files is empty. We should return 'no dependency'
+ in that case.
+
+* Passing an empty list for one or the other of test_targets and
+ additional_compile_targets is perfectly sensible: in the former case,
+ it can indicate that you don't want to run any tests, and in the latter,
+ it can indicate that you don't want to do build anything else in
+ addition to the test targets.
+
+* It doesn't make sense to call analyze if you don't want to compile
+ anything at all, so passing [] for both test_targets and
+ additional_compile_targets should probably return an error.
+
+In the output case, an empty list indicates that there was nothing to
+build, or that there were no affected test targets as appropriate.
+
+Note that passing no arguments to Ninja is equivalent to passing
+`all` to Ninja (at least given how GN and GYP work); however, we
+don't want to take advantage of this in most cases because we don't
+actually want to build every out of date target, only the targets
+potentially affected by the files. One could try to indicate
+to analyze that we wanted to use no arguments instead of an empty
+list, but using the existing fields for this seems fragile and/or
+confusing, and adding a new field for this seems unwarranted at this time.
+
+There is an "error" field in case something goes wrong (like the
+empty file list case, above, or an internal error in MB/GYP/GN). The
+analyze code should also return an error code to the shell if appropriate
+to indicate that the command failed.
+
+In the case where build files themselves are modified and analyze may
+not be able to determine a correct answer (point 7 above, where we return
+"Found dependency (all)"), we should also return the `test_targets` unmodified
+and return the union of `test_targets` and `additional_compile_targets` for
+`compile_targets`, to avoid confusion.
+
+### Examples
+
+Continuing the example given above, suppose we have the following build
+graph:
+
+* `blink_tests` is a meta target that depends on `webkit_unit_tests`,
+ `wtf_unittests`, and `webkit_tests` and represents all of the targets
+ needed to fully test Blink. Each of those is a separate test step.
+* `webkit_tests` is also a meta target; it depends on `content_shell`
+ and `image_diff`.
+* `base_unittests` is a separate test binary.
+* `wtf_unittests` depends on `Assertions.cpp` and `AssertionsTest.cpp`.
+* `webkit_unit_tests` depends on `WebNode.cpp` and `WebNodeTest.cpp`.
+* `content_shell` depends on `WebNode.cpp` and `Assertions.cpp`.
+* `base_unittests` depends on `logging.cc` and `logging_unittest.cc`.
+
+#### Example 1
+
+We wish to run 'wtf_unittests' and 'webkit_tests' on a bot, but not
+compile any additional targets.
+
+If a patch touches WebNode.cpp, then analyze gets as input:
+
+ {
+ "files": ["WebNode.cpp"],
+ "test_targets": ["wtf_unittests", "webkit_tests"],
+ "additional_compile_targets": []
+ }
+
+and should return as output:
+
+ {
+ "status": "Found dependency",
+ "compile_targets": ["webkit_unit_tests"],
+ "test_targets": ["webkit_tests"]
+ }
+
+Note how `webkit_tests` was pruned in compile_targets but not in test_targets.
+
+#### Example 2
+
+Using the same patch as Example 1, assume we wish to run only `wtf_unittests`,
+but additionally build everything needed to test Blink (`blink_tests`):
+
+We pass as input:
+
+ {
+ "files": ["WebNode.cpp"],
+ "test_targets": ["wtf_unittests"],
+ "additional_compile_targets": ["blink_tests"]
+ }
+
+And should get as output:
+
+ {
+ "status": "Found dependency",
+ "compile_targets": ["webkit_unit_tests"],
+ "test_targets": []
+ }
+
+Here `blink_tests` was pruned in the output compile_targets, and
+test_targets was empty, since blink_tests was not listed in the input
+test_targets.
+
+#### Example 3
+
+Build everything, but do not run any tests.
+
+Input:
+
+ {
+ "files": ["WebNode.cpp"],
+ "test_targets": [],
+ "additional_compile_targets": ["all"]
+ }
+
+Output:
+
+ {
+ "status": "Found dependency",
+ "compile_targets": ["webkit_unit_tests", "content_shell"],
+ "test_targets": []
+ }
+
+#### Example 4
+
+Same as Example 2, but a build file was modified instead of a source file.
+
+Input:
+
+ {
+ "files": ["BUILD.gn"],
+ "test_targets": ["wtf_unittests"],
+ "additional_compile_targets": ["blink_tests"]
+ }
+
+Output:
+
+ {
+ "status": "Found dependency (all)",
+ "compile_targets": ["webkit_unit_tests", "wtf_unittests"],
+ "test_targets": ["wtf_unittests"]
+ }
+
+test_targets was returned unchanged, compile_targets was pruned.
+
+## Random Requirements and Rationale
+
+This section is collection of semi-organized notes on why MB is the way
+it is ...
+
+### in-tree or out-of-tree
+
+The first issue is whether or not this should exist as a script in
+Chromium at all; an alternative would be to simply change the bot
+configurations to know whether to use GYP or GN, and which flags to
+pass.
+
+That would certainly work, but experience over the past two years
+suggests a few things:
+
+ * we should push as much logic as we can into the source repositories
+ so that they can be versioned and changed atomically with changes to
+ the product code; having to coordinate changes between src/ and
+ build/ is at best annoying and can lead to weird errors.
+ * the infra team would really like to move to providing
+ product-independent services (i.e., not have to do one thing for
+ Chromium, another for NaCl, a third for V8, etc.).
+ * we found that during the SVN->GIT migration the ability to flip bot
+ configurations between the two via changes to a file in chromium
+ was very useful.
+
+All of this suggests that the interface between bots and Chromium should
+be a simple one, hiding as much of the chromium logic as possible.
+
+### Why not have MB be smarter about de-duping flags?
+
+This just adds complexity to the MB implementation, and duplicates logic
+that GYP and GN already have to support anyway; in particular, it might
+require MB to know how to parse GYP and GN values. The belief is that
+if MB does *not* do this, it will lead to fewer surprises.
+
+It will not be hard to change this if need be.
+
+### Integration w/ gclient runhooks
+
+On the bots, we will disable `gyp_chromium` as part of runhooks (using
+`GYP_CHROMIUM_NO_ACTION=1`), so that mb shows up as a separate step.
+
+At the moment, we expect most developers to either continue to use
+`gyp_chromium` in runhooks or to disable at as above if they have no
+use for GYP at all. We may revisit how this works once we encourage more
+people to use GN full-time (i.e., we might take `gyp_chromium` out of
+runhooks altogether).
+
+### Config per flag set or config per (os/arch/flag set)?
+
+Currently, mb_config.pyl does not specify the host_os, target_os, host_cpu, or
+target_cpu values for every config that Chromium runs on, it only specifies
+them for when the values need to be explicitly set on the command line.
+
+Instead, we have one config per unique combination of flags only.
+
+In other words, rather than having `linux_rel_bot`, `win_rel_bot`, and
+`mac_rel_bot`, we just have `rel_bot`.
+
+This design allows us to determine easily all of the different sets
+of flags that we need to support, but *not* which flags are used on which
+host/target combinations.
+
+It may be that we should really track the latter. Doing so is just a
+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
+ to be set to user-defined paths for specific toolchains (e.g., where
+ ChromeOS 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
+ the GYP->GN migration is done, and so we should not add things for
+ developers that can't easily be added to GN itself.
+
+* MB is not intended to replace the
+ [CR tool](https://code.google.com/p/chromium/wiki/CRUserManual). Not
+ only is it only intended to replace the gyp\_chromium part of `'gclient
+ runhooks'`, it is not really meant as a developer-facing tool.
diff --git a/third_party/libwebrtc/tools_webrtc/mb/docs/user_guide.md b/third_party/libwebrtc/tools_webrtc/mb/docs/user_guide.md
new file mode 100644
index 0000000000..8c66cd328c
--- /dev/null
+++ b/third_party/libwebrtc/tools_webrtc/mb/docs/user_guide.md
@@ -0,0 +1,298 @@
+# The MB (Meta-Build wrapper) user guide
+
+[TOC]
+
+## Introduction
+
+`mb` is a simple python wrapper around the GYP and GN meta-build tools to
+be used as part of the GYP->GN migration.
+
+It is intended to be used by bots to make it easier to manage the configuration
+each bot builds (i.e., the configurations can be changed from chromium
+commits), and to consolidate the list of all of the various configurations
+that Chromium is built in.
+
+Ideally this tool will no longer be needed after the migration is complete.
+
+For more discussion of MB, see also [the design spec](design_spec.md).
+
+## MB subcommands
+
+### `mb analyze`
+
+`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):
+
+```
+mb analyze -c chromium_linux_rel //out/Release input.json output.json
+```
+
+Either the `-c/--config` flag or the `-m/--builder-group` and `-b/--builder`
+flags must be specified so that `mb` can figure out which config to use.
+
+The first positional argument must be a GN-style "source-absolute" path
+to the build directory.
+
+The second positional argument is a (normal) path to a JSON file containing
+a single object with the following fields:
+
+ * `files`: an array of the modified filenames to check (as paths relative to
+ the checkout root).
+ * `test_targets`: an array of (ninja) build targets that needed to run the
+ tests we wish to run. An empty array will be treated as if there are
+ no tests that will be run.
+ * `additional_compile_targets`: an array of (ninja) build targets that
+ 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.
+ 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`
+ and `test_targets`. Support for this field will be removed once the
+ bots have been updated to use compile_targets and test_targets instead.
+
+The third positional argument is a (normal) path to where mb will write
+the result, also as a JSON object. This object may contain the following
+fields:
+
+ * `error`: this should only be present if something failed.
+ * `compile_targets`: the list of ninja targets that should be passed
+ directly to the corresponding ninja / compile.py invocation. This
+ list may contain entries that are *not* listed in the input (see
+ the description of `additional_compile_targets` above and
+ [design_spec.md](the design spec) for how this works).
+ * `invalid_targets`: a list of any targets that were passed in
+ either of the input lists that weren't actually found in the graph.
+ * `test_targets`: the subset of the input `test_targets` that are
+ potentially out of date, indicating that the matching test steps
+ should be re-run.
+ * `targets`: a legacy field that indicates the subset of the input `targets`
+ that depend on the input `files`.
+ * `build_targets`: a legacy field that indicates the minimal subset of
+ targets needed to build all of `targets` that were affected.
+ * `status`: a field containing one of three strings:
+
+ * `"Found dependency"` (build the `compile_targets`)
+ * `"No dependency"` (i.e., no build needed)
+ * `"Found dependency (all)"` (`test_targets` is returned as-is;
+ `compile_targets` should contain the union of `test_targets` and
+ `additional_compile_targets`. In this case the targets do not
+ need to be pruned).
+
+See [design_spec.md](the design spec) for more details and examples; the
+differences can be subtle. We won't even go into how the `targets` and
+`build_targets` differ from each other or from `compile_targets` and
+`test_targets`.
+
+The `-b/--builder`, `-c/--config`, `-f/--config-file`, `-m/--builder-group`,
+`-q/--quiet`, and `-v/--verbose` flags work as documented for `mb gen`.
+
+### `mb audit`
+
+`mb audit` is used to track the progress of the GYP->GN migration. You can
+use it to check a single builder group, or all the builder groups we care
+about.
+See `mb help audit` for more details (most people are not expected to care
+about this).
+
+### `mb gen`
+
+`mb gen` is responsible for generating the Ninja files by invoking either GYP
+or GN as appropriate. It takes arguments to specify a build config and
+a directory, then runs GYP or GN as appropriate:
+
+```
+% mb gen -m tryserver.chromium.linux -b linux_rel //out/Release
+% mb gen -c linux_rel_trybot //out/Release
+```
+
+Either the `-c/--config` flag or the `-m/--builder-group` and `-b/--builder`
+flags must be specified so that `mb` can figure out which config to use. The
+`--phase` flag must also be used with builders that have multiple
+build/compile steps (and only with those builders).
+
+By default, MB will look for a bot config file under `//ios/build/bots` (see
+[design_spec.md](the design spec) for details of how the bot config files
+work). If no matching one is found, will then look in
+`//tools/mb/mb_config.pyl` to look up the config information, but you can
+specify a custom config file using the `-f/--config-file` flag.
+
+The path must be a GN-style "source-absolute" path (as above).
+
+You can pass the `-n/--dryrun` flag to mb gen to see what will happen without
+actually writing anything.
+
+You can pass the `-q/--quiet` flag to get mb to be silent unless there is an
+error, and pass the `-v/--verbose` flag to get mb to log all of the files
+that are read and written, and all the commands that are run.
+
+If the build config will use the Goma distributed-build system, you can pass
+the path to your Goma client in the `-g/--goma-dir` flag, and it will be
+incorporated into the appropriate flags for GYP or GN as needed.
+
+If gen ends up using GYP, the path must have a valid GYP configuration as the
+last component of the path (i.e., specify `//out/Release_x64`, not `//out`).
+The gyp script defaults to `//build/gyp_chromium`, but can be overridden with
+the `--gyp-script` flag, e.g. `--gyp-script=gypfiles/gyp_v8`.
+
+### `mb help`
+
+Produces help output on the other subcommands
+
+### `mb lookup`
+
+Prints what command will be run by `mb gen` (like `mb gen -n` but does
+not require you to specify a path).
+
+The `-b/--builder`, `-c/--config`, `-f/--config-file`, `-m/--builder-group`,
+`--phase`, `-q/--quiet`, and `-v/--verbose` flags work as documented for
+`mb gen`.
+
+### `mb validate`
+
+Does internal checking to make sure the config file is syntactically
+valid and that all of the entries are used properly. It does not validate
+that the flags make sense, or that the builder names are legal or
+comprehensive, but it does complain about configs and mixins that aren't
+used.
+
+The `-f/--config-file` and `-q/--quiet` flags work as documented for
+`mb gen`.
+
+This is mostly useful as a presubmit check and for verifying changes to
+the config file.
+
+## Isolates and Swarming
+
+`mb gen` is also responsible for generating the `.isolate` and
+`.isolated.gen.json` files needed to run test executables through swarming
+in a GN build (in a GYP build, this is done as part of the compile step).
+
+If you wish to generate the isolate files, pass `mb gen` the
+`--swarming-targets-file` command line argument; that arg should be a path
+to a file containing a list of ninja build targets to compute the runtime
+dependencies for (on Windows, use the ninja target name, not the file, so
+`base_unittests`, not `base_unittests.exe`).
+
+MB will take this file, translate each build target to the matching GN
+label (e.g., `base_unittests` -> `//base:base_unittests`, write that list
+to a file called `runtime_deps` in the build directory, and pass that to
+`gn gen $BUILD ... --runtime-deps-list-file=$BUILD/runtime_deps`.
+
+Once GN has computed the lists of runtime dependencies, MB will then
+look up the command line for each target (currently this is hard-coded
+in [mb.py](https://code.google.com/p/chromium/codesearch?q=mb.py#chromium/src/tools/mb/mb.py&q=mb.py%20GetIsolateCommand&sq=package:chromium&type=cs)), and write out the
+matching `.isolate` and `.isolated.gen.json` files.
+
+## The `mb_config.pyl` config file
+
+The `mb_config.pyl` config file is intended to enumerate all of the
+supported build configurations for Chromium. Generally speaking, you
+should never need to (or want to) build a configuration that isn't
+listed here, and so by using the configs in this file you can avoid
+having to juggle long lists of GYP_DEFINES and gn args by hand.
+
+`mb_config.pyl` is structured as a file containing a single PYthon Literal
+expression: a dictionary with three main keys, `builder_groups`, `configs` and
+`mixins`.
+
+The `builder_groups` key contains a nested series of dicts containing mappings
+of builder group -> builder -> config . This allows us to isolate the buildbot
+recipes from the actual details of the configs. The config should either
+be a single string value representing a key in the `configs` dictionary,
+or a list of strings, each of which is a key in the `configs` dictionary;
+the latter case is for builders that do multiple compiles with different
+arguments in a single build, and must *only* be used for such builders
+(where a --phase argument must be supplied in each lookup or gen call).
+
+The `configs` key points to a dictionary of named build configurations.
+
+There should be an key in this dict for every supported configuration
+of Chromium, meaning every configuration we have a bot for, and every
+configuration commonly used by develpers but that we may not have a bot
+for.
+
+The value of each key is a list of "mixins" that will define what that
+build_config does. Each item in the list must be an entry in the dictionary
+value of the `mixins` key.
+
+Each mixin value is itself a dictionary that contains one or more of the
+following keys:
+
+ * `gyp_crosscompile`: a boolean; if true, GYP_CROSSCOMPILE=1 is set in
+ the environment and passed to GYP.
+ * `gyp_defines`: a string containing a list of GYP_DEFINES.
+ * `gn_args`: a string containing a list of values passed to gn --args.
+ * `mixins`: a list of other mixins that should be included.
+ * `type`: a string with either the value `gyp` or `gn`;
+ setting this indicates which meta-build tool to use.
+
+When `mb gen` or `mb analyze` executes, it takes a config name, looks it
+up in the 'configs' dict, and then does a left-to-right expansion of the
+mixins; gyp_defines and gn_args values are concatenated, and the type values
+override each other.
+
+For example, if you had:
+
+```
+{
+ 'configs`: {
+ 'linux_release_trybot': ['gyp_release', 'trybot'],
+ 'gn_shared_debug': None,
+ }
+ 'mixins': {
+ 'bot': {
+ 'gyp_defines': 'use_goma=1 dcheck_always_on=0',
+ 'gn_args': 'use_goma=true dcheck_always_on=false',
+ },
+ 'debug': {
+ 'gn_args': 'is_debug=true',
+ },
+ 'gn': {'type': 'gn'},
+ 'gyp_release': {
+ 'mixins': ['release'],
+ 'type': 'gyp',
+ },
+ 'release': {
+ 'gn_args': 'is_debug=false',
+ }
+ 'shared': {
+ 'gn_args': 'is_component_build=true',
+ 'gyp_defines': 'component=shared_library',
+ },
+ 'trybot': {
+ 'gyp_defines': 'dcheck_always_on=1',
+ 'gn_args': 'dcheck_always_on=true',
+ }
+ }
+}
+```
+
+and you ran `mb gen -c linux_release_trybot //out/Release`, it would
+translate into a call to `gyp_chromium -G Release` with `GYP_DEFINES` set to
+`"use_goma=true dcheck_always_on=false dcheck_always_on=true"`.
+
+(From that you can see that mb is intentionally dumb and does not
+attempt to de-dup the flags, it lets gyp do that).
+
+## Debugging MB
+
+By design, MB should be simple enough that very little can go wrong.
+
+The most obvious issue is that you might see different commands being
+run than you expect; running `'mb -v'` will print what it's doing and
+run the commands; `'mb -n'` will print what it will do but *not* run
+the commands.
+
+If you hit weirder things than that, add some print statements to the
+python script, send a question to gn-dev@chromium.org, or
+[file a bug](https://crbug.com/new) with the label
+'mb' and cc: dpranke@chromium.org.
+
+
diff --git a/third_party/libwebrtc/tools_webrtc/mb/mb b/third_party/libwebrtc/tools_webrtc/mb/mb
new file mode 100755
index 0000000000..007d23cb64
--- /dev/null
+++ b/third_party/libwebrtc/tools_webrtc/mb/mb
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+base_dir=$(dirname "$0")
+
+PYTHONDONTWRITEBYTECODE=1 exec python3 "$base_dir/mb.py" "$@"
diff --git a/third_party/libwebrtc/tools_webrtc/mb/mb.bat b/third_party/libwebrtc/tools_webrtc/mb/mb.bat
new file mode 100755
index 0000000000..1252560a08
--- /dev/null
+++ b/third_party/libwebrtc/tools_webrtc/mb/mb.bat
@@ -0,0 +1,6 @@
+@echo off
+setlocal
+:: This is required with cygwin only.
+PATH=%~dp0;%PATH%
+set PYTHONDONTWRITEBYTECODE=1
+call vpython3 "%~dp0mb.py" %*
diff --git a/third_party/libwebrtc/tools_webrtc/mb/mb.py b/third_party/libwebrtc/tools_webrtc/mb/mb.py
new file mode 100755
index 0000000000..c23e69c09f
--- /dev/null
+++ b/third_party/libwebrtc/tools_webrtc/mb/mb.py
@@ -0,0 +1,156 @@
+#!/usr/bin/env vpython3
+
+# Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+#
+# Use of this source code is governed by a BSD-style license
+# that can be found in the LICENSE file in the root of the source
+# tree. An additional intellectual property rights grant can be found
+# in the file PATENTS. All contributing project authors may
+# be found in the AUTHORS file in the root of the source tree.
+
+"""MB - the Meta-Build wrapper around GN.
+
+MB is a wrapper script for GN that can be used to generate build files
+for sets of canned configurations and analyze them.
+"""
+
+import os
+import sys
+
+_SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
+_SRC_DIR = os.path.dirname(os.path.dirname(_SCRIPT_DIR))
+sys.path.insert(0, _SRC_DIR)
+
+from tools.mb import mb
+
+
+def _GetExecutable(target, platform):
+ executable_prefix = '.\\' if platform == 'win32' else './'
+ executable_suffix = '.exe' if platform == 'win32' else ''
+ return executable_prefix + target + executable_suffix
+
+
+def main(args):
+ mbw = WebRTCMetaBuildWrapper()
+ return mbw.Main(args)
+
+
+class WebRTCMetaBuildWrapper(mb.MetaBuildWrapper):
+ def __init__(self):
+ super().__init__()
+ # Make sure default_config and default_isolate_map are attributes of the
+ # parent class before changing their values.
+ # pylint: disable=access-member-before-definition
+ assert self.default_config
+ assert self.default_isolate_map
+ self.default_config = os.path.join(_SCRIPT_DIR, 'mb_config.pyl')
+ self.default_isolate_map = os.path.join(_SRC_DIR, 'infra', 'specs',
+ 'gn_isolate_map.pyl')
+
+ def GetSwarmingCommand(self, target, vals):
+ isolate_map = self.ReadIsolateMap()
+ test_type = isolate_map[target]['type']
+
+ is_android = 'target_os="android"' in vals['gn_args']
+ is_fuchsia = 'target_os="fuchsia"' in vals['gn_args']
+ is_ios = 'target_os="ios"' in vals['gn_args']
+ is_linux = self.platform.startswith('linux') and not is_android
+ is_win = self.platform.startswith('win')
+
+ if test_type == 'nontest':
+ self.WriteFailureAndRaise('We should not be isolating %s.' % target,
+ output_path=None)
+ if test_type not in ('console_test_launcher', 'windowed_test_launcher',
+ 'non_parallel_console_test_launcher', 'raw',
+ 'additional_compile_target', 'junit_test', 'script'):
+ self.WriteFailureAndRaise('No command line for '
+ '%s found (test type %s).' %
+ (target, test_type),
+ output_path=None)
+
+ cmdline = []
+ extra_files = [
+ '../../.vpython3',
+ '../../testing/test_env.py',
+ ]
+ vpython_exe = 'vpython3'
+
+ if isolate_map[target].get('script'):
+ cmdline += [
+ vpython_exe,
+ '../../' + self.ToSrcRelPath(isolate_map[target]['script'])
+ ]
+ elif is_android:
+ cmdline += [
+ 'luci-auth', 'context', '--', vpython_exe,
+ '../../build/android/test_wrapper/logdog_wrapper.py', '--target',
+ target, '--logdog-bin-cmd',
+ '../../.task_template_packages/logdog_butler', '--logcat-output-file',
+ '${ISOLATED_OUTDIR}/logcats', '--store-tombstones'
+ ]
+ elif is_ios or is_fuchsia or test_type == 'raw':
+ if is_win:
+ cmdline += ['bin\\run_{}.bat'.format(target)]
+ else:
+ cmdline += ['bin/run_{}'.format(target)]
+ else:
+ if isolate_map[target].get('use_webcam', False):
+ cmdline += [
+ vpython_exe, '../../tools_webrtc/ensure_webcam_is_running.py'
+ ]
+ extra_files.append('../../tools_webrtc/ensure_webcam_is_running.py')
+ if isolate_map[target].get('use_pipewire', False):
+ cmdline += [vpython_exe, '../../tools_webrtc/configure_pipewire.py']
+ extra_files.append('../../tools_webrtc/configure_pipewire.py')
+
+ # is_linux uses use_ozone and x11 by default.
+ use_x11 = is_linux
+
+ xvfb = use_x11 and test_type == 'windowed_test_launcher'
+ if xvfb:
+ cmdline += [vpython_exe, '../../testing/xvfb.py']
+ extra_files.append('../../testing/xvfb.py')
+ else:
+ cmdline += [vpython_exe, '../../testing/test_env.py']
+
+ extra_files += [
+ '../../third_party/gtest-parallel/gtest-parallel',
+ '../../third_party/gtest-parallel/gtest_parallel.py',
+ '../../tools_webrtc/gtest-parallel-wrapper.py',
+ ]
+ output_dir = '${ISOLATED_OUTDIR}/test_logs'
+ cmdline += [
+ '../../tools_webrtc/gtest-parallel-wrapper.py',
+ '--output_dir=%s' % output_dir,
+ '--gtest_color=no',
+ ]
+ if test_type == 'non_parallel_console_test_launcher':
+ # Still use the gtest-parallel-wrapper.py script since we
+ # need it to run tests on swarming, but don't execute tests
+ # in parallel.
+ cmdline.append('--workers=1')
+
+ asan = 'is_asan=true' in vals['gn_args']
+ lsan = 'is_lsan=true' in vals['gn_args']
+ msan = 'is_msan=true' in vals['gn_args']
+ tsan = 'is_tsan=true' in vals['gn_args']
+ sanitizer = asan or lsan or msan or tsan
+ if not sanitizer:
+ # Retry would hide most sanitizers detections.
+ cmdline.append('--retry_failed=3')
+
+ cmdline.append(_GetExecutable(target, self.platform))
+
+ cmdline.extend([
+ '--asan=%d' % asan,
+ '--lsan=%d' % lsan,
+ '--msan=%d' % msan,
+ '--tsan=%d' % tsan,
+ ])
+
+ cmdline += isolate_map[target].get('args', [])
+
+ return cmdline, extra_files
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/third_party/libwebrtc/tools_webrtc/mb/mb_config.pyl b/third_party/libwebrtc/tools_webrtc/mb/mb_config.pyl
new file mode 100644
index 0000000000..6aea3c9cba
--- /dev/null
+++ b/third_party/libwebrtc/tools_webrtc/mb/mb_config.pyl
@@ -0,0 +1,501 @@
+# Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+#
+# Use of this source code is governed by a BSD-style license
+# that can be found in the LICENSE file in the root of the source
+# tree. An additional intellectual property rights grant can be found
+# in the file PATENTS. All contributing project authors may
+# be found in the AUTHORS file in the root of the source tree.
+
+# FOR DETAILS ON THIS FILE SEE THE MAIN COPY IN //tools/mb/mb_config.pyl.
+# This is configuration for standalone WebRTC bots. It is used to keep the bot
+# configurations source-side instead of in the buildbot scripts. That makes it
+# easy to try different configurations of GN args in tryjob patches.
+
+{
+ # This is a map of buildbot builder group names -> buildbot builder names ->
+ # config names (where each config name is a key in the 'configs' dict,
+ # above). mb uses this dict to look up which config to use for a given bot.
+ # The builders should be sorted by the order they appear in the /builders
+ # page on the buildbots, *not* alphabetically.
+ 'builder_groups': {
+ # This is required because WebRTC mb.py overwrites the default configs
+ # and Chromium's mb.py checks the default config contains 'chromium'.
+ 'chromium': {},
+ 'chromium.infra.codesearch': {
+ 'codesearch-gen-webrtc-android': {
+ 'android': 'android_debug_static_bot_arm',
+ },
+ 'codesearch-gen-webrtc-linux': {
+ 'linux': 'codesearch_gen_linux_bot',
+ }
+ },
+ 'client.webrtc': {
+ # Android
+ 'Android32': 'android_release_bot_arm',
+ 'Android32 (dbg)': 'android_debug_static_bot_arm',
+ 'Android32 (more configs)': {
+ 'bwe_test_logging': 'bwe_test_logging_android_arm',
+ 'dummy_audio_file_devices_no_protobuf':
+ 'dummy_audio_file_devices_no_protobuf_android_arm',
+ 'rtti_no_sctp': 'rtti_no_sctp_android_arm',
+ },
+ 'Android32 Builder arm': 'android_pure_release_bot_arm',
+ 'Android32 Builder x86': 'android_release_bot_x86',
+ 'Android32 Builder x86 (dbg)': 'android_debug_static_bot_x86',
+ 'Android64': 'android_release_bot_arm64',
+ 'Android64 (dbg)': 'android_debug_static_bot_arm64',
+ 'Android64 Builder arm64': 'android_pure_release_bot_arm64',
+ 'Android64 Builder x64 (dbg)': 'android_debug_static_bot_x64',
+
+ # Fuchsia
+ 'Fuchsia Builder': 'release_bot_x64_fuchsia',
+ 'Fuchsia Release': 'release_bot_x64_fuchsia',
+
+ # Linux
+ # "More configs" bots will build all the following configs in sequence.
+ # This is using MB's "phases" feature.
+ 'Linux (more configs)': {
+ 'bwe_test_logging': 'bwe_test_logging_x64',
+ 'dummy_audio_file_devices_no_protobuf':
+ 'dummy_audio_file_devices_no_protobuf_x64',
+ 'rtti_no_sctp': 'rtti_no_sctp_x64',
+ },
+ 'Linux Asan': 'asan_lsan_clang_release_bot_x64',
+ 'Linux MSan': 'msan_clang_release_bot_x64',
+ 'Linux Tsan v2': 'tsan_clang_release_bot_x64',
+ 'Linux UBSan': 'ubsan_clang_release_bot_x64',
+ 'Linux UBSan vptr': 'ubsan_vptr_clang_release_bot_x64',
+ 'Linux32 Debug': 'no_h264_debug_bot_x86',
+ 'Linux32 Debug (ARM)': 'debug_bot_arm',
+ 'Linux32 Release': 'release_bot_x86',
+ 'Linux32 Release (ARM)': 'release_bot_arm',
+ 'Linux64 Builder': 'pure_release_bot_x64',
+ 'Linux64 Debug': 'debug_bot_x64',
+ 'Linux64 Debug (ARM)': 'debug_bot_arm64',
+ 'Linux64 Release': 'release_bot_x64',
+ 'Linux64 Release (ARM)': 'release_bot_arm64',
+ 'Linux64 Release (Libfuzzer)': 'libfuzzer_asan_release_bot_x64',
+
+ # Mac
+ 'Mac Asan': 'mac_asan_clang_release_bot_x64',
+ 'Mac64 Builder': 'pure_release_bot_x64',
+ 'Mac64 Debug': 'debug_bot_x64',
+ 'Mac64 Release': 'release_bot_x64',
+ 'MacARM64 M1 Release': 'release_bot_arm64',
+ 'MacArm64 Builder': 'release_bot_arm64',
+
+ # Windows
+ 'Win (more configs)': {
+ 'bwe_test_logging': 'bwe_test_logging_x86',
+ 'dummy_audio_file_devices_no_protobuf':
+ 'dummy_audio_file_devices_no_protobuf_x86',
+ 'rtti_no_sctp': 'rtti_no_sctp_no_unicode_win_x86',
+ },
+ 'Win32 Debug (Clang)': 'win_clang_debug_bot_x86',
+ 'Win32 Release (Clang)': 'win_clang_release_bot_x86',
+ 'Win64 ASan': 'win_asan_clang_release_bot_x64',
+ 'Win64 Builder (Clang)': 'win_clang_pure_release_bot_x64',
+ 'Win64 Debug (Clang)': 'win_clang_debug_bot_x64',
+ 'Win64 Release (Clang)': 'win_clang_release_bot_x64',
+
+ # iOS
+ 'iOS Debug (simulator)': 'ios_debug_bot_x64',
+ 'iOS64 Debug': 'ios_debug_bot_arm64',
+ 'iOS64 Release': 'ios_release_bot_arm64',
+ },
+ 'client.webrtc.fyi': {
+ # Android
+ 'Android ASan (swarming)': 'android_asan_shared_release_bot_arm',
+ 'Android Perf (swarming)': 'android_pure_release_bot_arm',
+
+ # Mac
+ 'Mac (swarming)': 'release_bot_x64',
+
+ # Windows
+ 'Win (swarming)': 'release_bot_x86',
+ 'Win64 Debug (Win10)': 'debug_bot_x64',
+ 'Win64 Debug (Win8)': 'debug_bot_x64',
+ },
+ 'client.webrtc.perf': {
+ # These are here because testers need to gn gen + ninja for the
+ # webrtc_dashboard_upload target (otherwise a tester would not need to
+ # build anything).
+ # TODO(http://crbug.com/1029452): Nuke these and isolate on builder
+ # instead?
+ 'Perf Android32 (R Pixel5)': 'release_bot_x64',
+ 'Perf Android64 (R Pixel5)': 'release_bot_x64',
+ 'Perf Fuchsia': 'release_bot_x64_fuchsia',
+ 'Perf Linux Bionic': 'release_bot_x64',
+ 'Perf Linux Trusty': 'release_bot_x64',
+ 'Perf Mac 11': 'release_bot_x64',
+ 'Perf Mac M1 Arm64 12': 'release_bot_x64',
+ 'Perf Win 10': 'release_bot_x64',
+ },
+ 'internal.client.webrtc': {
+ 'iOS64 Debug': 'ios_internal_debug_bot_arm64',
+ 'iOS64 Perf': 'ios_internal_pure_release_bot_arm64',
+ 'iOS64 Release': 'ios_internal_release_bot_arm64',
+ },
+ 'tryserver.webrtc': {
+ # Android
+ 'android_arm64_dbg': 'android_release_bot_arm64',
+ 'android_arm64_rel': 'android_release_bot_arm64',
+ 'android_arm_dbg': 'android_debug_static_bot_arm',
+ 'android_arm_more_configs': {
+ 'bwe_test_logging': 'bwe_test_logging_android_arm',
+ 'dummy_audio_file_devices_no_protobuf':
+ 'dummy_audio_file_devices_no_protobuf_android_arm',
+ 'rtti_no_sctp': 'rtti_no_sctp_android_arm',
+ },
+ 'android_arm_rel': 'android_release_bot_arm',
+ 'android_compile_arm64_dbg': 'android_debug_static_bot_arm64',
+ 'android_compile_arm64_rel': 'android_pure_release_bot_arm64',
+ 'android_compile_arm_dbg': 'android_debug_static_bot_arm',
+ 'android_compile_arm_rel': 'android_pure_release_bot_arm',
+ 'android_compile_x64_dbg': 'android_debug_static_bot_x64',
+ 'android_compile_x64_rel': 'android_release_bot_x64',
+ 'android_compile_x86_dbg': 'android_debug_static_bot_x86',
+ 'android_compile_x86_rel': 'android_release_bot_x86',
+
+ # Fuchsia
+ 'fuchsia_rel': 'release_bot_x64_fuchsia',
+
+ # iOS
+ 'ios_compile_arm64_dbg': 'ios_debug_bot_arm64',
+ 'ios_compile_arm64_rel': 'ios_release_bot_arm64',
+ 'ios_dbg_simulator': 'ios_debug_bot_x64',
+
+ # Linux
+ 'linux_asan': 'asan_lsan_clang_release_bot_x64',
+ 'linux_compile_arm64_dbg': 'debug_bot_arm64',
+ 'linux_compile_arm64_rel': 'release_bot_arm64',
+ 'linux_compile_arm_dbg': 'debug_bot_arm',
+ 'linux_compile_arm_rel': 'release_bot_arm',
+ 'linux_compile_dbg': 'debug_bot_x64',
+ 'linux_compile_rel': 'pure_release_bot_x64',
+ 'linux_compile_x86_dbg': 'debug_bot_x86',
+ 'linux_compile_x86_rel': 'pure_release_bot_x86',
+ 'linux_coverage': 'code_coverage_bot_x64',
+ 'linux_dbg': 'debug_bot_x64',
+ 'linux_libfuzzer_rel': 'libfuzzer_asan_release_bot_x64',
+ 'linux_more_configs': {
+ 'bwe_test_logging': 'bwe_test_logging_x64',
+ 'dummy_audio_file_devices_no_protobuf':
+ 'dummy_audio_file_devices_no_protobuf_x64',
+ 'rtti_no_sctp': 'rtti_no_sctp_x64',
+ },
+ 'linux_msan': 'msan_clang_release_bot_x64',
+ 'linux_rel': 'release_bot_x64',
+ 'linux_tsan2': 'tsan_clang_release_bot_x64',
+ 'linux_ubsan': 'ubsan_clang_release_bot_x64',
+ 'linux_ubsan_vptr': 'ubsan_vptr_clang_release_bot_x64',
+ 'linux_x86_dbg': 'no_h264_debug_bot_x86',
+ 'linux_x86_rel': 'release_bot_x86',
+
+ # Mac
+ 'mac_asan': 'mac_asan_clang_release_bot_x64',
+ 'mac_compile_dbg': 'debug_bot_x64',
+ 'mac_compile_rel': 'pure_release_bot_x64',
+ 'mac_dbg': 'debug_bot_x64',
+ 'mac_dbg_m1': 'debug_bot_arm64',
+ 'mac_rel': 'release_bot_x64',
+ 'mac_rel_m1': 'release_bot_arm64',
+
+ # Windows
+ 'win_asan': 'win_asan_clang_release_bot_x64',
+ 'win_compile_x64_clang_dbg': 'win_clang_debug_bot_x64',
+ 'win_compile_x64_clang_rel': 'win_clang_release_bot_x64',
+ 'win_compile_x86_clang_dbg': 'win_clang_debug_bot_x86',
+ 'win_compile_x86_clang_rel': 'win_clang_release_bot_x86',
+ 'win_x64_clang_dbg': 'win_clang_debug_bot_x64',
+ 'win_x64_clang_rel': 'win_clang_release_bot_x64',
+ 'win_x86_clang_dbg': 'win_clang_debug_bot_x86',
+ 'win_x86_clang_rel': 'win_clang_release_bot_x86',
+ 'win_x86_more_configs': {
+ 'bwe_test_logging': 'bwe_test_logging_x86',
+ 'dummy_audio_file_devices_no_protobuf':
+ 'dummy_audio_file_devices_no_protobuf_x86',
+ 'rtti_no_sctp': 'rtti_no_sctp_no_unicode_win_x86',
+ },
+ }
+ },
+
+ # This is the list of configs that you can pass to mb; each config
+ # represents a particular combination of gn args that we must support.
+ # A given config *may* be platform-specific but is not necessarily so (i.e.,
+ # we might have mac, win, and linux bots all using the 'release_bot' config).
+ 'configs': {
+ 'android_asan_shared_release_bot_arm':
+ ['android', 'asan', 'clang', 'pure_release_bot', 'arm'],
+ 'android_debug_static_bot_arm': ['android', 'debug_static_bot', 'arm'],
+ 'android_debug_static_bot_arm64': ['android', 'debug_static_bot', 'arm64'],
+ 'android_debug_static_bot_x64': ['android', 'debug_static_bot', 'x64'],
+ 'android_debug_static_bot_x86': ['android', 'debug_static_bot', 'x86'],
+ 'android_pure_release_bot_arm': ['android', 'pure_release_bot', 'arm'],
+ 'android_pure_release_bot_arm64': ['android', 'pure_release_bot', 'arm64'],
+ 'android_release_bot_arm': ['android', 'release_bot', 'arm'],
+ 'android_release_bot_arm64': ['android', 'release_bot', 'arm64'],
+ 'android_release_bot_x64': ['android', 'release_bot', 'x64'],
+ 'android_release_bot_x86': ['android', 'release_bot', 'x86'],
+ 'asan_lsan_clang_release_bot_x64':
+ ['asan', 'lsan', 'clang', 'openh264', 'pure_release_bot', 'x64', 'h265'],
+ 'bwe_test_logging_android_arm':
+ ['android', 'debug_static_bot', 'arm', 'bwe_test_logging'],
+ 'bwe_test_logging_x64': ['debug_bot', 'x64', 'bwe_test_logging'],
+ 'bwe_test_logging_x86': ['debug_bot', 'x86', 'bwe_test_logging'],
+ 'code_coverage_bot_x64': [
+ 'openh264', 'release_bot', 'x64', 'code_coverage',
+ 'partial_code_coverage_instrumentation', 'h265'
+ ],
+ 'codesearch_gen_linux_bot':
+ ['openh264', 'debug_bot', 'minimal_symbols', 'h265'],
+ 'debug_bot_arm': ['openh264', 'debug_bot', 'arm', 'h265'],
+ 'debug_bot_arm64': ['openh264', 'debug_bot', 'arm64', 'h265'],
+ 'debug_bot_x64': ['openh264', 'debug_bot', 'x64', 'h265'],
+ 'debug_bot_x86': ['openh264', 'debug_bot', 'x86', 'h265'],
+ 'dummy_audio_file_devices_no_protobuf_android_arm': [
+ 'android', 'debug_static_bot', 'arm', 'dummy_audio_file_devices',
+ 'no_protobuf'
+ ],
+ 'dummy_audio_file_devices_no_protobuf_x64':
+ ['debug_bot', 'x64', 'dummy_audio_file_devices', 'no_protobuf'],
+ 'dummy_audio_file_devices_no_protobuf_x86':
+ ['debug_bot', 'x86', 'dummy_audio_file_devices', 'no_protobuf'],
+ 'ios_debug_bot_arm64':
+ ['ios', 'debug_bot', 'arm64', 'no_ios_code_signing', 'xctest'],
+ 'ios_debug_bot_x64': ['ios', 'debug_bot', 'x64', 'xctest'],
+ 'ios_internal_debug_bot_arm64': [
+ 'ios', 'debug_bot', 'arm64', 'ios_code_signing_identity_description',
+ 'xctest'
+ ],
+ 'ios_internal_pure_release_bot_arm64': [
+ 'ios', 'pure_release_bot', 'arm64',
+ 'ios_code_signing_identity_description', 'xctest',
+ 'rtc_objc_test_prefix',
+ ],
+ 'ios_internal_release_bot_arm64': [
+ 'ios', 'release_bot', 'arm64', 'ios_code_signing_identity_description',
+ 'xctest'
+ ],
+ 'ios_release_bot_arm64': [
+ 'ios', 'release_bot', 'arm64', 'no_ios_code_signing', 'xctest',
+ ],
+ 'libfuzzer_asan_release_bot_x64': [
+ 'libfuzzer', 'asan', 'optimize_for_fuzzing', 'openh264',
+ 'pure_release_bot', 'x64', 'h265'
+ ],
+ 'mac_asan_clang_release_bot_x64': [
+ 'asan',
+ 'clang',
+ 'openh264',
+ 'pure_release_bot',
+ 'x64',
+ 'h265',
+ ],
+ 'msan_clang_release_bot_x64':
+ ['msan', 'clang', 'openh264', 'pure_release_bot', 'x64', 'h265'],
+ 'no_h264_debug_bot_x86': ['debug_bot', 'x86'],
+ 'pure_release_bot_x64': ['openh264', 'pure_release_bot', 'x64', 'h265'],
+ 'pure_release_bot_x86': ['openh264', 'pure_release_bot', 'x86', 'h265'],
+ 'release_bot_arm': ['openh264', 'release_bot', 'arm', 'h265'],
+ 'release_bot_arm64': ['openh264', 'release_bot', 'arm64', 'h265'],
+ 'release_bot_x64': ['openh264', 'release_bot', 'x64', 'h265'],
+ 'release_bot_x64_fuchsia':
+ ['openh264', 'release_bot', 'x64', 'fuchsia', 'h265'],
+ 'release_bot_x86': ['openh264', 'release_bot', 'x86', 'h265'],
+ 'rtti_no_sctp_android_arm':
+ ['android', 'debug_static_bot', 'arm', 'rtti', 'no_sctp'],
+ 'rtti_no_sctp_no_unicode_win_x86':
+ ['debug_bot', 'x86', 'rtti', 'no_sctp', 'win_undef_unicode'],
+ 'rtti_no_sctp_x64': ['debug_bot', 'x64', 'rtti', 'no_sctp'],
+ 'tsan_clang_release_bot_x64':
+ ['tsan', 'clang', 'openh264', 'pure_release_bot', 'x64', 'h265'],
+ 'ubsan_clang_release_bot_x64': [
+ 'ubsan', 'clang', 'openh264', 'pure_release_bot', 'x64', 'h265'
+ ],
+ 'ubsan_vptr_clang_release_bot_x64': [
+ 'ubsan_vptr', 'clang', 'openh264', 'pure_release_bot', 'x64', 'h265'
+ ],
+ 'win_asan_clang_release_bot_x64': [
+ 'asan',
+ 'clang',
+ 'full_symbols',
+ 'openh264',
+ 'pure_release_bot',
+ 'x64',
+ 'h265',
+ ],
+ 'win_clang_debug_bot_x64': [
+ 'clang',
+ 'openh264',
+ 'debug_bot',
+ 'x64',
+ 'h265',
+ ],
+ 'win_clang_debug_bot_x86': [
+ 'clang',
+ 'openh264',
+ 'debug_bot',
+ 'x86',
+ 'h265',
+ ],
+ 'win_clang_pure_release_bot_x64': [
+ 'clang',
+ 'openh264',
+ 'pure_release_bot',
+ 'x64',
+ 'h265',
+ ],
+ 'win_clang_release_bot_x64': [
+ 'clang',
+ 'openh264',
+ 'release_bot',
+ 'x64',
+ 'h265',
+ ],
+ 'win_clang_release_bot_x86': [
+ 'clang',
+ 'openh264',
+ 'release_bot',
+ 'x86',
+ 'h265',
+ ],
+ },
+
+ # This is a dict mapping a given 'mixin' name to a dict of settings that
+ # mb should use. See //tools/mb/docs/user_guide.md for more information.
+ 'mixins': {
+ 'android': {
+ 'gn_args': 'target_os="android"',
+ },
+ 'arm': {
+ 'gn_args': 'target_cpu="arm"',
+ },
+ 'arm64': {
+ 'gn_args': 'target_cpu="arm64"',
+ },
+ 'asan': {
+ 'gn_args': 'is_asan=true',
+ },
+ 'bwe_test_logging': {
+ 'gn_args': 'rtc_enable_bwe_test_logging=true',
+ },
+ # is_clang=true by default, this is only to guard from upstream changes.
+ 'clang': {
+ 'gn_args': 'is_clang=true',
+ },
+ 'code_coverage': {
+ 'gn_args': 'use_clang_coverage=true',
+ },
+ 'dcheck_always_on': {
+ 'gn_args': 'dcheck_always_on=true',
+ },
+ 'dcheck_off': {
+ 'gn_args': 'dcheck_always_on=false',
+ },
+ 'debug': {
+ 'gn_args': 'is_debug=true',
+ },
+ 'debug_bot': {
+ 'mixins': ['debug', 'reclient', 'strict_field_trials'],
+ },
+ 'debug_static_bot': {
+ 'mixins': ['debug', 'minimal_symbols', 'reclient', 'strict_field_trials'],
+ },
+ 'dummy_audio_file_devices': {
+ 'gn_args': 'rtc_use_dummy_audio_file_devices=true',
+ },
+ 'fuchsia': {
+ 'gn_args': 'target_os="fuchsia"'
+ },
+ 'full_symbols': {
+ 'gn_args': 'symbol_level=2',
+ },
+ 'h265': {
+ 'gn_args': 'rtc_use_h265=true',
+ },
+ 'ios': {
+ 'gn_args': 'target_os="ios"',
+ },
+ 'ios_code_signing_identity_description': {
+ 'gn_args': 'ios_code_signing_identity_description="Apple Development"',
+ },
+ 'libfuzzer': {
+ 'gn_args': 'use_libfuzzer=true',
+ },
+ 'lsan': {
+ 'gn_args': 'is_lsan=true',
+ },
+ 'minimal_symbols': {
+ 'gn_args': 'symbol_level=1',
+ },
+ 'msan': {
+ 'gn_args': 'is_msan=true msan_track_origins=2'
+ ' instrumented_libraries_release = "focal"',
+ },
+ 'no_ios_code_signing': {
+ 'gn_args': 'ios_enable_code_signing=false',
+ },
+ 'no_protobuf': {
+ 'gn_args': 'rtc_enable_protobuf=false',
+ },
+ 'no_sctp': {
+ 'gn_args': 'rtc_enable_sctp=false',
+ },
+ 'openh264': {
+ 'gn_args': 'ffmpeg_branding="Chrome" rtc_use_h264=true',
+ },
+ 'optimize_for_fuzzing': {
+ 'gn_args': 'optimize_for_fuzzing=true',
+ },
+ 'partial_code_coverage_instrumentation': {
+ 'gn_args':
+ 'coverage_instrumentation_input_file="//.code-coverage/files_to_instrument.txt"'
+ },
+ # The 'pure_release_bot' configuration is for release bots that are doing a
+ # 100% release build without DCHECKs while 'release_bot' is a partial
+ # release configs since `dcheck_always_on` is set to true.
+ 'pure_release_bot': {
+ 'mixins': ['release', 'reclient', 'dcheck_off'],
+ },
+ 'reclient': {
+ 'gn_args': 'use_remoteexec=true',
+ },
+ 'release': {
+ 'gn_args': 'is_debug=false',
+ },
+ 'release_bot': {
+ 'mixins': ['pure_release_bot', 'dcheck_always_on', 'strict_field_trials'],
+ },
+ 'rtc_objc_test_prefix': {
+ 'gn_args': 'rtc_objc_prefix="RTC_TESTING"',
+ },
+ 'rtti': {
+ 'gn_args': 'use_rtti=true',
+ },
+ 'strict_field_trials': {
+ 'gn_args': 'rtc_strict_field_trials="dcheck"',
+ },
+ 'tsan': {
+ 'gn_args': 'is_tsan=true',
+ },
+ 'ubsan': {
+ 'gn_args': 'is_ubsan=true is_ubsan_no_recover=true',
+ },
+ 'ubsan_vptr': {
+ 'gn_args': 'is_ubsan_vptr=true is_ubsan_no_recover=true',
+ },
+ 'win_undef_unicode': {
+ 'gn_args': 'rtc_win_undef_unicode=true',
+ },
+ 'x64': {
+ 'gn_args': 'target_cpu="x64"',
+ },
+ 'x86': {
+ 'gn_args': 'target_cpu="x86"',
+ },
+ 'xctest': {
+ 'gn_args': 'enable_run_ios_unittests_with_xctest=true',
+ },
+ },
+}
diff --git a/third_party/libwebrtc/tools_webrtc/mb/mb_unittest.py b/third_party/libwebrtc/tools_webrtc/mb/mb_unittest.py
new file mode 100755
index 0000000000..583fefd87e
--- /dev/null
+++ b/third_party/libwebrtc/tools_webrtc/mb/mb_unittest.py
@@ -0,0 +1,728 @@
+#!/usr/bin/env vpython3
+
+# Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+#
+# Use of this source code is governed by a BSD-style license
+# that can be found in the LICENSE file in the root of the source
+# tree. An additional intellectual property rights grant can be found
+# in the file PATENTS. All contributing project authors may
+# be found in the AUTHORS file in the root of the source tree.
+
+"""Tests for mb.py."""
+
+import ast
+import os
+import re
+import sys
+import tempfile
+import unittest
+
+_SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
+_SRC_DIR = os.path.dirname(os.path.dirname(_SCRIPT_DIR))
+sys.path.insert(0, _SRC_DIR)
+
+from tools_webrtc.mb import mb
+
+
+class FakeMBW(mb.WebRTCMetaBuildWrapper):
+ def __init__(self, win32=False):
+ super().__init__()
+
+ # Override vars for test portability.
+ if win32:
+ self.chromium_src_dir = 'c:\\fake_src'
+ self.default_config = 'c:\\fake_src\\tools_webrtc\\mb\\mb_config.pyl'
+ self.default_isolate_map = ('c:\\fake_src\\testing\\buildbot\\'
+ 'gn_isolate_map.pyl')
+ self.platform = 'win32'
+ self.executable = 'c:\\python\\vpython3.exe'
+ self.sep = '\\'
+ self.cwd = 'c:\\fake_src\\out\\Default'
+ else:
+ self.chromium_src_dir = '/fake_src'
+ self.default_config = '/fake_src/tools_webrtc/mb/mb_config.pyl'
+ self.default_isolate_map = '/fake_src/testing/buildbot/gn_isolate_map.pyl'
+ self.executable = '/usr/bin/vpython3'
+ self.platform = 'linux2'
+ self.sep = '/'
+ self.cwd = '/fake_src/out/Default'
+
+ self.files = {}
+ self.dirs = set()
+ self.calls = []
+ self.cmds = []
+ self.cross_compile = None
+ self.out = ''
+ self.err = ''
+ self.rmdirs = []
+
+ def ExpandUser(self, path):
+ # pylint: disable=no-self-use
+ return '$HOME/%s' % path
+
+ def Exists(self, path):
+ abs_path = self._AbsPath(path)
+ return self.files.get(abs_path) is not None or abs_path in self.dirs
+
+ def ListDir(self, path):
+ dir_contents = []
+ for f in list(self.files.keys()) + list(self.dirs):
+ head, _ = os.path.split(f)
+ if head == path:
+ dir_contents.append(f)
+ return dir_contents
+
+ def MaybeMakeDirectory(self, path):
+ abpath = self._AbsPath(path)
+ self.dirs.add(abpath)
+
+ def PathJoin(self, *comps):
+ return self.sep.join(comps)
+
+ def ReadFile(self, path):
+ try:
+ return self.files[self._AbsPath(path)]
+ except KeyError as e:
+ raise IOError('%s not found' % path) from e
+
+ def WriteFile(self, path, contents, force_verbose=False):
+ if self.args.dryrun or self.args.verbose or force_verbose:
+ self.Print('\nWriting """\\\n%s""" to %s.\n' % (contents, path))
+ abpath = self._AbsPath(path)
+ self.files[abpath] = contents
+
+ def Call(self, cmd, env=None, capture_output=True, input=None):
+ # pylint: disable=redefined-builtin
+ del env
+ del capture_output
+ del input
+ self.calls.append(cmd)
+ if self.cmds:
+ return self.cmds.pop(0)
+ return 0, '', ''
+
+ def Print(self, *args, **kwargs):
+ sep = kwargs.get('sep', ' ')
+ end = kwargs.get('end', '\n')
+ f = kwargs.get('file', sys.stdout)
+ if f == sys.stderr:
+ self.err += sep.join(args) + end
+ else:
+ self.out += sep.join(args) + end
+
+ def TempDir(self):
+ tmp_dir = os.path.join(tempfile.gettempdir(), 'mb_test')
+ self.dirs.add(tmp_dir)
+ return tmp_dir
+
+ def TempFile(self, mode='w'):
+ del mode
+ return FakeFile(self.files)
+
+ def RemoveFile(self, path):
+ abpath = self._AbsPath(path)
+ self.files[abpath] = None
+
+ def RemoveDirectory(self, abs_path):
+ # Normalize the passed-in path to handle different working directories
+ # used during unit testing.
+ abs_path = self._AbsPath(abs_path)
+ self.rmdirs.append(abs_path)
+ files_to_delete = [f for f in self.files if f.startswith(abs_path)]
+ for f in files_to_delete:
+ self.files[f] = None
+
+ def _AbsPath(self, path):
+ if not ((self.platform == 'win32' and path.startswith('c:')) or
+ (self.platform != 'win32' and path.startswith('/'))):
+ path = self.PathJoin(self.cwd, path)
+ if self.sep == '\\':
+ return re.sub(r'\\+', r'\\', path)
+ return re.sub('/+', '/', path)
+
+
+class FakeFile:
+ # pylint: disable=invalid-name
+ def __init__(self, files):
+ self.name = '/tmp/file'
+ self.buf = ''
+ self.files = files
+
+ def write(self, contents):
+ self.buf += contents
+
+ def close(self):
+ self.files[self.name] = self.buf
+
+
+TEST_CONFIG = """\
+{
+ 'builder_groups': {
+ 'chromium': {},
+ 'fake_group': {
+ 'fake_builder': 'rel_bot',
+ 'fake_debug_builder': 'debug_goma',
+ 'fake_args_bot': 'fake_args_bot',
+ 'fake_multi_phase': { 'phase_1': 'phase_1', 'phase_2': 'phase_2'},
+ 'fake_android_bot': 'android_bot',
+ 'fake_args_file': 'args_file_goma',
+ 'fake_ios_error': 'ios_error',
+ },
+ },
+ 'configs': {
+ 'args_file_goma': ['fake_args_bot', 'goma'],
+ 'fake_args_bot': ['fake_args_bot'],
+ 'rel_bot': ['rel', 'goma', 'fake_feature1'],
+ 'debug_goma': ['debug', 'goma'],
+ 'phase_1': ['rel', 'phase_1'],
+ 'phase_2': ['rel', 'phase_2'],
+ 'android_bot': ['android'],
+ 'ios_error': ['error'],
+ },
+ 'mixins': {
+ 'error': {
+ 'gn_args': 'error',
+ },
+ 'fake_args_bot': {
+ 'args_file': '//build/args/bots/fake_group/fake_args_bot.gn',
+ },
+ 'fake_feature1': {
+ 'gn_args': 'enable_doom_melon=true',
+ },
+ 'goma': {
+ 'gn_args': 'use_goma=true',
+ },
+ 'phase_1': {
+ 'gn_args': 'phase=1',
+ },
+ 'phase_2': {
+ 'gn_args': 'phase=2',
+ },
+ 'rel': {
+ 'gn_args': 'is_debug=false dcheck_always_on=false',
+ },
+ 'debug': {
+ 'gn_args': 'is_debug=true',
+ },
+ 'android': {
+ 'gn_args': 'target_os="android" dcheck_always_on=false',
+ }
+ },
+}
+"""
+
+
+def CreateFakeMBW(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_group/fake_args_bot.gn'),
+ 'is_debug = false\ndcheck_always_on=false\n')
+ mbw.files.setdefault(mbw.ToAbsPath('//tools/mb/rts_banned_suites.json'), '{}')
+ if files:
+ for path, contents in list(files.items()):
+ mbw.files[path] = contents
+ if path.endswith('.runtime_deps'):
+
+ def FakeCall(cmd, env=None, capture_output=True, stdin=None):
+ # pylint: disable=cell-var-from-loop
+ del cmd
+ del env
+ del capture_output
+ del stdin
+ mbw.files[path] = contents
+ return 0, '', ''
+
+ # pylint: disable=invalid-name
+ mbw.Call = FakeCall
+ return mbw
+
+
+class UnitTest(unittest.TestCase):
+ # pylint: disable=invalid-name
+ def check(self,
+ args,
+ mbw=None,
+ files=None,
+ out=None,
+ err=None,
+ ret=None,
+ env=None):
+ if not mbw:
+ mbw = CreateFakeMBW(files)
+
+ try:
+ prev_env = os.environ.copy()
+ os.environ = env if env else prev_env
+ actual_ret = mbw.Main(args)
+ finally:
+ os.environ = prev_env
+ self.assertEqual(
+ actual_ret, ret,
+ "ret: %s, out: %s, err: %s" % (actual_ret, mbw.out, mbw.err))
+ if out is not None:
+ self.assertEqual(mbw.out, out)
+ if err is not None:
+ self.assertEqual(mbw.err, err)
+ return mbw
+
+ def test_gen_swarming(self):
+ files = {
+ '/tmp/swarming_targets':
+ 'foo_unittests\n',
+ '/fake_src/testing/buildbot/gn_isolate_map.pyl':
+ ("{'foo_unittests': {"
+ " 'label': '//foo:foo_unittests',"
+ " 'type': 'raw',"
+ " 'args': [],"
+ "}}\n"),
+ '/fake_src/out/Default/foo_unittests.runtime_deps': ("foo_unittests\n"),
+ }
+ mbw = CreateFakeMBW(files)
+ self.check([
+ 'gen', '-c', 'debug_goma', '--swarming-targets-file',
+ '/tmp/swarming_targets', '//out/Default'
+ ],
+ mbw=mbw,
+ ret=0)
+ self.assertIn('/fake_src/out/Default/foo_unittests.isolate', mbw.files)
+ self.assertIn('/fake_src/out/Default/foo_unittests.isolated.gen.json',
+ mbw.files)
+
+ def test_gen_swarming_android(self):
+ test_files = {
+ '/tmp/swarming_targets':
+ 'foo_unittests\n',
+ '/fake_src/testing/buildbot/gn_isolate_map.pyl':
+ ("{'foo_unittests': {"
+ " 'label': '//foo:foo_unittests',"
+ " 'type': 'console_test_launcher',"
+ "}}\n"),
+ '/fake_src/out/Default/foo_unittests.runtime_deps': ("foo_unittests\n"),
+ }
+ mbw = self.check([
+ 'gen', '-c', 'android_bot', '//out/Default', '--swarming-targets-file',
+ '/tmp/swarming_targets', '--isolate-map-file',
+ '/fake_src/testing/buildbot/gn_isolate_map.pyl'
+ ],
+ files=test_files,
+ ret=0)
+
+ isolate_file = mbw.files['/fake_src/out/Default/foo_unittests.isolate']
+ isolate_file_contents = ast.literal_eval(isolate_file)
+ files = isolate_file_contents['variables']['files']
+ command = isolate_file_contents['variables']['command']
+
+ self.assertEqual(
+ files,
+ ['../../.vpython3', '../../testing/test_env.py', 'foo_unittests'])
+ self.assertEqual(command, [
+ 'luci-auth',
+ 'context',
+ '--',
+ 'vpython3',
+ '../../build/android/test_wrapper/logdog_wrapper.py',
+ '--target',
+ 'foo_unittests',
+ '--logdog-bin-cmd',
+ '../../.task_template_packages/logdog_butler',
+ '--logcat-output-file',
+ '${ISOLATED_OUTDIR}/logcats',
+ '--store-tombstones',
+ ])
+
+ def test_gen_swarming_android_junit_test(self):
+ test_files = {
+ '/tmp/swarming_targets':
+ 'foo_unittests\n',
+ '/fake_src/testing/buildbot/gn_isolate_map.pyl':
+ ("{'foo_unittests': {"
+ " 'label': '//foo:foo_unittests',"
+ " 'type': 'junit_test',"
+ "}}\n"),
+ '/fake_src/out/Default/foo_unittests.runtime_deps': ("foo_unittests\n"),
+ }
+ mbw = self.check([
+ 'gen', '-c', 'android_bot', '//out/Default', '--swarming-targets-file',
+ '/tmp/swarming_targets', '--isolate-map-file',
+ '/fake_src/testing/buildbot/gn_isolate_map.pyl'
+ ],
+ files=test_files,
+ ret=0)
+
+ isolate_file = mbw.files['/fake_src/out/Default/foo_unittests.isolate']
+ isolate_file_contents = ast.literal_eval(isolate_file)
+ files = isolate_file_contents['variables']['files']
+ command = isolate_file_contents['variables']['command']
+
+ self.assertEqual(
+ files,
+ ['../../.vpython3', '../../testing/test_env.py', 'foo_unittests'])
+ self.assertEqual(command, [
+ 'luci-auth',
+ 'context',
+ '--',
+ 'vpython3',
+ '../../build/android/test_wrapper/logdog_wrapper.py',
+ '--target',
+ 'foo_unittests',
+ '--logdog-bin-cmd',
+ '../../.task_template_packages/logdog_butler',
+ '--logcat-output-file',
+ '${ISOLATED_OUTDIR}/logcats',
+ '--store-tombstones',
+ ])
+
+ def test_gen_script(self):
+ test_files = {
+ '/tmp/swarming_targets':
+ 'foo_unittests_script\n',
+ '/fake_src/testing/buildbot/gn_isolate_map.pyl':
+ ("{'foo_unittests_script': {"
+ " 'label': '//foo:foo_unittests',"
+ " 'type': 'script',"
+ " 'script': '//foo/foo_unittests_script.py',"
+ "}}\n"),
+ '/fake_src/out/Default/foo_unittests_script.runtime_deps':
+ ("foo_unittests\n"
+ "foo_unittests_script.py\n"),
+ }
+ mbw = self.check([
+ 'gen', '-c', 'debug_goma', '//out/Default', '--swarming-targets-file',
+ '/tmp/swarming_targets', '--isolate-map-file',
+ '/fake_src/testing/buildbot/gn_isolate_map.pyl'
+ ],
+ files=test_files,
+ ret=0)
+
+ isolate_file = (
+ mbw.files['/fake_src/out/Default/foo_unittests_script.isolate'])
+ isolate_file_contents = ast.literal_eval(isolate_file)
+ files = isolate_file_contents['variables']['files']
+ command = isolate_file_contents['variables']['command']
+
+ self.assertEqual(files, [
+ '../../.vpython3',
+ '../../testing/test_env.py',
+ 'foo_unittests',
+ 'foo_unittests_script.py',
+ ])
+ self.assertEqual(command, [
+ 'vpython3',
+ '../../foo/foo_unittests_script.py',
+ ])
+
+ def test_gen_raw(self):
+ test_files = {
+ '/tmp/swarming_targets':
+ 'foo_unittests\n',
+ '/fake_src/testing/buildbot/gn_isolate_map.pyl':
+ ("{'foo_unittests': {"
+ " 'label': '//foo:foo_unittests',"
+ " 'type': 'raw',"
+ "}}\n"),
+ '/fake_src/out/Default/foo_unittests.runtime_deps': ("foo_unittests\n"),
+ }
+ mbw = self.check([
+ 'gen', '-c', 'debug_goma', '//out/Default', '--swarming-targets-file',
+ '/tmp/swarming_targets', '--isolate-map-file',
+ '/fake_src/testing/buildbot/gn_isolate_map.pyl'
+ ],
+ files=test_files,
+ ret=0)
+
+ isolate_file = mbw.files['/fake_src/out/Default/foo_unittests.isolate']
+ isolate_file_contents = ast.literal_eval(isolate_file)
+ files = isolate_file_contents['variables']['files']
+ command = isolate_file_contents['variables']['command']
+
+ self.assertEqual(files, [
+ '../../.vpython3',
+ '../../testing/test_env.py',
+ 'foo_unittests',
+ ])
+ self.assertEqual(command, ['bin/run_foo_unittests'])
+
+ def test_gen_non_parallel_console_test_launcher(self):
+ test_files = {
+ '/tmp/swarming_targets':
+ 'foo_unittests\n',
+ '/fake_src/testing/buildbot/gn_isolate_map.pyl':
+ ("{'foo_unittests': {"
+ " 'label': '//foo:foo_unittests',"
+ " 'type': 'non_parallel_console_test_launcher',"
+ "}}\n"),
+ '/fake_src/out/Default/foo_unittests.runtime_deps': ("foo_unittests\n"),
+ }
+ mbw = self.check([
+ 'gen', '-c', 'debug_goma', '//out/Default', '--swarming-targets-file',
+ '/tmp/swarming_targets', '--isolate-map-file',
+ '/fake_src/testing/buildbot/gn_isolate_map.pyl'
+ ],
+ files=test_files,
+ ret=0)
+
+ isolate_file = mbw.files['/fake_src/out/Default/foo_unittests.isolate']
+ isolate_file_contents = ast.literal_eval(isolate_file)
+ files = isolate_file_contents['variables']['files']
+ command = isolate_file_contents['variables']['command']
+
+ self.assertEqual(files, [
+ '../../.vpython3',
+ '../../testing/test_env.py',
+ '../../third_party/gtest-parallel/gtest-parallel',
+ '../../third_party/gtest-parallel/gtest_parallel.py',
+ '../../tools_webrtc/gtest-parallel-wrapper.py',
+ 'foo_unittests',
+ ])
+ self.assertEqual(command, [
+ 'vpython3',
+ '../../testing/test_env.py',
+ '../../tools_webrtc/gtest-parallel-wrapper.py',
+ '--output_dir=${ISOLATED_OUTDIR}/test_logs',
+ '--gtest_color=no',
+ '--workers=1',
+ '--retry_failed=3',
+ './foo_unittests',
+ '--asan=0',
+ '--lsan=0',
+ '--msan=0',
+ '--tsan=0',
+ ])
+
+ def test_isolate_windowed_test_launcher_linux(self):
+ test_files = {
+ '/tmp/swarming_targets':
+ 'foo_unittests\n',
+ '/fake_src/testing/buildbot/gn_isolate_map.pyl':
+ ("{'foo_unittests': {"
+ " 'label': '//foo:foo_unittests',"
+ " 'type': 'windowed_test_launcher',"
+ "}}\n"),
+ '/fake_src/out/Default/foo_unittests.runtime_deps':
+ ("foo_unittests\n"
+ "some_resource_file\n"),
+ }
+ mbw = self.check([
+ 'gen', '-c', 'debug_goma', '//out/Default', '--swarming-targets-file',
+ '/tmp/swarming_targets', '--isolate-map-file',
+ '/fake_src/testing/buildbot/gn_isolate_map.pyl'
+ ],
+ files=test_files,
+ ret=0)
+
+ isolate_file = mbw.files['/fake_src/out/Default/foo_unittests.isolate']
+ isolate_file_contents = ast.literal_eval(isolate_file)
+ files = isolate_file_contents['variables']['files']
+ command = isolate_file_contents['variables']['command']
+
+ self.assertEqual(files, [
+ '../../.vpython3',
+ '../../testing/test_env.py',
+ '../../testing/xvfb.py',
+ '../../third_party/gtest-parallel/gtest-parallel',
+ '../../third_party/gtest-parallel/gtest_parallel.py',
+ '../../tools_webrtc/gtest-parallel-wrapper.py',
+ 'foo_unittests',
+ 'some_resource_file',
+ ])
+ self.assertEqual(command, [
+ 'vpython3',
+ '../../testing/xvfb.py',
+ '../../tools_webrtc/gtest-parallel-wrapper.py',
+ '--output_dir=${ISOLATED_OUTDIR}/test_logs',
+ '--gtest_color=no',
+ '--retry_failed=3',
+ './foo_unittests',
+ '--asan=0',
+ '--lsan=0',
+ '--msan=0',
+ '--tsan=0',
+ ])
+
+ def test_gen_windowed_test_launcher_win(self):
+ files = {
+ 'c:\\fake_src\\out\\Default\\tmp\\swarming_targets':
+ 'unittests\n',
+ 'c:\\fake_src\\testing\\buildbot\\gn_isolate_map.pyl':
+ ("{'unittests': {"
+ " 'label': '//somewhere:unittests',"
+ " 'type': 'windowed_test_launcher',"
+ "}}\n"),
+ r'c:\fake_src\out\Default\unittests.exe.runtime_deps':
+ ("unittests.exe\n"
+ "some_dependency\n"),
+ }
+ mbw = CreateFakeMBW(files=files, win32=True)
+ self.check([
+ 'gen', '-c', 'debug_goma', '--swarming-targets-file',
+ 'c:\\fake_src\\out\\Default\\tmp\\swarming_targets',
+ '--isolate-map-file',
+ 'c:\\fake_src\\testing\\buildbot\\gn_isolate_map.pyl', '//out/Default'
+ ],
+ mbw=mbw,
+ ret=0)
+
+ isolate_file = mbw.files['c:\\fake_src\\out\\Default\\unittests.isolate']
+ isolate_file_contents = ast.literal_eval(isolate_file)
+ files = isolate_file_contents['variables']['files']
+ command = isolate_file_contents['variables']['command']
+
+ self.assertEqual(files, [
+ '../../.vpython3',
+ '../../testing/test_env.py',
+ '../../third_party/gtest-parallel/gtest-parallel',
+ '../../third_party/gtest-parallel/gtest_parallel.py',
+ '../../tools_webrtc/gtest-parallel-wrapper.py',
+ 'some_dependency',
+ 'unittests.exe',
+ ])
+ self.assertEqual(command, [
+ 'vpython3',
+ '../../testing/test_env.py',
+ '../../tools_webrtc/gtest-parallel-wrapper.py',
+ '--output_dir=${ISOLATED_OUTDIR}/test_logs',
+ '--gtest_color=no',
+ '--retry_failed=3',
+ r'.\unittests.exe',
+ '--asan=0',
+ '--lsan=0',
+ '--msan=0',
+ '--tsan=0',
+ ])
+
+ def test_gen_console_test_launcher(self):
+ test_files = {
+ '/tmp/swarming_targets':
+ 'foo_unittests\n',
+ '/fake_src/testing/buildbot/gn_isolate_map.pyl':
+ ("{'foo_unittests': {"
+ " 'label': '//foo:foo_unittests',"
+ " 'type': 'console_test_launcher',"
+ "}}\n"),
+ '/fake_src/out/Default/foo_unittests.runtime_deps': ("foo_unittests\n"),
+ }
+ mbw = self.check([
+ 'gen', '-c', 'debug_goma', '//out/Default', '--swarming-targets-file',
+ '/tmp/swarming_targets', '--isolate-map-file',
+ '/fake_src/testing/buildbot/gn_isolate_map.pyl'
+ ],
+ files=test_files,
+ ret=0)
+
+ isolate_file = mbw.files['/fake_src/out/Default/foo_unittests.isolate']
+ isolate_file_contents = ast.literal_eval(isolate_file)
+ files = isolate_file_contents['variables']['files']
+ command = isolate_file_contents['variables']['command']
+
+ self.assertEqual(files, [
+ '../../.vpython3',
+ '../../testing/test_env.py',
+ '../../third_party/gtest-parallel/gtest-parallel',
+ '../../third_party/gtest-parallel/gtest_parallel.py',
+ '../../tools_webrtc/gtest-parallel-wrapper.py',
+ 'foo_unittests',
+ ])
+ self.assertEqual(command, [
+ 'vpython3',
+ '../../testing/test_env.py',
+ '../../tools_webrtc/gtest-parallel-wrapper.py',
+ '--output_dir=${ISOLATED_OUTDIR}/test_logs',
+ '--gtest_color=no',
+ '--retry_failed=3',
+ './foo_unittests',
+ '--asan=0',
+ '--lsan=0',
+ '--msan=0',
+ '--tsan=0',
+ ])
+
+ def test_isolate_test_launcher_with_webcam(self):
+ test_files = {
+ '/tmp/swarming_targets':
+ 'foo_unittests\n',
+ '/fake_src/testing/buildbot/gn_isolate_map.pyl':
+ ("{'foo_unittests': {"
+ " 'label': '//foo:foo_unittests',"
+ " 'type': 'console_test_launcher',"
+ " 'use_webcam': True,"
+ "}}\n"),
+ '/fake_src/out/Default/foo_unittests.runtime_deps':
+ ("foo_unittests\n"
+ "some_resource_file\n"),
+ }
+ mbw = self.check([
+ 'gen', '-c', 'debug_goma', '//out/Default', '--swarming-targets-file',
+ '/tmp/swarming_targets', '--isolate-map-file',
+ '/fake_src/testing/buildbot/gn_isolate_map.pyl'
+ ],
+ files=test_files,
+ ret=0)
+
+ isolate_file = mbw.files['/fake_src/out/Default/foo_unittests.isolate']
+ isolate_file_contents = ast.literal_eval(isolate_file)
+ files = isolate_file_contents['variables']['files']
+ command = isolate_file_contents['variables']['command']
+
+ self.assertEqual(files, [
+ '../../.vpython3',
+ '../../testing/test_env.py',
+ '../../third_party/gtest-parallel/gtest-parallel',
+ '../../third_party/gtest-parallel/gtest_parallel.py',
+ '../../tools_webrtc/ensure_webcam_is_running.py',
+ '../../tools_webrtc/gtest-parallel-wrapper.py',
+ 'foo_unittests',
+ 'some_resource_file',
+ ])
+ self.assertEqual(command, [
+ 'vpython3',
+ '../../tools_webrtc/ensure_webcam_is_running.py',
+ 'vpython3',
+ '../../testing/test_env.py',
+ '../../tools_webrtc/gtest-parallel-wrapper.py',
+ '--output_dir=${ISOLATED_OUTDIR}/test_logs',
+ '--gtest_color=no',
+ '--retry_failed=3',
+ './foo_unittests',
+ '--asan=0',
+ '--lsan=0',
+ '--msan=0',
+ '--tsan=0',
+ ])
+
+ def test_isolate(self):
+ files = {
+ '/fake_src/out/Default/toolchain.ninja':
+ "",
+ '/fake_src/testing/buildbot/gn_isolate_map.pyl':
+ ("{'foo_unittests': {"
+ " 'label': '//foo:foo_unittests',"
+ " 'type': 'non_parallel_console_test_launcher',"
+ "}}\n"),
+ '/fake_src/out/Default/foo_unittests.runtime_deps': ("foo_unittests\n"),
+ }
+ self.check(
+ ['isolate', '-c', 'debug_goma', '//out/Default', 'foo_unittests'],
+ files=files,
+ ret=0)
+
+ # test running isolate on an existing build_dir
+ files['/fake_src/out/Default/args.gn'] = 'is_debug = true\n'
+ self.check(['isolate', '//out/Default', 'foo_unittests'],
+ files=files,
+ ret=0)
+ files['/fake_src/out/Default/mb_type'] = 'gn\n'
+ self.check(['isolate', '//out/Default', 'foo_unittests'],
+ files=files,
+ ret=0)
+
+if __name__ == '__main__':
+ unittest.main()