Remove .typemap file support
All typemap files have been converted to inlined GN target configuration. This removes all support for aggregating .typemap files at buildgen time and any associated build logic needed to process those files. Fixed: 1059389 Change-Id: Iabf8b8680d3bd9c9e310830c36a01c1c8ca5c389 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2408274 Commit-Queue: Ken Rockot <rockot@google.com> Reviewed-by: Oksana Zhuravlova <oksamyt@chromium.org> Cr-Commit-Position: refs/heads/master@{#807277}
This commit is contained in:
mojo/public/tools
@ -1,26 +0,0 @@
|
||||
# Copyright 2016 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.
|
||||
|
||||
_typemap_imports = []
|
||||
|
||||
_typemaps = []
|
||||
foreach(typemap_import, _typemap_imports) {
|
||||
# Avoid reassignment error by assigning to empty scope first.
|
||||
_imported = {
|
||||
}
|
||||
_imported = read_file(typemap_import, "scope")
|
||||
_typemaps += _imported.typemaps
|
||||
}
|
||||
|
||||
typemaps = []
|
||||
foreach(typemap, _typemaps) {
|
||||
typemaps += [
|
||||
{
|
||||
filename = typemap
|
||||
config = read_file(typemap, "scope")
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
component_macro_suffix = ""
|
@ -1,36 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright 2016 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.
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import sys
|
||||
|
||||
# This utility converts mojom dependencies into their corresponding typemap
|
||||
# paths and formats them to be consumed by generate_type_mappings.py.
|
||||
|
||||
|
||||
def FormatTypemap(typemap_filename):
|
||||
# A simple typemap is valid Python with a minor alteration.
|
||||
with open(typemap_filename) as f:
|
||||
typemap_content = f.read().replace('=\n', '=')
|
||||
typemap = {}
|
||||
exec typemap_content in typemap
|
||||
|
||||
for header in typemap.get('public_headers', []):
|
||||
yield 'public_headers=%s' % header
|
||||
for header in typemap.get('traits_headers', []):
|
||||
yield 'traits_headers=%s' % header
|
||||
for header in typemap.get('type_mappings', []):
|
||||
yield 'type_mappings=%s' % header
|
||||
|
||||
|
||||
def main():
|
||||
typemaps = sys.argv[1:]
|
||||
print(' '.join('--start-typemap %s' % ' '.join(FormatTypemap(typemap))
|
||||
for typemap in typemaps))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
@ -75,14 +75,6 @@ def ReadTypemap(path):
|
||||
return json.load(f)['c++']
|
||||
|
||||
|
||||
def ParseTypemapArgs(args):
|
||||
typemaps = [s for s in '\n'.join(args).split('--start-typemap\n') if s]
|
||||
result = {}
|
||||
for typemap in typemaps:
|
||||
result.update(ParseTypemap(typemap))
|
||||
return result
|
||||
|
||||
|
||||
def LoadCppTypemapConfig(path):
|
||||
configs = {}
|
||||
with open(path) as f:
|
||||
@ -102,52 +94,6 @@ def LoadCppTypemapConfig(path):
|
||||
}
|
||||
return configs
|
||||
|
||||
|
||||
def ParseTypemap(typemap):
|
||||
values = {'type_mappings': [], 'public_headers': [], 'traits_headers': []}
|
||||
for line in typemap.split('\n'):
|
||||
if not line:
|
||||
continue
|
||||
key, _, value = line.partition('=')
|
||||
values[key].append(value.lstrip('/'))
|
||||
result = {}
|
||||
mapping_pattern = \
|
||||
re.compile(r"""^([^=]+) # mojom type
|
||||
=
|
||||
([^[]+) # native type
|
||||
(?:\[([^]]+)\])?$ # optional attribute in square brackets
|
||||
""", re.X)
|
||||
for typename in values['type_mappings']:
|
||||
match_result = mapping_pattern.match(typename)
|
||||
assert match_result, (
|
||||
"Cannot parse entry in the \"type_mappings\" section: %s" % typename)
|
||||
|
||||
mojom_type = match_result.group(1)
|
||||
native_type = match_result.group(2)
|
||||
attributes = []
|
||||
if match_result.group(3):
|
||||
attributes = match_result.group(3).split(',')
|
||||
|
||||
assert mojom_type not in result, (
|
||||
"Cannot map multiple native types (%s, %s) to the same mojom type: %s" %
|
||||
(result[mojom_type]['typename'], native_type, mojom_type))
|
||||
|
||||
result[mojom_type] = {
|
||||
'public_headers': values['public_headers'],
|
||||
'traits_headers': values['traits_headers'],
|
||||
'typename': native_type,
|
||||
|
||||
# Attributes supported for individual mappings.
|
||||
'copyable_pass_by_value': 'copyable_pass_by_value' in attributes,
|
||||
'force_serialize': 'force_serialize' in attributes,
|
||||
'hashable': 'hashable' in attributes,
|
||||
'move_only': 'move_only' in attributes,
|
||||
'non_copyable_non_movable': 'non_copyable_non_movable' in attributes,
|
||||
'nullable_is_same_type': 'nullable_is_same_type' in attributes,
|
||||
}
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description=__doc__,
|
||||
@ -170,10 +116,10 @@ def main():
|
||||
type=str,
|
||||
required=True,
|
||||
help='The path to which to write the generated JSON.')
|
||||
params, typemap_params = parser.parse_known_args()
|
||||
typemaps = ParseTypemapArgs(typemap_params)
|
||||
params, _ = parser.parse_known_args()
|
||||
typemaps = {}
|
||||
if params.cpp_config_path:
|
||||
typemaps.update(LoadCppTypemapConfig(params.cpp_config_path))
|
||||
typemaps = LoadCppTypemapConfig(params.cpp_config_path)
|
||||
missing = [path for path in params.dependency if not os.path.exists(path)]
|
||||
if missing:
|
||||
raise IOError('Missing dependencies: %s' % ', '.join(missing))
|
||||
|
@ -140,43 +140,6 @@ if (enable_scrambled_message_ids) {
|
||||
message_scrambling_inputs = []
|
||||
}
|
||||
|
||||
if (enable_mojom_typemapping) {
|
||||
_bindings_configuration_files =
|
||||
[ "//mojo/public/tools/bindings/chromium_bindings_configuration.gni" ]
|
||||
_bindings_configurations = []
|
||||
foreach(config_file, _bindings_configuration_files) {
|
||||
_bindings_configurations += [ read_file(config_file, "scope") ]
|
||||
}
|
||||
foreach(configuration, _bindings_configurations) {
|
||||
# Check that the mojom field of each typemap refers to a mojom that exists.
|
||||
foreach(typemap, configuration.typemaps) {
|
||||
_typemap_config = {
|
||||
}
|
||||
_typemap_config = typemap.config
|
||||
read_file(_typemap_config.mojom, "")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_bindings_configuration_files = []
|
||||
_bindings_configurations = [
|
||||
{
|
||||
typemaps = []
|
||||
component_macro_suffix = ""
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
if (!is_ios) {
|
||||
_bindings_configurations += [
|
||||
{
|
||||
variant = "blink"
|
||||
component_macro_suffix = "_BLINK"
|
||||
for_blink = true
|
||||
typemaps = []
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
# Generates targets for building C++, JavaScript and Java bindings from mojom
|
||||
# files. The output files will go under the generated file directory tree with
|
||||
# the same path as each input file.
|
||||
@ -995,12 +958,22 @@ template("mojom") {
|
||||
}
|
||||
|
||||
# Generate code for variants.
|
||||
if (!defined(invoker.disable_variants) || !invoker.disable_variants) {
|
||||
enabled_configurations = _bindings_configurations
|
||||
default_variant = {
|
||||
component_macro_suffix = ""
|
||||
}
|
||||
if ((!defined(invoker.disable_variants) || !invoker.disable_variants) &&
|
||||
!is_ios) {
|
||||
blink_variant = {
|
||||
variant = "blink"
|
||||
component_macro_suffix = "_BLINK"
|
||||
for_blink = true
|
||||
}
|
||||
enabled_configurations = [
|
||||
default_variant,
|
||||
blink_variant,
|
||||
]
|
||||
} else {
|
||||
first_config = _bindings_configurations[0]
|
||||
assert(!defined(first_config.variant))
|
||||
enabled_configurations = [ first_config ]
|
||||
enabled_configurations = [ default_variant ]
|
||||
}
|
||||
foreach(bindings_configuration, enabled_configurations) {
|
||||
cpp_only = false
|
||||
@ -1089,7 +1062,6 @@ template("mojom") {
|
||||
type_mappings_target_name = "${target_name}${variant_suffix}__type_mappings"
|
||||
type_mappings_path =
|
||||
"$target_gen_dir/${target_name}${variant_suffix}__type_mappings"
|
||||
active_typemaps = []
|
||||
if (sources_list != []) {
|
||||
generator_cpp_output_suffixes = []
|
||||
variant_dash_suffix = ""
|
||||
@ -1104,17 +1076,6 @@ template("mojom") {
|
||||
"${variant_dash_suffix}.cc",
|
||||
"${variant_dash_suffix}.h",
|
||||
]
|
||||
foreach(source, sources_list) {
|
||||
# TODO(sammc): Use a map instead of a linear scan when GN supports maps.
|
||||
foreach(typemap, bindings_configuration.typemaps) {
|
||||
_typemap_config = {
|
||||
}
|
||||
_typemap_config = typemap.config
|
||||
if (get_path_info(source, "abspath") == _typemap_config.mojom) {
|
||||
active_typemaps += [ typemap ]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
generator_target_name = "${target_name}${variant_suffix}__generator"
|
||||
action(generator_target_name) {
|
||||
@ -1260,18 +1221,6 @@ template("mojom") {
|
||||
public_deps += [ "${full_name}_mojolpm" ]
|
||||
}
|
||||
|
||||
foreach(typemap, active_typemaps) {
|
||||
_typemap_config = {
|
||||
}
|
||||
_typemap_config = typemap.config
|
||||
|
||||
if (defined(_typemap_config.deps)) {
|
||||
deps += _typemap_config.deps
|
||||
}
|
||||
if (defined(_typemap_config.public_deps)) {
|
||||
public_deps += _typemap_config.public_deps
|
||||
}
|
||||
}
|
||||
foreach(config, cpp_typemap_configs) {
|
||||
if (defined(config.traits_deps)) {
|
||||
deps += config.traits_deps
|
||||
@ -1321,8 +1270,8 @@ template("mojom") {
|
||||
}
|
||||
|
||||
action(type_mappings_target_name) {
|
||||
inputs = _bindings_configuration_files + mojom_generator_sources +
|
||||
jinja2_sources + [ _typemap_stamp_filename ]
|
||||
inputs =
|
||||
mojom_generator_sources + jinja2_sources + [ _typemap_stamp_filename ]
|
||||
outputs = [ type_mappings_path ]
|
||||
script = "$mojom_generator_root/generate_type_mappings.py"
|
||||
deps = [ ":$_typemap_validator_target_name" ]
|
||||
@ -1349,46 +1298,12 @@ template("mojom") {
|
||||
]
|
||||
}
|
||||
|
||||
if (sources_list != []) {
|
||||
# TODO(sammc): Pass the typemap description in a file to avoid command
|
||||
# line length limitations.
|
||||
typemap_description = []
|
||||
foreach(typemap, active_typemaps) {
|
||||
_typemap_config = {
|
||||
}
|
||||
_typemap_config = typemap.config
|
||||
|
||||
typemap_description += [ "--start-typemap" ]
|
||||
if (defined(_typemap_config.public_headers)) {
|
||||
foreach(value, _typemap_config.public_headers) {
|
||||
typemap_description += [ "public_headers=$value" ]
|
||||
}
|
||||
}
|
||||
if (defined(_typemap_config.traits_headers)) {
|
||||
foreach(value, _typemap_config.traits_headers) {
|
||||
typemap_description += [ "traits_headers=$value" ]
|
||||
}
|
||||
}
|
||||
foreach(value, _typemap_config.type_mappings) {
|
||||
typemap_description += [ "type_mappings=$value" ]
|
||||
}
|
||||
|
||||
# The typemap configuration files are not actually used as inputs here
|
||||
# but this establishes a necessary build dependency to ensure that
|
||||
# typemap changes force a rebuild of affected targets.
|
||||
if (defined(typemap.filename)) {
|
||||
inputs += [ typemap.filename ]
|
||||
}
|
||||
}
|
||||
args += typemap_description
|
||||
|
||||
# Newer GN-based typemaps are aggregated into a single config.
|
||||
inputs += [ _typemap_config_filename ]
|
||||
args += [
|
||||
"--cpp-typemap-config",
|
||||
rebase_path(_typemap_config_filename, root_build_dir),
|
||||
]
|
||||
}
|
||||
# Newer GN-based typemaps are aggregated into a single config.
|
||||
inputs += [ _typemap_config_filename ]
|
||||
args += [
|
||||
"--cpp-typemap-config",
|
||||
rebase_path(_typemap_config_filename, root_build_dir),
|
||||
]
|
||||
}
|
||||
|
||||
group("${target_name}${variant_suffix}_headers") {
|
||||
@ -1493,20 +1408,6 @@ template("mojom") {
|
||||
public_deps += invoker.component_deps
|
||||
}
|
||||
}
|
||||
foreach(typemap, active_typemaps) {
|
||||
_typemap_config = {
|
||||
}
|
||||
_typemap_config = typemap.config
|
||||
if (defined(_typemap_config.sources)) {
|
||||
sources += _typemap_config.sources
|
||||
}
|
||||
if (defined(_typemap_config.public_deps)) {
|
||||
public_deps += _typemap_config.public_deps
|
||||
}
|
||||
if (defined(_typemap_config.deps)) {
|
||||
deps += _typemap_config.deps
|
||||
}
|
||||
}
|
||||
foreach(config, cpp_typemap_configs) {
|
||||
if (defined(config.traits_sources)) {
|
||||
sources += config.traits_sources
|
||||
|
@ -1,453 +0,0 @@
|
||||
#! /usr/bin/env python
|
||||
# Copyright 2016 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.
|
||||
"""A generator of mojom interfaces and typemaps from Chrome IPC messages.
|
||||
|
||||
For example,
|
||||
generate_mojom.py content/common/file_utilities_messages.h
|
||||
--output_mojom=content/common/file_utilities.mojom
|
||||
--output_typemap=content/common/file_utilities.typemap
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
_MESSAGE_PATTERN = re.compile(
|
||||
r'(?:\n|^)IPC_(SYNC_)?MESSAGE_(ROUTED|CONTROL)(\d_)?(\d)')
|
||||
_VECTOR_PATTERN = re.compile(r'std::(vector|set)<(.*)>')
|
||||
_MAP_PATTERN = re.compile(r'std::map<(.*), *(.*)>')
|
||||
_NAMESPACE_PATTERN = re.compile(r'([a-z_]*?)::([A-Z].*)')
|
||||
|
||||
_unused_arg_count = 0
|
||||
|
||||
|
||||
def _git_grep(pattern, paths_pattern):
|
||||
try:
|
||||
args = ['git', 'grep', '-l', '-e', pattern, '--'] + paths_pattern
|
||||
result = subprocess.check_output(args).strip().splitlines()
|
||||
logging.debug('%s => %s', ' '.join(args), result)
|
||||
return result
|
||||
except subprocess.CalledProcessError:
|
||||
logging.debug('%s => []', ' '.join(args))
|
||||
return []
|
||||
|
||||
|
||||
def _git_multigrep(patterns, paths):
|
||||
"""Find a list of files that match all of the provided patterns."""
|
||||
if isinstance(paths, str):
|
||||
paths = [paths]
|
||||
if isinstance(patterns, str):
|
||||
patterns = [patterns]
|
||||
for pattern in patterns:
|
||||
# Search only the files that matched previous patterns.
|
||||
paths = _git_grep(pattern, paths)
|
||||
if not paths:
|
||||
return []
|
||||
return paths
|
||||
|
||||
|
||||
class Typemap(object):
|
||||
|
||||
def __init__(self, typemap_files):
|
||||
self._typemap_files = typemap_files
|
||||
self._custom_mappings = {}
|
||||
self._new_custom_mappings = {}
|
||||
self._imports = set()
|
||||
self._public_includes = set()
|
||||
self._traits_includes = set()
|
||||
self._enums = set()
|
||||
|
||||
def load_typemaps(self):
|
||||
for typemap in self._typemap_files:
|
||||
self.load_typemap(typemap)
|
||||
|
||||
def load_typemap(self, path):
|
||||
typemap = {}
|
||||
with open(path) as f:
|
||||
content = f.read().replace('=\n', '=')
|
||||
exec content in typemap
|
||||
for mapping in typemap['type_mappings']:
|
||||
mojom, native = mapping.split('=')
|
||||
self._custom_mappings[native] = {'name': mojom,
|
||||
'mojom': typemap['mojom'].strip('/')}
|
||||
|
||||
def generate_typemap(self, output_mojom, input_filename, namespace):
|
||||
new_mappings = sorted(self._format_new_mappings(namespace))
|
||||
if not new_mappings:
|
||||
return
|
||||
yield """# Copyright 2016 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.
|
||||
"""
|
||||
yield 'mojom = "//%s"' % output_mojom
|
||||
yield 'public_headers = [%s\n]' % ''.join(
|
||||
'\n "//%s",' % include for include in sorted(self._public_includes))
|
||||
yield 'traits_headers = [%s\n]' % ''.join(
|
||||
'\n "//%s",' % include
|
||||
for include in sorted(self._traits_includes.union([os.path.normpath(
|
||||
input_filename)])))
|
||||
yield 'deps = [ "//ipc" ]'
|
||||
yield 'type_mappings = [\n %s\n]' % '\n '.join(new_mappings)
|
||||
|
||||
def _format_new_mappings(self, namespace):
|
||||
for native, mojom in self._new_custom_mappings.items():
|
||||
yield '"%s.%s=::%s",' % (namespace, mojom, native)
|
||||
|
||||
def format_new_types(self):
|
||||
for native_type, typename in self._new_custom_mappings.items():
|
||||
if native_type in self._enums:
|
||||
yield '[Native]\nenum %s;\n' % typename
|
||||
else:
|
||||
yield '[Native]\nstruct %s;\n' % typename
|
||||
|
||||
_BUILTINS = {
|
||||
'bool': 'bool',
|
||||
'int': 'int32',
|
||||
'unsigned': 'uint32',
|
||||
'char': 'uint8',
|
||||
'unsigned char': 'uint8',
|
||||
'short': 'int16',
|
||||
'unsigned short': 'uint16',
|
||||
'int8_t': 'int8',
|
||||
'int16_t': 'int16',
|
||||
'int32_t': 'int32',
|
||||
'int64_t': 'int64',
|
||||
'uint8_t': 'uint8',
|
||||
'uint16_t': 'uint16',
|
||||
'uint32_t': 'uint32',
|
||||
'uint64_t': 'uint64',
|
||||
'float': 'float',
|
||||
'double': 'double',
|
||||
'std::string': 'string',
|
||||
'base::string16': 'string',
|
||||
'base::FilePath::StringType': 'string',
|
||||
'base::SharedMemoryHandle': 'handle<shared_memory>',
|
||||
'IPC::PlatformFileForTransit': 'handle',
|
||||
'base::FileDescriptor': 'handle',
|
||||
}
|
||||
|
||||
def lookup_type(self, typename):
|
||||
try:
|
||||
return self._BUILTINS[typename]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
vector_match = _VECTOR_PATTERN.search(typename)
|
||||
if vector_match:
|
||||
return 'array<%s>' % self.lookup_type(vector_match.groups()[1].strip())
|
||||
map_match = _MAP_PATTERN.search(typename)
|
||||
if map_match:
|
||||
return 'map<%s, %s>' % tuple(self.lookup_type(t.strip())
|
||||
for t in map_match.groups())
|
||||
try:
|
||||
result = self._custom_mappings[typename]['name']
|
||||
mojom = self._custom_mappings[typename].get('mojom', None)
|
||||
if mojom:
|
||||
self._imports.add(mojom)
|
||||
return result
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
match = _NAMESPACE_PATTERN.match(typename)
|
||||
if match:
|
||||
namespace, name = match.groups()
|
||||
else:
|
||||
namespace = ''
|
||||
name = typename
|
||||
namespace = namespace.replace('::', '.')
|
||||
cpp_name = name
|
||||
name = name.replace('::', '')
|
||||
|
||||
if name.endswith('Params'):
|
||||
try:
|
||||
_, name = name.rsplit('Msg_')
|
||||
except ValueError:
|
||||
try:
|
||||
_, name = name.split('_', 1)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if namespace.endswith('.mojom'):
|
||||
generated_mojom_name = '%s.%s' % (namespace, name)
|
||||
elif not namespace:
|
||||
generated_mojom_name = 'mojom.%s' % name
|
||||
else:
|
||||
generated_mojom_name = '%s.mojom.%s' % (namespace, name)
|
||||
|
||||
self._new_custom_mappings[typename] = name
|
||||
self._add_includes(namespace, cpp_name, typename)
|
||||
generated_mojom_name = name
|
||||
self._custom_mappings[typename] = {'name': generated_mojom_name}
|
||||
return generated_mojom_name
|
||||
|
||||
def _add_includes(self, namespace, name, fullname):
|
||||
name_components = name.split('::')
|
||||
is_enum = False
|
||||
for i in xrange(len(name_components)):
|
||||
subname = '::'.join(name_components[i:])
|
||||
extra_names = name_components[:i] + [subname]
|
||||
patterns = [r'\(struct\|class\|enum\)[A-Z_ ]* %s {' % s
|
||||
for s in extra_names]
|
||||
if namespace:
|
||||
patterns.extend(r'namespace %s' % namespace_component
|
||||
for namespace_component in namespace.split('.'))
|
||||
includes = _git_multigrep(patterns, '*.h')
|
||||
if includes:
|
||||
if _git_grep(r'enum[A-Z_ ]* %s {' % subname, includes):
|
||||
self._enums.add(fullname)
|
||||
is_enum = True
|
||||
logging.info('%s => public_headers = %s', fullname, includes)
|
||||
self._public_includes.update(includes)
|
||||
break
|
||||
|
||||
if is_enum:
|
||||
patterns = ['IPC_ENUM_TRAITS[A-Z_]*(%s' % fullname]
|
||||
else:
|
||||
patterns = [r'\(IPC_STRUCT_TRAITS_BEGIN(\|ParamTraits<\)%s' % fullname]
|
||||
includes = _git_multigrep(
|
||||
patterns,
|
||||
['*messages.h', '*struct_traits.h', 'ipc/ipc_message_utils.h'])
|
||||
if includes:
|
||||
logging.info('%s => traits_headers = %s', fullname, includes)
|
||||
self._traits_includes.update(includes)
|
||||
|
||||
def format_imports(self):
|
||||
for import_name in sorted(self._imports):
|
||||
yield 'import "%s";' % import_name
|
||||
if self._imports:
|
||||
yield ''
|
||||
|
||||
|
||||
class Argument(object):
|
||||
|
||||
def __init__(self, typename, name):
|
||||
self.typename = typename.strip()
|
||||
self.name = name.strip().replace('\n', '').replace(' ', '_').lower()
|
||||
if not self.name:
|
||||
global _unused_arg_count
|
||||
self.name = 'unnamed_arg%d' % _unused_arg_count
|
||||
_unused_arg_count += 1
|
||||
|
||||
def format(self, typemaps):
|
||||
return '%s %s' % (typemaps.lookup_type(self.typename), self.name)
|
||||
|
||||
|
||||
class Message(object):
|
||||
|
||||
def __init__(self, match, content):
|
||||
self.sync = bool(match[0])
|
||||
self.routed = match[1] == 'ROUTED'
|
||||
self.args = []
|
||||
self.response_args = []
|
||||
if self.sync:
|
||||
num_expected_args = int(match[2][:-1])
|
||||
num_expected_response_args = int(match[3])
|
||||
else:
|
||||
num_expected_args = int(match[3])
|
||||
num_expected_response_args = 0
|
||||
body = content.split(',')
|
||||
name = body[0].strip()
|
||||
try:
|
||||
self.group, self.name = name.split('Msg_')
|
||||
except ValueError:
|
||||
try:
|
||||
self.group, self.name = name.split('_')
|
||||
except ValueError:
|
||||
self.group = 'UnnamedInterface'
|
||||
self.name = name
|
||||
self.group = '%s%s' % (self.group, match[1].title())
|
||||
args = list(self.parse_args(','.join(body[1:])))
|
||||
if len(args) != num_expected_args + num_expected_response_args:
|
||||
raise Exception('Incorrect number of args parsed for %s' % (name))
|
||||
self.args = args[:num_expected_args]
|
||||
self.response_args = args[num_expected_args:]
|
||||
|
||||
def parse_args(self, args_str):
|
||||
args_str = args_str.strip()
|
||||
if not args_str:
|
||||
return
|
||||
looking_for_type = False
|
||||
type_start = 0
|
||||
comment_start = None
|
||||
comment_end = None
|
||||
type_end = None
|
||||
angle_bracket_nesting = 0
|
||||
i = 0
|
||||
while i < len(args_str):
|
||||
if args_str[i] == ',' and not angle_bracket_nesting:
|
||||
looking_for_type = True
|
||||
if type_end is None:
|
||||
type_end = i
|
||||
elif args_str[i:i + 2] == '/*':
|
||||
if type_end is None:
|
||||
type_end = i
|
||||
comment_start = i + 2
|
||||
comment_end = args_str.index('*/', i + 2)
|
||||
i = comment_end + 1
|
||||
elif args_str[i:i + 2] == '//':
|
||||
if type_end is None:
|
||||
type_end = i
|
||||
comment_start = i + 2
|
||||
comment_end = args_str.index('\n', i + 2)
|
||||
i = comment_end
|
||||
elif args_str[i] == '<':
|
||||
angle_bracket_nesting += 1
|
||||
elif args_str[i] == '>':
|
||||
angle_bracket_nesting -= 1
|
||||
elif looking_for_type and args_str[i].isalpha():
|
||||
if comment_start is not None and comment_end is not None:
|
||||
yield Argument(args_str[type_start:type_end],
|
||||
args_str[comment_start:comment_end])
|
||||
else:
|
||||
yield Argument(args_str[type_start:type_end], '')
|
||||
type_start = i
|
||||
type_end = None
|
||||
comment_start = None
|
||||
comment_end = None
|
||||
looking_for_type = False
|
||||
i += 1
|
||||
if comment_start is not None and comment_end is not None:
|
||||
yield Argument(args_str[type_start:type_end],
|
||||
args_str[comment_start:comment_end])
|
||||
else:
|
||||
yield Argument(args_str[type_start:type_end], '')
|
||||
|
||||
def format(self, typemaps):
|
||||
result = '%s(%s)' % (self.name, ','.join('\n %s' % arg.format(typemaps)
|
||||
for arg in self.args))
|
||||
if self.sync:
|
||||
result += ' => (%s)' % (',\n'.join('\n %s' % arg.format(typemaps)
|
||||
for arg in self.response_args))
|
||||
result = '[Sync]\n %s' % result
|
||||
return '%s;' % result
|
||||
|
||||
|
||||
class Generator(object):
|
||||
|
||||
def __init__(self, input_name, output_namespace):
|
||||
self._input_name = input_name
|
||||
with open(input_name) as f:
|
||||
self._content = f.read()
|
||||
self._namespace = output_namespace
|
||||
self._typemaps = Typemap(self._find_typemaps())
|
||||
self._interface_definitions = []
|
||||
|
||||
def _get_messages(self):
|
||||
for m in _MESSAGE_PATTERN.finditer(self._content):
|
||||
i = m.end() + 1
|
||||
while i < len(self._content):
|
||||
if self._content[i:i + 2] == '/*':
|
||||
i = self._content.index('*/', i + 2) + 1
|
||||
elif self._content[i] == ')':
|
||||
yield Message(m.groups(), self._content[m.end() + 1:i])
|
||||
break
|
||||
i += 1
|
||||
|
||||
def _extract_messages(self):
|
||||
grouped_messages = {}
|
||||
for m in self._get_messages():
|
||||
grouped_messages.setdefault(m.group, []).append(m)
|
||||
self._typemaps.load_typemaps()
|
||||
for interface, messages in grouped_messages.items():
|
||||
self._interface_definitions.append(self._format_interface(interface,
|
||||
messages))
|
||||
|
||||
def count(self):
|
||||
grouped_messages = {}
|
||||
for m in self._get_messages():
|
||||
grouped_messages.setdefault(m.group, []).append(m)
|
||||
return sum(len(messages) for messages in grouped_messages.values())
|
||||
|
||||
def generate_mojom(self):
|
||||
self._extract_messages()
|
||||
if not self._interface_definitions:
|
||||
return
|
||||
yield """// Copyright 2016 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.
|
||||
"""
|
||||
yield 'module %s;\n' % self._namespace
|
||||
for import_statement in self._typemaps.format_imports():
|
||||
yield import_statement
|
||||
for typemap in self._typemaps.format_new_types():
|
||||
yield typemap
|
||||
for interface in self._interface_definitions:
|
||||
yield interface
|
||||
yield ''
|
||||
|
||||
def generate_typemap(self, output_mojom, input_filename):
|
||||
return '\n'.join(self._typemaps.generate_typemap(
|
||||
output_mojom, input_filename, self._namespace)).strip()
|
||||
|
||||
@staticmethod
|
||||
def _find_typemaps():
|
||||
return subprocess.check_output(
|
||||
['git', 'ls-files', '*.typemap']).strip().split('\n')
|
||||
|
||||
def _format_interface(self, name, messages):
|
||||
return 'interface %s {\n %s\n};' % (name,
|
||||
'\n '.join(m.format(self._typemaps)
|
||||
for m in messages))
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('input', help='input messages.h file')
|
||||
parser.add_argument(
|
||||
'--output_namespace',
|
||||
default='mojom',
|
||||
help='the mojom module name to use in the generated mojom file '
|
||||
'(default: %(default)s)')
|
||||
parser.add_argument('--output_mojom', help='output mojom path')
|
||||
parser.add_argument('--output_typemap', help='output typemap path')
|
||||
parser.add_argument(
|
||||
'--count',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='count the number of messages in the input instead of generating '
|
||||
'a mojom file')
|
||||
parser.add_argument('-v',
|
||||
'--verbose',
|
||||
action='store_true',
|
||||
help='enable logging')
|
||||
parser.add_argument('-vv', action='store_true', help='enable debug logging')
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
if args.vv:
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
elif args.verbose:
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
generator = Generator(args.input, args.output_namespace)
|
||||
if args.count:
|
||||
count = generator.count()
|
||||
if count:
|
||||
print('%d %s' % (generator.count(), args.input))
|
||||
return
|
||||
mojom = '\n'.join(generator.generate_mojom()).strip()
|
||||
if not mojom:
|
||||
return
|
||||
typemap = generator.generate_typemap(args.output_mojom, args.input)
|
||||
|
||||
if args.output_mojom:
|
||||
with open(args.output_mojom, 'w') as f:
|
||||
f.write(mojom)
|
||||
else:
|
||||
print(mojom)
|
||||
if typemap:
|
||||
if args.output_typemap:
|
||||
with open(args.output_typemap, 'w') as f:
|
||||
f.write(typemap)
|
||||
else:
|
||||
print(typemap)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
Reference in New Issue
Block a user