Handle -Cflag syntax and use prebuilt cargo.
* Accept both "-C flag" and "-Cflag" syntax.
* Newer cargo uses "-Cembed-bitcode=no".
* Some -C flags are filtered out, not used in .bp file,
because they are meaningless in .bp file build system.
* Remaining -C flags are passed to rustc in .bp files,
which was missing before this change.
* Look up cargo in the prebuilts directory.
* Now limit this script to run only on linux.
* Try to find cargo/rust version from
build/soong/rust/config/global.go first.
* Otherwise, use the latest (largest) version in prebuilt.
* Add a new --cargo_bin flag to use any user-selected cargo.
* Add the selected cargo directory to PATH before calling cargo,
so it can find rustc in the same directory.
Bug: 161825397
Bug: 161927172
Test: regenerate and check .bp files in external/rust/crates
Change-Id: Ica46f536c2b37b62238d1245ced59685deebad33
diff --git a/scripts/cargo2android.py b/scripts/cargo2android.py
index 4a05eeb..2e93521 100755
--- a/scripts/cargo2android.py
+++ b/scripts/cargo2android.py
@@ -60,6 +60,7 @@
import argparse
import os
import os.path
+import platform
import re
import sys
@@ -363,6 +364,16 @@
return
dir_name = os.path.dirname(dir_name)
+ def add_codegens_flag(self, flag):
+ # ignore options not used in Android
+ # 'prefer-dynamic' does not work with common flag -C lto
+ if not (flag.startswith('debuginfo=') or
+ flag.startswith('extra-filename=') or
+ flag.startswith('incremental=') or
+ flag.startswith('metadata=') or
+ flag == 'prefer-dynamic'):
+ self.codegens.append(flag)
+
def parse(self, line_num, line):
"""Find important rustc arguments to convert to Android.bp properties."""
self.line_num = line_num
@@ -398,12 +409,11 @@
self.core_externs.append(re.sub(' = .*', '', extern_names))
elif arg == '-C': # codegen options
i += 1
- # ignore options not used in Android
- if not (args[i].startswith('debuginfo=') or
- args[i].startswith('extra-filename=') or
- args[i].startswith('incremental=') or
- args[i].startswith('metadata=')):
- self.codegens.append(args[i])
+ self.add_codegens_flag(args[i])
+ elif arg.startswith('-C'):
+ # cargo has been passing "-C <xyz>" flag to rustc,
+ # but newer cargo could pass '-Cembed-bitcode=no' to rustc.
+ self.add_codegens_flag(arg[2:])
elif arg == '--cap-lints':
i += 1
self.cap_lints = args[i]
@@ -660,17 +670,16 @@
def dump_android_flags(self):
"""Dump Android module flags property."""
- cfg_fmt = '"--cfg %s"'
+ if not self.cfgs and not self.codegens and not self.cap_lints:
+ return
+ self.write(' flags: [')
if self.cap_lints:
- allowed = '"--cap-lints ' + self.cap_lints + '"'
- if not self.cfgs:
- self.write(' flags: [' + allowed + '],')
- else:
- self.write(' flags: [\n ' + allowed + ',')
- self.dump_android_property_list_items(cfg_fmt, self.cfgs)
- self.write(' ],')
- else:
- self.dump_android_property_list('flags', cfg_fmt, self.cfgs)
+ self.write(' "--cap-lints ' + self.cap_lints + '",')
+ cfg_fmt = '"--cfg %s"'
+ codegens_fmt = '"-C %s"'
+ self.dump_android_property_list_items(cfg_fmt, self.cfgs)
+ self.dump_android_property_list_items(codegens_fmt, self.codegens)
+ self.write(' ],')
def dump_edition_flags_libs(self):
if self.edition:
@@ -1013,6 +1022,7 @@
self.args = args
self.dry_run = not args.run
self.skip_cargo = args.skipcargo
+ self.cargo_path = './cargo' # path to cargo, will be set later
# All cc/ar objects, crates, dependencies, and warning files
self.cc_objects = list()
self.pkg_obj2cc = {}
@@ -1025,6 +1035,7 @@
self.name_owners = {}
# Save and dump all errors from cargo to Android.bp.
self.errors = ''
+ self.setup_cargo_path()
# Default action is cargo clean, followed by build or user given actions.
if args.cargo:
self.cargo = ['clean'] + args.cargo
@@ -1042,6 +1053,58 @@
elif args.tests and not args.no_host:
self.cargo.append('build --tests')
+ def setup_cargo_path(self):
+ """Find cargo in the --cargo_bin or prebuilt rust bin directory."""
+ if self.args.cargo_bin:
+ self.cargo_path = os.path.join(self.args.cargo_bin, 'cargo')
+ if not os.path.isfile(self.cargo_path):
+ sys.exit('ERROR: cannot find cargo in ' + self.args.cargo_bin)
+ print('WARNING: using cargo in ' + self.args.cargo_bin)
+ return
+ # We have only tested this on Linux.
+ if platform.system() != 'Linux':
+ sys.exit('ERROR: this script has only been tested on Linux with cargo.')
+ # Assuming that this script is in development/scripts.
+ my_dir = os.path.dirname(os.path.abspath(__file__))
+ linux_dir = os.path.join(my_dir, '..', '..',
+ 'prebuilts', 'rust', 'linux-x86')
+ if not os.path.isdir(linux_dir):
+ sys.exit('ERROR: cannot find directory ' + linux_dir)
+ rust_version = self.find_rust_version(my_dir, linux_dir)
+ cargo_bin = os.path.join(linux_dir, rust_version, 'bin')
+ self.cargo_path = os.path.join(cargo_bin, 'cargo')
+ if not os.path.isfile(self.cargo_path):
+ sys.exit('ERROR: cannot find cargo in ' + cargo_bin
+ + '; please try --cargo_bin= flag.')
+ return
+
+ def find_rust_version(self, my_dir, linux_dir):
+ """Use my script directory, find prebuilt rust version."""
+ # First look up build/soong/rust/config/global.go.
+ path2global = os.path.join(my_dir, '..', '..',
+ 'build', 'soong', 'rust', 'config', 'global.go')
+ if os.path.isfile(path2global):
+ # try to find: RustDefaultVersion = "1.44.0"
+ version_pat = re.compile(
+ r'\s*RustDefaultVersion\s*=\s*"([0-9]+\.[0-9]+\.[0-9]+)".*$')
+ with open(path2global, 'r') as inf:
+ for line in inf:
+ result = version_pat.match(line)
+ if result:
+ return result.group(1)
+ print('WARNING: cannot find RustDefaultVersion in ' + path2global)
+ # Otherwise, find the newest (largest) version number in linux_dir.
+ rust_version = (0, 0, 0) # the prebuilt version to use
+ version_pat = re.compile(r'([0-9]+)\.([0-9]+)\.([0-9]+)$')
+ for dir_name in os.listdir(linux_dir):
+ result = version_pat.match(dir_name)
+ if not result:
+ continue
+ version = (result.group(1), result.group(2), result.group(3))
+ if version > rust_version:
+ rust_version = version
+ return '.'.join(rust_version)
+
def init_bp_file(self, name):
if name not in self.bp_files:
self.bp_files.add(name)
@@ -1107,6 +1170,9 @@
if not self.dry_run and os.path.exists('cargo.out'):
os.remove('cargo.out')
cmd_tail = ' --target-dir ' + TARGET_TMP + ' >> cargo.out 2>&1'
+ # set up search PATH for cargo to find the correct rustc
+ saved_path = os.environ['PATH']
+ os.environ['PATH'] = os.path.dirname(self.cargo_path) + ':' + saved_path
for c in self.cargo:
features = ''
if c != 'clean':
@@ -1114,7 +1180,8 @@
features = ' --no-default-features'
if self.args.features:
features += ' --features ' + self.args.features
- cmd = 'cargo -vv ' if self.args.vv else 'cargo -v '
+ cmd_v_flag = ' -vv ' if self.args.vv else ' -v '
+ cmd = self.cargo_path + cmd_v_flag
cmd += c + features + cmd_tail
if self.args.rustflags and c != 'clean':
cmd = 'RUSTFLAGS="' + self.args.rustflags + '" ' + cmd
@@ -1126,6 +1193,7 @@
with open('cargo.out', 'a') as cargo_out:
cargo_out.write('### Running: ' + cmd + '\n')
os.system(cmd)
+ os.environ['PATH'] = saved_path
return self
def dump_dependencies(self):
@@ -1306,6 +1374,10 @@
help=('extra cargo build -v args in a string, ' +
'each --cargo flag calls cargo build -v once'))
parser.add_argument(
+ '--cargo_bin',
+ type=str,
+ help='use cargo in the cargo_bin directory instead of the prebuilt one')
+ parser.add_argument(
'--debug',
action='store_true',
default=False,