More options and rename maps; one file per rust_test
* Add a --no-host option.
* Add a --host-first-multilib option.
* Add special renaming for protoc_gen_rust.
* Dump a rust_defaults module to be shared by rust_test modules.
* Every test source file is in a rust_test module.
* Add RENAME_DEFAULTS_MAP for altered rust_defaults names.
* Suppress old error message on --extern proc_macro.
Test: regenerate Android.bp files and check differences
Bug: 161090277
Bug: 161089524
Change-Id: I8c70f568facf58d282ea2a2e72b60c8a2972b27d
diff --git a/scripts/cargo2android.py b/scripts/cargo2android.py
index 992872c..08f23fa 100755
--- a/scripts/cargo2android.py
+++ b/scripts/cargo2android.py
@@ -61,13 +61,26 @@
import sys
RENAME_MAP = {
- # This map includes all changes to the default rust library module
- # names to resolve name conflicts or avoid confusion.
+ # This map includes all changes to the default rust module names
+ # to resolve name conflicts, avoid confusion, or work as plugin.
'libbacktrace': 'libbacktrace_rust',
'libgcc': 'libgcc_rust',
'liblog': 'liblog_rust',
'libsync': 'libsync_rust',
'libx86_64': 'libx86_64_rust',
+ 'protoc_gen_rust': 'protoc-gen-rust',
+}
+
+RENAME_STEM_MAP = {
+ # This map includes all changes to the default rust module stem names,
+ # which is used for output files when different from the module name.
+ 'protoc_gen_rust': 'protoc-gen-rust',
+}
+
+RENAME_DEFAULTS_MAP = {
+ # This map includes all changes to the default prefix of rust_default
+ # module names, to avoid conflict with existing Android modules.
+ 'libc': 'rust_libc',
}
# Header added to all generated Android.bp files.
@@ -109,6 +122,14 @@
return RENAME_MAP[name] if (name in RENAME_MAP) else name
+def altered_stem(name):
+ return RENAME_STEM_MAP[name] if (name in RENAME_STEM_MAP) else name
+
+
+def altered_defaults(name):
+ return RENAME_DEFAULTS_MAP[name] if (name in RENAME_DEFAULTS_MAP) else name
+
+
def is_build_crate_name(name):
# We added special prefix to build script crate names.
return name.startswith('build_script_')
@@ -172,6 +193,7 @@
# Android module properties derived from rustc parameters.
self.module_name = '' # unique in Android build system
self.module_type = '' # rust_{binary,library,test}[_host] etc.
+ self.defaults = '' # rust_defaults used by rust_test* modules
self.root_pkg = '' # parent package name of a sub/test packge, from -L
self.srcs = list() # main_src or merged multiple source files
self.stem = '' # real base name of output file
@@ -407,6 +429,8 @@
if self.target:
self.device_supported = True
self.host_supported = True # assume host supported for all builds
+ if self.runner.args.no_host: # unless --no-host was specified
+ self.host_supported = False
self.cfgs = sorted(set(self.cfgs))
self.features = sorted(set(self.features))
self.codegens = sorted(set(self.codegens))
@@ -495,15 +519,68 @@
dump_list('// -l (dylib) = %s', self.shared_libs)
def dump_android_module(self):
- # Dump one Android module per crate_type.
+ """Dump one or more Android module definition, depending on crate_types."""
if len(self.crate_types) == 1:
- # do not change self.stem or self.module_name
- self.dump_one_android_module(self.crate_types[0])
+ self.dump_single_type_android_module()
return
+ if 'test' in self.crate_types:
+ self.write('\nERROR: multiple crate types cannot include test type')
+ return
+ # Dump one Android module per crate_type.
for crate_type in self.crate_types:
self.decide_one_module_type(crate_type)
self.dump_one_android_module(crate_type)
+ def dump_defaults_module(self):
+ """Dump a rust_defaults module to be shared by other modules."""
+ name = altered_defaults(self.root_pkg) + '_defaults'
+ name = self.runner.claim_module_name(name, self, 0)
+ self.defaults = name
+ self.write('\nrust_defaults {')
+ self.write(' name: "' + name + '",')
+ self.write(' crate_name: "' + self.crate_name + '",')
+ if 'test' in self.crate_types:
+ self.write(' test_suites: ["general-tests"],')
+ self.write(' auto_gen_config: true,')
+ self.dump_edition_flags_libs()
+ self.write('}')
+
+ def dump_single_type_android_module(self):
+ """Dump one simple Android module, which has only one crate_type."""
+ crate_type = self.crate_types[0]
+ if crate_type != 'test':
+ # do not change self.stem or self.module_name
+ self.dump_one_android_module(crate_type)
+ return
+ # Dump one test module per source file, and separate host and device tests.
+ # crate_type == 'test'
+ if (self.host_supported and self.device_supported) or len(self.srcs) > 1:
+ self.srcs = sorted(set(self.srcs))
+ self.dump_defaults_module()
+ saved_srcs = self.srcs
+ for src in saved_srcs:
+ self.srcs = [src]
+ saved_device_supported = self.device_supported
+ saved_host_supported = self.host_supported
+ saved_main_src = self.main_src
+ self.main_src = src
+ if saved_host_supported:
+ self.device_supported = False
+ self.host_supported = True
+ self.module_name = self.test_module_name()
+ self.decide_one_module_type(crate_type)
+ self.dump_one_android_module(crate_type)
+ if saved_device_supported:
+ self.device_supported = True
+ self.host_supported = False
+ self.module_name = self.test_module_name()
+ self.decide_one_module_type(crate_type)
+ self.dump_one_android_module(crate_type)
+ self.host_supported = saved_host_supported
+ self.device_supported = saved_device_supported
+ self.main_src = saved_main_src
+ self.srcs = saved_srcs
+
def dump_one_android_module(self, crate_type):
"""Dump one Android module definition."""
if not self.module_type:
@@ -511,9 +588,14 @@
return
self.write('\n' + self.module_type + ' {')
self.dump_android_core_properties()
- if self.edition:
- self.write(' edition: "' + self.edition + '",')
- self.dump_android_property_list('features', '"%s"', self.features)
+ if not self.defaults:
+ self.dump_edition_flags_libs()
+ if self.runner.args.host_first_multilib and self.host_supported and crate_type != 'test':
+ self.write(' compile_multilib: "first",')
+ self.write('}')
+
+ def dump_android_flags(self):
+ """Dump Android module flags property."""
cfg_fmt = '"--cfg %s"'
if self.cap_lints:
allowed = '"--cap-lints ' + self.cap_lints + '"'
@@ -525,19 +607,25 @@
self.write(' ],')
else:
self.dump_android_property_list('flags', cfg_fmt, self.cfgs)
+
+ def dump_edition_flags_libs(self):
+ if self.edition:
+ self.write(' edition: "' + self.edition + '",')
+ self.dump_android_property_list('features', '"%s"', self.features)
+ self.dump_android_flags()
if self.externs:
self.dump_android_externs()
self.dump_android_property_list('static_libs', '"lib%s"', self.static_libs)
self.dump_android_property_list('shared_libs', '"lib%s"', self.shared_libs)
- self.write('}')
def test_module_name(self):
"""Return a unique name for a test module."""
- # root_pkg+'_tests_'+(crate_name|source_file_path)
- suffix = self.crate_name
- if not suffix:
- suffix = re.sub('/', '_', re.sub('.rs$', '', self.main_src))
- return self.root_pkg + '_tests_' + suffix
+ # root_pkg+(_host|_device) + '_test_'+source_file_name
+ suffix = re.sub('/', '_', re.sub('.rs$', '', self.main_src))
+ host_device = '_host'
+ if self.device_supported:
+ host_device = '_device'
+ return self.root_pkg + host_device + '_test_' + suffix
def decide_module_type(self):
# Use the first crate type for the default/first module.
@@ -549,8 +637,10 @@
host = '' if self.device_supported else '_host'
if crate_type == 'bin': # rust_binary[_host]
self.module_type = 'rust_binary' + host
- self.stem = self.crate_name
- self.module_name = altered_name(self.stem)
+ # In rare cases like protobuf-codegen, the output binary name must
+ # be renamed to use as a plugin for protoc.
+ self.stem = altered_stem(self.crate_name)
+ self.module_name = altered_name(self.crate_name)
elif crate_type == 'lib': # rust_library[_host]
# TODO(chh): should this be rust_library[_host]?
# Assuming that Cargo.toml do not use both 'lib' and 'rlib',
@@ -581,8 +671,7 @@
self.stem = self.test_module_name()
# self.stem will be changed after merging with other tests.
# self.stem is NOT used for final test binary name.
- # rust_test uses each source file base name as its output file name,
- # unless crate_name is specified by user in Cargo.toml.
+ # rust_test uses each source file base name as part of output file name.
# In do_merge, this function is called again, with a module_name.
# We make sure that the module name is unique in each package.
if self.module_name:
@@ -616,19 +705,23 @@
def dump_android_core_properties(self):
"""Dump the module header, name, stem, etc."""
self.write(' name: "' + self.module_name + '",')
+ # see properties shared by dump_defaults_module
+ if self.defaults:
+ self.write(' defaults: ["' + self.defaults + '"],')
if self.stem != self.module_name:
self.write(' stem: "' + self.stem + '",')
if self.has_warning and not self.cap_lints:
self.write(' // has rustc warnings')
if self.host_supported and self.device_supported:
self.write(' host_supported: true,')
- self.write(' crate_name: "' + self.crate_name + '",')
+ if not self.defaults:
+ self.write(' crate_name: "' + self.crate_name + '",')
if len(self.srcs) > 1:
self.srcs = sorted(set(self.srcs))
self.dump_android_property_list('srcs', '"%s"', self.srcs)
else:
self.write(' srcs: ["' + self.main_src + '"],')
- if 'test' in self.crate_types:
+ if 'test' in self.crate_types and not self.defaults:
# self.root_pkg can have multiple test modules, with different *_tests[n]
# names, but their executables can all be installed under the same _tests
# directory. When built from Cargo.toml, all tests should have different
@@ -660,8 +753,8 @@
rust_libs += ' "' + altered_name('lib' + lib_name) + '",\n'
elif lib.endswith('.so'):
so_libs.append(lib_name)
- else:
- rust_libs += ' // ERROR: unknown type of lib ' + lib_name + '\n'
+ elif lib != 'proc_macro': # --extern proc_macro is special and ignored
+ rust_libs += ' // ERROR: unknown type of lib ' + lib + '\n'
if rust_libs:
self.write(' rustlibs: [\n' + rust_libs + ' ],')
# Are all dependent .so files proc_macros?
@@ -869,13 +962,16 @@
self.cargo = ['clean'] + args.cargo
else:
self.cargo = ['clean', 'build']
+ if args.no_host: # do not run "cargo build" for host
+ self.cargo = ['clean']
default_target = '--target x86_64-unknown-linux-gnu'
if args.device:
self.cargo.append('build ' + default_target)
if args.tests:
- self.cargo.append('build --tests')
+ if not args.no_host:
+ self.cargo.append('build --tests')
self.cargo.append('build --tests ' + default_target)
- elif args.tests:
+ elif args.tests and not args.no_host:
self.cargo.append('build --tests')
def init_bp_file(self, name):
@@ -1138,6 +1234,17 @@
default=False,
help='run cargo also for a default device target')
parser.add_argument(
+ '--no-host',
+ action='store_true',
+ default=False,
+ help='do not run cargo for the host; only for the device target')
+ parser.add_argument(
+ '--host-first-multilib',
+ action='store_true',
+ default=False,
+ help=('add a compile_multilib:"first" property ' +
+ 'to Android.bp host modules.'))
+ parser.add_argument(
'--features',
type=str,
help=('pass features to cargo build, ' +