Patch Detail
Show a patch.
GET /api/1.1/patches/12408/?format=api
{ "id": 12408, "url": "https://patchwork.libcamera.org/api/1.1/patches/12408/?format=api", "web_url": "https://patchwork.libcamera.org/patch/12408/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/1.1/projects/1/?format=api", "name": "libcamera", "link_name": "libcamera", "list_id": "libcamera_core", "list_email": "libcamera-devel@lists.libcamera.org", "web_url": "", "scm_url": "", "webscm_url": "" }, "msgid": "<20210525100750.1239934-2-paul.elder@ideasonboard.com>", "date": "2021-05-25T10:07:50", "name": "[libcamera-devel,v2,2/2] utils: ipc: Update mojo", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "a1b80ef516147c1027272dad82e5333853d3812c", "submitter": { "id": 17, "url": "https://patchwork.libcamera.org/api/1.1/people/17/?format=api", "name": "Paul Elder", "email": "paul.elder@ideasonboard.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/12408/mbox/", "series": [ { "id": 2065, "url": "https://patchwork.libcamera.org/api/1.1/series/2065/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=2065", "date": "2021-05-25T10:07:49", "name": "[libcamera-devel,v2,1/2] utils: update-mojo.sh: Add script for updating mojo", "version": 2, "mbox": "https://patchwork.libcamera.org/series/2065/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/12408/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/12408/checks/", "tags": {}, "headers": { "Return-Path": "<libcamera-devel-bounces@lists.libcamera.org>", "X-Original-To": "parsemail@patchwork.libcamera.org", "Delivered-To": "parsemail@patchwork.libcamera.org", "Received": [ "from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 2CF3EC3200\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 25 May 2021 10:08:07 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id DE2E368926;\n\tTue, 25 May 2021 12:08:06 +0200 (CEST)", "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id E395C6891D\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 25 May 2021 12:08:04 +0200 (CEST)", "from pyrite.rasen.tech (unknown\n\t[IPv6:2400:4051:61:600:2c71:1b79:d06d:5032])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 7758C880;\n\tTue, 25 May 2021 12:08:02 +0200 (CEST)" ], "Authentication-Results": "lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"Dnhggd3f\"; dkim-atps=neutral", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1621937284;\n\tbh=TpOgmdzzXHk+T5LiN+/qy2Z9+y+xc5F6VA1P3oVJYUU=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=Dnhggd3f2MTRoxTBSA8SZvyUOrk0Gocq/AoIW4M/baKUEFLOkW245qlgLeYJbYG0/\n\tDpoDa8HwcDv3QoEpYXjkQ0f4v9RFrL7uWppzlBtrYwDMTvmNxMXLXFMEIrymtwFus2\n\ti3dJVfY4vYYzUuIBlZ4+xhQQkmjl1eGpByoJTjFs=", "From": "Paul Elder <paul.elder@ideasonboard.com>", "To": "libcamera-devel@lists.libcamera.org", "Date": "Tue, 25 May 2021 19:07:50 +0900", "Message-Id": "<20210525100750.1239934-2-paul.elder@ideasonboard.com>", "X-Mailer": "git-send-email 2.27.0", "In-Reply-To": "<20210525100750.1239934-1-paul.elder@ideasonboard.com>", "References": "<20210525100750.1239934-1-paul.elder@ideasonboard.com>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "Subject": "[libcamera-devel] [PATCH v2 2/2] utils: ipc: Update mojo", "X-BeenThere": "libcamera-devel@lists.libcamera.org", "X-Mailman-Version": "2.1.29", "Precedence": "list", "List-Id": "<libcamera-devel.lists.libcamera.org>", "List-Unsubscribe": "<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>", "List-Archive": "<https://lists.libcamera.org/pipermail/libcamera-devel/>", "List-Post": "<mailto:libcamera-devel@lists.libcamera.org>", "List-Help": "<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>", "List-Subscribe": "<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>", "Errors-To": "libcamera-devel-bounces@lists.libcamera.org", "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>" }, "content": "Update mojo from the Chromium repository. The commit from which this was\ntaken is:\n6dd20682b77604585026b47b81171ff68167c5fd \"[devtools] Migrate Attribution\nReporting Issues to new API\"\n\nThe update-mojo.sh script was used for this update.\n\nSigned-off-by: Paul Elder <paul.elder@ideasonboard.com>\n---\n utils/ipc/mojo/README | 4 +\n utils/ipc/mojo/public/tools/BUILD.gn | 8 +-\n utils/ipc/mojo/public/tools/bindings/BUILD.gn | 9 +-\n .../ipc/mojo/public/tools/bindings/README.md | 187 +++++--\n .../tools/bindings/gen_data_files_list.py | 8 +-\n .../tools/bindings/generate_type_mappings.py | 60 +-\n .../ipc/mojo/public/tools/bindings/mojom.gni | 526 +++++++++++-------\n utils/ipc/mojo/public/tools/mojom/README.md | 2 +-\n .../mojom/check_stable_mojom_compatibility.py | 3 +-\n .../mojo/public/tools/mojom/mojom/BUILD.gn | 1 -\n .../tools/mojom/mojom/generate/generator.py | 14 +-\n .../tools/mojom/mojom/generate/module.py | 163 +++++-\n .../mojom/mojom/generate/template_expander.py | 11 +-\n .../tools/mojom/mojom/generate/translate.py | 26 +-\n .../mojo/public/tools/mojom/mojom_parser.py | 210 +++++--\n .../mojom/version_compatibility_unittest.py | 46 +-\n utils/ipc/tools/README | 4 +\n 17 files changed, 852 insertions(+), 430 deletions(-)\n create mode 100644 utils/ipc/mojo/README\n create mode 100644 utils/ipc/tools/README", "diff": "diff --git a/utils/ipc/mojo/README b/utils/ipc/mojo/README\nnew file mode 100644\nindex 00000000..0450c9d0\n--- /dev/null\n+++ b/utils/ipc/mojo/README\n@@ -0,0 +1,4 @@\n+# SPDX-License-Identifier: CC0-1.0\n+\n+Files in this directory are imported from 6dd20682b776 of Chromium. Do not\n+modify them manually.\ndiff --git a/utils/ipc/mojo/public/tools/BUILD.gn b/utils/ipc/mojo/public/tools/BUILD.gn\nindex 4c68350b..eb6391a6 100644\n--- a/utils/ipc/mojo/public/tools/BUILD.gn\n+++ b/utils/ipc/mojo/public/tools/BUILD.gn\n@@ -8,11 +8,11 @@\n group(\"mojo_python_unittests\") {\n data = [\n \"run_all_python_unittests.py\",\n- \"//testing/scripts/common.py\",\n \"//testing/scripts/run_isolated_script_test.py\",\n- \"//testing/test_env.py\",\n- \"//testing/xvfb.py\",\n ]\n deps = [ \"//mojo/public/tools/mojom/mojom:tests\" ]\n- data_deps = [ \"//third_party/catapult/third_party/typ/\" ]\n+ data_deps = [\n+ \"//testing:test_scripts_shared\",\n+ \"//third_party/catapult/third_party/typ/\",\n+ ]\n }\ndiff --git a/utils/ipc/mojo/public/tools/bindings/BUILD.gn b/utils/ipc/mojo/public/tools/bindings/BUILD.gn\nindex 8ba6e922..3e242532 100644\n--- a/utils/ipc/mojo/public/tools/bindings/BUILD.gn\n+++ b/utils/ipc/mojo/public/tools/bindings/BUILD.gn\n@@ -2,10 +2,12 @@\n # Use of this source code is governed by a BSD-style license that can be\n # found in the LICENSE file.\n \n+import(\"//build/config/python.gni\")\n import(\"//mojo/public/tools/bindings/mojom.gni\")\n import(\"//third_party/jinja2/jinja2.gni\")\n \n-action(\"precompile_templates\") {\n+# TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.\n+python2_action(\"precompile_templates\") {\n sources = mojom_generator_sources\n sources += [\n \"$mojom_generator_root/generators/cpp_templates/enum_macros.tmpl\",\n@@ -69,11 +71,16 @@ action(\"precompile_templates\") {\n \"$mojom_generator_root/generators/js_templates/fuzzing.tmpl\",\n \"$mojom_generator_root/generators/js_templates/interface_definition.tmpl\",\n \"$mojom_generator_root/generators/js_templates/lite/enum_definition.tmpl\",\n+ \"$mojom_generator_root/generators/js_templates/lite/enum_definition_for_module.tmpl\",\n \"$mojom_generator_root/generators/js_templates/lite/interface_definition.tmpl\",\n+ \"$mojom_generator_root/generators/js_templates/lite/interface_definition_for_module.tmpl\",\n \"$mojom_generator_root/generators/js_templates/lite/module_definition.tmpl\",\n \"$mojom_generator_root/generators/js_templates/lite/mojom-lite.js.tmpl\",\n+ \"$mojom_generator_root/generators/js_templates/lite/mojom.m.js.tmpl\",\n \"$mojom_generator_root/generators/js_templates/lite/struct_definition.tmpl\",\n+ \"$mojom_generator_root/generators/js_templates/lite/struct_definition_for_module.tmpl\",\n \"$mojom_generator_root/generators/js_templates/lite/union_definition.tmpl\",\n+ \"$mojom_generator_root/generators/js_templates/lite/union_definition_for_module.tmpl\",\n \"$mojom_generator_root/generators/js_templates/module.amd.tmpl\",\n \"$mojom_generator_root/generators/js_templates/module_definition.tmpl\",\n \"$mojom_generator_root/generators/js_templates/struct_definition.tmpl\",\ndiff --git a/utils/ipc/mojo/public/tools/bindings/README.md b/utils/ipc/mojo/public/tools/bindings/README.md\nindex 1a3d5c58..43882450 100644\n--- a/utils/ipc/mojo/public/tools/bindings/README.md\n+++ b/utils/ipc/mojo/public/tools/bindings/README.md\n@@ -113,8 +113,8 @@ for message parameters.\n \n Every Mojom file may optionally specify a single **module** to which it belongs.\n \n-This is used strictly for aggregaging all defined symbols therein within a\n-common Mojom namespace. The specific impact this has on generated binidngs code\n+This is used strictly for aggregating all defined symbols therein within a\n+common Mojom namespace. The specific impact this has on generated bindings code\n varies for each target language. For example, if the following Mojom is used to\n generate bindings:\n \n@@ -132,7 +132,7 @@ Generated C++ bindings will define a class interface `MoneyGenerator` in the\n bindings at this time are unaffected by module declarations.\n \n **NOTE:** By convention in the Chromium codebase, **all** Mojom files should\n-declare a module name with at least (and preferrably exactly) one top-level name\n+declare a module name with at least (and preferably exactly) one top-level name\n as well as an inner `mojom` module suffix. *e.g.*, `chrome.mojom`,\n `business.mojom`, *etc.*\n \n@@ -271,7 +271,7 @@ code, see\n ### Unions\n \n Mojom supports tagged unions using the **union** keyword. A union is a\n-collection of fields which may taken the value of any single one of those fields\n+collection of fields which may take the value of any single one of those fields\n at a time. Thus they provide a way to represent a variant value type while\n minimizing storage requirements.\n \n@@ -320,7 +320,7 @@ enum definition. By default, values are based at zero and increment by\n 1 sequentially.\n \n The effect of nested definitions on generated bindings varies depending on the\n-target language. See [documentation for individual target languages](#Generated-Code-For-Target-Languages)\n+target language. See [documentation for individual target languages](#Generated-Code-For-Target-Languages).\n \n ### Constants\n \n@@ -346,7 +346,7 @@ struct Employee {\n ```\n \n The effect of nested definitions on generated bindings varies depending on the\n-target language. See [documentation for individual target languages](#Generated-Code-For-Target-Languages)\n+target language. See [documentation for individual target languages](#Generated-Code-For-Target-Languages).\n \n ### Interfaces\n \n@@ -379,58 +379,82 @@ Mojom definitions may have their meaning altered by **attributes**, specified\n with a syntax similar to Java or C# attributes. There are a handle of\n interesting attributes supported today.\n \n-**`[Sync]`**\n-: The `Sync` attribute may be specified for any interface method which expects\n- a response. This makes it so that callers of the method can wait\n- synchronously for a response. See\n- [Synchronous Calls](/mojo/public/cpp/bindings/README.md#Synchronous-Calls)\n- in the C++ bindings documentation. Note that sync methods are only actually\n- synchronous when called from C++.\n-\n-**`[Extensible]`**\n-: The `Extensible` attribute may be specified for any enum definition. This\n- essentially disables builtin range validation when receiving values of the\n- enum type in a message, allowing older bindings to tolerate unrecognized\n- values from newer versions of the enum.\n-\n-**`[Native]`**\n-: The `Native` attribute may be specified for an empty struct declaration to\n- provide a nominal bridge between Mojo IPC and legacy `IPC::ParamTraits` or\n- `IPC_STRUCT_TRAITS*` macros.\n- See\n- [Repurposing Legacy IPC Traits](/docs/mojo_ipc_conversion.md#repurposing-and-invocations)\n- for more details. Note support for this attribute is strictly limited to C++\n- bindings generation.\n-\n-**`[MinVersion=N]`**\n-: The `MinVersion` attribute is used to specify the version at which a given\n- field, enum value, interface method, or method parameter was introduced.\n- See [Versioning](#Versioning) for more details.\n-\n-**`[Stable]`**\n-: The `Stable` attribute specifies that a given mojom type or interface\n- definition can be considered stable over time, meaning it is safe to use for\n- things like persistent storage or communication between independent\n- version-skewed binaries. Stable definitions may only depend on builtin mojom\n- types or other stable definitions, and changes to such definitions MUST\n- preserve backward-compatibility through appropriate use of versioning.\n- Backward-compatibility of changes is enforced in the Chromium tree using a\n- strict presubmit check. See [Versioning](#Versioning) for more details on\n- backward-compatibility constraints.\n-\n-**`[EnableIf=value]`**\n-: The `EnableIf` attribute is used to conditionally enable definitions when\n- the mojom is parsed. If the `mojom` target in the GN file does not include\n- the matching `value` in the list of `enabled_features`, the definition\n- will be disabled. This is useful for mojom definitions that only make\n- sense on one platform. Note that the `EnableIf` attribute can only be set\n- once per definition.\n+* **`[Sync]`**:\n+ The `Sync` attribute may be specified for any interface method which expects a\n+ response. This makes it so that callers of the method can wait synchronously\n+ for a response. See [Synchronous\n+ Calls](/mojo/public/cpp/bindings/README.md#Synchronous-Calls) in the C++\n+ bindings documentation. Note that sync methods are only actually synchronous\n+ when called from C++.\n+\n+* **`[NoInterrupt]`**:\n+ When a thread is waiting for a reply to a `Sync` message, it's possible to be\n+ woken up to dispatch other unrelated incoming `Sync` messages. This measure\n+ helps to avoid deadlocks. If a `Sync` message is also marked as `NoInterrupt`\n+ however, this behavior is disabled: instead the calling thread will only wake\n+ up for the precise message being waited upon. This attribute must be used with\n+ extreme caution, because it can lead to deadlocks otherwise.\n+\n+* **`[Default]`**:\n+ The `Default` attribute may be used to specify an enumerator value that\n+ will be used if an `Extensible` enumeration does not deserialize to a known\n+ value on the receiver side, i.e. the sender is using a newer version of the\n+ enum. This allows unknown values to be mapped to a well-defined value that can\n+ be appropriately handled.\n+\n+* **`[Extensible]`**:\n+ The `Extensible` attribute may be specified for any enum definition. This\n+ essentially disables builtin range validation when receiving values of the\n+ enum type in a message, allowing older bindings to tolerate unrecognized\n+ values from newer versions of the enum.\n+\n+ Note: in the future, an `Extensible` enumeration will require that a `Default`\n+ enumerator value also be specified.\n+\n+* **`[Native]`**:\n+ The `Native` attribute may be specified for an empty struct declaration to\n+ provide a nominal bridge between Mojo IPC and legacy `IPC::ParamTraits` or\n+ `IPC_STRUCT_TRAITS*` macros. See [Repurposing Legacy IPC\n+ Traits](/docs/mojo_ipc_conversion.md#repurposing-and-invocations) for more\n+ details. Note support for this attribute is strictly limited to C++ bindings\n+ generation.\n+\n+* **`[MinVersion=N]`**:\n+ The `MinVersion` attribute is used to specify the version at which a given\n+ field, enum value, interface method, or method parameter was introduced.\n+ See [Versioning](#Versioning) for more details.\n+\n+* **`[Stable]`**:\n+ The `Stable` attribute specifies that a given mojom type or interface\n+ definition can be considered stable over time, meaning it is safe to use for\n+ things like persistent storage or communication between independent\n+ version-skewed binaries. Stable definitions may only depend on builtin mojom\n+ types or other stable definitions, and changes to such definitions MUST\n+ preserve backward-compatibility through appropriate use of versioning.\n+ Backward-compatibility of changes is enforced in the Chromium tree using a\n+ strict presubmit check. See [Versioning](#Versioning) for more details on\n+ backward-compatibility constraints.\n+\n+* **`[Uuid=<UUID>]`**:\n+ Specifies a UUID to be associated with a given interface. The UUID is intended\n+ to remain stable across all changes to the interface definition, including\n+ name changes. The value given for this attribute should be a standard UUID\n+ string representation as specified by RFC 4122. New UUIDs can be generated\n+ with common tools such as `uuidgen`.\n+\n+* **`[EnableIf=value]`**:\n+ The `EnableIf` attribute is used to conditionally enable definitions when the\n+ mojom is parsed. If the `mojom` target in the GN file does not include the\n+ matching `value` in the list of `enabled_features`, the definition will be\n+ disabled. This is useful for mojom definitions that only make sense on one\n+ platform. Note that the `EnableIf` attribute can only be set once per\n+ definition.\n \n ## Generated Code For Target Languages\n \n When the bindings generator successfully processes an input Mojom file, it emits\n corresponding code for each supported target language. For more details on how\n-Mojom concepts translate to a given target langauge, please refer to the\n+Mojom concepts translate to a given target language, please refer to the\n bindings API documentation for that language:\n \n * [C++ Bindings](/mojo/public/cpp/bindings/README.md)\n@@ -441,7 +465,7 @@ bindings API documentation for that language:\n \n Regardless of target language, all interface messages are validated during\n deserialization before they are dispatched to a receiving implementation of the\n-interface. This helps to ensure consitent validation across interfaces without\n+interface. This helps to ensure consistent validation across interfaces without\n leaving the burden to developers and security reviewers every time a new message\n is added.\n \n@@ -555,25 +579,37 @@ struct Employee {\n };\n ```\n \n-and you would like to add a birthday field. You can do:\n+and you would like to add birthday and nickname fields. You can add them as\n+optional types with a `MinVersion` like so:\n \n ``` cpp\n struct Employee {\n uint64 employee_id;\n string name;\n [MinVersion=1] Date? birthday;\n+ [MinVersion=1] string? nickname;\n };\n ```\n \n+*** note\n+**NOTE:** Mojo object or handle types added with a `MinVersion` **MUST** be\n+optional (nullable). See [Primitive Types](#Primitive-Types) for details on\n+nullable values.\n+***\n+\n By default, fields belong to version 0. New fields must be appended to the\n struct definition (*i.e*., existing fields must not change **ordinal value**)\n with the `MinVersion` attribute set to a number greater than any previous\n existing versions.\n \n+The value of `MinVersion` is unrelated to ordinals. The choice of a particular\n+version number is arbitrary. All its usage means is that a field isn't present\n+before the numbered version.\n+\n *** note\n **NOTE:** do not change existing fields in versioned structs, as this is\n not backwards-compatible. Instead, rename the old field to make its\n-deprecation clear and add a new field with the new version number.\n+deprecation clear and add a new field with a new `MinVersion` number.\n ***\n \n **Ordinal value** refers to the relative positional layout of a struct's fields\n@@ -602,14 +638,10 @@ struct Employee {\n uint64 employee_id@0;\n [MinVersion=1] Date? birthday@2;\n string name@1;\n+ [MinVersion=1] string? nickname@3;\n };\n ```\n \n-*** note\n-**NOTE:** Newly added fields of Mojo object or handle types MUST be nullable.\n-See [Primitive Types](#Primitive-Types).\n-***\n-\n ### Versioned Interfaces\n \n There are two dimensions on which an interface can be extended\n@@ -706,6 +738,39 @@ values and will need to deal with them gracefully. See\n [C++ Versioning Considerations](/mojo/public/cpp/bindings/README.md#Versioning-Considerations)\n for details.\n \n+### Renaming versioned structs\n+It's possible to rename versioned structs by using the `[RenamedFrom]` attribute.\n+RenamedFrom\n+\n+``` cpp\n+module asdf.mojom;\n+\n+// Old version:\n+[Stable]\n+struct OldStruct {\n+};\n+\n+// New version:\n+[Stable, RenamedFrom=\"asdf.mojom.OldStruct\"]\n+struct NewStruct {\n+};\n+```\n+\n+## Component targets\n+\n+If there are multiple components depending on the same mojom target within one binary,\n+the target will need to be defined as `mojom_component` instead of `mojom`.\n+Since `mojom` targets are generated `source_set` targets and `mojom_component` targets\n+are generated `component` targets, you would use `mojom_component` in the same cases\n+where you would use `component` for non-mojom files.\n+*** note\n+**NOTE**: by default, components for both blink and non-blink bindings are generated.\n+Use the `disable_variants` target parameter to generate only non-blink bindings.\n+You can also generate a `source_set` for one of the variants by defining\n+[export_*](https://source.chromium.org/chromium/chromium/src/+/main:mojo/public/tools/bindings/mojom.gni;drc=739b9fbce50310c1dd2b59c279cd90a9319cb6e8;l=318)\n+parameters for the `mojom_component` target.\n+***\n+\n ## Grammar Reference\n \n Below is the (BNF-ish) context-free grammar of the Mojom language:\ndiff --git a/utils/ipc/mojo/public/tools/bindings/gen_data_files_list.py b/utils/ipc/mojo/public/tools/bindings/gen_data_files_list.py\nindex 79c9e50e..8b78d092 100644\n--- a/utils/ipc/mojo/public/tools/bindings/gen_data_files_list.py\n+++ b/utils/ipc/mojo/public/tools/bindings/gen_data_files_list.py\n@@ -18,7 +18,6 @@ import os\n import re\n import sys\n \n-from cStringIO import StringIO\n from optparse import OptionParser\n \n sys.path.insert(\n@@ -41,12 +40,9 @@ def main():\n pattern = re.compile(options.pattern)\n files = [f for f in os.listdir(options.directory) if pattern.match(f)]\n \n- stream = StringIO()\n- for f in files:\n- print(f, file=stream)\n+ contents = '\\n'.join(f for f in files) + '\\n'\n+ WriteFile(contents, options.output)\n \n- WriteFile(stream.getvalue(), options.output)\n- stream.close()\n \n if __name__ == '__main__':\n sys.exit(main())\ndiff --git a/utils/ipc/mojo/public/tools/bindings/generate_type_mappings.py b/utils/ipc/mojo/public/tools/bindings/generate_type_mappings.py\nindex 64ca048f..a0096649 100755\n--- a/utils/ipc/mojo/public/tools/bindings/generate_type_mappings.py\n+++ b/utils/ipc/mojo/public/tools/bindings/generate_type_mappings.py\n@@ -75,14 +75,6 @@ def ReadTypemap(path):\n return json.load(f)['c++']\n \n \n-def ParseTypemapArgs(args):\n- typemaps = [s for s in '\\n'.join(args).split('--start-typemap\\n') if s]\n- result = {}\n- for typemap in typemaps:\n- result.update(ParseTypemap(typemap))\n- return result\n-\n-\n def LoadCppTypemapConfig(path):\n configs = {}\n with open(path) as f:\n@@ -102,52 +94,6 @@ def LoadCppTypemapConfig(path):\n }\n return configs\n \n-\n-def ParseTypemap(typemap):\n- values = {'type_mappings': [], 'public_headers': [], 'traits_headers': []}\n- for line in typemap.split('\\n'):\n- if not line:\n- continue\n- key, _, value = line.partition('=')\n- values[key].append(value.lstrip('/'))\n- result = {}\n- mapping_pattern = \\\n- re.compile(r\"\"\"^([^=]+) # mojom type\n- =\n- ([^[]+) # native type\n- (?:\\[([^]]+)\\])?$ # optional attribute in square brackets\n- \"\"\", re.X)\n- for typename in values['type_mappings']:\n- match_result = mapping_pattern.match(typename)\n- assert match_result, (\n- \"Cannot parse entry in the \\\"type_mappings\\\" section: %s\" % typename)\n-\n- mojom_type = match_result.group(1)\n- native_type = match_result.group(2)\n- attributes = []\n- if match_result.group(3):\n- attributes = match_result.group(3).split(',')\n-\n- assert mojom_type not in result, (\n- \"Cannot map multiple native types (%s, %s) to the same mojom type: %s\" %\n- (result[mojom_type]['typename'], native_type, mojom_type))\n-\n- result[mojom_type] = {\n- 'public_headers': values['public_headers'],\n- 'traits_headers': values['traits_headers'],\n- 'typename': native_type,\n-\n- # Attributes supported for individual mappings.\n- 'copyable_pass_by_value': 'copyable_pass_by_value' in attributes,\n- 'force_serialize': 'force_serialize' in attributes,\n- 'hashable': 'hashable' in attributes,\n- 'move_only': 'move_only' in attributes,\n- 'non_copyable_non_movable': 'non_copyable_non_movable' in attributes,\n- 'nullable_is_same_type': 'nullable_is_same_type' in attributes,\n- }\n- return result\n-\n-\n def main():\n parser = argparse.ArgumentParser(\n description=__doc__,\n@@ -170,10 +116,10 @@ def main():\n type=str,\n required=True,\n help='The path to which to write the generated JSON.')\n- params, typemap_params = parser.parse_known_args()\n- typemaps = ParseTypemapArgs(typemap_params)\n+ params, _ = parser.parse_known_args()\n+ typemaps = {}\n if params.cpp_config_path:\n- typemaps.update(LoadCppTypemapConfig(params.cpp_config_path))\n+ typemaps = LoadCppTypemapConfig(params.cpp_config_path)\n missing = [path for path in params.dependency if not os.path.exists(path)]\n if missing:\n raise IOError('Missing dependencies: %s' % ', '.join(missing))\ndiff --git a/utils/ipc/mojo/public/tools/bindings/mojom.gni b/utils/ipc/mojo/public/tools/bindings/mojom.gni\nindex a739fa6e..fe2a1da3 100644\n--- a/utils/ipc/mojo/public/tools/bindings/mojom.gni\n+++ b/utils/ipc/mojo/public/tools/bindings/mojom.gni\n@@ -2,7 +2,7 @@\n # Use of this source code is governed by a BSD-style license that can be\n # found in the LICENSE file.\n \n-import(\"//build/config/jumbo.gni\")\n+import(\"//build/config/python.gni\")\n import(\"//third_party/closure_compiler/closure_args.gni\")\n import(\"//third_party/closure_compiler/compile_js.gni\")\n import(\"//third_party/protobuf/proto_library.gni\")\n@@ -64,10 +64,13 @@ declare_args() {\n # ARC) we have to explicitly opt out there even when NaCl is enabled (and\n # consequently also when building for NaCl toolchains.) For this reason we\n # check |target_os| explicitly, as it's consistent across all toolchains.\n+#\n+# TODO(crbug.com/1052397): Remove !chromeos_is_browser_only once\n+# lacros-chrome switches to target_os=\"chromeos\"\n enable_scrambled_message_ids =\n enable_mojom_message_id_scrambling &&\n- (is_mac || is_win || (is_linux && !is_chromeos && !is_chromecast &&\n- !chromeos_is_browser_only) ||\n+ (is_mac || is_win ||\n+ (is_linux && !is_chromeos_ash && !is_chromecast && !is_chromeos_lacros) ||\n ((enable_nacl || is_nacl || is_nacl_nonsfi) &&\n (target_os != \"chromeos\" && !chromeos_is_browser_only)))\n \n@@ -78,7 +81,6 @@ mojom_parser_sources = [\n \"$_mojom_library_root/__init__.py\",\n \"$_mojom_library_root/error.py\",\n \"$_mojom_library_root/generate/__init__.py\",\n- \"$_mojom_library_root/generate/constant_resolver.py\",\n \"$_mojom_library_root/generate/generator.py\",\n \"$_mojom_library_root/generate/module.py\",\n \"$_mojom_library_root/generate/pack.py\",\n@@ -94,6 +96,7 @@ mojom_generator_root = \"$_mojom_tools_root/bindings\"\n mojom_generator_script = \"$mojom_generator_root/mojom_bindings_generator.py\"\n mojom_generator_sources =\n mojom_parser_sources + [\n+ \"$mojom_generator_root/generators/cpp_util.py\",\n \"$mojom_generator_root/generators/mojom_cpp_generator.py\",\n \"$mojom_generator_root/generators/mojom_java_generator.py\",\n \"$mojom_generator_root/generators/mojom_mojolpm_generator.py\",\n@@ -107,17 +110,6 @@ if (enable_scrambled_message_ids) {\n # The path to a file whose contents can be used as the basis for a message\n # ID scrambling salt.\n mojom_message_id_salt_path = \"//chrome/VERSION\"\n-\n- # The path to a file whose contents will be concatenated to the contents of\n- # the file at |mojom_message_id_salt_path| to form a complete salt for\n- # message ID scrambling. May be the empty string, in which case the contents\n- # of the above file alone are used as the complete salt.\n- if (is_chrome_branded) {\n- mojom_message_id_salt_suffix_path =\n- \"//mojo/internal/chrome-message-id-salt-suffix\"\n- } else {\n- mojom_message_id_salt_suffix_path = \"\"\n- }\n }\n \n assert(mojom_message_id_salt_path != \"\")\n@@ -126,56 +118,11 @@ if (enable_scrambled_message_ids) {\n rebase_path(mojom_message_id_salt_path, root_build_dir),\n ]\n message_scrambling_inputs = [ mojom_message_id_salt_path ]\n-\n- if (mojom_message_id_salt_suffix_path != \"\") {\n- message_scrambling_args += [\n- \"--scrambled_message_id_salt_path\",\n- rebase_path(mojom_message_id_salt_suffix_path, root_build_dir),\n- ]\n- message_scrambling_inputs += [ mojom_message_id_salt_suffix_path ]\n- }\n } else {\n message_scrambling_args = []\n message_scrambling_inputs = []\n }\n \n-if (enable_mojom_typemapping) {\n- _bindings_configuration_files =\n- [ \"//mojo/public/tools/bindings/chromium_bindings_configuration.gni\" ]\n- _bindings_configurations = []\n- foreach(config_file, _bindings_configuration_files) {\n- _bindings_configurations += [ read_file(config_file, \"scope\") ]\n- }\n- foreach(configuration, _bindings_configurations) {\n- # Check that the mojom field of each typemap refers to a mojom that exists.\n- foreach(typemap, configuration.typemaps) {\n- _typemap_config = {\n- }\n- _typemap_config = typemap.config\n- read_file(_typemap_config.mojom, \"\")\n- }\n- }\n-} else {\n- _bindings_configuration_files = []\n- _bindings_configurations = [\n- {\n- typemaps = []\n- component_macro_suffix = \"\"\n- },\n- ]\n-}\n-\n-if (!is_ios) {\n- _bindings_configurations += [\n- {\n- variant = \"blink\"\n- component_macro_suffix = \"_BLINK\"\n- for_blink = true\n- typemaps = []\n- },\n- ]\n-}\n-\n # Generates targets for building C++, JavaScript and Java bindings from mojom\n # files. The output files will go under the generated file directory tree with\n # the same path as each input file.\n@@ -218,6 +165,15 @@ if (!is_ios) {\n # import_dirs (optional)\n # List of import directories that will get added when processing sources.\n #\n+# input_root_override (optional)\n+# Root path for the .mojom files used to generate the namespaces for\n+# interfaces. Useful with targets outside //, e.g. in parent directories\n+# above \"//\". The default input root is //\n+# Example: Vivaldi's source root is \"//vivaldi/\",\n+# and \"//vivaldi/chromium/\" is \"//\"\n+# In such cases, not using this argument lead to the output files being\n+# located in different directories than expected.\n+#\n # testonly (optional)\n #\n # visibility (optional)\n@@ -245,6 +201,43 @@ if (!is_ios) {\n # blink_cpp_typemaps (optional)\n # Same as above, but for the Blink variant of generated C++ bindings.\n #\n+# cpp_proxy_target (optional)\n+# The name of a target which all C++ dependencies will link against\n+# instead of linking directly against this mojom target's generated C++\n+# sources. Normally when declaring invoking the mojom(\"foo\") target, GN\n+# emits a source_set or component target named \"foo\" which encompasses the\n+# default variant of generated C++ bindings. This changes that to instead\n+# emit a group(\"foo\") which merely forwards public_deps to the named\n+# `cpp_proxy_target`. That target must in turn depend on\n+# \"foo_cpp_sources\".\n+#\n+# This is useful primarily in conjunction with export_define et al to\n+# embed generated C++ bindings within an existing component target.\n+#\n+# blink_cpp_proxy_target (optional)\n+# Same concept as `cpp_proxy_target` above, but affects the generated\n+# \"foo_blink\" Blink-variant C++ bindings.\n+#\n+# cpp_configs (optional)\n+# A list of extra configs to apply to the default variant of generated C++\n+# bindings.\n+#\n+# blink_cpp_configs (optional)\n+# A list of extra configs to apply to the Blink variant of generated C++\n+# bindings.\n+#\n+# mojom_source_deps (optional)\n+# A list of mojoms this target depends upon. This is equivalent to\n+# public_deps except that the C++ bindings depend on each of the named\n+# \"foo\" targets' \"foo_cpp_sources\" rather than on foo's\n+# `cpp_proxy_target`. It only makes sense to use this for dependencies\n+# that set `cpp_proxy_target`, and only when the dependent mojom() would\n+# otherwise have circular dependencies with that proxy target.\n+#\n+# mojom_blink_source_deps (optional)\n+# Same as above but depends on \"foo_blink_cpp_sources\" and is used for\n+# dependencies that specify a `blink_cpp_proxy_target`.\n+#\n # generate_java (optional)\n # If set to true, Java bindings are generated for Android builds. If\n # |cpp_only| is set to true, it overrides this to prevent generation of\n@@ -327,6 +320,21 @@ if (!is_ios) {\n # List of extra C++ templates that are used to generate additional source\n # and/or header files. The templates should end with extension \".tmpl\".\n #\n+# webui_module_path (optional)\n+# The path or URL at which modules generated by this target will be\n+# accessible to WebUI pages. This may either be an absolute path or\n+# a full URL path starting with \"chrome://resources/mojo\".\n+#\n+# If an absolute path, a WebUI page may only import these modules if\n+# they are manually packaged and mapped independently by that page's\n+# WebUIDataSource. The mapped path must match the path given here.\n+#\n+# If this is is instead a URL string starting with\n+# \"chrome://resources/mojo\", the generated resources must be added to\n+# content_resources.grd and registered with\n+# content::SharedResourcesDataSource with a corresponding path, at which\n+# point they will be made available to all WebUI pages at the given URL.\n+#\n # The following parameters are used to support the component build. They are\n # needed so that bindings which are linked with a component can use the same\n # export settings for classes. The first three are for the chromium variant, and\n@@ -410,8 +418,8 @@ if (!is_ios) {\n # traits for the type must define IsNull and SetToNull methods.\n #\n # When false, nullable fields are represented by wrapping the C++\n-# type with base::Optional, and null values are simply\n-# base::nullopt.\n+# type with absl::optional, and null values are simply\n+# absl::nullopt.\n #\n # hashable (optional)\n # A boolean value (default false) indicating whether the C++ type is\n@@ -463,14 +471,15 @@ template(\"mojom\") {\n if (defined(invoker.export_class_attribute) ||\n defined(invoker.export_define) || defined(invoker.export_header)) {\n assert(defined(invoker.export_class_attribute))\n- assert(defined(invoker.export_define))\n+ assert(defined(invoker.export_define) || defined(invoker.cpp_configs))\n assert(defined(invoker.export_header))\n }\n if (defined(invoker.export_class_attribute_blink) ||\n defined(invoker.export_define_blink) ||\n defined(invoker.export_header_blink)) {\n assert(defined(invoker.export_class_attribute_blink))\n- assert(defined(invoker.export_define_blink))\n+ assert(defined(invoker.export_define_blink) ||\n+ defined(invoker.blink_cpp_configs))\n assert(defined(invoker.export_header_blink))\n \n # Not all platforms use the Blink variant, so make sure GN doesn't complain\n@@ -506,12 +515,22 @@ template(\"mojom\") {\n !invoker.disallow_interfaces\n \n all_deps = []\n+ mojom_cpp_deps = []\n if (defined(invoker.deps)) {\n all_deps += invoker.deps\n+ mojom_cpp_deps += invoker.deps\n }\n if (defined(invoker.public_deps)) {\n all_deps += invoker.public_deps\n+ mojom_cpp_deps += invoker.public_deps\n+ }\n+ if (defined(invoker.mojom_source_deps)) {\n+ all_deps += invoker.mojom_source_deps\n }\n+ if (defined(invoker.mojom_blink_source_deps)) {\n+ all_deps += invoker.mojom_blink_source_deps\n+ }\n+ not_needed([ \"mojom_deps\" ])\n \n if (defined(invoker.component_macro_prefix)) {\n assert(defined(invoker.component_output_prefix))\n@@ -536,12 +555,6 @@ template(\"mojom\") {\n sources_list = invoker.sources\n }\n \n- # Reset sources_assignment_filter for the BUILD.gn file to prevent\n- # regression during the migration of Chromium away from the feature.\n- # See docs/no_sources_assignment_filter.md for more information.\n- # TODO(crbug.com/1018739): remove this when migration is done.\n- set_sources_assignment_filter([])\n-\n # Listed sources may be relative to the current target dir, or they may be\n # absolute paths, including paths to generated mojom files. While those are\n # fine as-is for input references, deriving output paths can be more subtle.\n@@ -571,7 +584,11 @@ template(\"mojom\") {\n # for the naming of any generated output files derived from their\n # corresponding input mojoms. These paths are always considered to be relative\n # to root_gen_dir.\n- source_abspaths = rebase_path(sources_list, \"//\")\n+ if (defined(invoker.input_root_override)) {\n+ source_abspaths = rebase_path(sources_list, invoker.input_root_override)\n+ } else {\n+ source_abspaths = rebase_path(sources_list, \"//\")\n+ }\n output_file_base_paths = []\n foreach(path, source_abspaths) {\n output_file_base_paths +=\n@@ -643,21 +660,31 @@ template(\"mojom\") {\n }\n if (is_android) {\n enabled_features += [ \"is_android\" ]\n- } else if (is_chromeos) {\n- enabled_features += [ \"is_chromeos\" ]\n+ } else if (is_chromeos_ash) {\n+ enabled_features += [\n+ \"is_chromeos\",\n+ \"is_chromeos_ash\",\n+ ]\n } else if (is_fuchsia) {\n enabled_features += [ \"is_fuchsia\" ]\n } else if (is_ios) {\n enabled_features += [ \"is_ios\" ]\n- } else if (is_linux) {\n+ } else if (is_linux || is_chromeos_lacros) {\n enabled_features += [ \"is_linux\" ]\n+ if (is_chromeos_lacros) {\n+ enabled_features += [\n+ \"is_chromeos\",\n+ \"is_chromeos_lacros\",\n+ ]\n+ }\n } else if (is_mac) {\n enabled_features += [ \"is_mac\" ]\n } else if (is_win) {\n enabled_features += [ \"is_win\" ]\n }\n \n- action(parser_target_name) {\n+ # TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.\n+ python2_action(parser_target_name) {\n script = mojom_parser_script\n inputs = mojom_parser_sources + [ build_metadata_filename ]\n sources = sources_list\n@@ -679,7 +706,7 @@ template(\"mojom\") {\n # Resolve relative input mojom paths against both the root src dir and\n # the root gen dir.\n \"--input-root\",\n- rebase_path(\"//\"),\n+ rebase_path(\"//.\"),\n \"--input-root\",\n rebase_path(root_gen_dir),\n \n@@ -692,12 +719,26 @@ template(\"mojom\") {\n rebase_path(build_metadata_filename),\n ]\n \n+ if (defined(invoker.input_root_override)) {\n+ args += [\n+ \"--input-root\",\n+ rebase_path(invoker.input_root_override),\n+ ]\n+ }\n+\n foreach(enabled_feature, enabled_features) {\n args += [\n \"--enable-feature\",\n enabled_feature,\n ]\n }\n+\n+ if (defined(invoker.webui_module_path)) {\n+ args += [\n+ \"--add-module-metadata\",\n+ \"webui_module_path=${invoker.webui_module_path}\",\n+ ]\n+ }\n }\n }\n \n@@ -705,18 +746,29 @@ template(\"mojom\") {\n \n # Generate code that is shared by different variants.\n if (sources_list != []) {\n+ base_dir = \"//\"\n+ if (defined(invoker.input_root_override)) {\n+ base_dir = invoker.input_root_override\n+ }\n+\n common_generator_args = [\n \"--use_bundled_pylibs\",\n \"-o\",\n rebase_path(root_gen_dir, root_build_dir),\n \"generate\",\n \"-d\",\n- rebase_path(\"//\", root_build_dir),\n+ rebase_path(base_dir, root_build_dir),\n \"-I\",\n rebase_path(\"//\", root_build_dir),\n \"--bytecode_path\",\n rebase_path(\"$root_gen_dir/mojo/public/tools/bindings\", root_build_dir),\n ]\n+ if (defined(invoker.input_root_override)) {\n+ common_generator_args += [\n+ \"-I\",\n+ rebase_path(invoker.input_root_override, root_build_dir),\n+ ]\n+ }\n \n if (defined(invoker.disallow_native_types) &&\n invoker.disallow_native_types) {\n@@ -767,7 +819,8 @@ template(\"mojom\") {\n }\n }\n \n- action(generator_cpp_message_ids_target_name) {\n+ # TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.\n+ python2_action(generator_cpp_message_ids_target_name) {\n script = mojom_generator_script\n inputs = mojom_generator_sources + jinja2_sources\n sources = sources_list\n@@ -806,7 +859,9 @@ template(\"mojom\") {\n }\n \n generator_shared_target_name = \"${target_name}_shared__generator\"\n- action(generator_shared_target_name) {\n+\n+ # TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.\n+ python2_action(generator_shared_target_name) {\n visibility = [ \":*\" ]\n script = mojom_generator_script\n inputs = mojom_generator_sources + jinja2_sources\n@@ -864,7 +919,7 @@ template(\"mojom\") {\n }\n \n shared_cpp_sources_target_name = \"${target_name}_shared_cpp_sources\"\n- jumbo_source_set(shared_cpp_sources_target_name) {\n+ source_set(shared_cpp_sources_target_name) {\n if (defined(invoker.testonly)) {\n testonly = invoker.testonly\n }\n@@ -925,7 +980,9 @@ template(\"mojom\") {\n \n generator_mojolpm_proto_target_name =\n \"${target_name}_mojolpm_proto_generator\"\n- action(generator_mojolpm_proto_target_name) {\n+\n+ # TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.\n+ python2_action(generator_mojolpm_proto_target_name) {\n script = mojom_generator_script\n inputs = mojom_generator_sources + jinja2_sources\n sources = invoker.sources\n@@ -960,11 +1017,10 @@ template(\"mojom\") {\n sources = process_file_template(\n invoker.sources,\n [ \"{{source_gen_dir}}/{{source_file_part}}.mojolpm.proto\" ])\n- import_dirs = [ \"${root_gen_dir}\" ]\n+ import_dirs = [ \"//\" ]\n proto_in_dir = \"${root_gen_dir}\"\n proto_out_dir = \".\"\n- proto_deps = [ \"//mojo/public/tools/fuzzers:mojolpm_proto_copy\" ]\n- proto_deps += [ \":$generator_mojolpm_proto_target_name\" ]\n+ proto_deps = [ \":$generator_mojolpm_proto_target_name\" ]\n link_deps = [ \"//mojo/public/tools/fuzzers:mojolpm_proto\" ]\n \n foreach(d, all_deps) {\n@@ -995,12 +1051,22 @@ template(\"mojom\") {\n }\n \n # Generate code for variants.\n- if (!defined(invoker.disable_variants) || !invoker.disable_variants) {\n- enabled_configurations = _bindings_configurations\n+ default_variant = {\n+ component_macro_suffix = \"\"\n+ }\n+ if ((!defined(invoker.disable_variants) || !invoker.disable_variants) &&\n+ !is_ios) {\n+ blink_variant = {\n+ variant = \"blink\"\n+ component_macro_suffix = \"_BLINK\"\n+ for_blink = true\n+ }\n+ enabled_configurations = [\n+ default_variant,\n+ blink_variant,\n+ ]\n } else {\n- first_config = _bindings_configurations[0]\n- assert(!defined(first_config.variant))\n- enabled_configurations = [ first_config ]\n+ enabled_configurations = [ default_variant ]\n }\n foreach(bindings_configuration, enabled_configurations) {\n cpp_only = false\n@@ -1018,6 +1084,11 @@ template(\"mojom\") {\n export_defines = []\n export_defines_overridden = false\n force_source_set = false\n+ proxy_target = \"\"\n+ extra_configs = []\n+ output_visibility = []\n+ output_visibility = [ \"*\" ]\n+ cpp_source_deps = []\n if (defined(bindings_configuration.for_blink) &&\n bindings_configuration.for_blink) {\n if (defined(invoker.blink_cpp_typemaps)) {\n@@ -1028,18 +1099,47 @@ template(\"mojom\") {\n export_defines = [ invoker.export_define_blink ]\n force_source_set = true\n }\n+ if (defined(invoker.blink_cpp_configs)) {\n+ extra_configs += invoker.blink_cpp_configs\n+ }\n+ if (defined(invoker.blink_cpp_proxy_target)) {\n+ proxy_target = invoker.blink_cpp_proxy_target\n+ }\n+ if (defined(invoker.visibility_blink)) {\n+ output_visibility = []\n+ output_visibility = invoker.visibility_blink\n+ }\n+ if (defined(invoker.mojom_blink_source_deps)) {\n+ cpp_source_deps = invoker.mojom_blink_source_deps\n+ }\n } else {\n if (defined(invoker.cpp_typemaps)) {\n cpp_typemap_configs = invoker.cpp_typemaps\n }\n-\n if (defined(invoker.export_define)) {\n export_defines_overridden = true\n export_defines = [ invoker.export_define ]\n force_source_set = true\n }\n+ if (defined(invoker.cpp_configs)) {\n+ extra_configs += invoker.cpp_configs\n+ }\n+ if (defined(invoker.cpp_proxy_target)) {\n+ proxy_target = invoker.cpp_proxy_target\n+ }\n+ if (defined(invoker.visibility)) {\n+ output_visibility = []\n+ output_visibility = invoker.visibility\n+ }\n+ if (defined(invoker.mojom_source_deps)) {\n+ cpp_source_deps = invoker.mojom_source_deps\n+ }\n }\n not_needed([ \"cpp_typemap_configs\" ])\n+ if (proxy_target != \"\") {\n+ group(\"${target_name}${variant_suffix}__has_cpp_proxy\") {\n+ }\n+ }\n \n if (!export_defines_overridden && defined(invoker.component_macro_prefix)) {\n output_name_override =\n@@ -1089,7 +1189,6 @@ template(\"mojom\") {\n type_mappings_target_name = \"${target_name}${variant_suffix}__type_mappings\"\n type_mappings_path =\n \"$target_gen_dir/${target_name}${variant_suffix}__type_mappings\"\n- active_typemaps = []\n if (sources_list != []) {\n generator_cpp_output_suffixes = []\n variant_dash_suffix = \"\"\n@@ -1104,20 +1203,11 @@ template(\"mojom\") {\n \"${variant_dash_suffix}.cc\",\n \"${variant_dash_suffix}.h\",\n ]\n- foreach(source, sources_list) {\n- # TODO(sammc): Use a map instead of a linear scan when GN supports maps.\n- foreach(typemap, bindings_configuration.typemaps) {\n- _typemap_config = {\n- }\n- _typemap_config = typemap.config\n- if (get_path_info(source, \"abspath\") == _typemap_config.mojom) {\n- active_typemaps += [ typemap ]\n- }\n- }\n- }\n \n generator_target_name = \"${target_name}${variant_suffix}__generator\"\n- action(generator_target_name) {\n+\n+ # TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.\n+ python2_action(generator_target_name) {\n visibility = [ \":*\" ]\n script = mojom_generator_script\n inputs = mojom_generator_sources + jinja2_sources\n@@ -1249,6 +1339,7 @@ template(\"mojom\") {\n # mojolpm only uses the no-variant type.\n \":$mojolpm_generator_target_name\",\n \":$mojolpm_proto_target_name\",\n+ \"//base\",\n \"//mojo/public/tools/fuzzers:mojolpm\",\n ]\n \n@@ -1260,18 +1351,6 @@ template(\"mojom\") {\n public_deps += [ \"${full_name}_mojolpm\" ]\n }\n \n- foreach(typemap, active_typemaps) {\n- _typemap_config = {\n- }\n- _typemap_config = typemap.config\n-\n- if (defined(_typemap_config.deps)) {\n- deps += _typemap_config.deps\n- }\n- if (defined(_typemap_config.public_deps)) {\n- public_deps += _typemap_config.public_deps\n- }\n- }\n foreach(config, cpp_typemap_configs) {\n if (defined(config.traits_deps)) {\n deps += config.traits_deps\n@@ -1309,7 +1388,9 @@ template(\"mojom\") {\n }\n write_file(_typemap_config_filename, _rebased_typemap_configs, \"json\")\n _mojom_target_name = target_name\n- action(_typemap_validator_target_name) {\n+\n+ # TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.\n+ python2_action(_typemap_validator_target_name) {\n script = \"$mojom_generator_root/validate_typemap_config.py\"\n inputs = [ _typemap_config_filename ]\n outputs = [ _typemap_stamp_filename ]\n@@ -1320,9 +1401,10 @@ template(\"mojom\") {\n ]\n }\n \n- action(type_mappings_target_name) {\n- inputs = _bindings_configuration_files + mojom_generator_sources +\n- jinja2_sources + [ _typemap_stamp_filename ]\n+ # TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.\n+ python2_action(type_mappings_target_name) {\n+ inputs =\n+ mojom_generator_sources + jinja2_sources + [ _typemap_stamp_filename ]\n outputs = [ type_mappings_path ]\n script = \"$mojom_generator_root/generate_type_mappings.py\"\n deps = [ \":$_typemap_validator_target_name\" ]\n@@ -1349,46 +1431,12 @@ template(\"mojom\") {\n ]\n }\n \n- if (sources_list != []) {\n- # TODO(sammc): Pass the typemap description in a file to avoid command\n- # line length limitations.\n- typemap_description = []\n- foreach(typemap, active_typemaps) {\n- _typemap_config = {\n- }\n- _typemap_config = typemap.config\n-\n- typemap_description += [ \"--start-typemap\" ]\n- if (defined(_typemap_config.public_headers)) {\n- foreach(value, _typemap_config.public_headers) {\n- typemap_description += [ \"public_headers=$value\" ]\n- }\n- }\n- if (defined(_typemap_config.traits_headers)) {\n- foreach(value, _typemap_config.traits_headers) {\n- typemap_description += [ \"traits_headers=$value\" ]\n- }\n- }\n- foreach(value, _typemap_config.type_mappings) {\n- typemap_description += [ \"type_mappings=$value\" ]\n- }\n-\n- # The typemap configuration files are not actually used as inputs here\n- # but this establishes a necessary build dependency to ensure that\n- # typemap changes force a rebuild of affected targets.\n- if (defined(typemap.filename)) {\n- inputs += [ typemap.filename ]\n- }\n- }\n- args += typemap_description\n-\n- # Newer GN-based typemaps are aggregated into a single config.\n- inputs += [ _typemap_config_filename ]\n- args += [\n- \"--cpp-typemap-config\",\n- rebase_path(_typemap_config_filename, root_build_dir),\n- ]\n- }\n+ # Newer GN-based typemaps are aggregated into a single config.\n+ inputs += [ _typemap_config_filename ]\n+ args += [\n+ \"--cpp-typemap-config\",\n+ rebase_path(_typemap_config_filename, root_build_dir),\n+ ]\n }\n \n group(\"${target_name}${variant_suffix}_headers\") {\n@@ -1404,32 +1452,45 @@ template(\"mojom\") {\n full_name = get_label_info(\"$d\", \"label_no_toolchain\")\n public_deps += [ \"${full_name}${variant_suffix}_headers\" ]\n }\n+ if (defined(bindings_configuration.for_blink) &&\n+ bindings_configuration.for_blink) {\n+ public_deps += [ \"//mojo/public/cpp/bindings:wtf_support\" ]\n+ }\n }\n \n+ js_data_deps_target_name = target_name + \"_js_data_deps\"\n+ not_needed([ \"js_data_deps_target_name\" ])\n+\n if (!force_source_set && defined(invoker.component_macro_prefix)) {\n- output_target_type = \"component\"\n+ sources_target_type = \"component\"\n } else {\n- output_target_type = \"source_set\"\n+ sources_target_type = \"source_set\"\n }\n \n- js_data_deps_target_name = target_name + \"_js_data_deps\"\n- not_needed([ \"js_data_deps_target_name\" ])\n+ output_target_name = \"${target_name}${variant_suffix}\"\n+ if (proxy_target != \"\") {\n+ group(output_target_name) {\n+ public_deps = [ proxy_target ]\n+ visibility = output_visibility\n+ if (defined(invoker.testonly)) {\n+ testonly = invoker.testonly\n+ }\n+ }\n+ sources_target_name = \"${output_target_name}_cpp_sources\"\n+ } else {\n+ sources_target_name = output_target_name\n+ }\n \n- target(\"jumbo_\" + output_target_type, \"${target_name}${variant_suffix}\") {\n+ target(sources_target_type, sources_target_name) {\n if (defined(output_name_override)) {\n output_name = output_name_override\n }\n- if (defined(bindings_configuration.for_blink) &&\n- bindings_configuration.for_blink &&\n- defined(invoker.visibility_blink)) {\n- visibility = invoker.visibility_blink\n- } else if (defined(invoker.visibility)) {\n- visibility = invoker.visibility\n- }\n+ visibility = output_visibility + [ \":$output_target_name\" ]\n if (defined(invoker.testonly)) {\n testonly = invoker.testonly\n }\n defines = export_defines\n+ configs += extra_configs\n if (output_file_base_paths != []) {\n sources = []\n foreach(base_path, output_file_base_paths) {\n@@ -1456,13 +1517,20 @@ template(\"mojom\") {\n if (sources_list != []) {\n public_deps += [ \":$generator_target_name\" ]\n }\n- foreach(d, all_deps) {\n+ foreach(d, mojom_cpp_deps) {\n # Resolve the name, so that a target //mojo/something becomes\n # //mojo/something:something and we can append variant_suffix to\n # get the cpp dependency name.\n- full_name = get_label_info(\"$d\", \"label_no_toolchain\")\n+ full_name = get_label_info(d, \"label_no_toolchain\")\n public_deps += [ \"${full_name}${variant_suffix}\" ]\n }\n+ foreach(d, cpp_source_deps) {\n+ full_name = get_label_info(d, \"label_no_toolchain\")\n+ public_deps += [\n+ \"${full_name}${variant_suffix}__has_cpp_proxy\",\n+ \"${full_name}${variant_suffix}_cpp_sources\",\n+ ]\n+ }\n if (defined(bindings_configuration.for_blink) &&\n bindings_configuration.for_blink) {\n if (defined(invoker.overridden_deps_blink)) {\n@@ -1493,20 +1561,6 @@ template(\"mojom\") {\n public_deps += invoker.component_deps\n }\n }\n- foreach(typemap, active_typemaps) {\n- _typemap_config = {\n- }\n- _typemap_config = typemap.config\n- if (defined(_typemap_config.sources)) {\n- sources += _typemap_config.sources\n- }\n- if (defined(_typemap_config.public_deps)) {\n- public_deps += _typemap_config.public_deps\n- }\n- if (defined(_typemap_config.deps)) {\n- deps += _typemap_config.deps\n- }\n- }\n foreach(config, cpp_typemap_configs) {\n if (defined(config.traits_sources)) {\n sources += config.traits_sources\n@@ -1518,18 +1572,10 @@ template(\"mojom\") {\n public_deps += config.traits_public_deps\n }\n }\n- if (defined(invoker.export_header)) {\n- sources += [ \"//\" + invoker.export_header ]\n- }\n if (defined(bindings_configuration.for_blink) &&\n bindings_configuration.for_blink) {\n public_deps += [ \"//mojo/public/cpp/bindings:wtf_support\" ]\n }\n-\n- if (generate_fuzzing) {\n- # Generate JS bindings by default if IPC fuzzer is enabled.\n- public_deps += [ \":$js_data_deps_target_name\" ]\n- }\n }\n \n if (generate_java && is_android) {\n@@ -1537,7 +1583,8 @@ template(\"mojom\") {\n \n java_generator_target_name = target_name + \"_java__generator\"\n if (sources_list != []) {\n- action(java_generator_target_name) {\n+ # TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.\n+ python2_action(java_generator_target_name) {\n script = mojom_generator_script\n inputs = mojom_generator_sources + jinja2_sources\n sources = sources_list\n@@ -1576,7 +1623,9 @@ template(\"mojom\") {\n }\n \n java_srcjar_target_name = target_name + \"_java_sources\"\n- action(java_srcjar_target_name) {\n+\n+ # TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.\n+ python2_action(java_srcjar_target_name) {\n script = \"//build/android/gyp/zip.py\"\n inputs = []\n if (output_file_base_paths != []) {\n@@ -1605,6 +1654,7 @@ template(\"mojom\") {\n \"//base:base_java\",\n \"//mojo/public/java:bindings_java\",\n \"//mojo/public/java:system_java\",\n+ \"//third_party/androidx:androidx_annotation_annotation_java\",\n ]\n \n # Disable warnings/checks on these generated files.\n@@ -1635,7 +1685,9 @@ template(\"mojom\") {\n !use_typescript_for_target) {\n if (sources_list != []) {\n generator_js_target_name = \"${target_name}_js__generator\"\n- action(generator_js_target_name) {\n+\n+ # TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.\n+ python2_action(generator_js_target_name) {\n script = mojom_generator_script\n inputs = mojom_generator_sources + jinja2_sources\n sources = sources_list\n@@ -1656,10 +1708,15 @@ template(\"mojom\") {\n outputs += [\n \"$root_gen_dir/$base_path.js\",\n \"$root_gen_dir/$base_path.externs.js\",\n+ \"$root_gen_dir/$base_path.m.js\",\n \"$root_gen_dir/$base_path-lite.js\",\n \"$root_gen_dir/$base_path.html\",\n \"$root_gen_dir/$base_path-lite-for-compile.js\",\n ]\n+\n+ if (defined(invoker.webui_module_path)) {\n+ outputs += [ \"$root_gen_dir/mojom-webui/$base_path-webui.js\" ]\n+ }\n }\n \n response_file_contents = filelist\n@@ -1708,13 +1765,19 @@ template(\"mojom\") {\n foreach(base_path, output_file_base_paths) {\n data += [\n \"$root_gen_dir/${base_path}.js\",\n+ \"$root_gen_dir/${base_path}.m.js\",\n \"$root_gen_dir/${base_path}-lite.js\",\n ]\n }\n deps += [ \":$generator_js_target_name\" ]\n }\n \n- data_deps = []\n+ if (defined(invoker.disallow_native_types) &&\n+ invoker.disallow_native_types) {\n+ data_deps = []\n+ } else {\n+ data_deps = [ \"//mojo/public/js:bindings_module\" ]\n+ }\n foreach(d, all_deps) {\n full_name = get_label_info(d, \"label_no_toolchain\")\n data_deps += [ \"${full_name}_js_data_deps\" ]\n@@ -1770,6 +1833,64 @@ template(\"mojom\") {\n group(js_library_for_compile_target_name) {\n }\n }\n+\n+ js_modules_target_name = \"${target_name}_js_modules\"\n+ if (sources_list != []) {\n+ js_library(js_modules_target_name) {\n+ extra_public_deps = [ \":$generator_js_target_name\" ]\n+ sources = []\n+ foreach(base_path, output_file_base_paths) {\n+ sources += [ \"$root_gen_dir/${base_path}.m.js\" ]\n+ }\n+ externs_list = [\n+ \"${externs_path}/mojo_core.js\",\n+ \"${externs_path}/pending.js\",\n+ ]\n+ if (defined(invoker.disallow_native_types) &&\n+ invoker.disallow_native_types) {\n+ deps = []\n+ } else {\n+ deps = [ \"//mojo/public/js:bindings_uncompiled\" ]\n+ }\n+ foreach(d, all_deps) {\n+ full_name = get_label_info(d, \"label_no_toolchain\")\n+ deps += [ \"${full_name}_js_modules\" ]\n+ }\n+ }\n+ } else {\n+ group(js_modules_target_name) {\n+ }\n+ }\n+\n+ if (defined(invoker.webui_module_path)) {\n+ webui_js_target_name = \"${target_name}_webui_js\"\n+ if (sources_list != []) {\n+ js_library(webui_js_target_name) {\n+ extra_public_deps = [ \":$generator_js_target_name\" ]\n+ sources = []\n+ foreach(base_path, output_file_base_paths) {\n+ sources += [ \"$root_gen_dir/mojom-webui/${base_path}-webui.js\" ]\n+ }\n+ externs_list = [\n+ \"${externs_path}/mojo_core.js\",\n+ \"${externs_path}/pending.js\",\n+ ]\n+ if (defined(invoker.disallow_native_types) &&\n+ invoker.disallow_native_types) {\n+ deps = []\n+ } else {\n+ deps = [ \"//mojo/public/js:bindings_uncompiled\" ]\n+ }\n+ foreach(d, all_deps) {\n+ full_name = get_label_info(d, \"label_no_toolchain\")\n+ deps += [ \"${full_name}_webui_js\" ]\n+ }\n+ }\n+ } else {\n+ group(webui_js_target_name) {\n+ }\n+ }\n+ }\n }\n if ((generate_fuzzing || !defined(invoker.cpp_only) || !invoker.cpp_only) &&\n use_typescript_for_target) {\n@@ -1806,7 +1927,9 @@ template(\"mojom\") {\n # Generate Typescript bindings.\n generator_ts_target_name =\n \"${target_name}_${dependency_type.name}__ts__generator\"\n- action(generator_ts_target_name) {\n+\n+ # TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.\n+ python2_action(generator_ts_target_name) {\n script = mojom_generator_script\n inputs = mojom_generator_sources + jinja2_sources\n sources = sources_list\n@@ -1873,7 +1996,8 @@ template(\"mojom\") {\n \"${target_name}_${dependency_type.name}__js__generator\"\n generator_js_target_names += [ generator_js_target_name ]\n \n- action(generator_js_target_name) {\n+ # TODO(crbug.com/1194274): Investigate nondeterminism in Py3 builds.\n+ python2_action(generator_js_target_name) {\n script = \"$mojom_generator_root/compile_typescript.py\"\n sources = ts_outputs\n outputs = js_outputs\ndiff --git a/utils/ipc/mojo/public/tools/mojom/README.md b/utils/ipc/mojo/public/tools/mojom/README.md\nindex 6a4ff78a..e5d17ab0 100644\n--- a/utils/ipc/mojo/public/tools/mojom/README.md\n+++ b/utils/ipc/mojo/public/tools/mojom/README.md\n@@ -3,7 +3,7 @@\n The Mojom format is an interface definition language (IDL) for describing\n interprocess communication (IPC) messages and data types for use with the\n low-level cross-platform\n-[Mojo IPC library](https://chromium.googlesource.com/chromium/src/+/master/mojo/public/c/system/README.md).\n+[Mojo IPC library](https://chromium.googlesource.com/chromium/src/+/main/mojo/public/c/system/README.md).\n \n This directory consists of a `mojom` Python module, its tests, and supporting\n command-line tools. The Python module implements the parser used by the\ndiff --git a/utils/ipc/mojo/public/tools/mojom/check_stable_mojom_compatibility.py b/utils/ipc/mojo/public/tools/mojom/check_stable_mojom_compatibility.py\nindex 7e746112..08bd672f 100755\n--- a/utils/ipc/mojo/public/tools/mojom/check_stable_mojom_compatibility.py\n+++ b/utils/ipc/mojo/public/tools/mojom/check_stable_mojom_compatibility.py\n@@ -131,7 +131,8 @@ def _ValidateDelta(root, delta):\n 'renamed, please add a [RenamedFrom] attribute to the new type. This '\n 'can be deleted by a subsequent change.' % qualified_name)\n \n- if not new_types[new_name].IsBackwardCompatible(kind):\n+ checker = module.BackwardCompatibilityChecker()\n+ if not checker.IsBackwardCompatible(new_types[new_name], kind):\n raise Exception('Stable type %s appears to have changed in a way which '\n 'breaks backward-compatibility. Please fix!\\n\\nIf you '\n 'believe this assessment to be incorrect, please file a '\ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom/BUILD.gn b/utils/ipc/mojo/public/tools/mojom/mojom/BUILD.gn\nindex 7416ef19..51facc0c 100644\n--- a/utils/ipc/mojo/public/tools/mojom/mojom/BUILD.gn\n+++ b/utils/ipc/mojo/public/tools/mojom/mojom/BUILD.gn\n@@ -8,7 +8,6 @@ group(\"mojom\") {\n \"error.py\",\n \"fileutil.py\",\n \"generate/__init__.py\",\n- \"generate/constant_resolver.py\",\n \"generate/generator.py\",\n \"generate/module.py\",\n \"generate/pack.py\",\ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom/generate/generator.py b/utils/ipc/mojo/public/tools/mojom/mojom/generate/generator.py\nindex de62260a..4a1c73fc 100644\n--- a/utils/ipc/mojo/public/tools/mojom/mojom/generate/generator.py\n+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/generator.py\n@@ -136,9 +136,14 @@ class Stylizer(object):\n \n def WriteFile(contents, full_path):\n # If |contents| is same with the file content, we skip updating.\n+ if not isinstance(contents, bytes):\n+ data = contents.encode('utf8')\n+ else:\n+ data = contents\n+\n if os.path.isfile(full_path):\n with open(full_path, 'rb') as destination_file:\n- if destination_file.read() == contents:\n+ if destination_file.read() == data:\n return\n \n # Make sure the containing directory exists.\n@@ -146,11 +151,8 @@ def WriteFile(contents, full_path):\n fileutil.EnsureDirectoryExists(full_dir)\n \n # Dump the data to disk.\n- with open(full_path, \"wb\") as f:\n- if not isinstance(contents, bytes):\n- f.write(contents.encode('utf-8'))\n- else:\n- f.write(contents)\n+ with open(full_path, 'wb') as f:\n+ f.write(data)\n \n \n def AddComputedData(module):\ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom/generate/module.py b/utils/ipc/mojo/public/tools/mojom/mojom/generate/module.py\nindex 8547ff64..9bdb28e0 100644\n--- a/utils/ipc/mojo/public/tools/mojom/mojom/generate/module.py\n+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/module.py\n@@ -12,7 +12,33 @@\n # method = interface.AddMethod('Tat', 0)\n # method.AddParameter('baz', 0, mojom.INT32)\n \n-import pickle\n+import sys\n+if sys.version_info.major == 2:\n+ import cPickle as pickle\n+else:\n+ import pickle\n+from uuid import UUID\n+\n+\n+class BackwardCompatibilityChecker(object):\n+ \"\"\"Used for memoization while recursively checking two type definitions for\n+ backward-compatibility.\"\"\"\n+\n+ def __init__(self):\n+ self._cache = {}\n+\n+ def IsBackwardCompatible(self, new_kind, old_kind):\n+ key = (new_kind, old_kind)\n+ result = self._cache.get(key)\n+ if result is None:\n+ # Assume they're compatible at first to effectively ignore recursive\n+ # checks between these types, e.g. if both kinds are a struct or union\n+ # that references itself in a field.\n+ self._cache[key] = True\n+ result = new_kind.IsBackwardCompatible(old_kind, self)\n+ self._cache[key] = result\n+ return result\n+\n \n # We use our own version of __repr__ when displaying the AST, as the\n # AST currently doesn't capture which nodes are reference (e.g. to\n@@ -114,6 +140,10 @@ class Kind(object):\n # during a subsequent run of the parser.\n return hash((self.spec, self.parent_kind))\n \n+ # pylint: disable=unused-argument\n+ def IsBackwardCompatible(self, rhs, checker):\n+ return self == rhs\n+\n \n class ReferenceKind(Kind):\n \"\"\"ReferenceKind represents pointer and handle types.\n@@ -195,6 +225,10 @@ class ReferenceKind(Kind):\n def __hash__(self):\n return hash((super(ReferenceKind, self).__hash__(), self.is_nullable))\n \n+ def IsBackwardCompatible(self, rhs, checker):\n+ return (super(ReferenceKind, self).IsBackwardCompatible(rhs, checker)\n+ and self.is_nullable == rhs.is_nullable)\n+\n \n # Initialize the set of primitive types. These can be accessed by clients.\n BOOL = Kind('b')\n@@ -253,9 +287,13 @@ PRIMITIVES = (\n )\n \n ATTRIBUTE_MIN_VERSION = 'MinVersion'\n+ATTRIBUTE_DEFAULT = 'Default'\n ATTRIBUTE_EXTENSIBLE = 'Extensible'\n+ATTRIBUTE_NO_INTERRUPT = 'NoInterrupt'\n ATTRIBUTE_STABLE = 'Stable'\n ATTRIBUTE_SYNC = 'Sync'\n+ATTRIBUTE_UNLIMITED_SIZE = 'UnlimitedSize'\n+ATTRIBUTE_UUID = 'Uuid'\n \n \n class NamedValue(object):\n@@ -274,6 +312,9 @@ class NamedValue(object):\n and (self.parent_kind, self.mojom_name) == (rhs.parent_kind,\n rhs.mojom_name))\n \n+ def __hash__(self):\n+ return hash((self.parent_kind, self.mojom_name))\n+\n \n class BuiltinValue(object):\n def __init__(self, value):\n@@ -368,21 +409,19 @@ class Field(object):\n \n \n class StructField(Field):\n- pass\n+ def __hash__(self):\n+ return super(Field, self).__hash__()\n \n \n class UnionField(Field):\n pass\n \n \n-def _IsFieldBackwardCompatible(new_field, old_field):\n+def _IsFieldBackwardCompatible(new_field, old_field, checker):\n if (new_field.min_version or 0) != (old_field.min_version or 0):\n return False\n \n- if isinstance(new_field.kind, (Enum, Struct, Union)):\n- return new_field.kind.IsBackwardCompatible(old_field.kind)\n-\n- return new_field.kind == old_field.kind\n+ return checker.IsBackwardCompatible(new_field.kind, old_field.kind)\n \n \n class Struct(ReferenceKind):\n@@ -457,7 +496,7 @@ class Struct(ReferenceKind):\n for constant in self.constants:\n constant.Stylize(stylizer)\n \n- def IsBackwardCompatible(self, older_struct):\n+ def IsBackwardCompatible(self, older_struct, checker):\n \"\"\"This struct is backward-compatible with older_struct if and only if all\n of the following conditions hold:\n - Any newly added field is tagged with a [MinVersion] attribute specifying\n@@ -496,7 +535,7 @@ class Struct(ReferenceKind):\n old_field = old_fields[ordinal]\n if (old_field.min_version or 0) > max_old_min_version:\n max_old_min_version = old_field.min_version\n- if not _IsFieldBackwardCompatible(new_field, old_field):\n+ if not _IsFieldBackwardCompatible(new_field, old_field, checker):\n # Type or min-version mismatch between old and new versions of the same\n # ordinal field.\n return False\n@@ -590,7 +629,7 @@ class Union(ReferenceKind):\n for field in self.fields:\n field.Stylize(stylizer)\n \n- def IsBackwardCompatible(self, older_union):\n+ def IsBackwardCompatible(self, older_union, checker):\n \"\"\"This union is backward-compatible with older_union if and only if all\n of the following conditions hold:\n - Any newly added field is tagged with a [MinVersion] attribute specifying\n@@ -623,7 +662,7 @@ class Union(ReferenceKind):\n if not new_field:\n # A field was removed, which is not OK.\n return False\n- if not _IsFieldBackwardCompatible(new_field, old_field):\n+ if not _IsFieldBackwardCompatible(new_field, old_field, checker):\n # An field changed its type or MinVersion, which is not OK.\n return False\n old_min_version = old_field.min_version or 0\n@@ -703,6 +742,10 @@ class Array(ReferenceKind):\n def __hash__(self):\n return id(self)\n \n+ def IsBackwardCompatible(self, rhs, checker):\n+ return (isinstance(rhs, Array) and self.length == rhs.length\n+ and checker.IsBackwardCompatible(self.kind, rhs.kind))\n+\n \n class Map(ReferenceKind):\n \"\"\"A map.\n@@ -747,6 +790,11 @@ class Map(ReferenceKind):\n def __hash__(self):\n return id(self)\n \n+ def IsBackwardCompatible(self, rhs, checker):\n+ return (isinstance(rhs, Map)\n+ and checker.IsBackwardCompatible(self.key_kind, rhs.key_kind)\n+ and checker.IsBackwardCompatible(self.value_kind, rhs.value_kind))\n+\n \n class PendingRemote(ReferenceKind):\n ReferenceKind.AddSharedProperty('kind')\n@@ -768,6 +816,10 @@ class PendingRemote(ReferenceKind):\n def __hash__(self):\n return id(self)\n \n+ def IsBackwardCompatible(self, rhs, checker):\n+ return (isinstance(rhs, PendingRemote)\n+ and checker.IsBackwardCompatible(self.kind, rhs.kind))\n+\n \n class PendingReceiver(ReferenceKind):\n ReferenceKind.AddSharedProperty('kind')\n@@ -789,6 +841,10 @@ class PendingReceiver(ReferenceKind):\n def __hash__(self):\n return id(self)\n \n+ def IsBackwardCompatible(self, rhs, checker):\n+ return isinstance(rhs, PendingReceiver) and checker.IsBackwardCompatible(\n+ self.kind, rhs.kind)\n+\n \n class PendingAssociatedRemote(ReferenceKind):\n ReferenceKind.AddSharedProperty('kind')\n@@ -810,6 +866,11 @@ class PendingAssociatedRemote(ReferenceKind):\n def __hash__(self):\n return id(self)\n \n+ def IsBackwardCompatible(self, rhs, checker):\n+ return isinstance(rhs,\n+ PendingAssociatedRemote) and checker.IsBackwardCompatible(\n+ self.kind, rhs.kind)\n+\n \n class PendingAssociatedReceiver(ReferenceKind):\n ReferenceKind.AddSharedProperty('kind')\n@@ -831,6 +892,11 @@ class PendingAssociatedReceiver(ReferenceKind):\n def __hash__(self):\n return id(self)\n \n+ def IsBackwardCompatible(self, rhs, checker):\n+ return isinstance(\n+ rhs, PendingAssociatedReceiver) and checker.IsBackwardCompatible(\n+ self.kind, rhs.kind)\n+\n \n class InterfaceRequest(ReferenceKind):\n ReferenceKind.AddSharedProperty('kind')\n@@ -851,6 +917,10 @@ class InterfaceRequest(ReferenceKind):\n def __hash__(self):\n return id(self)\n \n+ def IsBackwardCompatible(self, rhs, checker):\n+ return isinstance(rhs, InterfaceRequest) and checker.IsBackwardCompatible(\n+ self.kind, rhs.kind)\n+\n \n class AssociatedInterfaceRequest(ReferenceKind):\n ReferenceKind.AddSharedProperty('kind')\n@@ -873,6 +943,11 @@ class AssociatedInterfaceRequest(ReferenceKind):\n def __hash__(self):\n return id(self)\n \n+ def IsBackwardCompatible(self, rhs, checker):\n+ return isinstance(\n+ rhs, AssociatedInterfaceRequest) and checker.IsBackwardCompatible(\n+ self.kind, rhs.kind)\n+\n \n class Parameter(object):\n def __init__(self,\n@@ -976,6 +1051,16 @@ class Method(object):\n return self.attributes.get(ATTRIBUTE_SYNC) \\\n if self.attributes else None\n \n+ @property\n+ def allow_interrupt(self):\n+ return not self.attributes.get(ATTRIBUTE_NO_INTERRUPT) \\\n+ if self.attributes else True\n+\n+ @property\n+ def unlimited_message_size(self):\n+ return self.attributes.get(ATTRIBUTE_UNLIMITED_SIZE) \\\n+ if self.attributes else False\n+\n def __eq__(self, rhs):\n return (isinstance(rhs, Method) and\n (self.mojom_name, self.ordinal, self.parameters,\n@@ -1029,7 +1114,7 @@ class Interface(ReferenceKind):\n for constant in self.constants:\n constant.Stylize(stylizer)\n \n- def IsBackwardCompatible(self, older_interface):\n+ def IsBackwardCompatible(self, older_interface, checker):\n \"\"\"This interface is backward-compatible with older_interface if and only\n if all of the following conditions hold:\n - All defined methods in older_interface (when identified by ordinal) have\n@@ -1067,8 +1152,8 @@ class Interface(ReferenceKind):\n # A method was removed, which is not OK.\n return False\n \n- if not new_method.param_struct.IsBackwardCompatible(\n- old_method.param_struct):\n+ if not checker.IsBackwardCompatible(new_method.param_struct,\n+ old_method.param_struct):\n # The parameter list is not backward-compatible, which is not OK.\n return False\n \n@@ -1081,8 +1166,8 @@ class Interface(ReferenceKind):\n if new_method.response_param_struct is None:\n # A reply was removed from a message, which is not OK.\n return False\n- if not new_method.response_param_struct.IsBackwardCompatible(\n- old_method.response_param_struct):\n+ if not checker.IsBackwardCompatible(new_method.response_param_struct,\n+ old_method.response_param_struct):\n # The new message's reply is not backward-compatible with the old\n # message's reply, which is not OK.\n return False\n@@ -1120,6 +1205,20 @@ class Interface(ReferenceKind):\n self.attributes) == (rhs.mojom_name, rhs.methods, rhs.enums,\n rhs.constants, rhs.attributes))\n \n+ @property\n+ def uuid(self):\n+ uuid_str = self.attributes.get(ATTRIBUTE_UUID) if self.attributes else None\n+ if uuid_str is None:\n+ return None\n+\n+ try:\n+ u = UUID(uuid_str)\n+ except:\n+ raise ValueError('Invalid format for Uuid attribute on interface {}. '\n+ 'Expected standard RFC 4122 string representation of '\n+ 'a UUID.'.format(self.mojom_name))\n+ return (int(u.hex[:16], 16), int(u.hex[16:], 16))\n+\n def __hash__(self):\n return id(self)\n \n@@ -1144,6 +1243,11 @@ class AssociatedInterface(ReferenceKind):\n def __hash__(self):\n return id(self)\n \n+ def IsBackwardCompatible(self, rhs, checker):\n+ return isinstance(rhs,\n+ AssociatedInterface) and checker.IsBackwardCompatible(\n+ self.kind, rhs.kind)\n+\n \n class EnumField(object):\n def __init__(self,\n@@ -1160,6 +1264,11 @@ class EnumField(object):\n def Stylize(self, stylizer):\n self.name = stylizer.StylizeEnumField(self.mojom_name)\n \n+ @property\n+ def default(self):\n+ return self.attributes.get(ATTRIBUTE_DEFAULT, False) \\\n+ if self.attributes else False\n+\n @property\n def min_version(self):\n return self.attributes.get(ATTRIBUTE_MIN_VERSION) \\\n@@ -1186,6 +1295,7 @@ class Enum(Kind):\n self.attributes = attributes\n self.min_value = None\n self.max_value = None\n+ self.default_field = None\n \n def Repr(self, as_ref=True):\n if as_ref:\n@@ -1216,7 +1326,8 @@ class Enum(Kind):\n prefix = self.module.GetNamespacePrefix()\n return '%s%s' % (prefix, self.mojom_name)\n \n- def IsBackwardCompatible(self, older_enum):\n+ # pylint: disable=unused-argument\n+ def IsBackwardCompatible(self, older_enum, checker):\n \"\"\"This enum is backward-compatible with older_enum if and only if one of\n the following conditions holds:\n - Neither enum is [Extensible] and both have the exact same set of valid\n@@ -1250,9 +1361,10 @@ class Enum(Kind):\n def __eq__(self, rhs):\n return (isinstance(rhs, Enum) and\n (self.mojom_name, self.native_only, self.fields, self.attributes,\n- self.min_value,\n- self.max_value) == (rhs.mojom_name, rhs.native_only, rhs.fields,\n- rhs.attributes, rhs.min_value, rhs.max_value))\n+ self.min_value, self.max_value,\n+ self.default_field) == (rhs.mojom_name, rhs.native_only,\n+ rhs.fields, rhs.attributes, rhs.min_value,\n+ rhs.max_value, rhs.default_field))\n \n def __hash__(self):\n return id(self)\n@@ -1272,6 +1384,7 @@ class Module(object):\n self.attributes = attributes\n self.imports = []\n self.imported_kinds = {}\n+ self.metadata = {}\n \n def __repr__(self):\n # Gives us a decent __repr__ for modules.\n@@ -1285,6 +1398,9 @@ class Module(object):\n rhs.imports, rhs.constants, rhs.enums,\n rhs.structs, rhs.unions, rhs.interfaces))\n \n+ def __hash__(self):\n+ return id(self)\n+\n def Repr(self, as_ref=True):\n if as_ref:\n return '<%s path=%r mojom_namespace=%r>' % (\n@@ -1555,6 +1671,13 @@ def HasSyncMethods(interface):\n return False\n \n \n+def HasUninterruptableMethods(interface):\n+ for method in interface.methods:\n+ if not method.allow_interrupt:\n+ return True\n+ return False\n+\n+\n def ContainsHandlesOrInterfaces(kind):\n \"\"\"Check if the kind contains any handles.\n \ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom/generate/template_expander.py b/utils/ipc/mojo/public/tools/mojom/mojom/generate/template_expander.py\nindex 7a300560..0da90058 100644\n--- a/utils/ipc/mojo/public/tools/mojom/mojom/generate/template_expander.py\n+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/template_expander.py\n@@ -75,9 +75,8 @@ def PrecompileTemplates(generator_modules, output_dir):\n os.path.dirname(module.__file__), generator.GetTemplatePrefix())\n ]))\n jinja_env.filters.update(generator.GetFilters())\n- jinja_env.compile_templates(\n- os.path.join(output_dir, \"%s.zip\" % generator.GetTemplatePrefix()),\n- extensions=[\"tmpl\"],\n- zip=\"stored\",\n- py_compile=True,\n- ignore_errors=False)\n+ jinja_env.compile_templates(os.path.join(\n+ output_dir, \"%s.zip\" % generator.GetTemplatePrefix()),\n+ extensions=[\"tmpl\"],\n+ zip=\"stored\",\n+ ignore_errors=False)\ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom/generate/translate.py b/utils/ipc/mojo/public/tools/mojom/mojom/generate/translate.py\nindex d6df3ca6..7580b780 100644\n--- a/utils/ipc/mojo/public/tools/mojom/mojom/generate/translate.py\n+++ b/utils/ipc/mojo/public/tools/mojom/mojom/generate/translate.py\n@@ -472,6 +472,9 @@ def _Method(module, parsed_method, interface):\n \"attribute. If no response parameters are needed, you \"\n \"could use an empty response parameter list, i.e., \"\n \"\\\"=> ()\\\".\")\n+ # And only methods with the [Sync] attribute can specify [NoInterrupt].\n+ if not method.allow_interrupt and not method.sync:\n+ raise Exception(\"Only [Sync] methods can be marked [NoInterrupt].\")\n \n return method\n \n@@ -592,6 +595,16 @@ def _Enum(module, parsed_enum, parent_kind):\n map(lambda field: _EnumField(module, enum, field),\n parsed_enum.enum_value_list))\n _ResolveNumericEnumValues(enum)\n+ # TODO(https://crbug.com/731893): Require a default value to be\n+ # specified.\n+ for field in enum.fields:\n+ if field.default:\n+ if not enum.extensible:\n+ raise Exception('Non-extensible enums may not specify a default')\n+ if enum.default_field is not None:\n+ raise Exception(\n+ 'Only one enumerator value may be specified as the default')\n+ enum.default_field = field\n \n module.kinds[enum.spec] = enum\n \n@@ -650,7 +663,9 @@ def _CollectReferencedKinds(module, all_defined_kinds):\n if mojom.IsMapKind(kind):\n return (extract_referenced_user_kinds(kind.key_kind) +\n extract_referenced_user_kinds(kind.value_kind))\n- if mojom.IsInterfaceRequestKind(kind) or mojom.IsAssociatedKind(kind):\n+ if (mojom.IsInterfaceRequestKind(kind) or mojom.IsAssociatedKind(kind)\n+ or mojom.IsPendingRemoteKind(kind)\n+ or mojom.IsPendingReceiverKind(kind)):\n return [kind.kind]\n if mojom.IsStructKind(kind):\n return [kind]\n@@ -678,12 +693,9 @@ def _CollectReferencedKinds(module, all_defined_kinds):\n for method in interface.methods:\n for param in itertools.chain(method.parameters or [],\n method.response_parameters or []):\n- if (mojom.IsStructKind(param.kind) or mojom.IsUnionKind(param.kind)\n- or mojom.IsEnumKind(param.kind)\n- or mojom.IsAnyInterfaceKind(param.kind)):\n- for referenced_kind in extract_referenced_user_kinds(param.kind):\n- sanitized_kind = sanitize_kind(referenced_kind)\n- referenced_user_kinds[sanitized_kind.spec] = sanitized_kind\n+ for referenced_kind in extract_referenced_user_kinds(param.kind):\n+ sanitized_kind = sanitize_kind(referenced_kind)\n+ referenced_user_kinds[sanitized_kind.spec] = sanitized_kind\n \n return referenced_user_kinds\n \ndiff --git a/utils/ipc/mojo/public/tools/mojom/mojom_parser.py b/utils/ipc/mojo/public/tools/mojom/mojom_parser.py\nindex 12adbfb9..eb90c825 100755\n--- a/utils/ipc/mojo/public/tools/mojom/mojom_parser.py\n+++ b/utils/ipc/mojo/public/tools/mojom/mojom_parser.py\n@@ -14,6 +14,8 @@ import argparse\n import codecs\n import errno\n import json\n+import logging\n+import multiprocessing\n import os\n import os.path\n import sys\n@@ -25,6 +27,19 @@ from mojom.parse import parser\n from mojom.parse import conditional_features\n \n \n+# Disable this for easier debugging.\n+# In Python 2, subprocesses just hang when exceptions are thrown :(.\n+_ENABLE_MULTIPROCESSING = sys.version_info[0] > 2\n+\n+if sys.version_info < (3, 4):\n+ _MULTIPROCESSING_USES_FORK = sys.platform.startswith('linux')\n+else:\n+ # https://docs.python.org/3/library/multiprocessing.html#:~:text=bpo-33725\n+ if __name__ == '__main__' and sys.platform == 'darwin':\n+ multiprocessing.set_start_method('fork')\n+ _MULTIPROCESSING_USES_FORK = multiprocessing.get_start_method() == 'fork'\n+\n+\n def _ResolveRelativeImportPath(path, roots):\n \"\"\"Attempts to resolve a relative import path against a set of possible roots.\n \n@@ -98,7 +113,7 @@ def _GetModuleFilename(mojom_filename):\n \n \n def _EnsureInputLoaded(mojom_abspath, module_path, abs_paths, asts,\n- dependencies, loaded_modules):\n+ dependencies, loaded_modules, module_metadata):\n \"\"\"Recursively ensures that a module and its dependencies are loaded.\n \n Args:\n@@ -111,10 +126,8 @@ def _EnsureInputLoaded(mojom_abspath, module_path, abs_paths, asts,\n by absolute file path.\n loaded_modules: A mapping of all modules loaded so far, including non-input\n modules that were pulled in as transitive dependencies of the inputs.\n- import_set: The working set of mojom imports processed so far in this\n- call stack. Used to detect circular dependencies.\n- import_stack: An ordered list of imports processed so far in this call\n- stack. Used to report circular dependencies.\n+ module_metadata: Metadata to be attached to every module loaded by this\n+ helper.\n \n Returns:\n None\n@@ -129,7 +142,7 @@ def _EnsureInputLoaded(mojom_abspath, module_path, abs_paths, asts,\n for dep_abspath, dep_path in dependencies[mojom_abspath]:\n if dep_abspath not in loaded_modules:\n _EnsureInputLoaded(dep_abspath, dep_path, abs_paths, asts, dependencies,\n- loaded_modules)\n+ loaded_modules, module_metadata)\n \n imports = {}\n for imp in asts[mojom_abspath].import_list:\n@@ -137,6 +150,7 @@ def _EnsureInputLoaded(mojom_abspath, module_path, abs_paths, asts,\n imports[path] = loaded_modules[abs_paths[path]]\n loaded_modules[mojom_abspath] = translate.OrderedModule(\n asts[mojom_abspath], module_path, imports)\n+ loaded_modules[mojom_abspath].metadata = dict(module_metadata)\n \n \n def _CollectAllowedImportsFromBuildMetadata(build_metadata_filename):\n@@ -157,10 +171,67 @@ def _CollectAllowedImportsFromBuildMetadata(build_metadata_filename):\n return allowed_imports\n \n \n+# multiprocessing helper.\n+def _ParseAstHelper(args):\n+ mojom_abspath, enabled_features = args\n+ with codecs.open(mojom_abspath, encoding='utf-8') as f:\n+ ast = parser.Parse(f.read(), mojom_abspath)\n+ conditional_features.RemoveDisabledDefinitions(ast, enabled_features)\n+ return mojom_abspath, ast\n+\n+\n+# multiprocessing helper.\n+def _SerializeHelper(args):\n+ mojom_abspath, mojom_path = args\n+ module_path = os.path.join(_SerializeHelper.output_root_path,\n+ _GetModuleFilename(mojom_path))\n+ module_dir = os.path.dirname(module_path)\n+ if not os.path.exists(module_dir):\n+ try:\n+ # Python 2 doesn't support exist_ok on makedirs(), so we just ignore\n+ # that failure if it happens. It's possible during build due to races\n+ # among build steps with module outputs in the same directory.\n+ os.makedirs(module_dir)\n+ except OSError as e:\n+ if e.errno != errno.EEXIST:\n+ raise\n+ with open(module_path, 'wb') as f:\n+ _SerializeHelper.loaded_modules[mojom_abspath].Dump(f)\n+\n+\n+def _Shard(target_func, args, processes=None):\n+ args = list(args)\n+ if processes is None:\n+ processes = multiprocessing.cpu_count()\n+ # Seems optimal to have each process perform at least 2 tasks.\n+ processes = min(processes, len(args) // 2)\n+\n+ if sys.platform == 'win32':\n+ # TODO(crbug.com/1190269) - we can't use more than 56\n+ # cores on Windows or Python3 may hang.\n+ processes = min(processes, 56)\n+\n+ # Don't spin up processes unless there is enough work to merit doing so.\n+ if not _ENABLE_MULTIPROCESSING or processes < 2:\n+ for result in map(target_func, args):\n+ yield result\n+ return\n+\n+ pool = multiprocessing.Pool(processes=processes)\n+ try:\n+ for result in pool.imap_unordered(target_func, args):\n+ yield result\n+ finally:\n+ pool.close()\n+ pool.join() # Needed on Windows to avoid WindowsError during terminate.\n+ pool.terminate()\n+\n+\n def _ParseMojoms(mojom_files,\n input_root_paths,\n output_root_path,\n enabled_features,\n+ module_metadata,\n allowed_imports=None):\n \"\"\"Parses a set of mojom files and produces serialized module outputs.\n \n@@ -176,6 +247,8 @@ def _ParseMojoms(mojom_files,\n modules for any transitive dependencies not listed in mojom_files.\n enabled_features: A list of enabled feature names, controlling which AST\n nodes are filtered by [EnableIf] attributes.\n+ module_metadata: A list of 2-tuples representing metadata key-value pairs to\n+ attach to each compiled module output.\n \n Returns:\n None.\n@@ -193,72 +266,79 @@ def _ParseMojoms(mojom_files,\n for abs_path in mojom_files)\n abs_paths = dict(\n (path, abs_path) for abs_path, path in mojom_files_to_parse.items())\n- for mojom_abspath, _ in mojom_files_to_parse.items():\n- with codecs.open(mojom_abspath, encoding='utf-8') as f:\n- ast = parser.Parse(''.join(f.readlines()), mojom_abspath)\n- conditional_features.RemoveDisabledDefinitions(ast, enabled_features)\n- loaded_mojom_asts[mojom_abspath] = ast\n- invalid_imports = []\n- for imp in ast.import_list:\n- import_abspath = _ResolveRelativeImportPath(imp.import_filename,\n- input_root_paths)\n- if allowed_imports and import_abspath not in allowed_imports:\n- invalid_imports.append(imp.import_filename)\n-\n- abs_paths[imp.import_filename] = import_abspath\n- if import_abspath in mojom_files_to_parse:\n- # This import is in the input list, so we're going to translate it\n- # into a module below; however it's also a dependency of another input\n- # module. We retain record of dependencies to help with input\n- # processing later.\n- input_dependencies[mojom_abspath].add((import_abspath,\n- imp.import_filename))\n- else:\n- # We have an import that isn't being parsed right now. It must already\n- # be parsed and have a module file sitting in a corresponding output\n- # location.\n- module_path = _GetModuleFilename(imp.import_filename)\n- module_abspath = _ResolveRelativeImportPath(module_path,\n- [output_root_path])\n- with open(module_abspath, 'rb') as module_file:\n- loaded_modules[import_abspath] = module.Module.Load(module_file)\n-\n- if invalid_imports:\n- raise ValueError(\n- '\\nThe file %s imports the following files not allowed by build '\n- 'dependencies:\\n\\n%s\\n' % (mojom_abspath,\n- '\\n'.join(invalid_imports)))\n \n+ logging.info('Parsing %d .mojom into ASTs', len(mojom_files_to_parse))\n+ map_args = ((mojom_abspath, enabled_features)\n+ for mojom_abspath in mojom_files_to_parse)\n+ for mojom_abspath, ast in _Shard(_ParseAstHelper, map_args):\n+ loaded_mojom_asts[mojom_abspath] = ast\n+\n+ logging.info('Processing dependencies')\n+ for mojom_abspath, ast in loaded_mojom_asts.items():\n+ invalid_imports = []\n+ for imp in ast.import_list:\n+ import_abspath = _ResolveRelativeImportPath(imp.import_filename,\n+ input_root_paths)\n+ if allowed_imports and import_abspath not in allowed_imports:\n+ invalid_imports.append(imp.import_filename)\n+\n+ abs_paths[imp.import_filename] = import_abspath\n+ if import_abspath in mojom_files_to_parse:\n+ # This import is in the input list, so we're going to translate it\n+ # into a module below; however it's also a dependency of another input\n+ # module. We retain record of dependencies to help with input\n+ # processing later.\n+ input_dependencies[mojom_abspath].add(\n+ (import_abspath, imp.import_filename))\n+ elif import_abspath not in loaded_modules:\n+ # We have an import that isn't being parsed right now. It must already\n+ # be parsed and have a module file sitting in a corresponding output\n+ # location.\n+ module_path = _GetModuleFilename(imp.import_filename)\n+ module_abspath = _ResolveRelativeImportPath(module_path,\n+ [output_root_path])\n+ with open(module_abspath, 'rb') as module_file:\n+ loaded_modules[import_abspath] = module.Module.Load(module_file)\n+\n+ if invalid_imports:\n+ raise ValueError(\n+ '\\nThe file %s imports the following files not allowed by build '\n+ 'dependencies:\\n\\n%s\\n' % (mojom_abspath, '\\n'.join(invalid_imports)))\n+ logging.info('Loaded %d modules from dependencies', len(loaded_modules))\n \n # At this point all transitive imports not listed as inputs have been loaded\n # and we have a complete dependency tree of the unprocessed inputs. Now we can\n # load all the inputs, resolving dependencies among them recursively as we go.\n+ logging.info('Ensuring inputs are loaded')\n num_existing_modules_loaded = len(loaded_modules)\n for mojom_abspath, mojom_path in mojom_files_to_parse.items():\n _EnsureInputLoaded(mojom_abspath, mojom_path, abs_paths, loaded_mojom_asts,\n- input_dependencies, loaded_modules)\n+ input_dependencies, loaded_modules, module_metadata)\n assert (num_existing_modules_loaded +\n len(mojom_files_to_parse) == len(loaded_modules))\n \n # Now we have fully translated modules for every input and every transitive\n # dependency. We can dump the modules to disk for other tools to use.\n- for mojom_abspath, mojom_path in mojom_files_to_parse.items():\n- module_path = os.path.join(output_root_path, _GetModuleFilename(mojom_path))\n- module_dir = os.path.dirname(module_path)\n- if not os.path.exists(module_dir):\n- try:\n- # Python 2 doesn't support exist_ok on makedirs(), so we just ignore\n- # that failure if it happens. It's possible during build due to races\n- # among build steps with module outputs in the same directory.\n- os.makedirs(module_dir)\n- except OSError as e:\n- if e.errno != errno.EEXIST:\n- raise\n- with open(module_path, 'wb') as f:\n- loaded_modules[mojom_abspath].Dump(f)\n+ logging.info('Serializing %d modules', len(mojom_files_to_parse))\n+\n+ # Windows does not use fork() for multiprocessing, so we'd need to pass\n+ # loaded_module via IPC rather than via globals. Doing so is slower than not\n+ # using multiprocessing.\n+ _SerializeHelper.loaded_modules = loaded_modules\n+ _SerializeHelper.output_root_path = output_root_path\n+ # Doesn't seem to help past 4. Perhaps IO bound here?\n+ processes = 4 if _MULTIPROCESSING_USES_FORK else 0\n+ map_args = mojom_files_to_parse.items()\n+ for _ in _Shard(_SerializeHelper, map_args, processes=processes):\n+ pass\n \n \n def Run(command_line):\n+ debug_logging = os.environ.get('MOJOM_PARSER_DEBUG', '0') != '0'\n+ logging.basicConfig(level=logging.DEBUG if debug_logging else logging.WARNING,\n+ format='%(levelname).1s %(relativeCreated)6d %(message)s')\n+ logging.info('Started (%s)', os.path.basename(sys.argv[0]))\n+\n arg_parser = argparse.ArgumentParser(\n description=\"\"\"\n Parses one or more mojom files and produces corresponding module outputs fully\n@@ -333,6 +413,16 @@ already present in the provided output root.\"\"\")\n 'build-time dependency checking for mojom imports, where each build '\n 'metadata file corresponds to a build target in the dependency graph of '\n 'a typical build system.')\n+ arg_parser.add_argument(\n+ '--add-module-metadata',\n+ dest='module_metadata',\n+ default=[],\n+ action='append',\n+ metavar='KEY=VALUE',\n+ help='Adds a metadata key-value pair to the output module. This can be '\n+ 'used by build toolchains to augment parsed mojom modules with product-'\n+ 'specific metadata for later extraction and use by custom bindings '\n+ 'generators.')\n \n args, _ = arg_parser.parse_known_args(command_line)\n if args.mojom_file_list:\n@@ -353,8 +443,14 @@ already present in the provided output root.\"\"\")\n else:\n allowed_imports = None\n \n+ module_metadata = list(\n+ map(lambda kvp: tuple(kvp.split('=')), args.module_metadata))\n _ParseMojoms(mojom_files, input_roots, output_root, args.enabled_features,\n- allowed_imports)\n+ module_metadata, allowed_imports)\n+ logging.info('Finished')\n+ # Exit without running GC, which can save multiple seconds due the large\n+ # number of object created.\n+ os._exit(0)\n \n \n if __name__ == '__main__':\ndiff --git a/utils/ipc/mojo/public/tools/mojom/version_compatibility_unittest.py b/utils/ipc/mojo/public/tools/mojom/version_compatibility_unittest.py\nindex a0ee150e..65db4dc9 100644\n--- a/utils/ipc/mojo/public/tools/mojom/version_compatibility_unittest.py\n+++ b/utils/ipc/mojo/public/tools/mojom/version_compatibility_unittest.py\n@@ -2,6 +2,7 @@\n # Use of this source code is governed by a BSD-style license that can be\n # found in the LICENSE file.\n \n+from mojom.generate import module\n from mojom_parser_test_case import MojomParserTestCase\n \n \n@@ -20,9 +21,11 @@ class VersionCompatibilityTest(MojomParserTestCase):\n self.assertEqual(set(old.keys()), set(new.keys()),\n 'Old and new test mojoms should use the same type names.')\n \n+ checker = module.BackwardCompatibilityChecker()\n compatibility_map = {}\n for name in old.keys():\n- compatibility_map[name] = new[name].IsBackwardCompatible(old[name])\n+ compatibility_map[name] = checker.IsBackwardCompatible(\n+ new[name], old[name])\n return compatibility_map\n \n def assertBackwardCompatible(self, old_mojom, new_mojom):\n@@ -234,6 +237,47 @@ class VersionCompatibilityTest(MojomParserTestCase):\n self.assertNotBackwardCompatible('union U { string a; };',\n 'union U { string? a; };')\n \n+ def testFieldNestedTypeChanged(self):\n+ \"\"\"Changing the definition of a nested type within a field (such as an array\n+ element or interface endpoint type) should only break backward-compatibility\n+ if the changes to that type are not backward-compatible.\"\"\"\n+ self.assertBackwardCompatible(\n+ \"\"\"\\\n+ struct S { string a; };\n+ struct T { array<S> ss; };\n+ \"\"\", \"\"\"\\\n+ struct S {\n+ string a;\n+ [MinVersion=1] string? b;\n+ };\n+ struct T { array<S> ss; };\n+ \"\"\")\n+ self.assertBackwardCompatible(\n+ \"\"\"\\\n+ interface F { Do(); };\n+ struct S { pending_receiver<F> r; };\n+ \"\"\", \"\"\"\\\n+ interface F {\n+ Do();\n+ [MinVersion=1] Say();\n+ };\n+ struct S { pending_receiver<F> r; };\n+ \"\"\")\n+\n+ def testRecursiveTypeChange(self):\n+ \"\"\"Recursive types do not break the compatibility checker.\"\"\"\n+ self.assertBackwardCompatible(\n+ \"\"\"\\\n+ struct S {\n+ string a;\n+ array<S> others;\n+ };\"\"\", \"\"\"\\\n+ struct S {\n+ string a;\n+ array<S> others;\n+ [MinVersion=1] string? b;\n+ };\"\"\")\n+\n def testUnionFieldBecomingNonOptional(self):\n \"\"\"Changing a field from optional to non-optional breaks\n backward-compatibility.\"\"\"\ndiff --git a/utils/ipc/tools/README b/utils/ipc/tools/README\nnew file mode 100644\nindex 00000000..0450c9d0\n--- /dev/null\n+++ b/utils/ipc/tools/README\n@@ -0,0 +1,4 @@\n+# SPDX-License-Identifier: CC0-1.0\n+\n+Files in this directory are imported from 6dd20682b776 of Chromium. Do not\n+modify them manually.\n", "prefixes": [ "libcamera-devel", "v2", "2/2" ] }