[mojo] Add typemap and variant support to generators
This introduces two new flags for the mojom bindings generator:
--typemap can be used to provide typemap (JSON) files
to the bindings generator.
--variant specifies the name of the bindings variant to emit.
Both of these new flags only affect C++ generation, and specifying
--variant at all prevents JS or Java bindings from being emitted.
Both of these new flags have GN mojom template variables associated
with them as well.
Part of a series of changes to support custom mojom serialization:
1. https://codereview.chromium.org/1515423002
2. This CL
3. https://codereview.chromium.org/1524693002
4. https://codereview.chromium.org/1520153002
5. https://codereview.chromium.org/1524613002
6. https://codereview.chromium.org/1526533002
7. https://codereview.chromium.org/1524703002
BUG=569669
Review URL: https://codereview.chromium.org/1517043004
Cr-Commit-Position: refs/heads/master@{#365674}
CrOS-Libchrome-Original-Commit: 103fd974609482f3179a7a2c15d2d21760524fb2
diff --git a/mojo/public/interfaces/bindings/tests/blink_test.typemap b/mojo/public/interfaces/bindings/tests/blink_test.typemap
new file mode 100644
index 0000000..511a549
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/blink_test.typemap
@@ -0,0 +1,14 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+{
+ "c++": {
+ "mojo.test.PickledStruct": {
+ "typename": "mojo::test::PickledStructBlink",
+ "headers": [
+ "mojo/public/cpp/bindings/tests/pickled_struct_blink.h"
+ ]
+ }
+ }
+}
diff --git a/mojo/public/interfaces/bindings/tests/chromium_test.typemap b/mojo/public/interfaces/bindings/tests/chromium_test.typemap
new file mode 100644
index 0000000..1028982
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/chromium_test.typemap
@@ -0,0 +1,14 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+{
+ "c++": {
+ "mojo.test.PickledStruct": {
+ "typename": "mojo::test::PickledStructChromium",
+ "headers": [
+ "mojo/public/cpp/bindings/tests/pickled_struct_chromium.h"
+ ]
+ }
+ }
+}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module-internal.h.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module-internal.h.tmpl
index 2d748fc..a3be5a4 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/module-internal.h.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module-internal.h.tmpl
@@ -2,8 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-{%- set header_guard = "%s_INTERNAL_H_"|
- format(module.path|upper|replace("/","_")|replace(".","_")) %}
+{%- if variant -%}
+{%- set variant_path = "%s-%s"|format(module.path, variant) -%}
+{%- else -%}
+{%- set variant_path = module.path -%}
+{%- endif -%}
+
+{%- set header_guard = "%s_INTERNAL_H_"|format(
+ variant_path|upper|replace("/","_")|replace(".","_")|
+ replace("-", "_")) %}
#ifndef {{header_guard}}
#define {{header_guard}}
@@ -27,6 +34,9 @@
{%- for namespace in namespaces_as_array %}
namespace {{namespace}} {
{%- endfor %}
+{%- if variant %}
+namespace {{variant}} {
+{%- endif %}
{#--- Wrapper forward declarations #}
{% for struct in structs %}
@@ -65,6 +75,9 @@
#pragma pack(pop)
} // namespace internal
+{%- if variant %}
+} // namespace {{variant}}
+{%- endif %}
{%- for namespace in namespaces_as_array|reverse %}
} // namespace {{namespace}}
{%- endfor %}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl
index dddb6ab..fd2f610 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl
@@ -2,6 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+{%- if variant -%}
+{%- set variant_path = "%s-%s"|format(module.path, variant) -%}
+{%- else -%}
+{%- set variant_path = module.path -%}
+{%- endif %}
+
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-private-field"
@@ -12,7 +18,7 @@
#pragma warning(disable:4756)
#endif
-#include "{{module.path}}.h"
+#include "{{variant_path}}.h"
#include <math.h>
@@ -34,6 +40,9 @@
{%- for namespace in namespaces_as_array %}
namespace {{namespace}} {
{%- endfor %}
+{%- if variant %}
+namespace {{variant}} {
+{%- endif %}
{#--- Constants #}
{%- for constant in module.constants %}
@@ -121,6 +130,9 @@
{%- include "union_serialization_definition.tmpl" %}
{%- endfor %}
+{%- if variant %}
+} // namespace {{variant}}
+{%- endif %}
{%- for namespace in namespaces_as_array|reverse %}
} // namespace {{namespace}}
{%- endfor %}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl
index 4b1e658..c54668d 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl
@@ -2,8 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-{%- set header_guard = "%s_H_"|
- format(module.path|upper|replace("/","_")|replace(".","_")) %}
+{%- if variant -%}
+{%- set variant_path = "%s-%s"|format(module.path, variant) -%}
+{%- else -%}
+{%- set variant_path = module.path -%}
+{%- endif -%}
+
+{%- set header_guard = "%s_H_"|format(
+ variant_path|upper|replace("/","_")|replace(".","_")|
+ replace("-", "_")) %}
#ifndef {{header_guard}}
#define {{header_guard}}
@@ -24,7 +31,7 @@
#include "mojo/public/cpp/bindings/no_interface.h"
#include "mojo/public/cpp/bindings/string.h"
#include "mojo/public/cpp/bindings/struct_ptr.h"
-#include "{{module.path}}-internal.h"
+#include "{{variant_path}}-internal.h"
{%- for import in imports %}
#include "{{import.module.path}}.h"
{%- endfor %}
@@ -32,6 +39,9 @@
{%- for namespace in namespaces_as_array %}
namespace {{namespace}} {
{%- endfor %}
+{%- if variant %}
+namespace {{variant}} {
+{%- endif %}
{#--- Enums #}
{% from "enum_macros.tmpl" import enum_decl -%}
@@ -139,6 +149,9 @@
{%- endfor %}
{%- endif %}
+{%- if variant %}
+} // namespace {{variant}}
+{%- endif %}
{%- for namespace in namespaces_as_array|reverse %}
} // namespace {{namespace}}
{%- endfor %}
diff --git a/mojo/public/tools/bindings/generators/mojom_cpp_generator.py b/mojo/public/tools/bindings/generators/mojom_cpp_generator.py
index 8b153a5..4c63270 100644
--- a/mojo/public/tools/bindings/generators/mojom_cpp_generator.py
+++ b/mojo/public/tools/bindings/generators/mojom_cpp_generator.py
@@ -429,6 +429,7 @@
"structs": self.GetStructs(),
"unions": self.GetUnions(),
"interfaces": self.GetInterfaces(),
+ "variant": self.variant,
}
@UseJinja("cpp_templates/module.h.tmpl", filters=cpp_filters)
@@ -444,9 +445,10 @@
return self.GetJinjaExports()
def GenerateFiles(self, args):
+ suffix = "-%s" % self.variant if self.variant else ""
self.Write(self.GenerateModuleHeader(),
- self.MatchMojomFilePath("%s.h" % self.module.name))
+ self.MatchMojomFilePath("%s%s.h" % (self.module.name, suffix)))
self.Write(self.GenerateModuleInternalHeader(),
- self.MatchMojomFilePath("%s-internal.h" % self.module.name))
+ self.MatchMojomFilePath("%s%s-internal.h" % (self.module.name, suffix)))
self.Write(self.GenerateModuleSource(),
- self.MatchMojomFilePath("%s.cc" % self.module.name))
+ self.MatchMojomFilePath("%s%s.cc" % (self.module.name, suffix)))
diff --git a/mojo/public/tools/bindings/generators/mojom_java_generator.py b/mojo/public/tools/bindings/generators/mojom_java_generator.py
index c091a5f..5edaf94 100644
--- a/mojo/public/tools/bindings/generators/mojom_java_generator.py
+++ b/mojo/public/tools/bindings/generators/mojom_java_generator.py
@@ -503,6 +503,10 @@
'%s.java' % GetConstantsMainEntityName(self.module))
def GenerateFiles(self, unparsed_args):
+ # TODO(rockot): Support variant output for Java.
+ if self.variant:
+ raise Exception("Variants not supported in Java bindings.")
+
parser = argparse.ArgumentParser()
parser.add_argument('--java_output_directory', dest='java_output_directory')
args = parser.parse_args(unparsed_args)
diff --git a/mojo/public/tools/bindings/generators/mojom_js_generator.py b/mojo/public/tools/bindings/generators/mojom_js_generator.py
index 23c203a..638f1cb 100644
--- a/mojo/public/tools/bindings/generators/mojom_js_generator.py
+++ b/mojo/public/tools/bindings/generators/mojom_js_generator.py
@@ -385,6 +385,9 @@
return self.GetParameters()
def GenerateFiles(self, args):
+ if self.variant:
+ raise Exception("Variants not supported in JavaScript bindings.")
+
self.Write(self.GenerateAMDModule(),
self.MatchMojomFilePath("%s.js" % self.module.name))
diff --git a/mojo/public/tools/bindings/mojom.gni b/mojo/public/tools/bindings/mojom.gni
index 174e5b8..61f43e5 100644
--- a/mojo/public/tools/bindings/mojom.gni
+++ b/mojo/public/tools/bindings/mojom.gni
@@ -25,6 +25,13 @@
# Mojo environment implementation. Defaults to |true| and in general
# should only be overridden by mojom targets within the Mojo EDK.
#
+# typemaps (optional)
+# A list of typemap files to apply during bindings generation.
+#
+# variant (optional)
+# A variant name to apply to generated bindings. Variant influences
+# generated source filenames as wells the symbols they define.
+#
# testonly (optional)
#
# visibility (optional)
@@ -102,15 +109,26 @@
"$generator_root/pylib/mojom/parse/parser.py",
"$generator_root/pylib/mojom/parse/translate.py",
]
- generator_cpp_outputs = [
- "{{source_gen_dir}}/{{source_name_part}}.mojom.cc",
- "{{source_gen_dir}}/{{source_name_part}}.mojom.h",
- "{{source_gen_dir}}/{{source_name_part}}.mojom-internal.h",
- ]
- generator_js_outputs =
- [ "{{source_gen_dir}}/{{source_name_part}}.mojom.js" ]
- generator_java_outputs =
- [ "{{source_gen_dir}}/{{source_name_part}}.mojom.srcjar" ]
+ if (defined(invoker.variant)) {
+ variant = invoker.variant
+ generator_cpp_outputs = [
+ "{{source_gen_dir}}/{{source_name_part}}.mojom-${variant}.cc",
+ "{{source_gen_dir}}/{{source_name_part}}.mojom-${variant}.h",
+ "{{source_gen_dir}}/{{source_name_part}}.mojom-${variant}-internal.h",
+ ]
+ generator_js_outputs = []
+ generator_java_outputs = []
+ } else {
+ generator_cpp_outputs = [
+ "{{source_gen_dir}}/{{source_name_part}}.mojom.cc",
+ "{{source_gen_dir}}/{{source_name_part}}.mojom.h",
+ "{{source_gen_dir}}/{{source_name_part}}.mojom-internal.h",
+ ]
+ generator_js_outputs =
+ [ "{{source_gen_dir}}/{{source_name_part}}.mojom.js" ]
+ generator_java_outputs =
+ [ "{{source_gen_dir}}/{{source_name_part}}.mojom.srcjar" ]
+ }
}
if (defined(invoker.sources)) {
@@ -140,6 +158,29 @@
]
}
}
+
+ if (defined(invoker.variant)) {
+ args += [
+ "--variant",
+ invoker.variant,
+ "-g",
+ "c++",
+ ]
+ } else {
+ args += [
+ "-g",
+ "c++,javascript,java",
+ ]
+ }
+
+ if (defined(invoker.typemaps)) {
+ foreach(typemap, invoker.typemaps) {
+ args += [
+ "--typemap",
+ rebase_path(typemap, root_build_dir),
+ ]
+ }
+ }
}
}
@@ -150,7 +191,7 @@
if (defined(invoker.testonly)) {
testonly = invoker.testonly
}
- if (defined(invoker.sources)) {
+ if (defined(invoker.sources) && !defined(invoker.variant)) {
data = process_file_template(invoker.sources, generator_js_outputs)
}
@@ -225,7 +266,7 @@
}
}
- if (is_android) {
+ if (is_android && !defined(invoker.variant)) {
import("//build/config/android/rules.gni")
java_srcjar_target_name = target_name + "_java_sources"
diff --git a/mojo/public/tools/bindings/mojom_bindings_generator.py b/mojo/public/tools/bindings/mojom_bindings_generator.py
index 96098e0..83ab590 100755
--- a/mojo/public/tools/bindings/mojom_bindings_generator.py
+++ b/mojo/public/tools/bindings/mojom_bindings_generator.py
@@ -8,8 +8,10 @@
import argparse
import imp
+import json
import os
import pprint
+import re
import sys
# Disable lint check for finding modules:
@@ -40,32 +42,29 @@
from mojom.parse.translate import Translate
+_BUILTIN_GENERATORS = {
+ "c++": "mojom_cpp_generator.py",
+ "javascript": "mojom_js_generator.py",
+ "java": "mojom_java_generator.py",
+}
+
def LoadGenerators(generators_string):
if not generators_string:
return [] # No generators.
script_dir = os.path.dirname(os.path.abspath(__file__))
- generators = []
+ generators = {}
for generator_name in [s.strip() for s in generators_string.split(",")]:
- # "Built-in" generators:
- if generator_name.lower() == "c++":
+ language = generator_name.lower()
+ if language in _BUILTIN_GENERATORS:
generator_name = os.path.join(script_dir, "generators",
- "mojom_cpp_generator.py")
- elif generator_name.lower() == "javascript":
- generator_name = os.path.join(script_dir, "generators",
- "mojom_js_generator.py")
- elif generator_name.lower() == "java":
- generator_name = os.path.join(script_dir, "generators",
- "mojom_java_generator.py")
- # Specified generator python module:
- elif generator_name.endswith(".py"):
- pass
+ _BUILTIN_GENERATORS[language])
else:
print "Unknown generator name %s" % generator_name
sys.exit(1)
generator_module = imp.load_source(os.path.basename(generator_name)[:-3],
generator_name)
- generators.append(generator_module)
+ generators[language] = generator_module
return generators
@@ -89,6 +88,20 @@
self._should_generate = should_generate
self._processed_files = {}
self._parsed_files = {}
+ self._typemap = {}
+
+ def LoadTypemaps(self, typemaps):
+ # Support some very simple single-line comments in typemap JSON.
+ comment_expr = r"^\s*//.*$"
+ def no_comments(line):
+ return not re.match(comment_expr, line)
+ for filename in typemaps:
+ with open(filename) as f:
+ typemaps = json.loads("".join(filter(no_comments, f.readlines())))
+ for language, typemap in typemaps.iteritems():
+ language_map = self._typemap.get(language, {})
+ language_map.update(typemap)
+ self._typemap[language] = language_map
def ProcessFile(self, args, remaining_args, generator_modules, filename):
self._ParseFileAndImports(filename, args.import_directories, [])
@@ -126,8 +139,10 @@
module.path = module.path.replace('\\', '/')
if self._should_generate(filename):
- for generator_module in generator_modules:
- generator = generator_module.Generator(module, args.output_dir)
+ for language, generator_module in generator_modules.iteritems():
+ generator = generator_module.Generator(
+ module, args.output_dir, typemap=self._typemap.get(language, {}),
+ variant=args.variant)
filtered_args = []
if hasattr(generator_module, 'GENERATOR_PREFIX'):
prefix = '--' + generator_module.GENERATOR_PREFIX + '_'
@@ -195,13 +210,22 @@
help="add a directory to be searched for import files")
parser.add_argument("--use_bundled_pylibs", action="store_true",
help="use Python modules bundled in the SDK")
+ parser.add_argument("--typemap", action="append", metavar="TYPEMAP",
+ default=[], dest="typemaps",
+ help="apply TYPEMAP to generated output")
+ parser.add_argument("--variant", dest="variant", default=None,
+ help="output a named variant of the bindings")
(args, remaining_args) = parser.parse_known_args()
+ if args.variant == "none":
+ args.variant = None
+
generator_modules = LoadGenerators(args.generators_string)
fileutil.EnsureDirectoryExists(args.output_dir)
processor = MojomProcessor(lambda filename: filename in args.filename)
+ processor.LoadTypemaps(set(args.typemaps))
for filename in args.filename:
processor.ProcessFile(args, remaining_args, generator_modules, filename)
diff --git a/mojo/public/tools/bindings/mojom_list_outputs.py b/mojo/public/tools/bindings/mojom_list_outputs.py
index ed8bdfa..267bd80 100755
--- a/mojo/public/tools/bindings/mojom_list_outputs.py
+++ b/mojo/public/tools/bindings/mojom_list_outputs.py
@@ -11,20 +11,32 @@
parser = argparse.ArgumentParser(
description="GYP helper script for mapping mojoms => generated outputs.")
parser.add_argument("--basedir", required=True)
+ parser.add_argument("--variant", required=True)
parser.add_argument("mojom", nargs="*")
args = parser.parse_args()
+ variant = args.variant if args.variant != "none" else None
+
for mojom in args.mojom:
full = os.path.join("<(SHARED_INTERMEDIATE_DIR)", args.basedir, mojom)
base, ext = os.path.splitext(full)
- assert ext == ".mojom", mojom
+
+ # Ignore non-mojom files.
+ if ext != ".mojom":
+ continue
+
# Fix filename escaping issues on Windows.
base = base.replace("\\", "/")
- print base + ".mojom.cc"
- print base + ".mojom.h"
- print base + ".mojom-internal.h"
- print base + ".mojom.js"
+ if variant:
+ print base + ".mojom-%s.cc" % variant
+ print base + ".mojom-%s.h" % variant
+ print base + ".mojom-%s-internal.h" % variant
+ else:
+ print base + ".mojom.cc"
+ print base + ".mojom.h"
+ print base + ".mojom-internal.h"
+ print base + ".mojom.js"
return 0
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/generator.py b/mojo/public/tools/bindings/pylib/mojom/generate/generator.py
index 666ef43..8c3e642 100644
--- a/mojo/public/tools/bindings/pylib/mojom/generate/generator.py
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/generator.py
@@ -40,9 +40,11 @@
class Generator(object):
# Pass |output_dir| to emit files to disk. Omit |output_dir| to echo all
# files to stdout.
- def __init__(self, module, output_dir=None):
+ def __init__(self, module, output_dir=None, typemap={}, variant=None):
self.module = module
self.output_dir = output_dir
+ self.typemap = typemap
+ self.variant = variant
def GetStructsFromMethods(self):
result = []