1808 lines
69 KiB
Python
Executable File
1808 lines
69 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# Copyright (C) 2022 The Android Open Source Project
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
# This tool translates a collection of BUILD.gn files into a mostly equivalent
|
|
# Android.bp file for the Android Soong build system. The input to the tool is a
|
|
# JSON description of the GN build definition generated with the following
|
|
# command:
|
|
#
|
|
# gn desc out --format=json --all-toolchains "//*" > desc.json
|
|
#
|
|
# The tool is then given a list of GN labels for which to generate Android.bp
|
|
# build rules. The dependencies for the GN labels are squashed to the generated
|
|
# Android.bp target, except for actions which get their own genrule. Some
|
|
# libraries are also mapped to their Android equivalents -- see |builtin_deps|.
|
|
|
|
import argparse
|
|
import json
|
|
import logging as log
|
|
import operator
|
|
import os
|
|
import re
|
|
import sys
|
|
import copy
|
|
from pathlib import Path
|
|
|
|
import gn_utils
|
|
|
|
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
|
|
CRONET_LICENSE_NAME = "external_cronet_license"
|
|
|
|
# Default targets to translate to the blueprint file.
|
|
DEFAULT_TARGETS = [
|
|
'//components/cronet/android:cronet',
|
|
'//components/cronet/android:cronet_android_mainline',
|
|
]
|
|
|
|
DEFAULT_TESTS = [
|
|
'//components/cronet/android:cronet_unittests_android__library',
|
|
'//net:net_unittests__library',
|
|
'//components/cronet/android:cronet_tests',
|
|
]
|
|
|
|
EXTRAS_ANDROID_BP_FILE = "Android.extras.bp"
|
|
|
|
CRONET_API_MODULE_NAME = "cronet_aml_api_java"
|
|
|
|
# All module names are prefixed with this string to avoid collisions.
|
|
module_prefix = 'cronet_aml_'
|
|
|
|
# Shared libraries which are directly translated to Android system equivalents.
|
|
shared_library_allowlist = [
|
|
'android',
|
|
'log',
|
|
]
|
|
|
|
# Include directories that will be removed from all targets.
|
|
local_include_dirs_denylist = [
|
|
'third_party/zlib/',
|
|
]
|
|
|
|
# Name of the module which settings such as compiler flags for all other
|
|
# modules.
|
|
defaults_module = module_prefix + 'defaults'
|
|
|
|
# Location of the project in the Android source tree.
|
|
tree_path = 'external/cronet'
|
|
|
|
# Path for the protobuf sources in the standalone build.
|
|
buildtools_protobuf_src = '//buildtools/protobuf/src'
|
|
|
|
# Location of the protobuf src dir in the Android source tree.
|
|
android_protobuf_src = 'external/protobuf/src'
|
|
|
|
# put all args on a new line for better diffs.
|
|
NEWLINE = ' " +\n "'
|
|
|
|
# Compiler flags which are passed through to the blueprint.
|
|
cflag_allowlist = [
|
|
# needed for zlib:zlib
|
|
"-mpclmul",
|
|
# needed for zlib:zlib
|
|
"-mssse3",
|
|
# needed for zlib:zlib
|
|
"-msse3",
|
|
# needed for zlib:zlib
|
|
"-msse4.2",
|
|
# flags to reduce binary size
|
|
"-O1",
|
|
"-O2",
|
|
"-O3",
|
|
"-Oz",
|
|
"-g1",
|
|
"-g2",
|
|
"-fdata-sections",
|
|
"-ffunction-sections",
|
|
"-fvisibility=hidden",
|
|
"-fvisibility-inlines-hidden",
|
|
"-fstack-protector",
|
|
"-mno-outline",
|
|
"-mno-outline-atomics",
|
|
"-fno-asynchronous-unwind-tables",
|
|
"-fno-unwind-tables",
|
|
]
|
|
|
|
# Linker flags which are passed through to the blueprint.
|
|
ldflag_allowlist = [
|
|
# flags to reduce binary size
|
|
"-Wl,--as-needed",
|
|
"-Wl,--gc-sections",
|
|
"-Wl,--icf=all",
|
|
]
|
|
|
|
def get_linker_script_ldflag(script_path):
|
|
return f'-Wl,--script,{tree_path}/{script_path}'
|
|
|
|
# Additional arguments to apply to Android.bp rules.
|
|
additional_args = {
|
|
'cronet_aml_net_third_party_quiche_net_quic_test_tools_proto_gen_headers': [
|
|
('export_include_dirs', {
|
|
"net/third_party/quiche/src",
|
|
})
|
|
],
|
|
'cronet_aml_net_third_party_quiche_net_quic_test_tools_proto__testing_gen_headers': [
|
|
('export_include_dirs', {
|
|
"net/third_party/quiche/src",
|
|
})
|
|
],
|
|
'cronet_aml_third_party_quic_trace_quic_trace_proto__testing_gen_headers': [
|
|
('export_include_dirs', {
|
|
"third_party/quic_trace/src",
|
|
})
|
|
],
|
|
'cronet_aml_net_net': [
|
|
('export_static_lib_headers', {
|
|
'cronet_aml_net_third_party_quiche_quiche',
|
|
'cronet_aml_crypto_crypto',
|
|
}),
|
|
],
|
|
# TODO: fix upstream. Both //base:base and
|
|
# //base/allocator/partition_allocator:partition_alloc do not create a
|
|
# dependency on gtest despite using gtest_prod.h.
|
|
'cronet_aml_base_base': [
|
|
('header_libs', {
|
|
'libgtest_prod_headers',
|
|
}),
|
|
('export_header_lib_headers', {
|
|
'libgtest_prod_headers',
|
|
}),
|
|
],
|
|
'cronet_aml_base_allocator_partition_allocator_partition_alloc': [
|
|
('header_libs', {
|
|
'libgtest_prod_headers',
|
|
}),
|
|
],
|
|
}
|
|
|
|
def always_disable(module, arch):
|
|
return None
|
|
|
|
def enable_zlib(module, arch):
|
|
# Requires crrev/c/4109079
|
|
if arch == 'common':
|
|
module.shared_libs.add('libz')
|
|
else:
|
|
module.target[arch].shared_libs.add('libz')
|
|
|
|
def enable_boringssl(module, arch):
|
|
# Do not add boringssl targets to cc_genrules. This happens, because protobuf targets are
|
|
# originally static_libraries, but later get converted to a cc_genrule.
|
|
if module.is_genrule(): return
|
|
if arch == 'common':
|
|
shared_libs = module.shared_libs
|
|
else:
|
|
shared_libs = module.target[arch].shared_libs
|
|
shared_libs.add('//external/cronet/third_party/boringssl:libcrypto')
|
|
shared_libs.add('//external/cronet/third_party/boringssl:libssl')
|
|
|
|
# Android equivalents for third-party libraries that the upstream project
|
|
# depends on.
|
|
builtin_deps = {
|
|
'//buildtools/third_party/libunwind:libunwind':
|
|
always_disable,
|
|
'//buildtools/third_party/libunwind:libunwind__testing':
|
|
always_disable,
|
|
'//net/data/ssl/chrome_root_store:gen_root_store_inc':
|
|
always_disable,
|
|
'//net/data/ssl/chrome_root_store:gen_root_store_inc__testing':
|
|
always_disable,
|
|
'//net/tools/root_store_tool:root_store_tool':
|
|
always_disable,
|
|
'//net/tools/root_store_tool:root_store_tool__testing':
|
|
always_disable,
|
|
'//third_party/zlib:zlib':
|
|
enable_zlib,
|
|
'//third_party/zlib:zlib__testing':
|
|
enable_zlib,
|
|
'//third_party/boringssl:boringssl':
|
|
enable_boringssl,
|
|
'//third_party/boringssl:boringssl_asm':
|
|
# Due to FIPS requirements, downstream BoringSSL has a different "shape" than upstream's.
|
|
# We're guaranteed that if X depends on :boringssl it will also depend on :boringssl_asm.
|
|
# Hence, always drop :boringssl_asm and handle the translation entirely in :boringssl.
|
|
always_disable,
|
|
}
|
|
|
|
# Name of tethering apex module
|
|
tethering_apex = "com.android.tethering"
|
|
|
|
# Name of cronet api target
|
|
java_api_target_name = "//components/cronet/android:cronet_api_java"
|
|
|
|
# Visibility set for package default
|
|
package_default_visibility = ":__subpackages__"
|
|
|
|
# Visibility set for modules used from Connectivity
|
|
connectivity_visibility = "//packages/modules/Connectivity:__subpackages__"
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# End of configuration.
|
|
# ----------------------------------------------------------------------------
|
|
|
|
def write_blueprint_key_value(output, name, value, sort=True):
|
|
"""Writes a Blueprint key-value pair to the output"""
|
|
|
|
if isinstance(value, bool):
|
|
if value:
|
|
output.append(' %s: true,' % name)
|
|
else:
|
|
output.append(' %s: false,' % name)
|
|
return
|
|
if not value:
|
|
return
|
|
if isinstance(value, set):
|
|
value = sorted(value)
|
|
if isinstance(value, list):
|
|
output.append(' %s: [' % name)
|
|
for item in sorted(value) if sort else value:
|
|
output.append(' "%s",' % item)
|
|
output.append(' ],')
|
|
return
|
|
if isinstance(value, Module.Target):
|
|
value.to_string(output)
|
|
return
|
|
if isinstance(value, dict):
|
|
kv_output = []
|
|
for k, v in value.items():
|
|
write_blueprint_key_value(kv_output, k, v)
|
|
|
|
output.append(' %s: {' % name)
|
|
for line in kv_output:
|
|
output.append(' %s' % line)
|
|
output.append(' },')
|
|
return
|
|
output.append(' %s: "%s",' % (name, value))
|
|
|
|
|
|
|
|
class Module(object):
|
|
"""A single module (e.g., cc_binary, cc_test) in a blueprint."""
|
|
|
|
class Target(object):
|
|
"""A target-scoped part of a module"""
|
|
|
|
def __init__(self, name):
|
|
self.name = name
|
|
self.srcs = set()
|
|
self.shared_libs = set()
|
|
self.static_libs = set()
|
|
self.whole_static_libs = set()
|
|
self.header_libs = set()
|
|
self.cflags = set()
|
|
self.stl = None
|
|
self.cppflags = set()
|
|
self.local_include_dirs = set()
|
|
self.generated_headers = set()
|
|
self.export_generated_headers = set()
|
|
self.ldflags = set()
|
|
self.compile_multilib = None
|
|
if name == 'host':
|
|
self.compile_multilib = '64'
|
|
|
|
def to_string(self, output):
|
|
nested_out = []
|
|
self._output_field(nested_out, 'srcs')
|
|
self._output_field(nested_out, 'shared_libs')
|
|
self._output_field(nested_out, 'static_libs')
|
|
self._output_field(nested_out, 'whole_static_libs')
|
|
self._output_field(nested_out, 'header_libs')
|
|
self._output_field(nested_out, 'cflags')
|
|
self._output_field(nested_out, 'stl')
|
|
self._output_field(nested_out, 'cppflags')
|
|
self._output_field(nested_out, 'local_include_dirs')
|
|
self._output_field(nested_out, 'generated_headers')
|
|
self._output_field(nested_out, 'export_generated_headers')
|
|
self._output_field(nested_out, 'ldflags')
|
|
|
|
if nested_out:
|
|
# This is added here to make sure it doesn't add a `host` arch-specific module just for
|
|
# `compile_multilib` flag.
|
|
self._output_field(nested_out, 'compile_multilib')
|
|
output.append(' %s: {' % self.name)
|
|
for line in nested_out:
|
|
output.append(' %s' % line)
|
|
output.append(' },')
|
|
|
|
def _output_field(self, output, name, sort=True):
|
|
value = getattr(self, name)
|
|
return write_blueprint_key_value(output, name, value, sort)
|
|
|
|
|
|
def __init__(self, mod_type, name, gn_target):
|
|
self.type = mod_type
|
|
self.gn_target = gn_target
|
|
self.name = name
|
|
self.srcs = set()
|
|
self.comment = 'GN: ' + gn_target
|
|
self.shared_libs = set()
|
|
self.static_libs = set()
|
|
self.whole_static_libs = set()
|
|
self.tools = set()
|
|
self.cmd = None
|
|
self.host_supported = False
|
|
self.device_supported = True
|
|
self.init_rc = set()
|
|
self.out = set()
|
|
self.export_include_dirs = set()
|
|
self.generated_headers = set()
|
|
self.export_generated_headers = set()
|
|
self.export_static_lib_headers = set()
|
|
self.export_header_lib_headers = set()
|
|
self.defaults = set()
|
|
self.cflags = set()
|
|
self.include_dirs = set()
|
|
self.local_include_dirs = set()
|
|
self.header_libs = set()
|
|
self.tool_files = set()
|
|
# target contains a dict of Targets indexed by os_arch.
|
|
# example: { 'android_x86': Target('android_x86')
|
|
self.target = dict()
|
|
self.target['android'] = self.Target('android')
|
|
self.target['android_x86'] = self.Target('android_x86')
|
|
self.target['android_x86_64'] = self.Target('android_x86_64')
|
|
self.target['android_arm'] = self.Target('android_arm')
|
|
self.target['android_arm64'] = self.Target('android_arm64')
|
|
self.target['host'] = self.Target('host')
|
|
self.target['glibc'] = self.Target('glibc')
|
|
self.stl = None
|
|
self.cpp_std = None
|
|
self.strip = dict()
|
|
self.data = set()
|
|
self.apex_available = set()
|
|
self.min_sdk_version = None
|
|
self.proto = dict()
|
|
self.linker_scripts = set()
|
|
self.ldflags = set()
|
|
# The genrule_XXX below are properties that must to be propagated back
|
|
# on the module(s) that depend on the genrule.
|
|
self.genrule_headers = set()
|
|
self.genrule_srcs = set()
|
|
self.genrule_shared_libs = set()
|
|
self.genrule_header_libs = set()
|
|
self.version_script = None
|
|
self.test_suites = set()
|
|
self.test_config = None
|
|
self.cppflags = set()
|
|
self.rtti = False
|
|
# Name of the output. Used for setting .so file name for libcronet
|
|
self.libs = set()
|
|
self.stem = None
|
|
self.compile_multilib = None
|
|
self.aidl = dict()
|
|
self.plugins = set()
|
|
self.processor_class = None
|
|
self.sdk_version = None
|
|
self.javacflags = set()
|
|
self.c_std = None
|
|
self.default_applicable_licenses = set()
|
|
self.default_visibility = []
|
|
self.visibility = []
|
|
self.gn_type = None
|
|
|
|
def to_string(self, output):
|
|
if self.comment:
|
|
output.append('// %s' % self.comment)
|
|
output.append('%s {' % self.type)
|
|
self._output_field(output, 'name')
|
|
self._output_field(output, 'srcs')
|
|
self._output_field(output, 'shared_libs')
|
|
self._output_field(output, 'static_libs')
|
|
self._output_field(output, 'whole_static_libs')
|
|
self._output_field(output, 'tools')
|
|
self._output_field(output, 'cmd', sort=False)
|
|
if self.host_supported:
|
|
self._output_field(output, 'host_supported')
|
|
if not self.device_supported:
|
|
self._output_field(output, 'device_supported')
|
|
self._output_field(output, 'init_rc')
|
|
self._output_field(output, 'out')
|
|
self._output_field(output, 'export_include_dirs')
|
|
self._output_field(output, 'generated_headers')
|
|
self._output_field(output, 'export_generated_headers')
|
|
self._output_field(output, 'export_static_lib_headers')
|
|
self._output_field(output, 'export_header_lib_headers')
|
|
self._output_field(output, 'defaults')
|
|
self._output_field(output, 'cflags')
|
|
self._output_field(output, 'include_dirs')
|
|
self._output_field(output, 'local_include_dirs')
|
|
self._output_field(output, 'header_libs')
|
|
self._output_field(output, 'strip')
|
|
self._output_field(output, 'tool_files')
|
|
self._output_field(output, 'data')
|
|
self._output_field(output, 'stl')
|
|
self._output_field(output, 'cpp_std')
|
|
self._output_field(output, 'apex_available')
|
|
self._output_field(output, 'min_sdk_version')
|
|
self._output_field(output, 'version_script')
|
|
self._output_field(output, 'test_suites')
|
|
self._output_field(output, 'test_config')
|
|
self._output_field(output, 'proto')
|
|
self._output_field(output, 'linker_scripts')
|
|
self._output_field(output, 'ldflags')
|
|
self._output_field(output, 'cppflags')
|
|
self._output_field(output, 'libs')
|
|
self._output_field(output, 'stem')
|
|
self._output_field(output, 'compile_multilib')
|
|
self._output_field(output, 'aidl')
|
|
self._output_field(output, 'plugins')
|
|
self._output_field(output, 'processor_class')
|
|
self._output_field(output, 'sdk_version')
|
|
self._output_field(output, 'javacflags')
|
|
self._output_field(output, 'c_std')
|
|
self._output_field(output, 'default_applicable_licenses')
|
|
self._output_field(output, 'default_visibility')
|
|
self._output_field(output, 'visibility')
|
|
if self.rtti:
|
|
self._output_field(output, 'rtti')
|
|
|
|
target_out = []
|
|
for arch, target in sorted(self.target.items()):
|
|
# _output_field calls getattr(self, arch).
|
|
setattr(self, arch, target)
|
|
self._output_field(target_out, arch)
|
|
|
|
if target_out:
|
|
output.append(' target: {')
|
|
for line in target_out:
|
|
output.append(' %s' % line)
|
|
output.append(' },')
|
|
|
|
output.append('}')
|
|
output.append('')
|
|
|
|
def add_android_shared_lib(self, lib):
|
|
if self.type == 'cc_binary_host':
|
|
raise Exception('Adding Android shared lib for host tool is unsupported')
|
|
elif self.host_supported:
|
|
self.target['android'].shared_libs.add(lib)
|
|
else:
|
|
self.shared_libs.add(lib)
|
|
|
|
def is_test(self):
|
|
if gn_utils.TESTING_SUFFIX in self.name:
|
|
name_without_prefix = self.name[:self.name.find(gn_utils.TESTING_SUFFIX)]
|
|
return any([name_without_prefix == label_to_module_name(target) for target in DEFAULT_TESTS])
|
|
return False
|
|
|
|
def _output_field(self, output, name, sort=True):
|
|
value = getattr(self, name)
|
|
return write_blueprint_key_value(output, name, value, sort)
|
|
|
|
def is_compiled(self):
|
|
return self.type not in ('cc_genrule', 'filegroup', 'java_genrule')
|
|
|
|
def is_genrule(self):
|
|
return self.type == "cc_genrule"
|
|
|
|
def has_input_files(self):
|
|
if len(self.srcs) > 0:
|
|
return True
|
|
if any([len(target.srcs) > 0 for target in self.target.values()]):
|
|
return True
|
|
# Allow cc_static_library with export_generated_headers as those are crucial for
|
|
# the depending modules
|
|
return len(self.export_generated_headers) > 0
|
|
|
|
|
|
class Blueprint(object):
|
|
"""In-memory representation of an Android.bp file."""
|
|
|
|
def __init__(self):
|
|
self.modules = {}
|
|
|
|
def add_module(self, module):
|
|
"""Adds a new module to the blueprint, replacing any existing module
|
|
with the same name.
|
|
|
|
Args:
|
|
module: Module instance.
|
|
"""
|
|
self.modules[module.name] = module
|
|
|
|
def to_string(self, output):
|
|
for m in sorted(self.modules.values(), key=lambda m: m.name):
|
|
if m.type != "cc_library_static" or m.has_input_files():
|
|
# Don't print cc_library_static with empty srcs. These attributes are already
|
|
# propagated up the tree. Printing them messes the presubmits because
|
|
# every module is compiled while those targets are not reachable in
|
|
# a normal compilation path.
|
|
m.to_string(output)
|
|
|
|
|
|
def label_to_module_name(label):
|
|
"""Turn a GN label (e.g., //:perfetto_tests) into a module name."""
|
|
module = re.sub(r'^//:?', '', label)
|
|
module = re.sub(r'[^a-zA-Z0-9_]', '_', module)
|
|
|
|
if not module.startswith(module_prefix):
|
|
return module_prefix + module
|
|
return module
|
|
|
|
|
|
def is_supported_source_file(name):
|
|
"""Returns True if |name| can appear in a 'srcs' list."""
|
|
return os.path.splitext(name)[1] in ['.c', '.cc', '.cpp', '.java', '.proto', '.S']
|
|
|
|
|
|
def get_protoc_module_name(gn):
|
|
protoc_gn_target_name = gn.get_target('//third_party/protobuf:protoc').name
|
|
return label_to_module_name(protoc_gn_target_name)
|
|
|
|
|
|
def create_proto_modules(blueprint, gn, target):
|
|
"""Generate genrules for a proto GN target.
|
|
|
|
GN actions are used to dynamically generate files during the build. The
|
|
Soong equivalent is a genrule. This function turns a specific kind of
|
|
genrule which turns .proto files into source and header files into a pair
|
|
equivalent genrules.
|
|
|
|
Args:
|
|
blueprint: Blueprint instance which is being generated.
|
|
target: gn_utils.Target object.
|
|
|
|
Returns:
|
|
The source_genrule module.
|
|
"""
|
|
assert (target.type == 'proto_library')
|
|
|
|
protoc_module_name = get_protoc_module_name(gn)
|
|
tools = {protoc_module_name}
|
|
cpp_out_dir = '$(genDir)/%s/%s/' % (tree_path, target.proto_in_dir)
|
|
target_module_name = label_to_module_name(target.name)
|
|
|
|
# In GN builds the proto path is always relative to the output directory
|
|
# (out/tmp.xxx).
|
|
cmd = ['$(location %s)' % protoc_module_name]
|
|
cmd += ['--proto_path=%s/%s' % (tree_path, target.proto_in_dir)]
|
|
|
|
if buildtools_protobuf_src in target.proto_paths:
|
|
cmd += ['--proto_path=%s' % android_protobuf_src]
|
|
|
|
# We don't generate any targets for source_set proto modules because
|
|
# they will be inlined into other modules if required.
|
|
if target.proto_plugin == 'source_set':
|
|
return None
|
|
|
|
# Descriptor targets only generate a single target.
|
|
if target.proto_plugin == 'descriptor':
|
|
out = '{}.bin'.format(target_module_name)
|
|
|
|
cmd += ['--descriptor_set_out=$(out)']
|
|
cmd += ['$(in)']
|
|
|
|
descriptor_module = Module('cc_genrule', target_module_name, target.name)
|
|
descriptor_module.cmd = ' '.join(cmd)
|
|
descriptor_module.out = [out]
|
|
descriptor_module.tools = tools
|
|
blueprint.add_module(descriptor_module)
|
|
|
|
# Recursively extract the .proto files of all the dependencies and
|
|
# add them to srcs.
|
|
descriptor_module.srcs.update(
|
|
gn_utils.label_to_path(src) for src in target.sources)
|
|
for dep in target.transitive_proto_deps:
|
|
current_target = gn.get_target(dep)
|
|
descriptor_module.srcs.update(
|
|
gn_utils.label_to_path(src) for src in current_target.sources)
|
|
|
|
return descriptor_module
|
|
|
|
# We create two genrules for each proto target: one for the headers and
|
|
# another for the sources. This is because the module that depends on the
|
|
# generated files needs to declare two different types of dependencies --
|
|
# source files in 'srcs' and headers in 'generated_headers' -- and it's not
|
|
# valid to generate .h files from a source dependency and vice versa.
|
|
source_module_name = target_module_name + '_gen'
|
|
source_module = Module('cc_genrule', source_module_name, target.name)
|
|
blueprint.add_module(source_module)
|
|
source_module.srcs.update(
|
|
gn_utils.label_to_path(src) for src in target.sources)
|
|
|
|
header_module = Module('cc_genrule', source_module_name + '_headers',
|
|
target.name)
|
|
blueprint.add_module(header_module)
|
|
header_module.srcs = set(source_module.srcs)
|
|
|
|
# TODO(primiano): at some point we should remove this. This was introduced
|
|
# by aosp/1108421 when adding "protos/" to .proto include paths, in order to
|
|
# avoid doing multi-repo changes and allow old clients in the android tree
|
|
# to still do the old #include "perfetto/..." rather than
|
|
# #include "protos/perfetto/...".
|
|
header_module.export_include_dirs = {'.', 'protos'}
|
|
# Since the .cc file and .h get created by a different gerule target, they
|
|
# are not put in the same intermediate path, so local includes do not work
|
|
# without explictily exporting the include dir.
|
|
header_module.export_include_dirs.add(target.proto_in_dir)
|
|
|
|
# This function does not return header_module so setting apex_available attribute here.
|
|
header_module.apex_available.add(tethering_apex)
|
|
|
|
source_module.genrule_srcs.add(':' + source_module.name)
|
|
source_module.genrule_headers.add(header_module.name)
|
|
|
|
if target.proto_plugin == 'proto':
|
|
suffixes = ['pb']
|
|
source_module.genrule_shared_libs.add('libprotobuf-cpp-lite')
|
|
cmd += ['--cpp_out=lite=true:' + cpp_out_dir]
|
|
else:
|
|
raise Exception('Unsupported proto plugin: %s' % target.proto_plugin)
|
|
|
|
cmd += ['$(in)']
|
|
source_module.cmd = ' '.join(cmd)
|
|
header_module.cmd = source_module.cmd
|
|
source_module.tools = tools
|
|
header_module.tools = tools
|
|
|
|
for sfx in suffixes:
|
|
source_module.out.update('%s/%s' %
|
|
(tree_path, src.replace('.proto', '.%s.cc' % sfx))
|
|
for src in source_module.srcs)
|
|
header_module.out.update('%s/%s' %
|
|
(tree_path, src.replace('.proto', '.%s.h' % sfx))
|
|
for src in header_module.srcs)
|
|
return source_module
|
|
|
|
|
|
def create_gcc_preprocess_modules(blueprint, target):
|
|
# gcc_preprocess.py internally execute host gcc which is not allowed in genrule.
|
|
# So, this function create multiple modules and realize equivalent processing
|
|
# TODO: Consider to support gcc_preprocess.py in different way
|
|
# It's not great to have genrule and cc_object in the dependency from java_library
|
|
assert (len(target.sources) == 1)
|
|
source = list(target.sources)[0]
|
|
assert (Path(source).suffix == '.template')
|
|
stem = Path(source).stem
|
|
|
|
bp_module_name = label_to_module_name(target.name)
|
|
|
|
# Rename .template to .cc since cc_object does not accept .template file as srcs
|
|
rename_module = Module('genrule', bp_module_name + '_rename', target.name)
|
|
rename_module.srcs.add(gn_utils.label_to_path(source))
|
|
rename_module.out.add(stem + '.cc')
|
|
rename_module.cmd = 'cp $(in) $(out)'
|
|
blueprint.add_module(rename_module)
|
|
|
|
# Preprocess template file and generates java file
|
|
preprocess_module = Module('cc_object', bp_module_name + '_preprocess', target.name)
|
|
# -E: stop after preprocessing.
|
|
# -P: disable line markers, i.e. '#line 309'
|
|
preprocess_module.cflags.update(['-E', '-P', '-DANDROID'])
|
|
preprocess_module.srcs.add(':' + rename_module.name)
|
|
defines = ['-D' + target.args[i+1] for i, arg in enumerate(target.args) if arg == '--define']
|
|
preprocess_module.cflags.update(defines)
|
|
# HACK: Specifying compile_multilib to build cc_object only once.
|
|
# Without this, soong complain to genrule that depends on cc_object when built for 64bit target.
|
|
# It seems this is because cc object is a module with per-architecture variants and genrule is a
|
|
# module with default variant. For 64bit target, cc_object is built multiple times for 32/64bit
|
|
# modes and genrule doesn't know which one to depend on.
|
|
preprocess_module.compile_multilib = 'first'
|
|
blueprint.add_module(preprocess_module)
|
|
|
|
# Generates srcjar using soong_zip
|
|
module = Module('genrule', bp_module_name, target.name)
|
|
module.srcs.add(':' + preprocess_module.name)
|
|
module.out.add(stem + '.srcjar')
|
|
module.cmd = NEWLINE.join([
|
|
f'cp $(in) $(genDir)/{stem}.java &&',
|
|
f'$(location soong_zip) -o $(out) -srcjar -C $(genDir) -f $(genDir)/{stem}.java'
|
|
])
|
|
module.tools.add('soong_zip')
|
|
blueprint.add_module(module)
|
|
return module
|
|
|
|
|
|
class BaseActionSanitizer():
|
|
def __init__(self, target, arch):
|
|
# Just to be on the safe side, create a deep-copy.
|
|
self.target = copy.deepcopy(target)
|
|
if arch:
|
|
# Merge arch specific attributes
|
|
self.target.sources |= arch.sources
|
|
self.target.inputs |= arch.inputs
|
|
self.target.outputs |= arch.outputs
|
|
self.target.script = self.target.script or arch.script
|
|
self.target.args = self.target.args or arch.args
|
|
self.target.response_file_contents = \
|
|
self.target.response_file_contents or arch.response_file_contents
|
|
self.target.args = self._normalize_args()
|
|
|
|
def get_name(self):
|
|
return label_to_module_name(self.target.name)
|
|
|
|
def _normalize_args(self):
|
|
# Convert ['--param=value'] to ['--param', 'value'] for consistency.
|
|
# Escape quotations.
|
|
normalized_args = []
|
|
for arg in self.target.args:
|
|
arg = arg.replace('"', r'\"')
|
|
if arg.startswith('-'):
|
|
normalized_args.extend(arg.split('='))
|
|
else:
|
|
normalized_args.append(arg)
|
|
return normalized_args
|
|
|
|
# There are three types of args:
|
|
# - flags (--flag)
|
|
# - value args (--arg value)
|
|
# - list args (--arg value1 --arg value2)
|
|
# value args have exactly one arg value pair and list args have one or more arg value pairs.
|
|
# Note that the set of list args contains the set of value args.
|
|
# This is because list and value args are identical when the list args has only one arg value pair
|
|
# Some functions provide special implementations for each type, while others
|
|
# work on all of them.
|
|
def _has_arg(self, arg):
|
|
return arg in self.target.args
|
|
|
|
def _get_arg_indices(self, target_arg):
|
|
return [i for i, arg in enumerate(self.target.args) if arg == target_arg]
|
|
|
|
# Whether an arg value pair appears once or more times
|
|
def _is_list_arg(self, arg):
|
|
indices = self._get_arg_indices(arg)
|
|
return len(indices) > 0 and all([not self.target.args[i + 1].startswith('--') for i in indices])
|
|
|
|
def _update_list_arg(self, arg, func, throw_if_absent = True):
|
|
if self._should_fail_silently(arg, throw_if_absent):
|
|
return
|
|
assert(self._is_list_arg(arg))
|
|
indices = self._get_arg_indices(arg)
|
|
for i in indices:
|
|
self._set_arg_at(i + 1, func(self.target.args[i + 1]))
|
|
|
|
# Whether an arg value pair appears exactly once
|
|
def _is_value_arg(self, arg):
|
|
return operator.countOf(self.target.args, arg) == 1 and self._is_list_arg(arg)
|
|
|
|
def _get_value_arg(self, arg):
|
|
assert(self._is_value_arg(arg))
|
|
i = self.target.args.index(arg)
|
|
return self.target.args[i + 1]
|
|
|
|
# used to check whether a function call should cause an error when an arg is
|
|
# missing.
|
|
def _should_fail_silently(self, arg, throw_if_absent):
|
|
return not throw_if_absent and not self._has_arg(arg)
|
|
|
|
def _set_value_arg(self, arg, value, throw_if_absent = True):
|
|
if self._should_fail_silently(arg, throw_if_absent):
|
|
return
|
|
assert(self._is_value_arg(arg))
|
|
i = self.target.args.index(arg)
|
|
self.target.args[i + 1] = value
|
|
|
|
def _update_value_arg(self, arg, func, throw_if_absent = True):
|
|
if self._should_fail_silently(arg, throw_if_absent):
|
|
return
|
|
self._set_value_arg(arg, func(self._get_value_arg(arg)))
|
|
|
|
def _set_arg_at(self, position, value):
|
|
self.target.args[position] = value
|
|
|
|
def _update_arg_at(self, position, func):
|
|
self.target.args[position] = func(self.target.args[position])
|
|
|
|
def _delete_value_arg(self, arg, throw_if_absent = True):
|
|
if self._should_fail_silently(arg, throw_if_absent):
|
|
return
|
|
assert(self._is_value_arg(arg))
|
|
i = self.target.args.index(arg)
|
|
self.target.args.pop(i)
|
|
self.target.args.pop(i)
|
|
|
|
def _append_arg(self, arg, value):
|
|
self.target.args.append(arg)
|
|
self.target.args.append(value)
|
|
|
|
def _sanitize_filepath_with_location_tag(self, arg):
|
|
if arg.startswith('../../'):
|
|
arg = self._sanitize_filepath(arg)
|
|
arg = self._add_location_tag(arg)
|
|
return arg
|
|
|
|
# wrap filename in location tag.
|
|
def _add_location_tag(self, filename):
|
|
return '$(location %s)' % filename
|
|
|
|
# applies common directory transformation that *should* be universally applicable.
|
|
# TODO: verify if it actually *is* universally applicable.
|
|
def _sanitize_filepath(self, filepath):
|
|
# Careful, order matters!
|
|
# delete all leading ../
|
|
filepath = re.sub('^(\.\./)+', '', filepath)
|
|
filepath = re.sub('^gen/jni_headers', '$(genDir)', filepath)
|
|
filepath = re.sub('^gen', '$(genDir)', filepath)
|
|
return filepath
|
|
|
|
# Iterate through all the args and apply function
|
|
def _update_all_args(self, func):
|
|
self.target.args = [func(arg) for arg in self.target.args]
|
|
|
|
def get_cmd(self):
|
|
arg_string = NEWLINE.join(self.target.args)
|
|
cmd = '$(location %s) %s' % (
|
|
gn_utils.label_to_path(self.target.script), arg_string)
|
|
|
|
if self.use_response_file:
|
|
# Pipe response file contents into script
|
|
cmd = 'echo \'%s\' |%s%s' % (self.target.response_file_contents, NEWLINE, cmd)
|
|
return cmd
|
|
|
|
def get_outputs(self):
|
|
return self.target.outputs
|
|
|
|
def get_srcs(self):
|
|
# gn treats inputs and sources for actions equally.
|
|
# soong only supports source files inside srcs, non-source files are added as
|
|
# tool_files dependency.
|
|
files = self.target.sources.union(self.target.inputs)
|
|
return {gn_utils.label_to_path(file) for file in files if is_supported_source_file(file)}
|
|
|
|
def get_tools(self):
|
|
return set()
|
|
|
|
def get_tool_files(self):
|
|
# gn treats inputs and sources for actions equally.
|
|
# soong only supports source files inside srcs, non-source files are added as
|
|
# tool_files dependency.
|
|
files = self.target.sources.union(self.target.inputs)
|
|
tool_files = {gn_utils.label_to_path(file)
|
|
for file in files if not is_supported_source_file(file)}
|
|
tool_files.add(gn_utils.label_to_path(self.target.script))
|
|
return tool_files
|
|
|
|
def _sanitize_args(self):
|
|
# Handle passing parameters via response file by piping them into the script
|
|
# and reading them from /dev/stdin.
|
|
|
|
self.use_response_file = gn_utils.RESPONSE_FILE in self.target.args
|
|
if self.use_response_file:
|
|
# Replace {{response_file_contents}} with /dev/stdin
|
|
self.target.args = ['/dev/stdin' if it == gn_utils.RESPONSE_FILE else it
|
|
for it in self.target.args]
|
|
|
|
def _sanitize_outputs(self):
|
|
pass
|
|
|
|
def _sanitize_inputs(self):
|
|
pass
|
|
|
|
def sanitize(self):
|
|
self._sanitize_args()
|
|
self._sanitize_outputs()
|
|
self._sanitize_inputs()
|
|
|
|
# Whether this target generates header files
|
|
def is_header_generated(self):
|
|
return any(os.path.splitext(it)[1] == '.h' for it in self.target.outputs)
|
|
|
|
class WriteBuildDateHeaderSanitizer(BaseActionSanitizer):
|
|
def _sanitize_args(self):
|
|
self._set_arg_at(0, '$(out)')
|
|
super()._sanitize_args()
|
|
|
|
class WriteBuildFlagHeaderSanitizer(BaseActionSanitizer):
|
|
def _sanitize_args(self):
|
|
self._set_value_arg('--gen-dir', '.')
|
|
self._set_value_arg('--output', '$(out)')
|
|
super()._sanitize_args()
|
|
|
|
class GnRunBinarySanitizer(BaseActionSanitizer):
|
|
def __init__(self, target, arch):
|
|
super().__init__(target, arch)
|
|
self.binary_to_target = {
|
|
"clang_x64/transport_security_state_generator":
|
|
"cronet_aml_net_tools_transport_security_state_generator_transport_security_state_generator__testing",
|
|
}
|
|
self.binary = self.binary_to_target[self.target.args[0]]
|
|
|
|
def _replace_gen_with_location_tag(self, arg):
|
|
if arg.startswith("gen/"):
|
|
return "$(location %s)" % arg.replace("gen/", "")
|
|
return arg
|
|
|
|
def _replace_binary(self, arg):
|
|
if arg in self.binary_to_target:
|
|
return '$(location %s)' % self.binary
|
|
return arg
|
|
|
|
def _remove_python_args(self):
|
|
self.target.args = [arg for arg in self.target.args if "python3" not in arg]
|
|
|
|
def _sanitize_args(self):
|
|
self._update_all_args(self._sanitize_filepath_with_location_tag)
|
|
self._update_all_args(self._replace_gen_with_location_tag)
|
|
self._update_all_args(self._replace_binary)
|
|
self._remove_python_args()
|
|
super()._sanitize_args()
|
|
|
|
def get_tools(self):
|
|
tools = super().get_tools()
|
|
tools.add(self.binary)
|
|
return tools
|
|
|
|
def get_cmd(self):
|
|
# Remove the script and use the binary right away
|
|
return NEWLINE.join(self.target.args)
|
|
|
|
class JniGeneratorSanitizer(BaseActionSanitizer):
|
|
def __init__(self, target, arch, is_test_target):
|
|
self.is_test_target = is_test_target
|
|
super().__init__(target, arch)
|
|
|
|
def _add_location_tag_to_filepath(self, arg):
|
|
if not arg.endswith('.class'):
|
|
# --input_file supports both .class specifiers or source files as arguments.
|
|
# Only source files need to be wrapped inside a $(location <label>) tag.
|
|
arg = self._add_location_tag(arg)
|
|
return arg
|
|
|
|
def _sanitize_args(self):
|
|
self._set_value_arg('--jar_file', '$(location :current_android_jar)', False)
|
|
if self._has_arg('--jar_file'):
|
|
self._append_arg('--javap', '$$(find $${OUT_DIR:-out}/.path -name javap)')
|
|
self._update_value_arg('--output_dir', self._sanitize_filepath)
|
|
self._update_value_arg('--includes', self._sanitize_filepath, False)
|
|
self._delete_value_arg('--prev_output_dir', False)
|
|
self._update_list_arg('--input_file', self._sanitize_filepath)
|
|
self._update_list_arg('--input_file', self._add_location_tag_to_filepath)
|
|
if not self.is_test_target:
|
|
# Only jarjar platform code
|
|
self._append_arg('--package_prefix', 'android.net.connectivity')
|
|
super()._sanitize_args()
|
|
|
|
def _sanitize_outputs(self):
|
|
# fix target.output directory to match #include statements.
|
|
self.target.outputs = {re.sub('^jni_headers/', '', out) for out in self.target.outputs}
|
|
super()._sanitize_outputs()
|
|
|
|
def get_tool_files(self):
|
|
tool_files = super().get_tool_files()
|
|
# android_jar.classes should be part of the tools as it list implicit classes
|
|
# for the script to generate JNI headers.
|
|
tool_files.add("base/android/jni_generator/android_jar.classes")
|
|
|
|
# Filter android.jar and add :current_android_jar
|
|
tool_files = {file if not file.endswith('android.jar') else ':current_android_jar'
|
|
for file in tool_files }
|
|
return tool_files
|
|
|
|
class JniRegistrationGeneratorSanitizer(BaseActionSanitizer):
|
|
def __init__(self, target, arch, is_test_target):
|
|
self.is_test_target = is_test_target
|
|
super().__init__(target, arch)
|
|
|
|
def _sanitize_inputs(self):
|
|
self.target.inputs = [file for file in self.target.inputs if not file.startswith('//out/')]
|
|
|
|
def _sanitize_outputs(self):
|
|
self.target.outputs = {re.sub('^jni_headers/', '', out) for out in self.target.outputs}
|
|
|
|
def _sanitize_args(self):
|
|
self._update_value_arg('--depfile', self._sanitize_filepath)
|
|
self._update_value_arg('--srcjar-path', self._sanitize_filepath)
|
|
self._update_value_arg('--header-path', self._sanitize_filepath)
|
|
self._set_value_arg('--sources-files', '$(genDir)/java.sources')
|
|
# update_jni_registration_module removes them from the srcs of the module
|
|
# It might be better to remove sources by '--sources-exclusions'
|
|
self._delete_value_arg('--file-exclusions')
|
|
if not self.is_test_target:
|
|
# Only jarjar platform code
|
|
self._append_arg('--package_prefix', 'android.net.connectivity')
|
|
super()._sanitize_args()
|
|
|
|
def get_cmd(self):
|
|
# jni_registration_generator.py doesn't work with python2
|
|
cmd = "python3 " + super().get_cmd()
|
|
# Path in the original sources file does not work in genrule.
|
|
# So creating sources file in cmd based on the srcs of this target.
|
|
# Adding ../$(current_dir)/ to the head because jni_registration_generator.py uses the files
|
|
# whose path startswith(..)
|
|
commands = ["current_dir=`basename \\\`pwd\\\``;",
|
|
"for f in $(in);",
|
|
"do",
|
|
"echo \\\"../$$current_dir/$$f\\\" >> $(genDir)/java.sources;",
|
|
"done;",
|
|
cmd]
|
|
|
|
return NEWLINE.join(commands)
|
|
|
|
class JavaJniRegistrationGeneratorSanitizer(JniRegistrationGeneratorSanitizer):
|
|
def get_name(self):
|
|
name = super().get_name() + "__java"
|
|
if self.is_test_target:
|
|
name += gn_utils.TESTING_SUFFIX
|
|
return name
|
|
|
|
def _sanitize_outputs(self):
|
|
self.target.outputs = [out for out in self.target.outputs if
|
|
out.endswith(".srcjar")]
|
|
super()._sanitize_outputs()
|
|
|
|
class VersionSanitizer(BaseActionSanitizer):
|
|
def _sanitize_args(self):
|
|
self._set_value_arg('-o', '$(out)')
|
|
# args for the version.py contain file path without leading --arg key. So apply sanitize
|
|
# function for all the args.
|
|
self._update_all_args(self._sanitize_filepath_with_location_tag)
|
|
self._set_value_arg('-e', "'%s'" % self._get_value_arg('-e'))
|
|
super()._sanitize_args()
|
|
|
|
def get_tool_files(self):
|
|
tool_files = super().get_tool_files()
|
|
# android_chrome_version.py is not specified in anywhere but version.py imports this file
|
|
tool_files.add('build/util/android_chrome_version.py')
|
|
return tool_files
|
|
|
|
class JavaCppEnumSanitizer(BaseActionSanitizer):
|
|
def _sanitize_args(self):
|
|
self._update_all_args(self._sanitize_filepath_with_location_tag)
|
|
self._set_value_arg('--srcjar', '$(out)')
|
|
super()._sanitize_args()
|
|
|
|
class MakeDafsaSanitizer(BaseActionSanitizer):
|
|
def is_header_generated(self):
|
|
# This script generates .cc files but they are #included by other sources
|
|
# (e.g. registry_controlled_domain.cc)
|
|
return True
|
|
|
|
class JavaCppFeatureSanitizer(BaseActionSanitizer):
|
|
def _sanitize_args(self):
|
|
self._update_all_args(self._sanitize_filepath_with_location_tag)
|
|
self._set_value_arg('--srcjar', '$(out)')
|
|
super()._sanitize_args()
|
|
|
|
class JavaCppStringSanitizer(BaseActionSanitizer):
|
|
def _sanitize_args(self):
|
|
self._update_all_args(self._sanitize_filepath_with_location_tag)
|
|
self._set_value_arg('--srcjar', '$(out)')
|
|
super()._sanitize_args()
|
|
|
|
class WriteNativeLibrariesJavaSanitizer(BaseActionSanitizer):
|
|
def _sanitize_args(self):
|
|
self._set_value_arg('--output', '$(out)')
|
|
super()._sanitize_args()
|
|
|
|
|
|
class ProtocJavaSanitizer(BaseActionSanitizer):
|
|
def __init__(self, target, arch, gn):
|
|
super().__init__(target, arch)
|
|
self._protoc = get_protoc_module_name(gn)
|
|
|
|
def _sanitize_proto_path(self, arg):
|
|
arg = self._sanitize_filepath(arg)
|
|
return tree_path + '/' + arg
|
|
|
|
def _sanitize_args(self):
|
|
super()._sanitize_args()
|
|
self._delete_value_arg('--depfile')
|
|
self._set_value_arg('--protoc', '$(location %s)' % self._protoc)
|
|
self._update_value_arg('--proto-path', self._sanitize_proto_path)
|
|
self._set_value_arg('--srcjar', '$(out)')
|
|
self._update_arg_at(-1, self._sanitize_filepath_with_location_tag)
|
|
|
|
def get_tools(self):
|
|
tools = super().get_tools()
|
|
tools.add(self._protoc)
|
|
return tools
|
|
|
|
|
|
def get_action_sanitizer(gn, target, type, arch, is_test_target):
|
|
if target.script == "//build/write_buildflag_header.py":
|
|
return WriteBuildFlagHeaderSanitizer(target, arch)
|
|
elif target.script == "//base/write_build_date_header.py":
|
|
return WriteBuildDateHeaderSanitizer(target, arch)
|
|
elif target.script == '//base/android/jni_generator/jni_generator.py':
|
|
return JniGeneratorSanitizer(target, arch, is_test_target)
|
|
elif target.script == '//base/android/jni_generator/jni_registration_generator.py':
|
|
if type == 'java_genrule':
|
|
return JavaJniRegistrationGeneratorSanitizer(target, arch, is_test_target)
|
|
else:
|
|
return JniRegistrationGeneratorSanitizer(target, arch, is_test_target)
|
|
elif target.script == "//build/util/version.py":
|
|
return VersionSanitizer(target, arch)
|
|
elif target.script == "//build/android/gyp/java_cpp_enum.py":
|
|
return JavaCppEnumSanitizer(target, arch)
|
|
elif target.script == "//net/tools/dafsa/make_dafsa.py":
|
|
return MakeDafsaSanitizer(target, arch)
|
|
elif target.script == '//build/android/gyp/java_cpp_features.py':
|
|
return JavaCppFeatureSanitizer(target, arch)
|
|
elif target.script == '//build/android/gyp/java_cpp_strings.py':
|
|
return JavaCppStringSanitizer(target, arch)
|
|
elif target.script == '//build/android/gyp/write_native_libraries_java.py':
|
|
return WriteNativeLibrariesJavaSanitizer(target, arch)
|
|
elif target.script == '//build/gn_run_binary.py':
|
|
return GnRunBinarySanitizer(target, arch)
|
|
elif target.script == '//build/protoc_java.py':
|
|
return ProtocJavaSanitizer(target, arch, gn)
|
|
else:
|
|
raise Exception('Unsupported action %s' % target.script)
|
|
|
|
def create_action_foreach_modules(blueprint, gn, target, is_test_target):
|
|
""" The following assumes that rebase_path exists in the args.
|
|
The args of an action_foreach contains hints about which output files are generated
|
|
by which source files.
|
|
This is copied directly from the args
|
|
"gen/net/base/registry_controlled_domains/{{source_name_part}}-reversed-inc.cc"
|
|
So each source file will generate an output whose name is the {source_name-reversed-inc.cc}
|
|
"""
|
|
new_args = []
|
|
for i, src in enumerate(sorted(target.sources)):
|
|
# don't add script arg for the first source -- create_action_module
|
|
# already does this.
|
|
if i != 0:
|
|
new_args.append('&&')
|
|
new_args.append('python3 $(location %s)' %
|
|
gn_utils.label_to_path(target.script))
|
|
for arg in target.args:
|
|
if '{{source}}' in arg:
|
|
new_args.append('$(location %s)' % (gn_utils.label_to_path(src)))
|
|
elif '{{source_name_part}}' in arg:
|
|
source_name_part = src.split("/")[-1] # Get the file name only
|
|
source_name_part = source_name_part.split(".")[0] # Remove the extension (Ex: .cc)
|
|
file_name = arg.replace('{{source_name_part}}', source_name_part).split("/")[-1]
|
|
# file_name represent the output file name. But we need the whole path
|
|
# This can be found from target.outputs.
|
|
for out in target.outputs:
|
|
if out.endswith(file_name):
|
|
new_args.append('$(location %s)' % out)
|
|
|
|
for file in (target.sources | target.inputs):
|
|
if file.endswith(file_name):
|
|
new_args.append('$(location %s)' % gn_utils.label_to_path(file))
|
|
else:
|
|
new_args.append(arg)
|
|
|
|
target.args = new_args
|
|
return create_action_module(blueprint, gn, target, 'cc_genrule', is_test_target)
|
|
|
|
def create_action_module_internal(gn, target, type, is_test_target, arch=None):
|
|
sanitizer = get_action_sanitizer(gn, target, type, arch, is_test_target)
|
|
sanitizer.sanitize()
|
|
|
|
module = Module(type, sanitizer.get_name(), target.name)
|
|
module.cmd = sanitizer.get_cmd()
|
|
module.out = sanitizer.get_outputs()
|
|
if sanitizer.is_header_generated():
|
|
module.genrule_headers.add(module.name)
|
|
module.srcs = sanitizer.get_srcs()
|
|
module.tool_files = sanitizer.get_tool_files()
|
|
module.tools = sanitizer.get_tools()
|
|
|
|
return module
|
|
|
|
def get_cmd_condition(arch):
|
|
'''
|
|
:param arch: archtecture name e.g. android_x86_64, android_arm64
|
|
:return: condition that can be used in cc_genrule cmd to switch the behavior based on arch
|
|
'''
|
|
if arch == "android_x86_64":
|
|
return "( $$CC_ARCH == 'x86_64' && $$CC_OS == 'android' )"
|
|
elif arch == "android_x86":
|
|
return "( $$CC_ARCH == 'x86' && $$CC_OS == 'android' )"
|
|
elif arch == "android_arm":
|
|
return "( $$CC_ARCH == 'arm' && $$CC_OS == 'android' )"
|
|
elif arch == "android_arm64":
|
|
return "( $$CC_ARCH == 'arm64' && $$CC_OS == 'android' )"
|
|
elif arch == "host":
|
|
return "$$CC_OS != 'android'"
|
|
else:
|
|
raise Exception(f'Unknown architecture type {arch}')
|
|
|
|
def merge_cmd(modules, genrule_type):
|
|
'''
|
|
:param modules: dictionary whose key is arch name and value is module
|
|
:param genrule_type: cc_genrule or java_genrule
|
|
:return: merged command or common command if all the archs have the same command.
|
|
'''
|
|
commands = list({module.cmd for module in modules.values()})
|
|
if len(commands) == 1:
|
|
# If all the archs have the same command, return the command
|
|
return commands[0]
|
|
|
|
if genrule_type != 'cc_genrule':
|
|
raise Exception(f'{genrule_type} can not have different cmd between archs')
|
|
|
|
merged_cmd = []
|
|
for arch, module in modules.items():
|
|
merged_cmd.append(f'if [[ {get_cmd_condition(arch)} ]];')
|
|
merged_cmd.append('then')
|
|
merged_cmd.append(module.cmd + ';')
|
|
merged_cmd.append('fi;')
|
|
return NEWLINE.join(merged_cmd)
|
|
|
|
def merge_modules(modules, genrule_type):
|
|
'''
|
|
:param modules: dictionary whose key is arch name and value is module
|
|
:param genrule_type: cc_genrule or java_genrule
|
|
:return: merged module of input modules
|
|
'''
|
|
merged_module = list(modules.values())[0]
|
|
|
|
# Following attributes must be the same between archs
|
|
for key in ('out', 'genrule_headers', 'srcs', 'tool_files'):
|
|
if any([getattr(merged_module, key) != getattr(module, key) for module in modules.values()]):
|
|
raise Exception(f'{merged_module.name} has different values for {key} between archs')
|
|
|
|
merged_module.cmd = merge_cmd(modules, genrule_type)
|
|
return merged_module
|
|
|
|
def create_action_module(blueprint, gn, target, genrule_type, is_test_target):
|
|
'''
|
|
Create module for action target and add to the blueprint. If target has arch specific attributes
|
|
this function merge them and create a single module.
|
|
:param blueprint:
|
|
:param target: target which is converted to the module.
|
|
:param genrule_type: cc_genrule or java_genrule
|
|
:return: created module
|
|
'''
|
|
# TODO: Handle this target correctly, this target generates java_genrule but this target has
|
|
# different value for cpu-family arg between archs
|
|
if target.name in ['//build/android:native_libraries_gen',
|
|
'//build/android:native_libraries_gen__testing']:
|
|
module = create_action_module_internal(gn, target, genrule_type,
|
|
is_test_target,
|
|
target.arch['android_arm'])
|
|
blueprint.add_module(module)
|
|
return module
|
|
|
|
modules = {arch_name: create_action_module_internal(gn, target, genrule_type,
|
|
is_test_target, arch)
|
|
for arch_name, arch in target.get_archs().items()}
|
|
module = merge_modules(modules, genrule_type)
|
|
blueprint.add_module(module)
|
|
return module
|
|
|
|
|
|
def _get_cflags(cflags, defines):
|
|
cflags = {flag for flag in cflags if flag in cflag_allowlist}
|
|
# Consider proper allowlist or denylist if needed
|
|
cflags |= set("-D%s" % define.replace("\"", "\\\"") for define in defines)
|
|
return cflags
|
|
|
|
def _set_linker_script(module, libs):
|
|
for lib in libs:
|
|
if lib.endswith(".lds"):
|
|
module.ldflags.add(get_linker_script_ldflag(gn_utils.label_to_path(lib)))
|
|
|
|
def set_module_flags(module, module_type, cflags, defines, ldflags, libs):
|
|
module.cflags.update(_get_cflags(cflags, defines))
|
|
module.ldflags.update({flag for flag in ldflags
|
|
if flag in ldflag_allowlist or flag.startswith("-Wl,-wrap,")})
|
|
_set_linker_script(module, libs)
|
|
# TODO: implement proper cflag parsing.
|
|
for flag in cflags:
|
|
if '-std=' in flag:
|
|
module.cpp_std = flag[len('-std='):]
|
|
if '-fexceptions' in flag:
|
|
module.cppflags.add('-fexceptions')
|
|
|
|
def set_module_include_dirs(module, cflags, include_dirs):
|
|
for flag in cflags:
|
|
if '-isystem' in flag:
|
|
module.local_include_dirs.add(flag[len('-isystem../../'):])
|
|
|
|
# Adding local_include_dirs is necessary due to source_sets / filegroups
|
|
# which do not properly propagate include directories.
|
|
# Filter any directory inside //out as a) this directory does not exist for
|
|
# aosp / soong builds and b) the include directory should already be
|
|
# configured via library dependency.
|
|
module.local_include_dirs.update([gn_utils.label_to_path(d)
|
|
for d in include_dirs if not d.startswith('//out')])
|
|
# Remove prohibited include directories
|
|
module.local_include_dirs = [d for d in module.local_include_dirs
|
|
if d not in local_include_dirs_denylist]
|
|
|
|
|
|
def create_modules_from_target(blueprint, gn, gn_target_name, is_test_target):
|
|
"""Generate module(s) for a given GN target.
|
|
|
|
Given a GN target name, generate one or more corresponding modules into a
|
|
blueprint. The only case when this generates >1 module is proto libraries.
|
|
|
|
Args:
|
|
blueprint: Blueprint instance which is being generated.
|
|
gn: gn_utils.GnParser object.
|
|
gn_target_name: GN target for module generation.
|
|
"""
|
|
bp_module_name = label_to_module_name(gn_target_name)
|
|
if bp_module_name in blueprint.modules:
|
|
return blueprint.modules[bp_module_name]
|
|
target = gn.get_target(gn_target_name)
|
|
log.info('create modules for %s (%s)', target.name, target.type)
|
|
|
|
if target.type == 'executable':
|
|
if target.testonly:
|
|
module_type = 'cc_test'
|
|
else:
|
|
# Can be used for both host and device targets.
|
|
module_type = 'cc_binary'
|
|
module = Module(module_type, bp_module_name, gn_target_name)
|
|
elif target.type in ['static_library', 'source_set']:
|
|
module = Module('cc_library_static', bp_module_name, gn_target_name)
|
|
elif target.type == 'shared_library':
|
|
module = Module('cc_library_shared', bp_module_name, gn_target_name)
|
|
elif target.type == 'group':
|
|
# "group" targets are resolved recursively by gn_utils.get_target().
|
|
# There's nothing we need to do at this level for them.
|
|
return None
|
|
elif target.type == 'proto_library':
|
|
module = create_proto_modules(blueprint, gn, target)
|
|
if module is None:
|
|
return None
|
|
elif target.type == 'action':
|
|
module = create_action_module(blueprint, gn, target, 'cc_genrule', is_test_target)
|
|
elif target.type == 'action_foreach':
|
|
module = create_action_foreach_modules(blueprint, gn, target, is_test_target)
|
|
elif target.type == 'copy':
|
|
# TODO: careful now! copy targets are not supported yet, but this will stop
|
|
# traversing the dependency tree. For //base:base, this is not a big
|
|
# problem as libicu contains the only copy target which happens to be a
|
|
# leaf node.
|
|
return None
|
|
elif target.type == 'java_group':
|
|
# Java targets are handled outside of create_modules_from_target.
|
|
return None
|
|
else:
|
|
raise Exception('Unknown target %s (%s)' % (target.name, target.type))
|
|
|
|
blueprint.add_module(module)
|
|
module.srcs.update(gn_utils.label_to_path(src)
|
|
for src in target.sources if is_supported_source_file(src))
|
|
|
|
# Add arch-specific properties
|
|
for arch_name, arch in target.get_archs().items():
|
|
module.target[arch_name].srcs.update(gn_utils.label_to_path(src)
|
|
for src in arch.sources if is_supported_source_file(src))
|
|
|
|
module.rtti = target.rtti
|
|
|
|
if target.type in gn_utils.LINKER_UNIT_TYPES:
|
|
set_module_flags(module, module.type, target.cflags, target.defines, target.ldflags, target.libs)
|
|
set_module_include_dirs(module, target.cflags, target.include_dirs)
|
|
# TODO: set_module_xxx is confusing, apply similar function to module and target in better way.
|
|
for arch_name, arch in target.get_archs().items():
|
|
# TODO(aymanm): Make libs arch-specific.
|
|
set_module_flags(module.target[arch_name], module.type,
|
|
arch.cflags, arch.defines, arch.ldflags, [])
|
|
# -Xclang -target-feature -Xclang +mte are used to enable MTE (Memory Tagging Extensions).
|
|
# Flags which does not start with '-' could not be in the cflags so enabling MTE by
|
|
# -march and -mcpu Feature Modifiers. MTE is only available on arm64. This is needed for
|
|
# building //base/allocator/partition_allocator:partition_alloc for arm64.
|
|
if '+mte' in arch.cflags and arch_name == 'android_arm64':
|
|
module.target[arch_name].cflags.add('-march=armv8-a+memtag')
|
|
set_module_include_dirs(module.target[arch_name], arch.cflags, arch.include_dirs)
|
|
|
|
module.host_supported = target.host_supported()
|
|
module.device_supported = target.device_supported()
|
|
module.gn_type = target.type
|
|
|
|
if module.is_genrule():
|
|
module.apex_available.add(tethering_apex)
|
|
|
|
if module.is_compiled():
|
|
# Don't try to inject library/source dependencies into genrules or
|
|
# filegroups because they are not compiled in the traditional sense.
|
|
module.defaults = [defaults_module]
|
|
for lib in target.libs:
|
|
# Generally library names should be mangled as 'libXXX', unless they
|
|
# are HAL libraries (e.g., android.hardware.health@2.0) or AIDL c++ / NDK
|
|
# libraries (e.g. "android.hardware.power.stats-V1-cpp")
|
|
android_lib = lib if '@' in lib or "-cpp" in lib or "-ndk" in lib \
|
|
else 'lib' + lib
|
|
if lib in shared_library_allowlist:
|
|
module.add_android_shared_lib(android_lib)
|
|
|
|
# If the module is a static library, export all the generated headers.
|
|
if module.type == 'cc_library_static':
|
|
module.export_generated_headers = module.generated_headers
|
|
|
|
if module.name == 'cronet_aml_components_cronet_android_cronet':
|
|
if target.output_name is None:
|
|
raise Exception('Failed to get output_name for libcronet name')
|
|
# .so file name needs to match with CronetLibraryLoader.java (e.g. libcronet.109.0.5386.0.so)
|
|
# So setting the output name based on the output_name from the desc.json
|
|
module.stem = 'lib' + target.output_name
|
|
|
|
if module.is_test():
|
|
# Tests output should be a shared library in the format of 'lib[module_name]'
|
|
module.stem = 'lib' + target.get_target_name()[
|
|
:target.get_target_name().find(gn_utils.TESTING_SUFFIX)]
|
|
|
|
# dep_name is an unmangled GN target name (e.g. //foo:bar(toolchain)).
|
|
all_deps = [(dep_name, 'common') for dep_name in target.transitive_proto_deps]
|
|
for arch_name, arch in target.arch.items():
|
|
all_deps += [(dep_name, arch_name) for dep_name in arch.deps]
|
|
|
|
# Sort deps before iteration to make result deterministic.
|
|
for (dep_name, arch_name) in sorted(all_deps):
|
|
module_target = module.target[arch_name] if arch_name != 'common' else module
|
|
# |builtin_deps| override GN deps with Android-specific ones. See the
|
|
# config in the top of this file.
|
|
if dep_name in builtin_deps:
|
|
builtin_deps[dep_name](module, arch_name)
|
|
continue
|
|
|
|
dep_module = create_modules_from_target(blueprint, gn, dep_name, is_test_target)
|
|
|
|
if dep_module is None:
|
|
continue
|
|
# TODO: Proper dependency check for genrule.
|
|
# Currently, only propagating genrule dependencies.
|
|
# Also, currently, all the dependencies are propagated upwards.
|
|
# in gn, public_deps should be propagated but deps should not.
|
|
# Not sure this information is available in the desc.json.
|
|
# Following rule works for adding android_runtime_jni_headers to base:base.
|
|
# If this doesn't work for other target, hardcoding for specific target
|
|
# might be better.
|
|
if module.is_genrule() and dep_module.is_genrule():
|
|
module_target.genrule_headers.add(dep_module.name)
|
|
module_target.genrule_headers.update(dep_module.genrule_headers)
|
|
|
|
# For filegroups, and genrule, recurse but don't apply the
|
|
# deps.
|
|
if not module.is_compiled() or module.is_genrule():
|
|
continue
|
|
|
|
# Drop compiled modules that doesn't provide any benefit. This is mostly
|
|
# applicable to source_sets when converted to cc_static_library, sometimes
|
|
# the source set only has header files which are dropped so the module becomes empty.
|
|
# is_compiled is there to prevent dropping of genrules.
|
|
if dep_module.is_compiled() and not dep_module.has_input_files():
|
|
continue
|
|
|
|
if dep_module.type == 'cc_library_shared':
|
|
module_target.shared_libs.add(dep_module.name)
|
|
elif dep_module.type == 'cc_library_static':
|
|
if module.type in ['cc_library_shared', 'cc_binary'] and dep_module.gn_type == 'source_set':
|
|
module_target.whole_static_libs.add(dep_module.name)
|
|
else:
|
|
module_target.static_libs.add(dep_module.name)
|
|
elif dep_module.type == 'cc_genrule':
|
|
module_target.generated_headers.update(dep_module.genrule_headers)
|
|
module_target.srcs.update(dep_module.genrule_srcs)
|
|
module_target.shared_libs.update(dep_module.genrule_shared_libs)
|
|
module_target.header_libs.update(dep_module.genrule_header_libs)
|
|
else:
|
|
raise Exception('Unsupported arch-specific dependency %s of target %s with type %s' %
|
|
(dep_module.name, target.name, dep_module.type))
|
|
return module
|
|
|
|
def create_java_jni_preprocessor(blueprint):
|
|
bp_module_name = module_prefix + 'java_jni_annotation_preprocessor'
|
|
module = Module('java_plugin', bp_module_name, '//base/android/jni_generator:jni_processor')
|
|
module.srcs.update(
|
|
[
|
|
"base/android/jni_generator/java/src/org/chromium/jni_generator/JniProcessor.java",
|
|
# Avoids a circular dependency with base:base_java. This is okay because
|
|
# no target should ever expect to package an annotation processor.
|
|
"build/android/java/src/org/chromium/build/annotations/CheckDiscard.java",
|
|
"build/android/java/src/org/chromium/build/annotations/MainDex.java",
|
|
"base/android/java/src/org/chromium/base/JniStaticTestMocker.java",
|
|
"base/android/java/src/org/chromium/base/NativeLibraryLoadedStatus.java",
|
|
"base/android/java/src/org/chromium/base/annotations/NativeMethods.java",
|
|
"base/android/java/src/org/chromium/base/JniException.java",
|
|
":cronet_aml_build_android_build_config_gen",
|
|
])
|
|
module.static_libs.update({
|
|
"javapoet",
|
|
"guava",
|
|
"auto_service_annotations",
|
|
})
|
|
module.processor_class = "org.chromium.jni_generator.JniProcessor"
|
|
blueprint.add_module(module)
|
|
return module
|
|
|
|
def get_java_sources(gn, predicate):
|
|
java_sources = set()
|
|
for target_name, sources in gn.java_sources.items():
|
|
if predicate(target_name):
|
|
java_sources.update(sources)
|
|
return java_sources
|
|
|
|
def get_java_actions(gn, predicate):
|
|
java_actions = set()
|
|
for target_name, actions in gn.java_actions.items():
|
|
if predicate(target_name):
|
|
java_actions.update(actions)
|
|
return java_actions
|
|
|
|
def get_non_api_java_sources(gn):
|
|
return get_java_sources(gn, lambda name: name != java_api_target_name)
|
|
|
|
def get_non_api_java_actions(gn):
|
|
return get_java_actions(gn, lambda name: name != java_api_target_name)
|
|
|
|
def get_api_java_sources(gn):
|
|
return get_java_sources(gn, lambda name: name == java_api_target_name)
|
|
|
|
def get_api_java_actions(gn):
|
|
return get_java_actions(gn, lambda name: name == java_api_target_name)
|
|
|
|
def create_java_module(blueprint, gn, is_test_target):
|
|
bp_module_name = module_prefix + 'java'
|
|
if is_test_target:
|
|
bp_module_name += gn_utils.TESTING_SUFFIX
|
|
module = Module('java_library', bp_module_name, '//gn:java')
|
|
module.srcs.update([gn_utils.label_to_path(source) for source in get_non_api_java_sources(gn)])
|
|
module.libs = {
|
|
"androidx.annotation_annotation",
|
|
"androidx.annotation_annotation-experimental-nodeps",
|
|
"error_prone_annotations",
|
|
"framework-connectivity-t.stubs.module_lib",
|
|
"framework-connectivity.stubs.module_lib",
|
|
"framework-mediaprovider.stubs.module_lib",
|
|
"framework-tethering.stubs.module_lib",
|
|
"framework-wifi.stubs.module_lib",
|
|
"jsr305",
|
|
}
|
|
module.static_libs = {
|
|
"libprotobuf-java-lite",
|
|
"modules-utils-build_system",
|
|
}
|
|
module.aidl["include_dirs"] = {"frameworks/base/core/java/"}
|
|
module.aidl["local_include_dirs"] = gn.aidl_local_include_dirs
|
|
module.sdk_version = "module_current"
|
|
module.min_sdk_version = 30
|
|
module.apex_available.add(tethering_apex)
|
|
# TODO: support for this flag is removed upstream in crrev/c/4062652.
|
|
# Consider reverting this change upstream, or worst-case downstream. As an
|
|
# alternative hack, we could rename the generated file to not conflict. This
|
|
# would be less likely to conflict with upstream changes if the revert is not
|
|
# accepted.
|
|
module.javacflags.add("-Aorg.chromium.chrome.skipGenJni")
|
|
if not is_test_target:
|
|
module.javacflags.add("-Apackage_prefix=android.net.connectivity")
|
|
for dep in get_non_api_java_actions(gn):
|
|
target = gn.get_target(dep)
|
|
if target.script == '//build/android/gyp/gcc_preprocess.py':
|
|
module.srcs.add(':' + create_gcc_preprocess_modules(blueprint, target).name)
|
|
else:
|
|
module.srcs.add(':' + create_action_module(blueprint, gn, target,
|
|
'java_genrule',
|
|
is_test_target).name)
|
|
preprocessor_module = create_java_jni_preprocessor(blueprint)
|
|
module.plugins.add(preprocessor_module.name)
|
|
module.visibility.append(connectivity_visibility)
|
|
blueprint.add_module(module)
|
|
return module
|
|
|
|
def create_java_api_module(blueprint, gn):
|
|
source_module = Module('filegroup', module_prefix + 'api_sources', java_api_target_name)
|
|
# TODO add the API helpers separately after the main API is checked in and thoroughly reviewed
|
|
source_module.srcs.update([gn_utils.label_to_path(source)
|
|
for source in get_api_java_sources(gn)
|
|
if "apihelpers" not in source])
|
|
source_module.comment += "\n// TODO(danstahr): add the API helpers separately after the main" \
|
|
" API is checked in and thoroughly reviewed"
|
|
source_module.srcs.update([
|
|
':' + create_action_module(blueprint, gn, gn.get_target(dep), 'java_genrule', False).name
|
|
for dep in get_api_java_actions(gn)])
|
|
blueprint.add_module(source_module)
|
|
source_module.visibility.append(connectivity_visibility)
|
|
return source_module
|
|
|
|
def update_jni_registration_module(module, gn):
|
|
# TODO: java_sources might not contain all the required java files
|
|
module.srcs.update([gn_utils.label_to_path(source)
|
|
for source in get_non_api_java_sources(gn)
|
|
if source.endswith('.java')])
|
|
|
|
|
|
def turn_off_allocator_shim_for_musl(module):
|
|
allocation_shim = "base/allocator/partition_allocator/shim/allocator_shim.cc"
|
|
allocator_shim_files = {
|
|
allocation_shim,
|
|
"base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_glibc.cc",
|
|
}
|
|
module.srcs -= allocator_shim_files
|
|
for arch in module.target.values():
|
|
arch.srcs -= allocator_shim_files
|
|
module.target['android'].srcs.add(allocation_shim)
|
|
if gn_utils.TESTING_SUFFIX in module.name:
|
|
# allocator_shim_default_dispatch_to_glibc is only added to the __testing version of base
|
|
# since base_base__testing is compiled for host. When compiling for host. Soong compiles
|
|
# using glibc or musl(experimental). We currently only support compiling for glibc.
|
|
module.target['glibc'].srcs.update(allocator_shim_files)
|
|
else:
|
|
# allocator_shim_default_dispatch_to_glibc does not exist in the prod version of base
|
|
# `base_base` since this only compiles for android and bionic is used. Bionic is the equivalent
|
|
# of glibc but for android.
|
|
module.target['glibc'].srcs.add(allocation_shim)
|
|
|
|
def create_blueprint_for_targets(gn, targets, test_targets):
|
|
"""Generate a blueprint for a list of GN targets."""
|
|
blueprint = Blueprint()
|
|
|
|
# Default settings used by all modules.
|
|
defaults = Module('cc_defaults', defaults_module, '//gn:default_deps')
|
|
defaults.cflags = [
|
|
'-DGOOGLE_PROTOBUF_NO_RTTI',
|
|
'-DBORINGSSL_SHARED_LIBRARY',
|
|
'-Wno-error=return-type',
|
|
'-Wno-non-virtual-dtor',
|
|
'-Wno-macro-redefined',
|
|
'-Wno-missing-field-initializers',
|
|
'-Wno-sign-compare',
|
|
'-Wno-sign-promo',
|
|
'-Wno-unused-parameter',
|
|
'-Wno-null-pointer-subtraction', # Needed to libevent
|
|
'-Wno-ambiguous-reversed-operator', # needed for icui18n
|
|
'-Wno-unreachable-code-loop-increment', # needed for icui18n
|
|
'-fPIC',
|
|
'-Wno-c++11-narrowing',
|
|
]
|
|
defaults.c_std = 'gnu11'
|
|
# Chromium builds do not add a dependency for headers found inside the
|
|
# sysroot, so they are added globally via defaults.
|
|
defaults.target['android'].header_libs = [
|
|
'jni_headers',
|
|
]
|
|
defaults.target['android'].shared_libs = [
|
|
'libmediandk'
|
|
]
|
|
defaults.target['host'].cflags = [
|
|
# -DANDROID is added by default but target.defines contain -DANDROID if
|
|
# it's required. So adding -UANDROID to cancel default -DANDROID if it's
|
|
# not specified.
|
|
# Note: -DANDROID is not consistently applied across the chromium code
|
|
# base, so it is removed unconditionally for host targets.
|
|
'-UANDROID',
|
|
]
|
|
defaults.stl = 'none'
|
|
defaults.cpp_std = 'c++17'
|
|
defaults.min_sdk_version = 29
|
|
defaults.apex_available.add(tethering_apex)
|
|
blueprint.add_module(defaults)
|
|
|
|
for target in targets:
|
|
module = create_modules_from_target(blueprint, gn, target, is_test_target=False)
|
|
if module:
|
|
module.visibility.append(connectivity_visibility)
|
|
|
|
for test_target in test_targets:
|
|
module = create_modules_from_target(blueprint, gn, test_target + gn_utils.TESTING_SUFFIX, is_test_target=True)
|
|
if module:
|
|
module.visibility.append(connectivity_visibility)
|
|
|
|
create_java_api_module(blueprint, gn)
|
|
java_module = create_java_module(blueprint, gn, is_test_target=False)
|
|
java_module.libs.add(CRONET_API_MODULE_NAME)
|
|
java_module_testing = create_java_module(blueprint, gn, is_test_target=True)
|
|
java_module_testing.libs.add(CRONET_API_MODULE_NAME)
|
|
for module in blueprint.modules.values():
|
|
if 'cronet_jni_registration' in module.name:
|
|
update_jni_registration_module(module, gn)
|
|
if module.name in ['cronet_aml_base_base', 'cronet_aml_base_base' + gn_utils.TESTING_SUFFIX]:
|
|
turn_off_allocator_shim_for_musl(module)
|
|
|
|
# Merge in additional hardcoded arguments.
|
|
for module in blueprint.modules.values():
|
|
for key, add_val in additional_args.get(module.name, []):
|
|
curr = getattr(module, key)
|
|
if add_val and isinstance(add_val, set) and isinstance(curr, set):
|
|
curr.update(add_val)
|
|
elif isinstance(add_val, str) and (not curr or isinstance(curr, str)):
|
|
setattr(module, key, add_val)
|
|
elif isinstance(add_val, bool) and (not curr or isinstance(curr, bool)):
|
|
setattr(module, key, add_val)
|
|
elif isinstance(add_val, dict) and isinstance(curr, dict):
|
|
curr.update(add_val)
|
|
elif isinstance(add_val, dict) and isinstance(curr, Module.Target):
|
|
curr.__dict__.update(add_val)
|
|
else:
|
|
raise Exception('Unimplemented type %r of additional_args: %r' % (type(add_val), key))
|
|
|
|
return blueprint
|
|
|
|
def create_package_module(blueprint):
|
|
package = Module("package", "", "PACKAGE")
|
|
package.comment = "The actual license can be found in Android.extras.bp"
|
|
package.default_applicable_licenses.add(CRONET_LICENSE_NAME)
|
|
package.default_visibility.append(package_default_visibility)
|
|
blueprint.add_module(package)
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
description='Generate Android.bp from a GN description.')
|
|
parser.add_argument(
|
|
'--desc',
|
|
help='GN description (e.g., gn desc out --format=json --all-toolchains "//*".' +
|
|
'You can specify multiple --desc options for different target_cpu',
|
|
required=True,
|
|
action='append'
|
|
)
|
|
parser.add_argument(
|
|
'--extras',
|
|
help='Extra targets to include at the end of the Blueprint file',
|
|
default=os.path.join(gn_utils.repo_root(), 'Android.bp.extras'),
|
|
)
|
|
parser.add_argument(
|
|
'--output',
|
|
help='Blueprint file to create',
|
|
default=os.path.join(gn_utils.repo_root(), 'Android.bp'),
|
|
)
|
|
parser.add_argument(
|
|
'-v',
|
|
'--verbose',
|
|
help='Print debug logs.',
|
|
action='store_true',
|
|
)
|
|
parser.add_argument(
|
|
'targets',
|
|
nargs=argparse.REMAINDER,
|
|
help='Targets to include in the blueprint (e.g., "//:perfetto_tests")'
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
if args.verbose:
|
|
log.basicConfig(format='%(levelname)s:%(funcName)s:%(message)s', level=log.DEBUG)
|
|
|
|
targets = args.targets or DEFAULT_TARGETS
|
|
gn = gn_utils.GnParser(builtin_deps)
|
|
for desc_file in args.desc:
|
|
with open(desc_file) as f:
|
|
desc = json.load(f)
|
|
for target in targets:
|
|
gn.parse_gn_desc(desc, target)
|
|
for test_target in DEFAULT_TESTS:
|
|
gn.parse_gn_desc(desc, test_target, is_test_target=True)
|
|
blueprint = create_blueprint_for_targets(gn, targets, DEFAULT_TESTS)
|
|
project_root = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
|
|
tool_name = os.path.relpath(os.path.abspath(__file__), project_root)
|
|
|
|
create_package_module(blueprint)
|
|
output = [
|
|
"""// Copyright (C) 2022 The Android Open Source Project
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
// This file is automatically generated by %s. Do not edit.
|
|
|
|
build = ["Android.extras.bp"]
|
|
""" % (tool_name)
|
|
]
|
|
blueprint.to_string(output)
|
|
if os.path.exists(args.extras):
|
|
with open(args.extras, 'r') as r:
|
|
for line in r:
|
|
output.append(line.rstrip("\n\r"))
|
|
|
|
out_files = []
|
|
|
|
# Generate the Android.bp file.
|
|
out_files.append(args.output + '.swp')
|
|
with open(out_files[-1], 'w') as f:
|
|
f.write('\n'.join(output))
|
|
# Text files should have a trailing EOL.
|
|
f.write('\n')
|
|
|
|
return 0
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|