blob: 43a98ff94ad4b3cd441b1da4c35bcb3420c785a4 [file] [log] [blame]
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001#!/usr/bin/env python3
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08002#
3# Copyright (C) 2019 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16"""Call cargo -v, parse its output, and generate Android.bp.
17
18Usage: Run this script in a crate workspace root directory.
19The Cargo.toml file should work at least for the host platform.
20
21(1) Without other flags, "cargo2android.py --run"
22 calls cargo clean, calls cargo build -v, and generates Android.bp.
23 The cargo build only generates crates for the host,
24 without test crates.
25
26(2) To build crates for both host and device in Android.bp, use the
27 --device flag, for example:
28 cargo2android.py --run --device
29
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -070030 Note that cargo build is only called once with the default target
31 x86_64-unknown-linux-gnu.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080032
33(3) To build default and test crates, for host and device, use both
34 --device and --tests flags:
35 cargo2android.py --run --device --tests
36
37 This is equivalent to using the --cargo flag to add extra builds:
38 cargo2android.py --run
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080039 --cargo "build --target x86_64-unknown-linux-gnu"
40 --cargo "build --tests --target x86_64-unknown-linux-gnu"
41
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -070042If there are rustc warning messages, this script will add
43a warning comment to the owner crate module in Android.bp.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080044"""
45
46from __future__ import print_function
47
48import argparse
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -070049import glob
Joel Galenson0fbdafe2021-04-21 16:33:33 -070050import json
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080051import os
52import os.path
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -070053import platform
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080054import re
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -070055import shutil
Joel Galenson7e8247e2021-05-20 18:51:42 -070056import subprocess
Andrew Walbran80e90be2020-06-09 14:33:18 +010057import sys
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080058
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -070059# Some Rust packages include extra unwanted crates.
60# This set contains all such excluded crate names.
61EXCLUDED_CRATES = set(['protobuf_bin_gen_rust_do_not_use'])
62
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080063RENAME_MAP = {
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -070064 # This map includes all changes to the default rust module names
65 # to resolve name conflicts, avoid confusion, or work as plugin.
Jason Macnak051340d2021-09-04 11:04:26 -070066 'libash': 'libash_rust',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080067 'libbacktrace': 'libbacktrace_rust',
Andrew Walbrane51f1042020-08-11 16:42:48 +010068 'libbase': 'libbase_rust',
Luke Huanga1371af2021-06-29 18:04:40 +080069 'libbase64': 'libbase64_rust',
Victor Hsieh21bea792020-12-04 10:59:16 -080070 'libfuse': 'libfuse_rust',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080071 'libgcc': 'libgcc_rust',
72 'liblog': 'liblog_rust',
Chih-Hung Hsieh07119862020-07-24 15:34:06 -070073 'libminijail': 'libminijail_rust',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080074 'libsync': 'libsync_rust',
75 'libx86_64': 'libx86_64_rust',
Jooyung Hana427c9b2021-07-16 08:53:14 +090076 'libxml': 'libxml_rust',
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -070077 'protoc_gen_rust': 'protoc-gen-rust',
78}
79
80RENAME_STEM_MAP = {
81 # This map includes all changes to the default rust module stem names,
82 # which is used for output files when different from the module name.
83 'protoc_gen_rust': 'protoc-gen-rust',
84}
85
86RENAME_DEFAULTS_MAP = {
87 # This map includes all changes to the default prefix of rust_default
88 # module names, to avoid conflict with existing Android modules.
89 'libc': 'rust_libc',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080090}
91
92# Header added to all generated Android.bp files.
Joel Galenson56446742021-02-18 08:27:48 -080093ANDROID_BP_HEADER = (
94 '// This file is generated by cargo2android.py {args}.\n' +
95 '// Do not modify this file as changes will be overridden on upgrade.\n')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080096
97CARGO_OUT = 'cargo.out' # Name of file to keep cargo build -v output.
98
Joel Galenson3f42f802021-04-07 12:42:17 -070099# This should be kept in sync with tools/external_updater/crates_updater.py.
100ERRORS_LINE = 'Errors in ' + CARGO_OUT + ':'
101
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800102TARGET_TMP = 'target.tmp' # Name of temporary output directory.
103
104# Message to be displayed when this script is called without the --run flag.
105DRY_RUN_NOTE = (
106 'Dry-run: This script uses ./' + TARGET_TMP + ' for output directory,\n' +
107 'runs cargo clean, runs cargo build -v, saves output to ./cargo.out,\n' +
108 'and writes to Android.bp in the current and subdirectories.\n\n' +
109 'To do do all of the above, use the --run flag.\n' +
110 'See --help for other flags, and more usage notes in this script.\n')
111
112# Cargo -v output of a call to rustc.
113RUSTC_PAT = re.compile('^ +Running `rustc (.*)`$')
114
115# Cargo -vv output of a call to rustc could be split into multiple lines.
116# Assume that the first line will contain some CARGO_* env definition.
117RUSTC_VV_PAT = re.compile('^ +Running `.*CARGO_.*=.*$')
118# The combined -vv output rustc command line pattern.
119RUSTC_VV_CMD_ARGS = re.compile('^ *Running `.*CARGO_.*=.* rustc (.*)`$')
120
121# Cargo -vv output of a "cc" or "ar" command; all in one line.
122CC_AR_VV_PAT = re.compile(r'^\[([^ ]*)[^\]]*\] running:? "(cc|ar)" (.*)$')
123# Some package, such as ring-0.13.5, has pattern '... running "cc"'.
124
125# Rustc output of file location path pattern for a warning message.
126WARNING_FILE_PAT = re.compile('^ *--> ([^:]*):[0-9]+')
127
Joel Galenson7cfc6fe2021-10-14 13:09:32 -0700128# cargo test --list output of the start of running a binary.
129CARGO_TEST_LIST_START_PAT = re.compile('^\s*Running (.*) \(.*\)$')
130
131# cargo test --list output of the end of running a binary.
132CARGO_TEST_LIST_END_PAT = re.compile('^(\d+) tests, (\d+) benchmarks$')
133
Joel Galenson56b5f4b2021-11-30 10:02:57 -0800134CARGO2ANDROID_RUNNING_PAT = re.compile('^### Running: .*$')
135
Joel Galenson308f3522021-09-30 14:13:45 -0700136# Rust package name with suffix -d1.d2.d3(+.*)?.
137VERSION_SUFFIX_PAT = re.compile(r'^(.*)-[0-9]+\.[0-9]+\.[0-9]+(?:\+.*)?$')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800138
Matthew Maurer062709c2021-08-17 11:27:36 -0700139# Crate types corresponding to a C ABI library
140C_LIBRARY_CRATE_TYPES = ['staticlib', 'cdylib']
141# Crate types corresponding to a Rust ABI library
142RUST_LIBRARY_CRATE_TYPES = ['lib', 'rlib', 'dylib']
143# Crate types corresponding to a library
144LIBRARY_CRATE_TYPES = C_LIBRARY_CRATE_TYPES + RUST_LIBRARY_CRATE_TYPES
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800145
146def altered_name(name):
147 return RENAME_MAP[name] if (name in RENAME_MAP) else name
148
149
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700150def altered_stem(name):
151 return RENAME_STEM_MAP[name] if (name in RENAME_STEM_MAP) else name
152
153
154def altered_defaults(name):
155 return RENAME_DEFAULTS_MAP[name] if (name in RENAME_DEFAULTS_MAP) else name
156
157
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800158def is_build_crate_name(name):
159 # We added special prefix to build script crate names.
160 return name.startswith('build_script_')
161
162
163def is_dependent_file_path(path):
164 # Absolute or dependent '.../' paths are not main files of this crate.
165 return path.startswith('/') or path.startswith('.../')
166
167
168def get_module_name(crate): # to sort crates in a list
169 return crate.module_name
170
171
172def pkg2crate_name(s):
173 return s.replace('-', '_').replace('.', '_')
174
175
176def file_base_name(path):
177 return os.path.splitext(os.path.basename(path))[0]
178
179
180def test_base_name(path):
181 return pkg2crate_name(file_base_name(path))
182
183
184def unquote(s): # remove quotes around str
185 if s and len(s) > 1 and s[0] == '"' and s[-1] == '"':
186 return s[1:-1]
187 return s
188
189
190def remove_version_suffix(s): # remove -d1.d2.d3 suffix
191 if VERSION_SUFFIX_PAT.match(s):
192 return VERSION_SUFFIX_PAT.match(s).group(1)
193 return s
194
195
196def short_out_name(pkg, s): # replace /.../pkg-*/out/* with .../out/*
197 return re.sub('^/.*/' + pkg + '-[0-9a-f]*/out/', '.../out/', s)
198
199
200def escape_quotes(s): # replace '"' with '\\"'
201 return s.replace('"', '\\"')
202
203
204class Crate(object):
205 """Information of a Rust crate to collect/emit for an Android.bp module."""
206
207 def __init__(self, runner, outf_name):
208 # Remembered global runner and its members.
209 self.runner = runner
210 self.debug = runner.args.debug
211 self.cargo_dir = '' # directory of my Cargo.toml
212 self.outf_name = outf_name # path to Android.bp
213 self.outf = None # open file handle of outf_name during dump*
214 # Variants/results that could be merged from multiple rustc lines.
215 self.host_supported = False
216 self.device_supported = False
217 self.has_warning = False
218 # Android module properties derived from rustc parameters.
219 self.module_name = '' # unique in Android build system
220 self.module_type = '' # rust_{binary,library,test}[_host] etc.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700221 self.defaults = '' # rust_defaults used by rust_test* modules
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700222 self.default_srcs = False # use 'srcs' defined in self.defaults
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800223 self.root_pkg = '' # parent package name of a sub/test packge, from -L
224 self.srcs = list() # main_src or merged multiple source files
225 self.stem = '' # real base name of output file
226 # Kept parsed status
227 self.errors = '' # all errors found during parsing
228 self.line_num = 1 # runner told input source line number
229 self.line = '' # original rustc command line parameters
230 # Parameters collected from rustc command line.
231 self.crate_name = '' # follows --crate-name
232 self.main_src = '' # follows crate_name parameter, shortened
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700233 self.crate_types = list() # follows --crate-type
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800234 self.cfgs = list() # follows --cfg, without feature= prefix
235 self.features = list() # follows --cfg, name in 'feature="..."'
236 self.codegens = list() # follows -C, some ignored
237 self.externs = list() # follows --extern
238 self.core_externs = list() # first part of self.externs elements
239 self.static_libs = list() # e.g. -l static=host_cpuid
240 self.shared_libs = list() # e.g. -l dylib=wayland-client, -l z
241 self.cap_lints = '' # follows --cap-lints
242 self.emit_list = '' # e.g., --emit=dep-info,metadata,link
243 self.edition = '2015' # rustc default, e.g., --edition=2018
244 self.target = '' # follows --target
Ivan Lozanocc660f12021-08-11 16:49:46 -0400245 self.cargo_env_compat = True
246 self.cargo_pkg_version = '' # value extracted from Cargo.toml version field
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800247
248 def write(self, s):
249 # convenient way to output one line at a time with EOL.
250 self.outf.write(s + '\n')
251
252 def same_flags(self, other):
253 # host_supported, device_supported, has_warning are not compared but merged
254 # target is not compared, to merge different target/host modules
255 # externs is not compared; only core_externs is compared
256 return (not self.errors and not other.errors and
257 self.edition == other.edition and
258 self.cap_lints == other.cap_lints and
259 self.emit_list == other.emit_list and
260 self.core_externs == other.core_externs and
261 self.codegens == other.codegens and
262 self.features == other.features and
263 self.static_libs == other.static_libs and
264 self.shared_libs == other.shared_libs and self.cfgs == other.cfgs)
265
266 def merge_host_device(self, other):
267 """Returns true if attributes are the same except host/device support."""
268 return (self.crate_name == other.crate_name and
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700269 self.crate_types == other.crate_types and
270 self.main_src == other.main_src and
271 # before merge, each test module has an unique module name and stem
272 (self.stem == other.stem or self.crate_types == ['test']) and
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800273 self.root_pkg == other.root_pkg and not self.skip_crate() and
274 self.same_flags(other))
275
276 def merge_test(self, other):
277 """Returns true if self and other are tests of same root_pkg."""
278 # Before merger, each test has its own crate_name.
279 # A merged test uses its source file base name as output file name,
280 # so a test is mergeable only if its base name equals to its crate name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700281 return (self.crate_types == other.crate_types and
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700282 self.crate_types == ['test'] and self.root_pkg == other.root_pkg and
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800283 not self.skip_crate() and
284 other.crate_name == test_base_name(other.main_src) and
285 (len(self.srcs) > 1 or
286 (self.crate_name == test_base_name(self.main_src)) and
287 self.host_supported == other.host_supported and
288 self.device_supported == other.device_supported) and
289 self.same_flags(other))
290
291 def merge(self, other, outf_name):
292 """Try to merge crate into self."""
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700293 # Cargo build --tests could recompile a library for tests.
294 # We need to merge such duplicated calls to rustc, with
295 # the algorithm in merge_host_device.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800296 should_merge_host_device = self.merge_host_device(other)
297 should_merge_test = False
298 if not should_merge_host_device:
299 should_merge_test = self.merge_test(other)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800300 if should_merge_host_device or should_merge_test:
301 self.runner.init_bp_file(outf_name)
302 with open(outf_name, 'a') as outf: # to write debug info
303 self.outf = outf
304 other.outf = outf
305 self.do_merge(other, should_merge_test)
306 return True
307 return False
308
309 def do_merge(self, other, should_merge_test):
310 """Merge attributes of other to self."""
311 if self.debug:
312 self.write('\n// Before merge definition (1):')
313 self.dump_debug_info()
314 self.write('\n// Before merge definition (2):')
315 other.dump_debug_info()
316 # Merge properties of other to self.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800317 self.has_warning = self.has_warning or other.has_warning
318 if not self.target: # okay to keep only the first target triple
319 self.target = other.target
320 # decide_module_type sets up default self.stem,
321 # which can be changed if self is a merged test module.
322 self.decide_module_type()
323 if should_merge_test:
Joel Galenson7cfc6fe2021-10-14 13:09:32 -0700324 if (self.runner.should_ignore_test(self.main_src)
325 and not self.runner.should_ignore_test(other.main_src)):
Joel Galenson57fa23a2021-07-15 10:47:35 -0700326 self.main_src = other.main_src
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800327 self.srcs.append(other.main_src)
328 # use a short unique name as the merged module name.
329 prefix = self.root_pkg + '_tests'
330 self.module_name = self.runner.claim_module_name(prefix, self, 0)
331 self.stem = self.module_name
332 # This normalized root_pkg name although might be the same
333 # as other module's crate_name, it is not actually used for
334 # output file name. A merged test module always have multiple
335 # source files and each source file base name is used as
336 # its output file name.
337 self.crate_name = pkg2crate_name(self.root_pkg)
338 if self.debug:
339 self.write('\n// After merge definition (1):')
340 self.dump_debug_info()
341
342 def find_cargo_dir(self):
343 """Deepest directory with Cargo.toml and contains the main_src."""
344 if not is_dependent_file_path(self.main_src):
345 dir_name = os.path.dirname(self.main_src)
346 while dir_name:
347 if os.path.exists(dir_name + '/Cargo.toml'):
348 self.cargo_dir = dir_name
349 return
350 dir_name = os.path.dirname(dir_name)
351
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700352 def add_codegens_flag(self, flag):
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700353 """Ignore options not used in Android."""
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700354 # 'prefer-dynamic' does not work with common flag -C lto
Chih-Hung Hsieh63459ed2020-08-26 11:51:15 -0700355 # 'embed-bitcode' is ignored; we might control LTO with other .bp flag
Chih-Hung Hsieh6c13b722020-09-11 21:24:03 -0700356 # 'codegen-units' is set in Android global config or by default
Joel Galenson4f4ac2f2021-11-22 15:22:45 -0800357 # 'lto' is used in Android, but it's set by the build system
Chih-Hung Hsieh6c13b722020-09-11 21:24:03 -0700358 if not (flag.startswith('codegen-units=') or
359 flag.startswith('debuginfo=') or
Chih-Hung Hsieh63459ed2020-08-26 11:51:15 -0700360 flag.startswith('embed-bitcode=') or
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700361 flag.startswith('extra-filename=') or
362 flag.startswith('incremental=') or
363 flag.startswith('metadata=') or
Joel Galenson4f4ac2f2021-11-22 15:22:45 -0800364 flag.startswith('lto=') or
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700365 flag == 'prefer-dynamic'):
366 self.codegens.append(flag)
367
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800368 def parse(self, line_num, line):
369 """Find important rustc arguments to convert to Android.bp properties."""
370 self.line_num = line_num
371 self.line = line
372 args = line.split() # Loop through every argument of rustc.
373 i = 0
374 while i < len(args):
375 arg = args[i]
376 if arg == '--crate-name':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700377 i += 1
378 self.crate_name = args[i]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800379 elif arg == '--crate-type':
380 i += 1
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700381 # cargo calls rustc with multiple --crate-type flags.
382 # rustc can accept:
383 # --crate-type [bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]
384 self.crate_types.append(args[i])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800385 elif arg == '--test':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700386 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800387 elif arg == '--target':
388 i += 1
389 self.target = args[i]
390 elif arg == '--cfg':
391 i += 1
392 if args[i].startswith('\'feature='):
393 self.features.append(unquote(args[i].replace('\'feature=', '')[:-1]))
394 else:
395 self.cfgs.append(args[i])
396 elif arg == '--extern':
397 i += 1
398 extern_names = re.sub('=/[^ ]*/deps/', ' = ', args[i])
399 self.externs.append(extern_names)
400 self.core_externs.append(re.sub(' = .*', '', extern_names))
401 elif arg == '-C': # codegen options
402 i += 1
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700403 self.add_codegens_flag(args[i])
404 elif arg.startswith('-C'):
405 # cargo has been passing "-C <xyz>" flag to rustc,
406 # but newer cargo could pass '-Cembed-bitcode=no' to rustc.
407 self.add_codegens_flag(arg[2:])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800408 elif arg == '--cap-lints':
409 i += 1
410 self.cap_lints = args[i]
411 elif arg == '-L':
412 i += 1
413 if args[i].startswith('dependency=') and args[i].endswith('/deps'):
414 if '/' + TARGET_TMP + '/' in args[i]:
415 self.root_pkg = re.sub(
416 '^.*/', '', re.sub('/' + TARGET_TMP + '/.*/deps$', '', args[i]))
417 else:
418 self.root_pkg = re.sub('^.*/', '',
419 re.sub('/[^/]+/[^/]+/deps$', '', args[i]))
420 self.root_pkg = remove_version_suffix(self.root_pkg)
421 elif arg == '-l':
422 i += 1
423 if args[i].startswith('static='):
424 self.static_libs.append(re.sub('static=', '', args[i]))
425 elif args[i].startswith('dylib='):
426 self.shared_libs.append(re.sub('dylib=', '', args[i]))
427 else:
428 self.shared_libs.append(args[i])
429 elif arg == '--out-dir' or arg == '--color': # ignored
430 i += 1
431 elif arg.startswith('--error-format=') or arg.startswith('--json='):
432 _ = arg # ignored
433 elif arg.startswith('--emit='):
434 self.emit_list = arg.replace('--emit=', '')
435 elif arg.startswith('--edition='):
436 self.edition = arg.replace('--edition=', '')
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700437 elif not arg.startswith('-'):
438 # shorten imported crate main source paths like $HOME/.cargo/
439 # registry/src/github.com-1ecc6299db9ec823/memchr-2.3.3/src/lib.rs
440 self.main_src = re.sub(r'^/[^ ]*/registry/src/', '.../', args[i])
441 self.main_src = re.sub(r'^\.\.\./github.com-[0-9a-f]*/', '.../',
442 self.main_src)
443 self.find_cargo_dir()
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700444 if self.cargo_dir: # for a subdirectory
445 if self.runner.args.no_subdir: # all .bp content to /dev/null
446 self.outf_name = '/dev/null'
447 elif not self.runner.args.onefile:
448 # Write to Android.bp in the subdirectory with Cargo.toml.
449 self.outf_name = self.cargo_dir + '/Android.bp'
450 self.main_src = self.main_src[len(self.cargo_dir) + 1:]
Ivan Lozanocc660f12021-08-11 16:49:46 -0400451
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800452 else:
453 self.errors += 'ERROR: unknown ' + arg + '\n'
454 i += 1
455 if not self.crate_name:
456 self.errors += 'ERROR: missing --crate-name\n'
457 if not self.main_src:
458 self.errors += 'ERROR: missing main source file\n'
459 else:
460 self.srcs.append(self.main_src)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700461 if not self.crate_types:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800462 # Treat "--cfg test" as "--test"
463 if 'test' in self.cfgs:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700464 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800465 else:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700466 self.errors += 'ERROR: missing --crate-type or --test\n'
467 elif len(self.crate_types) > 1:
468 if 'test' in self.crate_types:
469 self.errors += 'ERROR: cannot handle both --crate-type and --test\n'
470 if 'lib' in self.crate_types and 'rlib' in self.crate_types:
471 self.errors += 'ERROR: cannot generate both lib and rlib crate types\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800472 if not self.root_pkg:
473 self.root_pkg = self.crate_name
Ivan Lozano26aa1c32021-08-16 11:20:32 -0400474
475 # get the package version from running cargo metadata
Joel Galenson69ba8072021-08-16 11:31:29 -0700476 if not self.runner.args.no_pkg_vers and not self.skip_crate():
Ivan Lozano26aa1c32021-08-16 11:20:32 -0400477 self.get_pkg_version()
478
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700479 self.device_supported = self.runner.args.device
480 self.host_supported = not self.runner.args.no_host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800481 self.cfgs = sorted(set(self.cfgs))
482 self.features = sorted(set(self.features))
483 self.codegens = sorted(set(self.codegens))
484 self.externs = sorted(set(self.externs))
485 self.core_externs = sorted(set(self.core_externs))
486 self.static_libs = sorted(set(self.static_libs))
487 self.shared_libs = sorted(set(self.shared_libs))
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700488 self.crate_types = sorted(set(self.crate_types))
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800489 self.decide_module_type()
490 self.module_name = altered_name(self.stem)
491 return self
492
Ivan Lozano26aa1c32021-08-16 11:20:32 -0400493 def get_pkg_version(self):
494 """Attempt to retrieve the package version from the Cargo.toml
495
496 If there is only one package, use its version. Otherwise, try to
497 match the emitted `--crate_name` arg against the package name.
498
499 This may fail in cases where multiple packages are defined (workspaces)
500 and where the package name does not match the emitted crate_name
501 (e.g. [lib.name] is set).
502 """
Joel Galenson69ba8072021-08-16 11:31:29 -0700503 cargo_metadata = subprocess.run([self.runner.cargo_path, 'metadata', '--no-deps',
504 '--format-version', '1'],
Joel Galensonc5186502021-08-16 11:22:47 -0700505 cwd=os.path.abspath(self.cargo_dir),
506 stdout=subprocess.PIPE)
Ivan Lozano26aa1c32021-08-16 11:20:32 -0400507 if cargo_metadata.returncode:
508 self.errors += ('ERROR: unable to get cargo metadata for package version; ' +
509 'return code ' + cargo_metadata.returncode + '\n')
510 else:
511 metadata_json = json.loads(cargo_metadata.stdout)
512 if len(metadata_json['packages']) > 1:
513 for package in metadata_json['packages']:
514 # package names may contain '-', but is changed to '_' in the crate_name
515 if package['name'].replace('-','_') == self.crate_name:
516 self.cargo_pkg_version = package['version']
517 break
518 else:
519 self.cargo_pkg_version = metadata_json['packages'][0]['version']
520
521 if not self.cargo_pkg_version:
522 self.errors += ('ERROR: Unable to retrieve package version; ' +
523 'to disable, run with arg "--no-pkg-vers"\n')
524
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800525 def dump_line(self):
526 self.write('\n// Line ' + str(self.line_num) + ' ' + self.line)
527
528 def feature_list(self):
529 """Return a string of main_src + "feature_list"."""
530 pkg = self.main_src
531 if pkg.startswith('.../'): # keep only the main package name
532 pkg = re.sub('/.*', '', pkg[4:])
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700533 elif pkg.startswith('/'): # use relative path for a local package
534 pkg = os.path.relpath(pkg)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800535 if not self.features:
536 return pkg
537 return pkg + ' "' + ','.join(self.features) + '"'
538
539 def dump_skip_crate(self, kind):
540 if self.debug:
541 self.write('\n// IGNORED: ' + kind + ' ' + self.main_src)
542 return self
543
544 def skip_crate(self):
545 """Return crate_name or a message if this crate should be skipped."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700546 if (is_build_crate_name(self.crate_name) or
547 self.crate_name in EXCLUDED_CRATES):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800548 return self.crate_name
549 if is_dependent_file_path(self.main_src):
550 return 'dependent crate'
551 return ''
552
553 def dump(self):
554 """Dump all error/debug/module code to the output .bp file."""
555 self.runner.init_bp_file(self.outf_name)
556 with open(self.outf_name, 'a') as outf:
557 self.outf = outf
558 if self.errors:
559 self.dump_line()
560 self.write(self.errors)
561 elif self.skip_crate():
562 self.dump_skip_crate(self.skip_crate())
563 else:
564 if self.debug:
565 self.dump_debug_info()
566 self.dump_android_module()
567
568 def dump_debug_info(self):
569 """Dump parsed data, when cargo2android is called with --debug."""
570
571 def dump(name, value):
572 self.write('//%12s = %s' % (name, value))
573
574 def opt_dump(name, value):
575 if value:
576 dump(name, value)
577
578 def dump_list(fmt, values):
579 for v in values:
580 self.write(fmt % v)
581
582 self.dump_line()
583 dump('module_name', self.module_name)
584 dump('crate_name', self.crate_name)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700585 dump('crate_types', self.crate_types)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800586 dump('main_src', self.main_src)
587 dump('has_warning', self.has_warning)
588 dump('for_host', self.host_supported)
589 dump('for_device', self.device_supported)
590 dump('module_type', self.module_type)
591 opt_dump('target', self.target)
592 opt_dump('edition', self.edition)
593 opt_dump('emit_list', self.emit_list)
594 opt_dump('cap_lints', self.cap_lints)
595 dump_list('// cfg = %s', self.cfgs)
596 dump_list('// cfg = \'feature "%s"\'', self.features)
597 # TODO(chh): escape quotes in self.features, but not in other dump_list
598 dump_list('// codegen = %s', self.codegens)
599 dump_list('// externs = %s', self.externs)
600 dump_list('// -l static = %s', self.static_libs)
601 dump_list('// -l (dylib) = %s', self.shared_libs)
602
603 def dump_android_module(self):
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700604 """Dump one or more Android module definition, depending on crate_types."""
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700605 if len(self.crate_types) == 1:
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700606 self.dump_single_type_android_module()
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700607 return
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700608 if 'test' in self.crate_types:
609 self.write('\nERROR: multiple crate types cannot include test type')
610 return
611 # Dump one Android module per crate_type.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700612 for crate_type in self.crate_types:
613 self.decide_one_module_type(crate_type)
614 self.dump_one_android_module(crate_type)
615
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700616 def build_default_name(self):
617 """Return a short and readable name for the rust_defaults module."""
Joel Galensond37d7e62021-07-13 09:03:01 -0700618 # Choices: (1) root_pkg + '_test'? + '_defaults',
619 # (2) root_pkg + '_test'? + '_defaults_' + crate_name
620 # (3) root_pkg + '_test'? + '_defaults_' + main_src_basename_path
621 # (4) root_pkg + '_test'? + '_defaults_' + a_positive_sequence_number
622 test = "_test" if self.crate_types == ['test'] else ""
623 name1 = altered_defaults(self.root_pkg) + test + '_defaults'
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700624 if self.runner.try_claim_module_name(name1, self):
625 return name1
626 name2 = name1 + '_' + self.crate_name
627 if self.runner.try_claim_module_name(name2, self):
628 return name2
629 name3 = name1 + '_' + self.main_src_basename_path()
630 if self.runner.try_claim_module_name(name3, self):
631 return name3
632 return self.runner.claim_module_name(name1, self, 0)
633
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700634 def dump_srcs_list(self):
635 """Dump the srcs list, for defaults or regular modules."""
636 if len(self.srcs) > 1:
637 srcs = sorted(set(self.srcs)) # make a copy and dedup
638 else:
639 srcs = [self.main_src]
640 copy_out = self.runner.copy_out_module_name()
641 if copy_out:
642 srcs.append(':' + copy_out)
643 self.dump_android_property_list('srcs', '"%s"', srcs)
644
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700645 def dump_defaults_module(self):
646 """Dump a rust_defaults module to be shared by other modules."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700647 name = self.build_default_name()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700648 self.defaults = name
649 self.write('\nrust_defaults {')
650 self.write(' name: "' + name + '",')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700651 if self.runner.args.global_defaults:
652 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700653 self.write(' crate_name: "' + self.crate_name + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700654 if len(self.srcs) == 1: # only one source file; share it in defaults
655 self.default_srcs = True
656 if self.has_warning and not self.cap_lints:
657 self.write(' // has rustc warnings')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700658 self.dump_srcs_list()
Joel Galensonb06c42a2021-08-31 14:28:48 -0700659 if self.cargo_env_compat:
660 self.write(' cargo_env_compat: true,')
Joel Galenson94b8a8d2021-09-28 11:53:51 -0700661 if not self.runner.args.no_pkg_vers:
662 self.write(' cargo_pkg_version: "' + self.cargo_pkg_version + '",')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700663 if 'test' in self.crate_types:
664 self.write(' test_suites: ["general-tests"],')
665 self.write(' auto_gen_config: true,')
666 self.dump_edition_flags_libs()
Joel Galensone4f53882021-07-19 11:14:55 -0700667 if 'test' in self.crate_types and len(self.srcs) == 1:
668 self.dump_test_data()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700669 self.write('}')
670
671 def dump_single_type_android_module(self):
672 """Dump one simple Android module, which has only one crate_type."""
673 crate_type = self.crate_types[0]
674 if crate_type != 'test':
675 # do not change self.stem or self.module_name
676 self.dump_one_android_module(crate_type)
677 return
678 # Dump one test module per source file, and separate host and device tests.
679 # crate_type == 'test'
Joel Galenson7cfc6fe2021-10-14 13:09:32 -0700680 self.srcs = [src for src in self.srcs if not self.runner.should_ignore_test(src)]
Joel Galensonf6b3c912021-06-03 16:00:54 -0700681 if ((self.host_supported and self.device_supported and len(self.srcs) > 0) or
682 len(self.srcs) > 1):
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700683 self.srcs = sorted(set(self.srcs))
684 self.dump_defaults_module()
685 saved_srcs = self.srcs
686 for src in saved_srcs:
687 self.srcs = [src]
688 saved_device_supported = self.device_supported
689 saved_host_supported = self.host_supported
690 saved_main_src = self.main_src
691 self.main_src = src
692 if saved_host_supported:
693 self.device_supported = False
694 self.host_supported = True
695 self.module_name = self.test_module_name()
696 self.decide_one_module_type(crate_type)
697 self.dump_one_android_module(crate_type)
698 if saved_device_supported:
699 self.device_supported = True
700 self.host_supported = False
701 self.module_name = self.test_module_name()
702 self.decide_one_module_type(crate_type)
703 self.dump_one_android_module(crate_type)
704 self.host_supported = saved_host_supported
705 self.device_supported = saved_device_supported
706 self.main_src = saved_main_src
707 self.srcs = saved_srcs
708
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700709 def dump_one_android_module(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800710 """Dump one Android module definition."""
711 if not self.module_type:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700712 self.write('\nERROR: unknown crate_type ' + crate_type)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800713 return
714 self.write('\n' + self.module_type + ' {')
715 self.dump_android_core_properties()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700716 if not self.defaults:
717 self.dump_edition_flags_libs()
718 if self.runner.args.host_first_multilib and self.host_supported and crate_type != 'test':
719 self.write(' compile_multilib: "first",')
Matthew Maurer062709c2021-08-17 11:27:36 -0700720 if self.runner.args.exported_c_header_dir and crate_type in C_LIBRARY_CRATE_TYPES:
721 self.write(' include_dirs: [')
722 for header_dir in self.runner.args.exported_c_header_dir:
723 self.write(' "%s",' % header_dir)
724 self.write(' ],')
Matthew Maurer9e4b7812021-08-16 14:21:01 -0700725 if self.runner.args.apex_available and crate_type in LIBRARY_CRATE_TYPES:
Joel Galensond9c4de62021-04-23 10:26:40 -0700726 self.write(' apex_available: [')
727 for apex in self.runner.args.apex_available:
728 self.write(' "%s",' % apex)
729 self.write(' ],')
Matthew Maurer70182e42021-08-17 13:53:52 -0700730 if crate_type != 'test':
731 if self.runner.args.native_bridge_supported:
732 self.write(' native_bridge_supported: true,')
733 if self.runner.args.product_available:
734 self.write(' product_available: true,')
735 if self.runner.args.recovery_available:
736 self.write(' recovery_available: true,')
737 if self.runner.args.vendor_available:
738 self.write(' vendor_available: true,')
739 if self.runner.args.vendor_ramdisk_available:
740 self.write(' vendor_ramdisk_available: true,')
741 if self.runner.args.ramdisk_available:
742 self.write(' ramdisk_available: true,')
Matthew Maurer9e4b7812021-08-16 14:21:01 -0700743 if self.runner.args.min_sdk_version and crate_type in LIBRARY_CRATE_TYPES:
Joel Galensond9c4de62021-04-23 10:26:40 -0700744 self.write(' min_sdk_version: "%s",' % self.runner.args.min_sdk_version)
Joel Galensone4f53882021-07-19 11:14:55 -0700745 if crate_type == 'test' and not self.default_srcs:
746 self.dump_test_data()
Joel Galenson5664f2a2021-06-10 10:13:49 -0700747 if self.runner.args.add_module_block:
748 with open(self.runner.args.add_module_block, 'r') as f:
749 self.write(' %s,' % f.read().replace('\n', '\n '))
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700750 self.write('}')
751
752 def dump_android_flags(self):
753 """Dump Android module flags property."""
ThiƩbaud Weksteena5a728b2021-04-08 14:23:49 +0200754 if not self.codegens and not self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700755 return
756 self.write(' flags: [')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800757 if self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700758 self.write(' "--cap-lints ' + self.cap_lints + '",')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700759 codegens_fmt = '"-C %s"'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700760 self.dump_android_property_list_items(codegens_fmt, self.codegens)
761 self.write(' ],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700762
763 def dump_edition_flags_libs(self):
764 if self.edition:
765 self.write(' edition: "' + self.edition + '",')
766 self.dump_android_property_list('features', '"%s"', self.features)
Joel Galenson3d6d1e72021-06-07 15:00:24 -0700767 cfgs = [cfg for cfg in self.cfgs if not cfg in self.runner.args.cfg_blocklist]
768 self.dump_android_property_list('cfgs', '"%s"', cfgs)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700769 self.dump_android_flags()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800770 if self.externs:
771 self.dump_android_externs()
Joel Galenson12467e52021-07-12 14:33:28 -0700772 all_static_libs = [lib for lib in self.static_libs if not lib in self.runner.args.lib_blocklist]
773 static_libs = [lib for lib in all_static_libs if not lib in self.runner.args.whole_static_libs]
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700774 self.dump_android_property_list('static_libs', '"lib%s"', static_libs)
Joel Galenson12467e52021-07-12 14:33:28 -0700775 whole_static_libs = [lib for lib in all_static_libs if lib in self.runner.args.whole_static_libs]
776 self.dump_android_property_list('whole_static_libs', '"lib%s"', whole_static_libs)
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700777 shared_libs = [lib for lib in self.shared_libs if not lib in self.runner.args.lib_blocklist]
778 self.dump_android_property_list('shared_libs', '"lib%s"', shared_libs)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800779
Joel Galensone4f53882021-07-19 11:14:55 -0700780 def dump_test_data(self):
781 data = [data for (name, data) in map(lambda kv: kv.split('=', 1), self.runner.args.test_data)
782 if self.srcs == [name]]
783 if data:
784 self.dump_android_property_list('data', '"%s"', data)
785
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700786 def main_src_basename_path(self):
787 return re.sub('/', '_', re.sub('.rs$', '', self.main_src))
788
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800789 def test_module_name(self):
790 """Return a unique name for a test module."""
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700791 # root_pkg+(_host|_device) + '_test_'+source_file_name
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700792 suffix = self.main_src_basename_path()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700793 host_device = '_host'
794 if self.device_supported:
795 host_device = '_device'
796 return self.root_pkg + host_device + '_test_' + suffix
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800797
798 def decide_module_type(self):
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700799 # Use the first crate type for the default/first module.
800 crate_type = self.crate_types[0] if self.crate_types else ''
801 self.decide_one_module_type(crate_type)
802
803 def decide_one_module_type(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800804 """Decide which Android module type to use."""
805 host = '' if self.device_supported else '_host'
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700806 rlib = '_rlib' if self.runner.args.force_rlib else ''
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700807 if crate_type == 'bin': # rust_binary[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800808 self.module_type = 'rust_binary' + host
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700809 # In rare cases like protobuf-codegen, the output binary name must
810 # be renamed to use as a plugin for protoc.
811 self.stem = altered_stem(self.crate_name)
812 self.module_name = altered_name(self.crate_name)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700813 elif crate_type == 'lib': # rust_library[_host]
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700814 # TODO(chh): should this be rust_library[_host]?
815 # Assuming that Cargo.toml do not use both 'lib' and 'rlib',
816 # because we map them both to rlib.
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700817 self.module_type = 'rust_library' + rlib + host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800818 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700819 self.module_name = altered_name(self.stem)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700820 elif crate_type == 'rlib': # rust_library[_host]
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700821 self.module_type = 'rust_library' + rlib + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700822 self.stem = 'lib' + self.crate_name
823 self.module_name = altered_name(self.stem)
824 elif crate_type == 'dylib': # rust_library[_host]_dylib
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800825 self.module_type = 'rust_library' + host + '_dylib'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700826 self.stem = 'lib' + self.crate_name
827 self.module_name = altered_name(self.stem) + '_dylib'
828 elif crate_type == 'cdylib': # rust_library[_host]_shared
Ivan Lozano0c057ad2020-12-15 10:41:26 -0500829 self.module_type = 'rust_ffi' + host + '_shared'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700830 self.stem = 'lib' + self.crate_name
831 self.module_name = altered_name(self.stem) + '_shared'
832 elif crate_type == 'staticlib': # rust_library[_host]_static
Ivan Lozano0c057ad2020-12-15 10:41:26 -0500833 self.module_type = 'rust_ffi' + host + '_static'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700834 self.stem = 'lib' + self.crate_name
835 self.module_name = altered_name(self.stem) + '_static'
836 elif crate_type == 'test': # rust_test[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800837 self.module_type = 'rust_test' + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700838 # Before do_merge, stem name is based on the --crate-name parameter.
839 # and test module name is based on stem.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800840 self.stem = self.test_module_name()
841 # self.stem will be changed after merging with other tests.
842 # self.stem is NOT used for final test binary name.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700843 # rust_test uses each source file base name as part of output file name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700844 # In do_merge, this function is called again, with a module_name.
845 # We make sure that the module name is unique in each package.
846 if self.module_name:
847 # Cargo uses "-C extra-filename=..." and "-C metadata=..." to add
848 # different suffixes and distinguish multiple tests of the same
849 # crate name. We ignore -C and use claim_module_name to get
850 # unique sequential suffix.
851 self.module_name = self.runner.claim_module_name(
852 self.module_name, self, 0)
853 # Now the module name is unique, stem should also match and unique.
854 self.stem = self.module_name
855 elif crate_type == 'proc-macro': # rust_proc_macro
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800856 self.module_type = 'rust_proc_macro'
857 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700858 self.module_name = altered_name(self.stem)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800859 else: # unknown module type, rust_prebuilt_dylib? rust_library[_host]?
860 self.module_type = ''
861 self.stem = ''
862
863 def dump_android_property_list_items(self, fmt, values):
864 for v in values:
865 # fmt has quotes, so we need escape_quotes(v)
866 self.write(' ' + (fmt % escape_quotes(v)) + ',')
867
868 def dump_android_property_list(self, name, fmt, values):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700869 if not values:
870 return
871 if len(values) > 1:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800872 self.write(' ' + name + ': [')
873 self.dump_android_property_list_items(fmt, values)
874 self.write(' ],')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700875 else:
876 self.write(' ' + name + ': [' +
877 (fmt % escape_quotes(values[0])) + '],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800878
879 def dump_android_core_properties(self):
880 """Dump the module header, name, stem, etc."""
881 self.write(' name: "' + self.module_name + '",')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700882 # see properties shared by dump_defaults_module
883 if self.defaults:
884 self.write(' defaults: ["' + self.defaults + '"],')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700885 elif self.runner.args.global_defaults:
886 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800887 if self.stem != self.module_name:
888 self.write(' stem: "' + self.stem + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700889 if self.has_warning and not self.cap_lints and not self.default_srcs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700890 self.write(' // has rustc warnings')
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700891 if self.host_supported and self.device_supported and self.module_type != 'rust_proc_macro':
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800892 self.write(' host_supported: true,')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700893 if not self.defaults:
894 self.write(' crate_name: "' + self.crate_name + '",')
Ivan Lozanocc660f12021-08-11 16:49:46 -0400895 if not self.defaults and self.cargo_env_compat:
896 self.write(' cargo_env_compat: true,')
Joel Galenson94b8a8d2021-09-28 11:53:51 -0700897 if not self.runner.args.no_pkg_vers:
898 self.write(' cargo_pkg_version: "' + self.cargo_pkg_version + '",')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700899 if not self.default_srcs:
900 self.dump_srcs_list()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700901 if 'test' in self.crate_types and not self.defaults:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800902 # self.root_pkg can have multiple test modules, with different *_tests[n]
903 # names, but their executables can all be installed under the same _tests
904 # directory. When built from Cargo.toml, all tests should have different
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700905 # file or crate names. So we used (root_pkg + '_tests') name as the
906 # relative_install_path.
907 # However, some package like 'slab' can have non-mergeable tests that
908 # must be separated by different module names. So, here we no longer
909 # emit relative_install_path.
910 # self.write(' relative_install_path: "' + self.root_pkg + '_tests",')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800911 self.write(' test_suites: ["general-tests"],')
912 self.write(' auto_gen_config: true,')
Joel Galensone261a152021-01-12 11:31:53 -0800913 if 'test' in self.crate_types and self.host_supported:
914 self.write(' test_options: {')
915 self.write(' unit_test: true,')
916 self.write(' },')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800917
918 def dump_android_externs(self):
919 """Dump the dependent rlibs and dylibs property."""
920 so_libs = list()
921 rust_libs = ''
922 deps_libname = re.compile('^.* = lib(.*)-[0-9a-f]*.(rlib|so|rmeta)$')
923 for lib in self.externs:
924 # normal value of lib: "libc = liblibc-*.rlib"
925 # strange case in rand crate: "getrandom_package = libgetrandom-*.rlib"
926 # we should use "libgetrandom", not "lib" + "getrandom_package"
927 groups = deps_libname.match(lib)
928 if groups is not None:
929 lib_name = groups.group(1)
930 else:
931 lib_name = re.sub(' .*$', '', lib)
Joel Galenson97e414a2021-05-27 09:42:32 -0700932 if lib_name in self.runner.args.dependency_blocklist:
933 continue
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800934 if lib.endswith('.rlib') or lib.endswith('.rmeta'):
935 # On MacOS .rmeta is used when Linux uses .rlib or .rmeta.
936 rust_libs += ' "' + altered_name('lib' + lib_name) + '",\n'
937 elif lib.endswith('.so'):
938 so_libs.append(lib_name)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700939 elif lib != 'proc_macro': # --extern proc_macro is special and ignored
940 rust_libs += ' // ERROR: unknown type of lib ' + lib + '\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800941 if rust_libs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700942 self.write(' rustlibs: [\n' + rust_libs + ' ],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800943 # Are all dependent .so files proc_macros?
944 # TODO(chh): Separate proc_macros and dylib.
945 self.dump_android_property_list('proc_macros', '"lib%s"', so_libs)
946
947
948class ARObject(object):
949 """Information of an "ar" link command."""
950
951 def __init__(self, runner, outf_name):
952 # Remembered global runner and its members.
953 self.runner = runner
954 self.pkg = ''
955 self.outf_name = outf_name # path to Android.bp
956 # "ar" arguments
957 self.line_num = 1
958 self.line = ''
959 self.flags = '' # e.g. "crs"
960 self.lib = '' # e.g. "/.../out/lib*.a"
961 self.objs = list() # e.g. "/.../out/.../*.o"
962
963 def parse(self, pkg, line_num, args_line):
964 """Collect ar obj/lib file names."""
965 self.pkg = pkg
966 self.line_num = line_num
967 self.line = args_line
968 args = args_line.split()
969 num_args = len(args)
970 if num_args < 3:
971 print('ERROR: "ar" command has too few arguments', args_line)
972 else:
973 self.flags = unquote(args[0])
974 self.lib = unquote(args[1])
975 self.objs = sorted(set(map(unquote, args[2:])))
976 return self
977
978 def write(self, s):
979 self.outf.write(s + '\n')
980
981 def dump_debug_info(self):
982 self.write('\n// Line ' + str(self.line_num) + ' "ar" ' + self.line)
983 self.write('// ar_object for %12s' % self.pkg)
984 self.write('// flags = %s' % self.flags)
985 self.write('// lib = %s' % short_out_name(self.pkg, self.lib))
986 for o in self.objs:
987 self.write('// obj = %s' % short_out_name(self.pkg, o))
988
989 def dump_android_lib(self):
990 """Write cc_library_static into Android.bp."""
991 self.write('\ncc_library_static {')
992 self.write(' name: "' + file_base_name(self.lib) + '",')
993 self.write(' host_supported: true,')
994 if self.flags != 'crs':
995 self.write(' // ar flags = %s' % self.flags)
996 if self.pkg not in self.runner.pkg_obj2cc:
997 self.write(' ERROR: cannot find source files.\n}')
998 return
999 self.write(' srcs: [')
1000 obj2cc = self.runner.pkg_obj2cc[self.pkg]
1001 # Note: wflags are ignored.
1002 dflags = list()
1003 fflags = list()
1004 for obj in self.objs:
1005 self.write(' "' + short_out_name(self.pkg, obj2cc[obj].src) + '",')
1006 # TODO(chh): union of dflags and flags of all obj
1007 # Now, just a temporary hack that uses the last obj's flags
1008 dflags = obj2cc[obj].dflags
1009 fflags = obj2cc[obj].fflags
1010 self.write(' ],')
1011 self.write(' cflags: [')
1012 self.write(' "-O3",') # TODO(chh): is this default correct?
1013 self.write(' "-Wno-error",')
1014 for x in fflags:
1015 self.write(' "-f' + x + '",')
1016 for x in dflags:
1017 self.write(' "-D' + x + '",')
1018 self.write(' ],')
1019 self.write('}')
1020
1021 def dump(self):
1022 """Dump error/debug/module info to the output .bp file."""
1023 self.runner.init_bp_file(self.outf_name)
1024 with open(self.outf_name, 'a') as outf:
1025 self.outf = outf
1026 if self.runner.args.debug:
1027 self.dump_debug_info()
1028 self.dump_android_lib()
1029
1030
1031class CCObject(object):
1032 """Information of a "cc" compilation command."""
1033
1034 def __init__(self, runner, outf_name):
1035 # Remembered global runner and its members.
1036 self.runner = runner
1037 self.pkg = ''
1038 self.outf_name = outf_name # path to Android.bp
1039 # "cc" arguments
1040 self.line_num = 1
1041 self.line = ''
1042 self.src = ''
1043 self.obj = ''
1044 self.dflags = list() # -D flags
1045 self.fflags = list() # -f flags
1046 self.iflags = list() # -I flags
1047 self.wflags = list() # -W flags
1048 self.other_args = list()
1049
1050 def parse(self, pkg, line_num, args_line):
1051 """Collect cc compilation flags and src/out file names."""
1052 self.pkg = pkg
1053 self.line_num = line_num
1054 self.line = args_line
1055 args = args_line.split()
1056 i = 0
1057 while i < len(args):
1058 arg = args[i]
1059 if arg == '"-c"':
1060 i += 1
1061 if args[i].startswith('"-o'):
1062 # ring-0.13.5 dumps: ... "-c" "-o/.../*.o" ".../*.c"
1063 self.obj = unquote(args[i])[2:]
1064 i += 1
1065 self.src = unquote(args[i])
1066 else:
1067 self.src = unquote(args[i])
1068 elif arg == '"-o"':
1069 i += 1
1070 self.obj = unquote(args[i])
1071 elif arg == '"-I"':
1072 i += 1
1073 self.iflags.append(unquote(args[i]))
1074 elif arg.startswith('"-D'):
1075 self.dflags.append(unquote(args[i])[2:])
1076 elif arg.startswith('"-f'):
1077 self.fflags.append(unquote(args[i])[2:])
1078 elif arg.startswith('"-W'):
1079 self.wflags.append(unquote(args[i])[2:])
1080 elif not (arg.startswith('"-O') or arg == '"-m64"' or arg == '"-g"' or
1081 arg == '"-g3"'):
1082 # ignore -O -m64 -g
1083 self.other_args.append(unquote(args[i]))
1084 i += 1
1085 self.dflags = sorted(set(self.dflags))
1086 self.fflags = sorted(set(self.fflags))
1087 # self.wflags is not sorted because some are order sensitive
1088 # and we ignore them anyway.
1089 if self.pkg not in self.runner.pkg_obj2cc:
1090 self.runner.pkg_obj2cc[self.pkg] = {}
1091 self.runner.pkg_obj2cc[self.pkg][self.obj] = self
1092 return self
1093
1094 def write(self, s):
1095 self.outf.write(s + '\n')
1096
1097 def dump_debug_flags(self, name, flags):
1098 self.write('// ' + name + ':')
1099 for f in flags:
1100 self.write('// %s' % f)
1101
1102 def dump(self):
1103 """Dump only error/debug info to the output .bp file."""
1104 if not self.runner.args.debug:
1105 return
1106 self.runner.init_bp_file(self.outf_name)
1107 with open(self.outf_name, 'a') as outf:
1108 self.outf = outf
1109 self.write('\n// Line ' + str(self.line_num) + ' "cc" ' + self.line)
1110 self.write('// cc_object for %12s' % self.pkg)
1111 self.write('// src = %s' % short_out_name(self.pkg, self.src))
1112 self.write('// obj = %s' % short_out_name(self.pkg, self.obj))
1113 self.dump_debug_flags('-I flags', self.iflags)
1114 self.dump_debug_flags('-D flags', self.dflags)
1115 self.dump_debug_flags('-f flags', self.fflags)
1116 self.dump_debug_flags('-W flags', self.wflags)
1117 if self.other_args:
1118 self.dump_debug_flags('other args', self.other_args)
1119
1120
1121class Runner(object):
1122 """Main class to parse cargo -v output and print Android module definitions."""
1123
1124 def __init__(self, args):
1125 self.bp_files = set() # Remember all output Android.bp files.
1126 self.root_pkg = '' # name of package in ./Cargo.toml
1127 # Saved flags, modes, and data.
1128 self.args = args
1129 self.dry_run = not args.run
1130 self.skip_cargo = args.skipcargo
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001131 self.cargo_path = './cargo' # path to cargo, will be set later
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001132 self.checked_out_files = False # to check only once
1133 self.build_out_files = [] # output files generated by build.rs
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001134 # All cc/ar objects, crates, dependencies, and warning files
1135 self.cc_objects = list()
1136 self.pkg_obj2cc = {}
1137 # pkg_obj2cc[cc_object[i].pkg][cc_objects[i].obj] = cc_objects[i]
1138 self.ar_objects = list()
1139 self.crates = list()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001140 self.warning_files = set()
1141 # Keep a unique mapping from (module name) to crate
1142 self.name_owners = {}
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001143 # Save and dump all errors from cargo to Android.bp.
1144 self.errors = ''
Joel Galenson56b5f4b2021-11-30 10:02:57 -08001145 self.test_errors = ''
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001146 self.setup_cargo_path()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001147 # Default action is cargo clean, followed by build or user given actions.
1148 if args.cargo:
1149 self.cargo = ['clean'] + args.cargo
1150 else:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001151 default_target = '--target x86_64-unknown-linux-gnu'
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -07001152 # Use the same target for both host and default device builds.
1153 # Same target is used as default in host x86_64 Android compilation.
1154 # Note: b/169872957, prebuilt cargo failed to build vsock
1155 # on x86_64-unknown-linux-musl systems.
1156 self.cargo = ['clean', 'build ' + default_target]
1157 if args.tests:
1158 self.cargo.append('build --tests ' + default_target)
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001159 self.empty_tests = set()
1160 self.empty_unittests = False
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001161
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001162 def setup_cargo_path(self):
1163 """Find cargo in the --cargo_bin or prebuilt rust bin directory."""
1164 if self.args.cargo_bin:
1165 self.cargo_path = os.path.join(self.args.cargo_bin, 'cargo')
1166 if not os.path.isfile(self.cargo_path):
1167 sys.exit('ERROR: cannot find cargo in ' + self.args.cargo_bin)
1168 print('WARNING: using cargo in ' + self.args.cargo_bin)
1169 return
1170 # We have only tested this on Linux.
1171 if platform.system() != 'Linux':
1172 sys.exit('ERROR: this script has only been tested on Linux with cargo.')
1173 # Assuming that this script is in development/scripts.
1174 my_dir = os.path.dirname(os.path.abspath(__file__))
1175 linux_dir = os.path.join(my_dir, '..', '..',
1176 'prebuilts', 'rust', 'linux-x86')
1177 if not os.path.isdir(linux_dir):
1178 sys.exit('ERROR: cannot find directory ' + linux_dir)
1179 rust_version = self.find_rust_version(my_dir, linux_dir)
1180 cargo_bin = os.path.join(linux_dir, rust_version, 'bin')
1181 self.cargo_path = os.path.join(cargo_bin, 'cargo')
1182 if not os.path.isfile(self.cargo_path):
1183 sys.exit('ERROR: cannot find cargo in ' + cargo_bin
1184 + '; please try --cargo_bin= flag.')
1185 return
1186
1187 def find_rust_version(self, my_dir, linux_dir):
1188 """Use my script directory, find prebuilt rust version."""
1189 # First look up build/soong/rust/config/global.go.
1190 path2global = os.path.join(my_dir, '..', '..',
1191 'build', 'soong', 'rust', 'config', 'global.go')
1192 if os.path.isfile(path2global):
1193 # try to find: RustDefaultVersion = "1.44.0"
1194 version_pat = re.compile(
1195 r'\s*RustDefaultVersion\s*=\s*"([0-9]+\.[0-9]+\.[0-9]+)".*$')
1196 with open(path2global, 'r') as inf:
1197 for line in inf:
1198 result = version_pat.match(line)
1199 if result:
1200 return result.group(1)
1201 print('WARNING: cannot find RustDefaultVersion in ' + path2global)
1202 # Otherwise, find the newest (largest) version number in linux_dir.
1203 rust_version = (0, 0, 0) # the prebuilt version to use
1204 version_pat = re.compile(r'([0-9]+)\.([0-9]+)\.([0-9]+)$')
1205 for dir_name in os.listdir(linux_dir):
1206 result = version_pat.match(dir_name)
1207 if not result:
1208 continue
1209 version = (result.group(1), result.group(2), result.group(3))
1210 if version > rust_version:
1211 rust_version = version
1212 return '.'.join(rust_version)
1213
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001214 def find_out_files(self):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001215 # list1 has build.rs output for normal crates
1216 list1 = glob.glob(TARGET_TMP + '/*/*/build/' + self.root_pkg + '-*/out/*')
1217 # list2 has build.rs output for proc-macro crates
1218 list2 = glob.glob(TARGET_TMP + '/*/build/' + self.root_pkg + '-*/out/*')
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001219 return list1 + list2
1220
1221 def copy_out_files(self):
1222 """Copy build.rs output files to ./out and set up build_out_files."""
1223 if self.checked_out_files:
1224 return
1225 self.checked_out_files = True
1226 cargo_out_files = self.find_out_files()
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001227 out_files = set()
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001228 if cargo_out_files:
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001229 os.makedirs('out', exist_ok=True)
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001230 for path in cargo_out_files:
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001231 file_name = path.split('/')[-1]
1232 out_files.add(file_name)
1233 shutil.copy(path, 'out/' + file_name)
1234 self.build_out_files = sorted(out_files)
1235
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001236 def has_used_out_dir(self):
1237 """Returns true if env!("OUT_DIR") is found."""
1238 return 0 == os.system('grep -rl --exclude build.rs --include \\*.rs' +
1239 ' \'env!("OUT_DIR")\' * > /dev/null')
1240
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001241 def copy_out_module_name(self):
1242 if self.args.copy_out and self.build_out_files:
1243 return 'copy_' + self.root_pkg + '_build_out'
1244 else:
1245 return ''
1246
Haibo Huang0f72c952021-03-19 11:34:15 -07001247 def read_license(self, name):
1248 if not os.path.isfile(name):
1249 return ''
1250 license = ''
1251 with open(name, 'r') as intf:
1252 line = intf.readline()
1253 # Firstly skip ANDROID_BP_HEADER
1254 while line.startswith('//'):
1255 line = intf.readline()
Joel Galensond9d13b82021-04-05 11:27:55 -07001256 # Read all lines until we see a rust_* or genrule rule.
1257 while line != '' and not (line.startswith('rust_') or line.startswith('genrule {')):
Haibo Huang0f72c952021-03-19 11:34:15 -07001258 license += line
1259 line = intf.readline()
1260 return license.strip()
1261
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001262 def dump_copy_out_module(self, outf):
1263 """Output the genrule module to copy out/* to $(genDir)."""
1264 copy_out = self.copy_out_module_name()
1265 if not copy_out:
1266 return
1267 outf.write('\ngenrule {\n')
1268 outf.write(' name: "' + copy_out + '",\n')
1269 outf.write(' srcs: ["out/*"],\n')
1270 outf.write(' cmd: "cp $(in) $(genDir)",\n')
1271 if len(self.build_out_files) > 1:
1272 outf.write(' out: [\n')
1273 for f in self.build_out_files:
1274 outf.write(' "' + f + '",\n')
1275 outf.write(' ],\n')
1276 else:
1277 outf.write(' out: ["' + self.build_out_files[0] + '"],\n')
1278 outf.write('}\n')
1279
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001280 def init_bp_file(self, name):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001281 # name could be Android.bp or sub_dir_path/Android.bp
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001282 if name not in self.bp_files:
1283 self.bp_files.add(name)
Haibo Huang0f72c952021-03-19 11:34:15 -07001284 license_section = self.read_license(name)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001285 with open(name, 'w') as outf:
Joel Galensonc3bfaf82021-08-18 09:39:36 -07001286 outf.write(ANDROID_BP_HEADER.format(args=' '.join(sys.argv[1:])))
Haibo Huang0f72c952021-03-19 11:34:15 -07001287 outf.write('\n')
1288 outf.write(license_section)
1289 outf.write('\n')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001290 # at most one copy_out module per .bp file
1291 self.dump_copy_out_module(outf)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001292
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001293 def try_claim_module_name(self, name, owner):
1294 """Reserve and return True if it has not been reserved yet."""
1295 if name not in self.name_owners or owner == self.name_owners[name]:
1296 self.name_owners[name] = owner
1297 return True
1298 return False
1299
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001300 def claim_module_name(self, prefix, owner, counter):
1301 """Return prefix if not owned yet, otherwise, prefix+str(counter)."""
1302 while True:
1303 name = prefix
1304 if counter > 0:
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001305 name += '_' + str(counter)
1306 if self.try_claim_module_name(name, owner):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001307 return name
1308 counter += 1
1309
1310 def find_root_pkg(self):
1311 """Read name of [package] in ./Cargo.toml."""
1312 if not os.path.exists('./Cargo.toml'):
1313 return
1314 with open('./Cargo.toml', 'r') as inf:
1315 pkg_section = re.compile(r'^ *\[package\]')
1316 name = re.compile('^ *name *= * "([^"]*)"')
1317 in_pkg = False
1318 for line in inf:
1319 if in_pkg:
1320 if name.match(line):
1321 self.root_pkg = name.match(line).group(1)
1322 break
1323 else:
1324 in_pkg = pkg_section.match(line) is not None
1325
1326 def run_cargo(self):
1327 """Calls cargo -v and save its output to ./cargo.out."""
1328 if self.skip_cargo:
1329 return self
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001330 cargo_toml = './Cargo.toml'
1331 cargo_out = './cargo.out'
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001332 # Do not use Cargo.lock, because .bp rules are designed to
1333 # run with "latest" crates avaialable on Android.
1334 cargo_lock = './Cargo.lock'
1335 cargo_lock_saved = './cargo.lock.saved'
1336 had_cargo_lock = os.path.exists(cargo_lock)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001337 if not os.access(cargo_toml, os.R_OK):
1338 print('ERROR: Cannot find or read', cargo_toml)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001339 return self
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001340 if not self.dry_run:
1341 if os.path.exists(cargo_out):
1342 os.remove(cargo_out)
1343 if not self.args.use_cargo_lock and had_cargo_lock: # save it
1344 os.rename(cargo_lock, cargo_lock_saved)
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001345 cmd_tail_target = ' --target-dir ' + TARGET_TMP
1346 cmd_tail_redir = ' >> ' + cargo_out + ' 2>&1'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001347 # set up search PATH for cargo to find the correct rustc
1348 saved_path = os.environ['PATH']
1349 os.environ['PATH'] = os.path.dirname(self.cargo_path) + ':' + saved_path
Joel Galenson4f4ac2f2021-11-22 15:22:45 -08001350 # We need to enable lto since our test prebuilts use it
1351 saved_rustflags = os.environ.get('RUSTFLAGS', '')
1352 os.environ['RUSTFLAGS'] = '-C lto=thin -C embed-bitcode=yes ' + saved_rustflags
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001353 # Add [workspace] to Cargo.toml if it is not there.
1354 added_workspace = False
1355 if self.args.add_workspace:
1356 with open(cargo_toml, 'r') as in_file:
1357 cargo_toml_lines = in_file.readlines()
1358 found_workspace = '[workspace]\n' in cargo_toml_lines
1359 if found_workspace:
1360 print('### WARNING: found [workspace] in Cargo.toml')
1361 else:
1362 with open(cargo_toml, 'a') as out_file:
1363 out_file.write('[workspace]\n')
1364 added_workspace = True
1365 if self.args.verbose:
1366 print('### INFO: added [workspace] to Cargo.toml')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001367 for c in self.cargo:
1368 features = ''
Chih-Hung Hsieh6c8d52f2020-03-30 18:28:52 -07001369 if c != 'clean':
1370 if self.args.features is not None:
1371 features = ' --no-default-features'
1372 if self.args.features:
1373 features += ' --features ' + self.args.features
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001374 cmd_v_flag = ' -vv ' if self.args.vv else ' -v '
1375 cmd = self.cargo_path + cmd_v_flag
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001376 cmd += c + features + cmd_tail_target + cmd_tail_redir
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001377 if self.args.rustflags and c != 'clean':
Joel Galenson4f4ac2f2021-11-22 15:22:45 -08001378 cmd = 'RUSTFLAGS="' + os.environ['RUSTFLAGS'] + ' ' + self.args.rustflags + '" ' + cmd
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001379 self.run_cmd(cmd, cargo_out)
1380 if self.args.tests:
1381 cmd = self.cargo_path + ' test' + features + cmd_tail_target + ' -- --list' + cmd_tail_redir
1382 self.run_cmd(cmd, cargo_out)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001383 if added_workspace: # restore original Cargo.toml
1384 with open(cargo_toml, 'w') as out_file:
1385 out_file.writelines(cargo_toml_lines)
1386 if self.args.verbose:
1387 print('### INFO: restored original Cargo.toml')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001388 os.environ['PATH'] = saved_path
Joel Galenson4f4ac2f2021-11-22 15:22:45 -08001389 os.environ['RUSTFLAGS'] = saved_rustflags
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001390 if not self.dry_run:
1391 if not had_cargo_lock: # restore to no Cargo.lock state
1392 os.remove(cargo_lock)
1393 elif not self.args.use_cargo_lock: # restore saved Cargo.lock
1394 os.rename(cargo_lock_saved, cargo_lock)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001395 return self
1396
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001397 def run_cmd(self, cmd, cargo_out):
1398 if self.dry_run:
1399 print('Dry-run skip:', cmd)
1400 else:
1401 if self.args.verbose:
1402 print('Running:', cmd)
1403 with open(cargo_out, 'a') as out_file:
1404 out_file.write('### Running: ' + cmd + '\n')
1405 ret = os.system(cmd)
1406 if ret != 0:
1407 print('*** There was an error while running cargo. ' +
1408 'See the cargo.out file for details.')
1409
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001410 def dump_pkg_obj2cc(self):
1411 """Dump debug info of the pkg_obj2cc map."""
1412 if not self.args.debug:
1413 return
1414 self.init_bp_file('Android.bp')
1415 with open('Android.bp', 'a') as outf:
1416 sorted_pkgs = sorted(self.pkg_obj2cc.keys())
1417 for pkg in sorted_pkgs:
1418 if not self.pkg_obj2cc[pkg]:
1419 continue
1420 outf.write('\n// obj => src for %s\n' % pkg)
1421 obj2cc = self.pkg_obj2cc[pkg]
1422 for obj in sorted(obj2cc.keys()):
1423 outf.write('// ' + short_out_name(pkg, obj) + ' => ' +
1424 short_out_name(pkg, obj2cc[obj].src) + '\n')
1425
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001426 def apply_patch(self):
1427 """Apply local patch file if it is given."""
1428 if self.args.patch:
1429 if self.dry_run:
1430 print('Dry-run skip patch file:', self.args.patch)
1431 else:
1432 if not os.path.exists(self.args.patch):
1433 self.append_to_bp('ERROR cannot find patch file: ' + self.args.patch)
1434 return self
1435 if self.args.verbose:
1436 print('### INFO: applying local patch file:', self.args.patch)
Joel Galenson7e8247e2021-05-20 18:51:42 -07001437 subprocess.run(['patch', '-s', '--no-backup-if-mismatch', './Android.bp',
1438 self.args.patch], check=True)
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001439 return self
1440
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001441 def gen_bp(self):
1442 """Parse cargo.out and generate Android.bp files."""
1443 if self.dry_run:
1444 print('Dry-run skip: read', CARGO_OUT, 'write Android.bp')
1445 elif os.path.exists(CARGO_OUT):
1446 self.find_root_pkg()
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001447 if self.args.copy_out:
1448 self.copy_out_files()
1449 elif self.find_out_files() and self.has_used_out_dir():
1450 print('WARNING: ' + self.root_pkg + ' has cargo output files; ' +
1451 'please rerun with the --copy-out flag.')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001452 with open(CARGO_OUT, 'r') as cargo_out:
1453 self.parse(cargo_out, 'Android.bp')
1454 self.crates.sort(key=get_module_name)
1455 for obj in self.cc_objects:
1456 obj.dump()
1457 self.dump_pkg_obj2cc()
1458 for crate in self.crates:
1459 crate.dump()
1460 dumped_libs = set()
1461 for lib in self.ar_objects:
1462 if lib.pkg == self.root_pkg:
1463 lib_name = file_base_name(lib.lib)
1464 if lib_name not in dumped_libs:
1465 dumped_libs.add(lib_name)
1466 lib.dump()
Joel Galenson5664f2a2021-06-10 10:13:49 -07001467 if self.args.add_toplevel_block:
1468 with open(self.args.add_toplevel_block, 'r') as f:
1469 self.append_to_bp('\n' + f.read() + '\n')
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001470 if self.errors:
Joel Galenson3f42f802021-04-07 12:42:17 -07001471 self.append_to_bp('\n' + ERRORS_LINE + '\n' + self.errors)
Joel Galenson56b5f4b2021-11-30 10:02:57 -08001472 if self.test_errors:
1473 self.append_to_bp('\n// Errors when listing tests:\n' + self.test_errors)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001474 return self
1475
1476 def add_ar_object(self, obj):
1477 self.ar_objects.append(obj)
1478
1479 def add_cc_object(self, obj):
1480 self.cc_objects.append(obj)
1481
1482 def add_crate(self, crate):
1483 """Merge crate with someone in crates, or append to it. Return crates."""
1484 if crate.skip_crate():
1485 if self.args.debug: # include debug info of all crates
1486 self.crates.append(crate)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001487 else:
1488 for c in self.crates:
1489 if c.merge(crate, 'Android.bp'):
1490 return
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001491 # If not merged, decide module type and name now.
1492 crate.decide_module_type()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001493 self.crates.append(crate)
1494
1495 def find_warning_owners(self):
1496 """For each warning file, find its owner crate."""
1497 missing_owner = False
1498 for f in self.warning_files:
1499 cargo_dir = '' # find lowest crate, with longest path
1500 owner = None # owner crate of this warning
1501 for c in self.crates:
1502 if (f.startswith(c.cargo_dir + '/') and
1503 len(cargo_dir) < len(c.cargo_dir)):
1504 cargo_dir = c.cargo_dir
1505 owner = c
1506 if owner:
1507 owner.has_warning = True
1508 else:
1509 missing_owner = True
1510 if missing_owner and os.path.exists('Cargo.toml'):
1511 # owner is the root cargo, with empty cargo_dir
1512 for c in self.crates:
1513 if not c.cargo_dir:
1514 c.has_warning = True
1515
1516 def rustc_command(self, n, rustc_line, line, outf_name):
1517 """Process a rustc command line from cargo -vv output."""
1518 # cargo build -vv output can have multiple lines for a rustc command
1519 # due to '\n' in strings for environment variables.
1520 # strip removes leading spaces and '\n' at the end
1521 new_rustc = (rustc_line.strip() + line) if rustc_line else line
1522 # Use an heuristic to detect the completions of a multi-line command.
1523 # This might fail for some very rare case, but easy to fix manually.
1524 if not line.endswith('`\n') or (new_rustc.count('`') % 2) != 0:
1525 return new_rustc
1526 if RUSTC_VV_CMD_ARGS.match(new_rustc):
1527 args = RUSTC_VV_CMD_ARGS.match(new_rustc).group(1)
1528 self.add_crate(Crate(self, outf_name).parse(n, args))
1529 else:
1530 self.assert_empty_vv_line(new_rustc)
1531 return ''
1532
1533 def cc_ar_command(self, n, groups, outf_name):
1534 pkg = groups.group(1)
1535 line = groups.group(3)
1536 if groups.group(2) == 'cc':
1537 self.add_cc_object(CCObject(self, outf_name).parse(pkg, n, line))
1538 else:
1539 self.add_ar_object(ARObject(self, outf_name).parse(pkg, n, line))
1540
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001541 def append_to_bp(self, line):
1542 self.init_bp_file('Android.bp')
1543 with open('Android.bp', 'a') as outf:
1544 outf.write(line)
1545
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001546 def assert_empty_vv_line(self, line):
1547 if line: # report error if line is not empty
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001548 self.append_to_bp('ERROR -vv line: ' + line)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001549 return ''
1550
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001551 def add_empty_test(self, name):
1552 if name == 'unittests':
1553 self.empty_unittests = True
1554 else:
1555 self.empty_tests.add(name)
1556
1557 def should_ignore_test(self, src):
1558 # cargo test outputs the source file for integration tests but "unittests"
1559 # for unit tests. To figure out to which crate this corresponds, we check
1560 # if the current source file is the main source of a non-test crate, e.g.,
1561 # a library or a binary.
1562 return (src in self.args.test_blocklist or src in self.empty_tests
1563 or (self.empty_unittests
1564 and src in [c.main_src for c in self.crates if c.crate_types != ['test']]))
1565
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001566 def parse(self, inf, outf_name):
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001567 """Parse rustc, test, and warning messages in inf, return a list of Crates."""
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001568 n = 0 # line number
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001569 # We read the file in two passes, where the first simply checks for empty tests.
1570 # Otherwise we would add and merge tests before seeing they're empty.
1571 cur_test_name = None
1572 for line in inf:
1573 if CARGO_TEST_LIST_START_PAT.match(line):
1574 cur_test_name = CARGO_TEST_LIST_START_PAT.match(line).group(1)
1575 elif cur_test_name and CARGO_TEST_LIST_END_PAT.match(line):
1576 match = CARGO_TEST_LIST_END_PAT.match(line)
1577 if int(match.group(1)) + int(match.group(2)) == 0:
1578 self.add_empty_test(cur_test_name)
1579 cur_test_name = None
1580 inf.seek(0)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001581 prev_warning = False # true if the previous line was warning: ...
1582 rustc_line = '' # previous line(s) matching RUSTC_VV_PAT
Joel Galenson56b5f4b2021-11-30 10:02:57 -08001583 in_tests = False
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001584 for line in inf:
1585 n += 1
1586 if line.startswith('warning: '):
1587 prev_warning = True
1588 rustc_line = self.assert_empty_vv_line(rustc_line)
1589 continue
1590 new_rustc = ''
1591 if RUSTC_PAT.match(line):
1592 args_line = RUSTC_PAT.match(line).group(1)
1593 self.add_crate(Crate(self, outf_name).parse(n, args_line))
1594 self.assert_empty_vv_line(rustc_line)
1595 elif rustc_line or RUSTC_VV_PAT.match(line):
1596 new_rustc = self.rustc_command(n, rustc_line, line, outf_name)
1597 elif CC_AR_VV_PAT.match(line):
1598 self.cc_ar_command(n, CC_AR_VV_PAT.match(line), outf_name)
1599 elif prev_warning and WARNING_FILE_PAT.match(line):
1600 self.assert_empty_vv_line(rustc_line)
1601 fpath = WARNING_FILE_PAT.match(line).group(1)
1602 if fpath[0] != '/': # ignore absolute path
1603 self.warning_files.add(fpath)
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001604 elif line.startswith('error: ') or line.startswith('error[E'):
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001605 if not self.args.ignore_cargo_errors:
Joel Galenson56b5f4b2021-11-30 10:02:57 -08001606 if in_tests:
1607 self.test_errors += '// ' + line
1608 else:
1609 self.errors += line
1610 elif CARGO2ANDROID_RUNNING_PAT.match(line):
1611 in_tests = "cargo test" in line and "--list" in line
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001612 prev_warning = False
1613 rustc_line = new_rustc
1614 self.find_warning_owners()
1615
1616
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001617def get_parser():
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001618 """Parse main arguments."""
1619 parser = argparse.ArgumentParser('cargo2android')
1620 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001621 '--add_workspace',
1622 action='store_true',
1623 default=False,
1624 help=('append [workspace] to Cargo.toml before calling cargo,' +
1625 ' to treat current directory as root of package source;' +
1626 ' otherwise the relative source file path in generated' +
1627 ' .bp file will be from the parent directory.'))
1628 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001629 '--cargo',
1630 action='append',
1631 metavar='args_string',
1632 help=('extra cargo build -v args in a string, ' +
1633 'each --cargo flag calls cargo build -v once'))
1634 parser.add_argument(
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001635 '--cargo_bin',
1636 type=str,
1637 help='use cargo in the cargo_bin directory instead of the prebuilt one')
1638 parser.add_argument(
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001639 '--copy-out',
1640 action='store_true',
1641 default=False,
1642 help=('only for root directory, ' +
1643 'copy build.rs output to ./out/* and add a genrule to copy ' +
1644 './out/* to genrule output; for crates with code pattern: ' +
1645 'include!(concat!(env!("OUT_DIR"), "/<some_file>.rs"))'))
1646 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001647 '--debug',
1648 action='store_true',
1649 default=False,
1650 help='dump debug info into Android.bp')
1651 parser.add_argument(
1652 '--dependencies',
1653 action='store_true',
1654 default=False,
Joel Galenson833848c2021-08-17 10:50:42 -07001655 help='Deprecated. Has no effect.')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001656 parser.add_argument(
1657 '--device',
1658 action='store_true',
1659 default=False,
1660 help='run cargo also for a default device target')
1661 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001662 '--features',
1663 type=str,
1664 help=('pass features to cargo build, ' +
1665 'empty string means no default features'))
1666 parser.add_argument(
1667 '--global_defaults',
1668 type=str,
1669 help='add a defaults name to every module')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001670 parser.add_argument(
1671 '--host-first-multilib',
1672 action='store_true',
1673 default=False,
1674 help=('add a compile_multilib:"first" property ' +
1675 'to Android.bp host modules.'))
1676 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001677 '--ignore-cargo-errors',
1678 action='store_true',
1679 default=False,
1680 help='do not append cargo/rustc error messages to Android.bp')
1681 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001682 '--no-host',
1683 action='store_true',
1684 default=False,
1685 help='do not run cargo for the host; only for the device target')
1686 parser.add_argument(
1687 '--no-subdir',
1688 action='store_true',
1689 default=False,
1690 help='do not output anything for sub-directories')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001691 parser.add_argument(
1692 '--onefile',
1693 action='store_true',
1694 default=False,
1695 help=('output all into one ./Android.bp, default will generate ' +
1696 'one Android.bp per Cargo.toml in subdirectories'))
1697 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001698 '--patch',
1699 type=str,
1700 help='apply the given patch file to generated ./Android.bp')
1701 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001702 '--run',
1703 action='store_true',
1704 default=False,
1705 help='run it, default is dry-run')
1706 parser.add_argument('--rustflags', type=str, help='passing flags to rustc')
1707 parser.add_argument(
1708 '--skipcargo',
1709 action='store_true',
1710 default=False,
1711 help='skip cargo command, parse cargo.out, and generate Android.bp')
1712 parser.add_argument(
1713 '--tests',
1714 action='store_true',
1715 default=False,
1716 help='run cargo build --tests after normal build')
1717 parser.add_argument(
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001718 '--use-cargo-lock',
1719 action='store_true',
1720 default=False,
1721 help=('run cargo build with existing Cargo.lock ' +
1722 '(used when some latest dependent crates failed)'))
1723 parser.add_argument(
Matthew Maurer062709c2021-08-17 11:27:36 -07001724 '--exported_c_header_dir',
1725 nargs='*',
1726 help='Directories with headers to export for C usage'
1727 )
1728 parser.add_argument(
Joel Galensond9c4de62021-04-23 10:26:40 -07001729 '--min-sdk-version',
1730 type=str,
1731 help='Minimum SDK version')
1732 parser.add_argument(
1733 '--apex-available',
1734 nargs='*',
1735 help='Mark the main library as apex_available with the given apexes.')
1736 parser.add_argument(
Matthew Maurerac677252021-08-13 15:52:52 -07001737 '--native-bridge-supported',
1738 action='store_true',
1739 default=False,
1740 help='Mark the main library as native_bridge_supported.')
1741 parser.add_argument(
1742 '--product-available',
1743 action='store_true',
1744 default=False,
1745 help='Mark the main library as product_available.')
1746 parser.add_argument(
1747 '--recovery-available',
1748 action='store_true',
1749 default=False,
1750 help='Mark the main library as recovery_available.')
1751 parser.add_argument(
Ivan Lozano91920862021-07-19 10:49:08 -04001752 '--vendor-available',
1753 action='store_true',
1754 default=False,
1755 help='Mark the main library as vendor_available.')
1756 parser.add_argument(
1757 '--vendor-ramdisk-available',
1758 action='store_true',
1759 default=False,
1760 help='Mark the main library as vendor_ramdisk_available.')
1761 parser.add_argument(
Matthew Maurerac677252021-08-13 15:52:52 -07001762 '--ramdisk-available',
1763 action='store_true',
1764 default=False,
1765 help='Mark the main library as ramdisk_available.')
1766 parser.add_argument(
Joel Galensoncb5f2f02021-06-08 14:47:55 -07001767 '--force-rlib',
1768 action='store_true',
1769 default=False,
1770 help='Make the main library an rlib.')
1771 parser.add_argument(
Joel Galenson12467e52021-07-12 14:33:28 -07001772 '--whole-static-libs',
1773 nargs='*',
1774 default=[],
1775 help='Make the given libraries (without lib prefixes) whole_static_libs.')
1776 parser.add_argument(
Ivan Lozano26aa1c32021-08-16 11:20:32 -04001777 '--no-pkg-vers',
1778 action='store_true',
1779 default=False,
1780 help='Do not attempt to determine the package version automatically.')
1781 parser.add_argument(
Joel Galensone4f53882021-07-19 11:14:55 -07001782 '--test-data',
1783 nargs='*',
1784 default=[],
1785 help=('Add the given file to the given test\'s data property. ' +
1786 'Usage: test-path=data-path'))
1787 parser.add_argument(
Joel Galenson97e414a2021-05-27 09:42:32 -07001788 '--dependency-blocklist',
1789 nargs='*',
1790 default=[],
Joel Galenson12467e52021-07-12 14:33:28 -07001791 help='Do not emit the given dependencies (without lib prefixes).')
Joel Galenson97e414a2021-05-27 09:42:32 -07001792 parser.add_argument(
Joel Galensoncb5f2f02021-06-08 14:47:55 -07001793 '--lib-blocklist',
1794 nargs='*',
1795 default=[],
Joel Galenson12467e52021-07-12 14:33:28 -07001796 help='Do not emit the given C libraries as dependencies (without lib prefixes).')
Joel Galensoncb5f2f02021-06-08 14:47:55 -07001797 parser.add_argument(
Joel Galensonf6b3c912021-06-03 16:00:54 -07001798 '--test-blocklist',
1799 nargs='*',
1800 default=[],
1801 help=('Do not emit the given tests. ' +
1802 'Pass the path to the test file to exclude.'))
1803 parser.add_argument(
Joel Galenson3d6d1e72021-06-07 15:00:24 -07001804 '--cfg-blocklist',
1805 nargs='*',
1806 default=[],
1807 help='Do not emit the given cfg.')
1808 parser.add_argument(
Joel Galenson5664f2a2021-06-10 10:13:49 -07001809 '--add-toplevel-block',
1810 type=str,
Joel Galenson493c26a2021-10-18 15:54:41 -07001811 help=('Add the contents of the given file to the top level of the Android.bp. ' +
1812 'The filename should start with cargo2android to work with the updater.'))
Joel Galenson5664f2a2021-06-10 10:13:49 -07001813 parser.add_argument(
1814 '--add-module-block',
1815 type=str,
Joel Galenson493c26a2021-10-18 15:54:41 -07001816 help=('Add the contents of the given file to the main module. '+
1817 'The filename should start with cargo2android to work with the updater.'))
Joel Galenson5664f2a2021-06-10 10:13:49 -07001818 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001819 '--verbose',
1820 action='store_true',
1821 default=False,
1822 help='echo executed commands')
1823 parser.add_argument(
1824 '--vv',
1825 action='store_true',
1826 default=False,
1827 help='run cargo with -vv instead of default -v')
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001828 parser.add_argument(
1829 '--dump-config-and-exit',
1830 type=str,
1831 help=('Dump command-line arguments (minus this flag) to a config file and exit. ' +
1832 'This is intended to help migrate from command line options to config files.'))
1833 parser.add_argument(
1834 '--config',
1835 type=str,
1836 help=('Load command-line options from the given config file. ' +
1837 'Options in this file will override those passed on the command line.'))
1838 return parser
1839
1840
1841def parse_args(parser):
1842 """Parses command-line options."""
1843 args = parser.parse_args()
1844 # Use the values specified in a config file if one was found.
1845 if args.config:
1846 with open(args.config, 'r') as f:
1847 config = json.load(f)
1848 args_dict = vars(args)
1849 for arg in config:
1850 args_dict[arg.replace('-', '_')] = config[arg]
1851 return args
1852
1853
1854def dump_config(parser, args):
1855 """Writes the non-default command-line options to the specified file."""
1856 args_dict = vars(args)
1857 # Filter out the arguments that have their default value.
Joel Galenson367360c2021-04-29 14:31:43 -07001858 # Also filter certain "temporary" arguments.
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001859 non_default_args = {}
1860 for arg in args_dict:
Joel Galenson9a82ad92021-08-17 17:52:04 -07001861 if (args_dict[arg] != parser.get_default(arg) and arg != 'dump_config_and_exit'
Joel Galensonc3bfaf82021-08-18 09:39:36 -07001862 and arg != 'config'):
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001863 non_default_args[arg.replace('_', '-')] = args_dict[arg]
1864 # Write to the specified file.
1865 with open(args.dump_config_and_exit, 'w') as f:
1866 json.dump(non_default_args, f, indent=2, sort_keys=True)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001867
1868
1869def main():
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001870 parser = get_parser()
1871 args = parse_args(parser)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001872 if not args.run: # default is dry-run
1873 print(DRY_RUN_NOTE)
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001874 if args.dump_config_and_exit:
1875 dump_config(parser, args)
1876 else:
ThiƩbaud Weksteen198e93f2021-07-02 14:49:19 +02001877 Runner(args).run_cargo().gen_bp().apply_patch()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001878
1879
1880if __name__ == '__main__':
1881 main()