0

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:
Ken Rockot
2020-09-16 00:57:48 +00:00
committed by Commit Bot
parent ce94b50c9e
commit 6160192248
5 changed files with 26 additions and 694 deletions

@ -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())