blob: 40d023022e9012cdce7085a240da5b1d928a0f42 [file] [log] [blame]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001#!/usr/bin/env python
2#
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
30 This is equivalent to using the --cargo flag to add extra builds:
31 cargo2android.py --run
32 --cargo "build"
33 --cargo "build --target x86_64-unknown-linux-gnu"
34
35 On MacOS, use x86_64-apple-darwin as target triple.
36 Here the host target triple is used as a fake cross compilation target.
37 If the crate's Cargo.toml and environment configuration works for an
38 Android target, use that target triple as the cargo build flag.
39
40(3) To build default and test crates, for host and device, use both
41 --device and --tests flags:
42 cargo2android.py --run --device --tests
43
44 This is equivalent to using the --cargo flag to add extra builds:
45 cargo2android.py --run
46 --cargo "build"
47 --cargo "build --tests"
48 --cargo "build --target x86_64-unknown-linux-gnu"
49 --cargo "build --tests --target x86_64-unknown-linux-gnu"
50
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -070051 Note that when there are test modules generated into Android.bp,
52 corresponding test entries will also be added into the TEST_MAPPING file.
53
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -070054If there are rustc warning messages, this script will add
55a warning comment to the owner crate module in Android.bp.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080056"""
57
58from __future__ import print_function
59
60import argparse
61import os
62import os.path
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -070063import platform
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080064import re
Andrew Walbran80e90be2020-06-09 14:33:18 +010065import sys
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080066
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -070067# Some Rust packages include extra unwanted crates.
68# This set contains all such excluded crate names.
69EXCLUDED_CRATES = set(['protobuf_bin_gen_rust_do_not_use'])
70
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080071RENAME_MAP = {
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -070072 # This map includes all changes to the default rust module names
73 # to resolve name conflicts, avoid confusion, or work as plugin.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080074 'libbacktrace': 'libbacktrace_rust',
Andrew Walbrane51f1042020-08-11 16:42:48 +010075 'libbase': 'libbase_rust',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080076 'libgcc': 'libgcc_rust',
77 'liblog': 'liblog_rust',
Chih-Hung Hsieh07119862020-07-24 15:34:06 -070078 'libminijail': 'libminijail_rust',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080079 'libsync': 'libsync_rust',
80 'libx86_64': 'libx86_64_rust',
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -070081 'protoc_gen_rust': 'protoc-gen-rust',
82}
83
84RENAME_STEM_MAP = {
85 # This map includes all changes to the default rust module stem names,
86 # which is used for output files when different from the module name.
87 'protoc_gen_rust': 'protoc-gen-rust',
88}
89
90RENAME_DEFAULTS_MAP = {
91 # This map includes all changes to the default prefix of rust_default
92 # module names, to avoid conflict with existing Android modules.
93 'libc': 'rust_libc',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080094}
95
96# Header added to all generated Android.bp files.
Andrew Walbran80e90be2020-06-09 14:33:18 +010097ANDROID_BP_HEADER = '// This file is generated by cargo2android.py {args}.\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080098
99CARGO_OUT = 'cargo.out' # Name of file to keep cargo build -v output.
100
101TARGET_TMP = 'target.tmp' # Name of temporary output directory.
102
103# Message to be displayed when this script is called without the --run flag.
104DRY_RUN_NOTE = (
105 'Dry-run: This script uses ./' + TARGET_TMP + ' for output directory,\n' +
106 'runs cargo clean, runs cargo build -v, saves output to ./cargo.out,\n' +
107 'and writes to Android.bp in the current and subdirectories.\n\n' +
108 'To do do all of the above, use the --run flag.\n' +
109 'See --help for other flags, and more usage notes in this script.\n')
110
111# Cargo -v output of a call to rustc.
112RUSTC_PAT = re.compile('^ +Running `rustc (.*)`$')
113
114# Cargo -vv output of a call to rustc could be split into multiple lines.
115# Assume that the first line will contain some CARGO_* env definition.
116RUSTC_VV_PAT = re.compile('^ +Running `.*CARGO_.*=.*$')
117# The combined -vv output rustc command line pattern.
118RUSTC_VV_CMD_ARGS = re.compile('^ *Running `.*CARGO_.*=.* rustc (.*)`$')
119
120# Cargo -vv output of a "cc" or "ar" command; all in one line.
121CC_AR_VV_PAT = re.compile(r'^\[([^ ]*)[^\]]*\] running:? "(cc|ar)" (.*)$')
122# Some package, such as ring-0.13.5, has pattern '... running "cc"'.
123
124# Rustc output of file location path pattern for a warning message.
125WARNING_FILE_PAT = re.compile('^ *--> ([^:]*):[0-9]+')
126
127# Rust package name with suffix -d1.d2.d3.
128VERSION_SUFFIX_PAT = re.compile(r'^(.*)-[0-9]+\.[0-9]+\.[0-9]+$')
129
130
131def altered_name(name):
132 return RENAME_MAP[name] if (name in RENAME_MAP) else name
133
134
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700135def altered_stem(name):
136 return RENAME_STEM_MAP[name] if (name in RENAME_STEM_MAP) else name
137
138
139def altered_defaults(name):
140 return RENAME_DEFAULTS_MAP[name] if (name in RENAME_DEFAULTS_MAP) else name
141
142
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800143def is_build_crate_name(name):
144 # We added special prefix to build script crate names.
145 return name.startswith('build_script_')
146
147
148def is_dependent_file_path(path):
149 # Absolute or dependent '.../' paths are not main files of this crate.
150 return path.startswith('/') or path.startswith('.../')
151
152
153def get_module_name(crate): # to sort crates in a list
154 return crate.module_name
155
156
157def pkg2crate_name(s):
158 return s.replace('-', '_').replace('.', '_')
159
160
161def file_base_name(path):
162 return os.path.splitext(os.path.basename(path))[0]
163
164
165def test_base_name(path):
166 return pkg2crate_name(file_base_name(path))
167
168
169def unquote(s): # remove quotes around str
170 if s and len(s) > 1 and s[0] == '"' and s[-1] == '"':
171 return s[1:-1]
172 return s
173
174
175def remove_version_suffix(s): # remove -d1.d2.d3 suffix
176 if VERSION_SUFFIX_PAT.match(s):
177 return VERSION_SUFFIX_PAT.match(s).group(1)
178 return s
179
180
181def short_out_name(pkg, s): # replace /.../pkg-*/out/* with .../out/*
182 return re.sub('^/.*/' + pkg + '-[0-9a-f]*/out/', '.../out/', s)
183
184
185def escape_quotes(s): # replace '"' with '\\"'
186 return s.replace('"', '\\"')
187
188
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700189class TestMapping(object):
190 """Entries for a TEST_MAPPING file."""
191
192 def __init__(self):
193 self.entries = []
194
195 def add_test(self, name, host):
196 self.entries.append((name, host))
197
198 def is_empty(self):
199 return not self.entries
200
201 def dump(self, outf_name):
202 """Append all entries into the output file."""
203 if self.is_empty():
204 return
205 with open(outf_name, 'w') as outf:
206 outf.write('// Generated by cargo2android.py for tests in Android.bp\n')
207 outf.write('{\n "presubmit": [\n')
208 is_first = True
209 for (name, host) in self.entries:
210 if not is_first: # add comma and '\n' after the previous entry
211 outf.write(',\n')
212 is_first = False
213 outf.write(' {\n "name": "' + name + '"')
214 if host:
215 outf.write(',\n "host": true\n }')
216 else:
217 outf.write('\n }')
218 outf.write('\n ]\n}\n')
219
220
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800221class Crate(object):
222 """Information of a Rust crate to collect/emit for an Android.bp module."""
223
224 def __init__(self, runner, outf_name):
225 # Remembered global runner and its members.
226 self.runner = runner
227 self.debug = runner.args.debug
228 self.cargo_dir = '' # directory of my Cargo.toml
229 self.outf_name = outf_name # path to Android.bp
230 self.outf = None # open file handle of outf_name during dump*
231 # Variants/results that could be merged from multiple rustc lines.
232 self.host_supported = False
233 self.device_supported = False
234 self.has_warning = False
235 # Android module properties derived from rustc parameters.
236 self.module_name = '' # unique in Android build system
237 self.module_type = '' # rust_{binary,library,test}[_host] etc.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700238 self.defaults = '' # rust_defaults used by rust_test* modules
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700239 self.default_srcs = False # use 'srcs' defined in self.defaults
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800240 self.root_pkg = '' # parent package name of a sub/test packge, from -L
241 self.srcs = list() # main_src or merged multiple source files
242 self.stem = '' # real base name of output file
243 # Kept parsed status
244 self.errors = '' # all errors found during parsing
245 self.line_num = 1 # runner told input source line number
246 self.line = '' # original rustc command line parameters
247 # Parameters collected from rustc command line.
248 self.crate_name = '' # follows --crate-name
249 self.main_src = '' # follows crate_name parameter, shortened
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700250 self.crate_types = list() # follows --crate-type
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800251 self.cfgs = list() # follows --cfg, without feature= prefix
252 self.features = list() # follows --cfg, name in 'feature="..."'
253 self.codegens = list() # follows -C, some ignored
254 self.externs = list() # follows --extern
255 self.core_externs = list() # first part of self.externs elements
256 self.static_libs = list() # e.g. -l static=host_cpuid
257 self.shared_libs = list() # e.g. -l dylib=wayland-client, -l z
258 self.cap_lints = '' # follows --cap-lints
259 self.emit_list = '' # e.g., --emit=dep-info,metadata,link
260 self.edition = '2015' # rustc default, e.g., --edition=2018
261 self.target = '' # follows --target
262
263 def write(self, s):
264 # convenient way to output one line at a time with EOL.
265 self.outf.write(s + '\n')
266
267 def same_flags(self, other):
268 # host_supported, device_supported, has_warning are not compared but merged
269 # target is not compared, to merge different target/host modules
270 # externs is not compared; only core_externs is compared
271 return (not self.errors and not other.errors and
272 self.edition == other.edition and
273 self.cap_lints == other.cap_lints and
274 self.emit_list == other.emit_list and
275 self.core_externs == other.core_externs and
276 self.codegens == other.codegens and
277 self.features == other.features and
278 self.static_libs == other.static_libs and
279 self.shared_libs == other.shared_libs and self.cfgs == other.cfgs)
280
281 def merge_host_device(self, other):
282 """Returns true if attributes are the same except host/device support."""
283 return (self.crate_name == other.crate_name and
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700284 self.crate_types == other.crate_types and
285 self.main_src == other.main_src and
286 # before merge, each test module has an unique module name and stem
287 (self.stem == other.stem or self.crate_types == ['test']) and
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800288 self.root_pkg == other.root_pkg and not self.skip_crate() and
289 self.same_flags(other))
290
291 def merge_test(self, other):
292 """Returns true if self and other are tests of same root_pkg."""
293 # Before merger, each test has its own crate_name.
294 # A merged test uses its source file base name as output file name,
295 # so a test is mergeable only if its base name equals to its crate name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700296 return (self.crate_types == other.crate_types and
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700297 self.crate_types == ['test'] and self.root_pkg == other.root_pkg and
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800298 not self.skip_crate() and
299 other.crate_name == test_base_name(other.main_src) and
300 (len(self.srcs) > 1 or
301 (self.crate_name == test_base_name(self.main_src)) and
302 self.host_supported == other.host_supported and
303 self.device_supported == other.device_supported) and
304 self.same_flags(other))
305
306 def merge(self, other, outf_name):
307 """Try to merge crate into self."""
308 should_merge_host_device = self.merge_host_device(other)
309 should_merge_test = False
310 if not should_merge_host_device:
311 should_merge_test = self.merge_test(other)
312 # A for-device test crate can be merged with its for-host version,
313 # or merged with a different test for the same host or device.
314 # Since we run cargo once for each device or host, test crates for the
315 # first device or host will be merged first. Then test crates for a
316 # different device or host should be allowed to be merged into a
317 # previously merged one, maybe for a different device or host.
318 if should_merge_host_device or should_merge_test:
319 self.runner.init_bp_file(outf_name)
320 with open(outf_name, 'a') as outf: # to write debug info
321 self.outf = outf
322 other.outf = outf
323 self.do_merge(other, should_merge_test)
324 return True
325 return False
326
327 def do_merge(self, other, should_merge_test):
328 """Merge attributes of other to self."""
329 if self.debug:
330 self.write('\n// Before merge definition (1):')
331 self.dump_debug_info()
332 self.write('\n// Before merge definition (2):')
333 other.dump_debug_info()
334 # Merge properties of other to self.
335 self.host_supported = self.host_supported or other.host_supported
336 self.device_supported = self.device_supported or other.device_supported
337 self.has_warning = self.has_warning or other.has_warning
338 if not self.target: # okay to keep only the first target triple
339 self.target = other.target
340 # decide_module_type sets up default self.stem,
341 # which can be changed if self is a merged test module.
342 self.decide_module_type()
343 if should_merge_test:
344 self.srcs.append(other.main_src)
345 # use a short unique name as the merged module name.
346 prefix = self.root_pkg + '_tests'
347 self.module_name = self.runner.claim_module_name(prefix, self, 0)
348 self.stem = self.module_name
349 # This normalized root_pkg name although might be the same
350 # as other module's crate_name, it is not actually used for
351 # output file name. A merged test module always have multiple
352 # source files and each source file base name is used as
353 # its output file name.
354 self.crate_name = pkg2crate_name(self.root_pkg)
355 if self.debug:
356 self.write('\n// After merge definition (1):')
357 self.dump_debug_info()
358
359 def find_cargo_dir(self):
360 """Deepest directory with Cargo.toml and contains the main_src."""
361 if not is_dependent_file_path(self.main_src):
362 dir_name = os.path.dirname(self.main_src)
363 while dir_name:
364 if os.path.exists(dir_name + '/Cargo.toml'):
365 self.cargo_dir = dir_name
366 return
367 dir_name = os.path.dirname(dir_name)
368
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700369 def add_codegens_flag(self, flag):
370 # ignore options not used in Android
371 # 'prefer-dynamic' does not work with common flag -C lto
Chih-Hung Hsieh63459ed2020-08-26 11:51:15 -0700372 # 'embed-bitcode' is ignored; we might control LTO with other .bp flag
Chih-Hung Hsieh6c13b722020-09-11 21:24:03 -0700373 # 'codegen-units' is set in Android global config or by default
374 if not (flag.startswith('codegen-units=') or
375 flag.startswith('debuginfo=') or
Chih-Hung Hsieh63459ed2020-08-26 11:51:15 -0700376 flag.startswith('embed-bitcode=') or
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700377 flag.startswith('extra-filename=') or
378 flag.startswith('incremental=') or
379 flag.startswith('metadata=') or
380 flag == 'prefer-dynamic'):
381 self.codegens.append(flag)
382
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800383 def parse(self, line_num, line):
384 """Find important rustc arguments to convert to Android.bp properties."""
385 self.line_num = line_num
386 self.line = line
387 args = line.split() # Loop through every argument of rustc.
388 i = 0
389 while i < len(args):
390 arg = args[i]
391 if arg == '--crate-name':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700392 i += 1
393 self.crate_name = args[i]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800394 elif arg == '--crate-type':
395 i += 1
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700396 # cargo calls rustc with multiple --crate-type flags.
397 # rustc can accept:
398 # --crate-type [bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]
399 self.crate_types.append(args[i])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800400 elif arg == '--test':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700401 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800402 elif arg == '--target':
403 i += 1
404 self.target = args[i]
405 elif arg == '--cfg':
406 i += 1
407 if args[i].startswith('\'feature='):
408 self.features.append(unquote(args[i].replace('\'feature=', '')[:-1]))
409 else:
410 self.cfgs.append(args[i])
411 elif arg == '--extern':
412 i += 1
413 extern_names = re.sub('=/[^ ]*/deps/', ' = ', args[i])
414 self.externs.append(extern_names)
415 self.core_externs.append(re.sub(' = .*', '', extern_names))
416 elif arg == '-C': # codegen options
417 i += 1
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700418 self.add_codegens_flag(args[i])
419 elif arg.startswith('-C'):
420 # cargo has been passing "-C <xyz>" flag to rustc,
421 # but newer cargo could pass '-Cembed-bitcode=no' to rustc.
422 self.add_codegens_flag(arg[2:])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800423 elif arg == '--cap-lints':
424 i += 1
425 self.cap_lints = args[i]
426 elif arg == '-L':
427 i += 1
428 if args[i].startswith('dependency=') and args[i].endswith('/deps'):
429 if '/' + TARGET_TMP + '/' in args[i]:
430 self.root_pkg = re.sub(
431 '^.*/', '', re.sub('/' + TARGET_TMP + '/.*/deps$', '', args[i]))
432 else:
433 self.root_pkg = re.sub('^.*/', '',
434 re.sub('/[^/]+/[^/]+/deps$', '', args[i]))
435 self.root_pkg = remove_version_suffix(self.root_pkg)
436 elif arg == '-l':
437 i += 1
438 if args[i].startswith('static='):
439 self.static_libs.append(re.sub('static=', '', args[i]))
440 elif args[i].startswith('dylib='):
441 self.shared_libs.append(re.sub('dylib=', '', args[i]))
442 else:
443 self.shared_libs.append(args[i])
444 elif arg == '--out-dir' or arg == '--color': # ignored
445 i += 1
446 elif arg.startswith('--error-format=') or arg.startswith('--json='):
447 _ = arg # ignored
448 elif arg.startswith('--emit='):
449 self.emit_list = arg.replace('--emit=', '')
450 elif arg.startswith('--edition='):
451 self.edition = arg.replace('--edition=', '')
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700452 elif not arg.startswith('-'):
453 # shorten imported crate main source paths like $HOME/.cargo/
454 # registry/src/github.com-1ecc6299db9ec823/memchr-2.3.3/src/lib.rs
455 self.main_src = re.sub(r'^/[^ ]*/registry/src/', '.../', args[i])
456 self.main_src = re.sub(r'^\.\.\./github.com-[0-9a-f]*/', '.../',
457 self.main_src)
458 self.find_cargo_dir()
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700459 if self.cargo_dir: # for a subdirectory
460 if self.runner.args.no_subdir: # all .bp content to /dev/null
461 self.outf_name = '/dev/null'
462 elif not self.runner.args.onefile:
463 # Write to Android.bp in the subdirectory with Cargo.toml.
464 self.outf_name = self.cargo_dir + '/Android.bp'
465 self.main_src = self.main_src[len(self.cargo_dir) + 1:]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800466 else:
467 self.errors += 'ERROR: unknown ' + arg + '\n'
468 i += 1
469 if not self.crate_name:
470 self.errors += 'ERROR: missing --crate-name\n'
471 if not self.main_src:
472 self.errors += 'ERROR: missing main source file\n'
473 else:
474 self.srcs.append(self.main_src)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700475 if not self.crate_types:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800476 # Treat "--cfg test" as "--test"
477 if 'test' in self.cfgs:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700478 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800479 else:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700480 self.errors += 'ERROR: missing --crate-type or --test\n'
481 elif len(self.crate_types) > 1:
482 if 'test' in self.crate_types:
483 self.errors += 'ERROR: cannot handle both --crate-type and --test\n'
484 if 'lib' in self.crate_types and 'rlib' in self.crate_types:
485 self.errors += 'ERROR: cannot generate both lib and rlib crate types\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800486 if not self.root_pkg:
487 self.root_pkg = self.crate_name
488 if self.target:
489 self.device_supported = True
490 self.host_supported = True # assume host supported for all builds
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700491 if self.runner.args.no_host: # unless --no-host was specified
492 self.host_supported = False
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800493 self.cfgs = sorted(set(self.cfgs))
494 self.features = sorted(set(self.features))
495 self.codegens = sorted(set(self.codegens))
496 self.externs = sorted(set(self.externs))
497 self.core_externs = sorted(set(self.core_externs))
498 self.static_libs = sorted(set(self.static_libs))
499 self.shared_libs = sorted(set(self.shared_libs))
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700500 self.crate_types = sorted(set(self.crate_types))
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800501 self.decide_module_type()
502 self.module_name = altered_name(self.stem)
503 return self
504
505 def dump_line(self):
506 self.write('\n// Line ' + str(self.line_num) + ' ' + self.line)
507
508 def feature_list(self):
509 """Return a string of main_src + "feature_list"."""
510 pkg = self.main_src
511 if pkg.startswith('.../'): # keep only the main package name
512 pkg = re.sub('/.*', '', pkg[4:])
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700513 elif pkg.startswith('/'): # use relative path for a local package
514 pkg = os.path.relpath(pkg)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800515 if not self.features:
516 return pkg
517 return pkg + ' "' + ','.join(self.features) + '"'
518
519 def dump_skip_crate(self, kind):
520 if self.debug:
521 self.write('\n// IGNORED: ' + kind + ' ' + self.main_src)
522 return self
523
524 def skip_crate(self):
525 """Return crate_name or a message if this crate should be skipped."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700526 if (is_build_crate_name(self.crate_name) or
527 self.crate_name in EXCLUDED_CRATES):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800528 return self.crate_name
529 if is_dependent_file_path(self.main_src):
530 return 'dependent crate'
531 return ''
532
533 def dump(self):
534 """Dump all error/debug/module code to the output .bp file."""
535 self.runner.init_bp_file(self.outf_name)
536 with open(self.outf_name, 'a') as outf:
537 self.outf = outf
538 if self.errors:
539 self.dump_line()
540 self.write(self.errors)
541 elif self.skip_crate():
542 self.dump_skip_crate(self.skip_crate())
543 else:
544 if self.debug:
545 self.dump_debug_info()
546 self.dump_android_module()
547
548 def dump_debug_info(self):
549 """Dump parsed data, when cargo2android is called with --debug."""
550
551 def dump(name, value):
552 self.write('//%12s = %s' % (name, value))
553
554 def opt_dump(name, value):
555 if value:
556 dump(name, value)
557
558 def dump_list(fmt, values):
559 for v in values:
560 self.write(fmt % v)
561
562 self.dump_line()
563 dump('module_name', self.module_name)
564 dump('crate_name', self.crate_name)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700565 dump('crate_types', self.crate_types)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800566 dump('main_src', self.main_src)
567 dump('has_warning', self.has_warning)
568 dump('for_host', self.host_supported)
569 dump('for_device', self.device_supported)
570 dump('module_type', self.module_type)
571 opt_dump('target', self.target)
572 opt_dump('edition', self.edition)
573 opt_dump('emit_list', self.emit_list)
574 opt_dump('cap_lints', self.cap_lints)
575 dump_list('// cfg = %s', self.cfgs)
576 dump_list('// cfg = \'feature "%s"\'', self.features)
577 # TODO(chh): escape quotes in self.features, but not in other dump_list
578 dump_list('// codegen = %s', self.codegens)
579 dump_list('// externs = %s', self.externs)
580 dump_list('// -l static = %s', self.static_libs)
581 dump_list('// -l (dylib) = %s', self.shared_libs)
582
583 def dump_android_module(self):
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700584 """Dump one or more Android module definition, depending on crate_types."""
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700585 if len(self.crate_types) == 1:
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700586 self.dump_single_type_android_module()
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700587 return
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700588 if 'test' in self.crate_types:
589 self.write('\nERROR: multiple crate types cannot include test type')
590 return
591 # Dump one Android module per crate_type.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700592 for crate_type in self.crate_types:
593 self.decide_one_module_type(crate_type)
594 self.dump_one_android_module(crate_type)
595
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700596 def build_default_name(self):
597 """Return a short and readable name for the rust_defaults module."""
598 # Choices: (1) root_pkg + '_defaults',
599 # (2) root_pkg + '_defaults_' + crate_name
600 # (3) root_pkg + '_defaults_' + main_src_basename_path
601 # (4) root_pkg + '_defaults_' + a_positive_sequence_number
602 name1 = altered_defaults(self.root_pkg) + '_defaults'
603 if self.runner.try_claim_module_name(name1, self):
604 return name1
605 name2 = name1 + '_' + self.crate_name
606 if self.runner.try_claim_module_name(name2, self):
607 return name2
608 name3 = name1 + '_' + self.main_src_basename_path()
609 if self.runner.try_claim_module_name(name3, self):
610 return name3
611 return self.runner.claim_module_name(name1, self, 0)
612
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700613 def dump_defaults_module(self):
614 """Dump a rust_defaults module to be shared by other modules."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700615 name = self.build_default_name()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700616 self.defaults = name
617 self.write('\nrust_defaults {')
618 self.write(' name: "' + name + '",')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700619 if self.runner.args.global_defaults:
620 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700621 self.write(' crate_name: "' + self.crate_name + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700622 if len(self.srcs) == 1: # only one source file; share it in defaults
623 self.default_srcs = True
624 if self.has_warning and not self.cap_lints:
625 self.write(' // has rustc warnings')
626 self.write(' srcs: ["' + self.main_src + '"],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700627 if 'test' in self.crate_types:
628 self.write(' test_suites: ["general-tests"],')
629 self.write(' auto_gen_config: true,')
630 self.dump_edition_flags_libs()
631 self.write('}')
632
633 def dump_single_type_android_module(self):
634 """Dump one simple Android module, which has only one crate_type."""
635 crate_type = self.crate_types[0]
636 if crate_type != 'test':
637 # do not change self.stem or self.module_name
638 self.dump_one_android_module(crate_type)
639 return
640 # Dump one test module per source file, and separate host and device tests.
641 # crate_type == 'test'
642 if (self.host_supported and self.device_supported) or len(self.srcs) > 1:
643 self.srcs = sorted(set(self.srcs))
644 self.dump_defaults_module()
645 saved_srcs = self.srcs
646 for src in saved_srcs:
647 self.srcs = [src]
648 saved_device_supported = self.device_supported
649 saved_host_supported = self.host_supported
650 saved_main_src = self.main_src
651 self.main_src = src
652 if saved_host_supported:
653 self.device_supported = False
654 self.host_supported = True
655 self.module_name = self.test_module_name()
656 self.decide_one_module_type(crate_type)
657 self.dump_one_android_module(crate_type)
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700658 self.runner.add_test(self.outf_name, self.module_name, True)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700659 if saved_device_supported:
660 self.device_supported = True
661 self.host_supported = False
662 self.module_name = self.test_module_name()
663 self.decide_one_module_type(crate_type)
664 self.dump_one_android_module(crate_type)
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700665 self.runner.add_test(self.outf_name, self.module_name, False)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700666 self.host_supported = saved_host_supported
667 self.device_supported = saved_device_supported
668 self.main_src = saved_main_src
669 self.srcs = saved_srcs
670
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700671 def dump_one_android_module(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800672 """Dump one Android module definition."""
673 if not self.module_type:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700674 self.write('\nERROR: unknown crate_type ' + crate_type)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800675 return
676 self.write('\n' + self.module_type + ' {')
677 self.dump_android_core_properties()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700678 if not self.defaults:
679 self.dump_edition_flags_libs()
680 if self.runner.args.host_first_multilib and self.host_supported and crate_type != 'test':
681 self.write(' compile_multilib: "first",')
682 self.write('}')
683
684 def dump_android_flags(self):
685 """Dump Android module flags property."""
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700686 if not self.cfgs and not self.codegens and not self.cap_lints:
687 return
688 self.write(' flags: [')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800689 if self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700690 self.write(' "--cap-lints ' + self.cap_lints + '",')
691 cfg_fmt = '"--cfg %s"'
692 codegens_fmt = '"-C %s"'
693 self.dump_android_property_list_items(cfg_fmt, self.cfgs)
694 self.dump_android_property_list_items(codegens_fmt, self.codegens)
695 self.write(' ],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700696
697 def dump_edition_flags_libs(self):
698 if self.edition:
699 self.write(' edition: "' + self.edition + '",')
700 self.dump_android_property_list('features', '"%s"', self.features)
701 self.dump_android_flags()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800702 if self.externs:
703 self.dump_android_externs()
704 self.dump_android_property_list('static_libs', '"lib%s"', self.static_libs)
705 self.dump_android_property_list('shared_libs', '"lib%s"', self.shared_libs)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800706
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700707 def main_src_basename_path(self):
708 return re.sub('/', '_', re.sub('.rs$', '', self.main_src))
709
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800710 def test_module_name(self):
711 """Return a unique name for a test module."""
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700712 # root_pkg+(_host|_device) + '_test_'+source_file_name
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700713 suffix = self.main_src_basename_path()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700714 host_device = '_host'
715 if self.device_supported:
716 host_device = '_device'
717 return self.root_pkg + host_device + '_test_' + suffix
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800718
719 def decide_module_type(self):
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700720 # Use the first crate type for the default/first module.
721 crate_type = self.crate_types[0] if self.crate_types else ''
722 self.decide_one_module_type(crate_type)
723
724 def decide_one_module_type(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800725 """Decide which Android module type to use."""
726 host = '' if self.device_supported else '_host'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700727 if crate_type == 'bin': # rust_binary[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800728 self.module_type = 'rust_binary' + host
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700729 # In rare cases like protobuf-codegen, the output binary name must
730 # be renamed to use as a plugin for protoc.
731 self.stem = altered_stem(self.crate_name)
732 self.module_name = altered_name(self.crate_name)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700733 elif crate_type == 'lib': # rust_library[_host]
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700734 # TODO(chh): should this be rust_library[_host]?
735 # Assuming that Cargo.toml do not use both 'lib' and 'rlib',
736 # because we map them both to rlib.
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700737 self.module_type = 'rust_library' + host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800738 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700739 self.module_name = altered_name(self.stem)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700740 elif crate_type == 'rlib': # rust_library[_host]
741 self.module_type = 'rust_library' + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700742 self.stem = 'lib' + self.crate_name
743 self.module_name = altered_name(self.stem)
744 elif crate_type == 'dylib': # rust_library[_host]_dylib
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800745 self.module_type = 'rust_library' + host + '_dylib'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700746 self.stem = 'lib' + self.crate_name
747 self.module_name = altered_name(self.stem) + '_dylib'
748 elif crate_type == 'cdylib': # rust_library[_host]_shared
749 self.module_type = 'rust_library' + host + '_shared'
750 self.stem = 'lib' + self.crate_name
751 self.module_name = altered_name(self.stem) + '_shared'
752 elif crate_type == 'staticlib': # rust_library[_host]_static
753 self.module_type = 'rust_library' + host + '_static'
754 self.stem = 'lib' + self.crate_name
755 self.module_name = altered_name(self.stem) + '_static'
756 elif crate_type == 'test': # rust_test[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800757 self.module_type = 'rust_test' + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700758 # Before do_merge, stem name is based on the --crate-name parameter.
759 # and test module name is based on stem.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800760 self.stem = self.test_module_name()
761 # self.stem will be changed after merging with other tests.
762 # self.stem is NOT used for final test binary name.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700763 # rust_test uses each source file base name as part of output file name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700764 # In do_merge, this function is called again, with a module_name.
765 # We make sure that the module name is unique in each package.
766 if self.module_name:
767 # Cargo uses "-C extra-filename=..." and "-C metadata=..." to add
768 # different suffixes and distinguish multiple tests of the same
769 # crate name. We ignore -C and use claim_module_name to get
770 # unique sequential suffix.
771 self.module_name = self.runner.claim_module_name(
772 self.module_name, self, 0)
773 # Now the module name is unique, stem should also match and unique.
774 self.stem = self.module_name
775 elif crate_type == 'proc-macro': # rust_proc_macro
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800776 self.module_type = 'rust_proc_macro'
777 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700778 self.module_name = altered_name(self.stem)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800779 else: # unknown module type, rust_prebuilt_dylib? rust_library[_host]?
780 self.module_type = ''
781 self.stem = ''
782
783 def dump_android_property_list_items(self, fmt, values):
784 for v in values:
785 # fmt has quotes, so we need escape_quotes(v)
786 self.write(' ' + (fmt % escape_quotes(v)) + ',')
787
788 def dump_android_property_list(self, name, fmt, values):
789 if values:
790 self.write(' ' + name + ': [')
791 self.dump_android_property_list_items(fmt, values)
792 self.write(' ],')
793
794 def dump_android_core_properties(self):
795 """Dump the module header, name, stem, etc."""
796 self.write(' name: "' + self.module_name + '",')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700797 # see properties shared by dump_defaults_module
798 if self.defaults:
799 self.write(' defaults: ["' + self.defaults + '"],')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700800 elif self.runner.args.global_defaults:
801 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800802 if self.stem != self.module_name:
803 self.write(' stem: "' + self.stem + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700804 if self.has_warning and not self.cap_lints and not self.default_srcs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700805 self.write(' // has rustc warnings')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800806 if self.host_supported and self.device_supported:
807 self.write(' host_supported: true,')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700808 if not self.defaults:
809 self.write(' crate_name: "' + self.crate_name + '",')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800810 if len(self.srcs) > 1:
811 self.srcs = sorted(set(self.srcs))
812 self.dump_android_property_list('srcs', '"%s"', self.srcs)
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700813 elif not self.default_srcs:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800814 self.write(' srcs: ["' + self.main_src + '"],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700815 if 'test' in self.crate_types and not self.defaults:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800816 # self.root_pkg can have multiple test modules, with different *_tests[n]
817 # names, but their executables can all be installed under the same _tests
818 # directory. When built from Cargo.toml, all tests should have different
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700819 # file or crate names. So we used (root_pkg + '_tests') name as the
820 # relative_install_path.
821 # However, some package like 'slab' can have non-mergeable tests that
822 # must be separated by different module names. So, here we no longer
823 # emit relative_install_path.
824 # self.write(' relative_install_path: "' + self.root_pkg + '_tests",')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800825 self.write(' test_suites: ["general-tests"],')
826 self.write(' auto_gen_config: true,')
827
828 def dump_android_externs(self):
829 """Dump the dependent rlibs and dylibs property."""
830 so_libs = list()
831 rust_libs = ''
832 deps_libname = re.compile('^.* = lib(.*)-[0-9a-f]*.(rlib|so|rmeta)$')
833 for lib in self.externs:
834 # normal value of lib: "libc = liblibc-*.rlib"
835 # strange case in rand crate: "getrandom_package = libgetrandom-*.rlib"
836 # we should use "libgetrandom", not "lib" + "getrandom_package"
837 groups = deps_libname.match(lib)
838 if groups is not None:
839 lib_name = groups.group(1)
840 else:
841 lib_name = re.sub(' .*$', '', lib)
842 if lib.endswith('.rlib') or lib.endswith('.rmeta'):
843 # On MacOS .rmeta is used when Linux uses .rlib or .rmeta.
844 rust_libs += ' "' + altered_name('lib' + lib_name) + '",\n'
845 elif lib.endswith('.so'):
846 so_libs.append(lib_name)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700847 elif lib != 'proc_macro': # --extern proc_macro is special and ignored
848 rust_libs += ' // ERROR: unknown type of lib ' + lib + '\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800849 if rust_libs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700850 self.write(' rustlibs: [\n' + rust_libs + ' ],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800851 # Are all dependent .so files proc_macros?
852 # TODO(chh): Separate proc_macros and dylib.
853 self.dump_android_property_list('proc_macros', '"lib%s"', so_libs)
854
855
856class ARObject(object):
857 """Information of an "ar" link command."""
858
859 def __init__(self, runner, outf_name):
860 # Remembered global runner and its members.
861 self.runner = runner
862 self.pkg = ''
863 self.outf_name = outf_name # path to Android.bp
864 # "ar" arguments
865 self.line_num = 1
866 self.line = ''
867 self.flags = '' # e.g. "crs"
868 self.lib = '' # e.g. "/.../out/lib*.a"
869 self.objs = list() # e.g. "/.../out/.../*.o"
870
871 def parse(self, pkg, line_num, args_line):
872 """Collect ar obj/lib file names."""
873 self.pkg = pkg
874 self.line_num = line_num
875 self.line = args_line
876 args = args_line.split()
877 num_args = len(args)
878 if num_args < 3:
879 print('ERROR: "ar" command has too few arguments', args_line)
880 else:
881 self.flags = unquote(args[0])
882 self.lib = unquote(args[1])
883 self.objs = sorted(set(map(unquote, args[2:])))
884 return self
885
886 def write(self, s):
887 self.outf.write(s + '\n')
888
889 def dump_debug_info(self):
890 self.write('\n// Line ' + str(self.line_num) + ' "ar" ' + self.line)
891 self.write('// ar_object for %12s' % self.pkg)
892 self.write('// flags = %s' % self.flags)
893 self.write('// lib = %s' % short_out_name(self.pkg, self.lib))
894 for o in self.objs:
895 self.write('// obj = %s' % short_out_name(self.pkg, o))
896
897 def dump_android_lib(self):
898 """Write cc_library_static into Android.bp."""
899 self.write('\ncc_library_static {')
900 self.write(' name: "' + file_base_name(self.lib) + '",')
901 self.write(' host_supported: true,')
902 if self.flags != 'crs':
903 self.write(' // ar flags = %s' % self.flags)
904 if self.pkg not in self.runner.pkg_obj2cc:
905 self.write(' ERROR: cannot find source files.\n}')
906 return
907 self.write(' srcs: [')
908 obj2cc = self.runner.pkg_obj2cc[self.pkg]
909 # Note: wflags are ignored.
910 dflags = list()
911 fflags = list()
912 for obj in self.objs:
913 self.write(' "' + short_out_name(self.pkg, obj2cc[obj].src) + '",')
914 # TODO(chh): union of dflags and flags of all obj
915 # Now, just a temporary hack that uses the last obj's flags
916 dflags = obj2cc[obj].dflags
917 fflags = obj2cc[obj].fflags
918 self.write(' ],')
919 self.write(' cflags: [')
920 self.write(' "-O3",') # TODO(chh): is this default correct?
921 self.write(' "-Wno-error",')
922 for x in fflags:
923 self.write(' "-f' + x + '",')
924 for x in dflags:
925 self.write(' "-D' + x + '",')
926 self.write(' ],')
927 self.write('}')
928
929 def dump(self):
930 """Dump error/debug/module info to the output .bp file."""
931 self.runner.init_bp_file(self.outf_name)
932 with open(self.outf_name, 'a') as outf:
933 self.outf = outf
934 if self.runner.args.debug:
935 self.dump_debug_info()
936 self.dump_android_lib()
937
938
939class CCObject(object):
940 """Information of a "cc" compilation command."""
941
942 def __init__(self, runner, outf_name):
943 # Remembered global runner and its members.
944 self.runner = runner
945 self.pkg = ''
946 self.outf_name = outf_name # path to Android.bp
947 # "cc" arguments
948 self.line_num = 1
949 self.line = ''
950 self.src = ''
951 self.obj = ''
952 self.dflags = list() # -D flags
953 self.fflags = list() # -f flags
954 self.iflags = list() # -I flags
955 self.wflags = list() # -W flags
956 self.other_args = list()
957
958 def parse(self, pkg, line_num, args_line):
959 """Collect cc compilation flags and src/out file names."""
960 self.pkg = pkg
961 self.line_num = line_num
962 self.line = args_line
963 args = args_line.split()
964 i = 0
965 while i < len(args):
966 arg = args[i]
967 if arg == '"-c"':
968 i += 1
969 if args[i].startswith('"-o'):
970 # ring-0.13.5 dumps: ... "-c" "-o/.../*.o" ".../*.c"
971 self.obj = unquote(args[i])[2:]
972 i += 1
973 self.src = unquote(args[i])
974 else:
975 self.src = unquote(args[i])
976 elif arg == '"-o"':
977 i += 1
978 self.obj = unquote(args[i])
979 elif arg == '"-I"':
980 i += 1
981 self.iflags.append(unquote(args[i]))
982 elif arg.startswith('"-D'):
983 self.dflags.append(unquote(args[i])[2:])
984 elif arg.startswith('"-f'):
985 self.fflags.append(unquote(args[i])[2:])
986 elif arg.startswith('"-W'):
987 self.wflags.append(unquote(args[i])[2:])
988 elif not (arg.startswith('"-O') or arg == '"-m64"' or arg == '"-g"' or
989 arg == '"-g3"'):
990 # ignore -O -m64 -g
991 self.other_args.append(unquote(args[i]))
992 i += 1
993 self.dflags = sorted(set(self.dflags))
994 self.fflags = sorted(set(self.fflags))
995 # self.wflags is not sorted because some are order sensitive
996 # and we ignore them anyway.
997 if self.pkg not in self.runner.pkg_obj2cc:
998 self.runner.pkg_obj2cc[self.pkg] = {}
999 self.runner.pkg_obj2cc[self.pkg][self.obj] = self
1000 return self
1001
1002 def write(self, s):
1003 self.outf.write(s + '\n')
1004
1005 def dump_debug_flags(self, name, flags):
1006 self.write('// ' + name + ':')
1007 for f in flags:
1008 self.write('// %s' % f)
1009
1010 def dump(self):
1011 """Dump only error/debug info to the output .bp file."""
1012 if not self.runner.args.debug:
1013 return
1014 self.runner.init_bp_file(self.outf_name)
1015 with open(self.outf_name, 'a') as outf:
1016 self.outf = outf
1017 self.write('\n// Line ' + str(self.line_num) + ' "cc" ' + self.line)
1018 self.write('// cc_object for %12s' % self.pkg)
1019 self.write('// src = %s' % short_out_name(self.pkg, self.src))
1020 self.write('// obj = %s' % short_out_name(self.pkg, self.obj))
1021 self.dump_debug_flags('-I flags', self.iflags)
1022 self.dump_debug_flags('-D flags', self.dflags)
1023 self.dump_debug_flags('-f flags', self.fflags)
1024 self.dump_debug_flags('-W flags', self.wflags)
1025 if self.other_args:
1026 self.dump_debug_flags('other args', self.other_args)
1027
1028
1029class Runner(object):
1030 """Main class to parse cargo -v output and print Android module definitions."""
1031
1032 def __init__(self, args):
1033 self.bp_files = set() # Remember all output Android.bp files.
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001034 self.test_mappings = {} # Map from Android.bp file path to TestMapping.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001035 self.root_pkg = '' # name of package in ./Cargo.toml
1036 # Saved flags, modes, and data.
1037 self.args = args
1038 self.dry_run = not args.run
1039 self.skip_cargo = args.skipcargo
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001040 self.cargo_path = './cargo' # path to cargo, will be set later
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001041 # All cc/ar objects, crates, dependencies, and warning files
1042 self.cc_objects = list()
1043 self.pkg_obj2cc = {}
1044 # pkg_obj2cc[cc_object[i].pkg][cc_objects[i].obj] = cc_objects[i]
1045 self.ar_objects = list()
1046 self.crates = list()
1047 self.dependencies = list() # dependent and build script crates
1048 self.warning_files = set()
1049 # Keep a unique mapping from (module name) to crate
1050 self.name_owners = {}
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001051 # Save and dump all errors from cargo to Android.bp.
1052 self.errors = ''
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001053 self.setup_cargo_path()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001054 # Default action is cargo clean, followed by build or user given actions.
1055 if args.cargo:
1056 self.cargo = ['clean'] + args.cargo
1057 else:
1058 self.cargo = ['clean', 'build']
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001059 if args.no_host: # do not run "cargo build" for host
1060 self.cargo = ['clean']
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001061 default_target = '--target x86_64-unknown-linux-gnu'
1062 if args.device:
1063 self.cargo.append('build ' + default_target)
1064 if args.tests:
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001065 if not args.no_host:
1066 self.cargo.append('build --tests')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001067 self.cargo.append('build --tests ' + default_target)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001068 elif args.tests and not args.no_host:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001069 self.cargo.append('build --tests')
1070
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001071 def setup_cargo_path(self):
1072 """Find cargo in the --cargo_bin or prebuilt rust bin directory."""
1073 if self.args.cargo_bin:
1074 self.cargo_path = os.path.join(self.args.cargo_bin, 'cargo')
1075 if not os.path.isfile(self.cargo_path):
1076 sys.exit('ERROR: cannot find cargo in ' + self.args.cargo_bin)
1077 print('WARNING: using cargo in ' + self.args.cargo_bin)
1078 return
1079 # We have only tested this on Linux.
1080 if platform.system() != 'Linux':
1081 sys.exit('ERROR: this script has only been tested on Linux with cargo.')
1082 # Assuming that this script is in development/scripts.
1083 my_dir = os.path.dirname(os.path.abspath(__file__))
1084 linux_dir = os.path.join(my_dir, '..', '..',
1085 'prebuilts', 'rust', 'linux-x86')
1086 if not os.path.isdir(linux_dir):
1087 sys.exit('ERROR: cannot find directory ' + linux_dir)
1088 rust_version = self.find_rust_version(my_dir, linux_dir)
1089 cargo_bin = os.path.join(linux_dir, rust_version, 'bin')
1090 self.cargo_path = os.path.join(cargo_bin, 'cargo')
1091 if not os.path.isfile(self.cargo_path):
1092 sys.exit('ERROR: cannot find cargo in ' + cargo_bin
1093 + '; please try --cargo_bin= flag.')
1094 return
1095
1096 def find_rust_version(self, my_dir, linux_dir):
1097 """Use my script directory, find prebuilt rust version."""
1098 # First look up build/soong/rust/config/global.go.
1099 path2global = os.path.join(my_dir, '..', '..',
1100 'build', 'soong', 'rust', 'config', 'global.go')
1101 if os.path.isfile(path2global):
1102 # try to find: RustDefaultVersion = "1.44.0"
1103 version_pat = re.compile(
1104 r'\s*RustDefaultVersion\s*=\s*"([0-9]+\.[0-9]+\.[0-9]+)".*$')
1105 with open(path2global, 'r') as inf:
1106 for line in inf:
1107 result = version_pat.match(line)
1108 if result:
1109 return result.group(1)
1110 print('WARNING: cannot find RustDefaultVersion in ' + path2global)
1111 # Otherwise, find the newest (largest) version number in linux_dir.
1112 rust_version = (0, 0, 0) # the prebuilt version to use
1113 version_pat = re.compile(r'([0-9]+)\.([0-9]+)\.([0-9]+)$')
1114 for dir_name in os.listdir(linux_dir):
1115 result = version_pat.match(dir_name)
1116 if not result:
1117 continue
1118 version = (result.group(1), result.group(2), result.group(3))
1119 if version > rust_version:
1120 rust_version = version
1121 return '.'.join(rust_version)
1122
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001123 def init_bp_file(self, name):
1124 if name not in self.bp_files:
1125 self.bp_files.add(name)
1126 with open(name, 'w') as outf:
Andrew Walbran80e90be2020-06-09 14:33:18 +01001127 outf.write(ANDROID_BP_HEADER.format(args=' '.join(sys.argv[1:])))
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001128
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001129 def dump_test_mapping_files(self):
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001130 """Dump all TEST_MAPPING files."""
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001131 if self.dry_run:
1132 print('Dry-run skip dump of TEST_MAPPING')
1133 else:
1134 for bp_file_name in self.test_mappings:
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001135 if bp_file_name != '/dev/null':
1136 name = os.path.join(os.path.dirname(bp_file_name), 'TEST_MAPPING')
1137 self.test_mappings[bp_file_name].dump(name)
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001138 return self
1139
1140 def add_test(self, bp_file_name, test_name, host):
1141 if bp_file_name not in self.test_mappings:
1142 self.test_mappings[bp_file_name] = TestMapping()
1143 mapping = self.test_mappings[bp_file_name]
1144 mapping.add_test(test_name, host)
1145
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001146 def try_claim_module_name(self, name, owner):
1147 """Reserve and return True if it has not been reserved yet."""
1148 if name not in self.name_owners or owner == self.name_owners[name]:
1149 self.name_owners[name] = owner
1150 return True
1151 return False
1152
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001153 def claim_module_name(self, prefix, owner, counter):
1154 """Return prefix if not owned yet, otherwise, prefix+str(counter)."""
1155 while True:
1156 name = prefix
1157 if counter > 0:
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001158 name += '_' + str(counter)
1159 if self.try_claim_module_name(name, owner):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001160 return name
1161 counter += 1
1162
1163 def find_root_pkg(self):
1164 """Read name of [package] in ./Cargo.toml."""
1165 if not os.path.exists('./Cargo.toml'):
1166 return
1167 with open('./Cargo.toml', 'r') as inf:
1168 pkg_section = re.compile(r'^ *\[package\]')
1169 name = re.compile('^ *name *= * "([^"]*)"')
1170 in_pkg = False
1171 for line in inf:
1172 if in_pkg:
1173 if name.match(line):
1174 self.root_pkg = name.match(line).group(1)
1175 break
1176 else:
1177 in_pkg = pkg_section.match(line) is not None
1178
1179 def run_cargo(self):
1180 """Calls cargo -v and save its output to ./cargo.out."""
1181 if self.skip_cargo:
1182 return self
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001183 cargo_toml = './Cargo.toml'
1184 cargo_out = './cargo.out'
1185 if not os.access(cargo_toml, os.R_OK):
1186 print('ERROR: Cannot find or read', cargo_toml)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001187 return self
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001188 if not self.dry_run and os.path.exists(cargo_out):
1189 os.remove(cargo_out)
1190 cmd_tail = ' --target-dir ' + TARGET_TMP + ' >> ' + cargo_out + ' 2>&1'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001191 # set up search PATH for cargo to find the correct rustc
1192 saved_path = os.environ['PATH']
1193 os.environ['PATH'] = os.path.dirname(self.cargo_path) + ':' + saved_path
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001194 # Add [workspace] to Cargo.toml if it is not there.
1195 added_workspace = False
1196 if self.args.add_workspace:
1197 with open(cargo_toml, 'r') as in_file:
1198 cargo_toml_lines = in_file.readlines()
1199 found_workspace = '[workspace]\n' in cargo_toml_lines
1200 if found_workspace:
1201 print('### WARNING: found [workspace] in Cargo.toml')
1202 else:
1203 with open(cargo_toml, 'a') as out_file:
1204 out_file.write('[workspace]\n')
1205 added_workspace = True
1206 if self.args.verbose:
1207 print('### INFO: added [workspace] to Cargo.toml')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001208 for c in self.cargo:
1209 features = ''
Chih-Hung Hsieh6c8d52f2020-03-30 18:28:52 -07001210 if c != 'clean':
1211 if self.args.features is not None:
1212 features = ' --no-default-features'
1213 if self.args.features:
1214 features += ' --features ' + self.args.features
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001215 cmd_v_flag = ' -vv ' if self.args.vv else ' -v '
1216 cmd = self.cargo_path + cmd_v_flag
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001217 cmd += c + features + cmd_tail
1218 if self.args.rustflags and c != 'clean':
1219 cmd = 'RUSTFLAGS="' + self.args.rustflags + '" ' + cmd
1220 if self.dry_run:
1221 print('Dry-run skip:', cmd)
1222 else:
1223 if self.args.verbose:
1224 print('Running:', cmd)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001225 with open(cargo_out, 'a') as out_file:
1226 out_file.write('### Running: ' + cmd + '\n')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001227 os.system(cmd)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001228 if added_workspace: # restore original Cargo.toml
1229 with open(cargo_toml, 'w') as out_file:
1230 out_file.writelines(cargo_toml_lines)
1231 if self.args.verbose:
1232 print('### INFO: restored original Cargo.toml')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001233 os.environ['PATH'] = saved_path
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001234 return self
1235
1236 def dump_dependencies(self):
1237 """Append dependencies and their features to Android.bp."""
1238 if not self.dependencies:
1239 return
1240 dependent_list = list()
1241 for c in self.dependencies:
1242 dependent_list.append(c.feature_list())
1243 sorted_dependencies = sorted(set(dependent_list))
1244 self.init_bp_file('Android.bp')
1245 with open('Android.bp', 'a') as outf:
1246 outf.write('\n// dependent_library ["feature_list"]\n')
1247 for s in sorted_dependencies:
1248 outf.write('// ' + s + '\n')
1249
1250 def dump_pkg_obj2cc(self):
1251 """Dump debug info of the pkg_obj2cc map."""
1252 if not self.args.debug:
1253 return
1254 self.init_bp_file('Android.bp')
1255 with open('Android.bp', 'a') as outf:
1256 sorted_pkgs = sorted(self.pkg_obj2cc.keys())
1257 for pkg in sorted_pkgs:
1258 if not self.pkg_obj2cc[pkg]:
1259 continue
1260 outf.write('\n// obj => src for %s\n' % pkg)
1261 obj2cc = self.pkg_obj2cc[pkg]
1262 for obj in sorted(obj2cc.keys()):
1263 outf.write('// ' + short_out_name(pkg, obj) + ' => ' +
1264 short_out_name(pkg, obj2cc[obj].src) + '\n')
1265
1266 def gen_bp(self):
1267 """Parse cargo.out and generate Android.bp files."""
1268 if self.dry_run:
1269 print('Dry-run skip: read', CARGO_OUT, 'write Android.bp')
1270 elif os.path.exists(CARGO_OUT):
1271 self.find_root_pkg()
1272 with open(CARGO_OUT, 'r') as cargo_out:
1273 self.parse(cargo_out, 'Android.bp')
1274 self.crates.sort(key=get_module_name)
1275 for obj in self.cc_objects:
1276 obj.dump()
1277 self.dump_pkg_obj2cc()
1278 for crate in self.crates:
1279 crate.dump()
1280 dumped_libs = set()
1281 for lib in self.ar_objects:
1282 if lib.pkg == self.root_pkg:
1283 lib_name = file_base_name(lib.lib)
1284 if lib_name not in dumped_libs:
1285 dumped_libs.add(lib_name)
1286 lib.dump()
1287 if self.args.dependencies and self.dependencies:
1288 self.dump_dependencies()
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001289 if self.errors:
1290 self.append_to_bp('\nErrors in ' + CARGO_OUT + ':\n' + self.errors)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001291 return self
1292
1293 def add_ar_object(self, obj):
1294 self.ar_objects.append(obj)
1295
1296 def add_cc_object(self, obj):
1297 self.cc_objects.append(obj)
1298
1299 def add_crate(self, crate):
1300 """Merge crate with someone in crates, or append to it. Return crates."""
1301 if crate.skip_crate():
1302 if self.args.debug: # include debug info of all crates
1303 self.crates.append(crate)
1304 if self.args.dependencies: # include only dependent crates
1305 if (is_dependent_file_path(crate.main_src) and
1306 not is_build_crate_name(crate.crate_name)):
1307 self.dependencies.append(crate)
1308 else:
1309 for c in self.crates:
1310 if c.merge(crate, 'Android.bp'):
1311 return
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001312 # If not merged, decide module type and name now.
1313 crate.decide_module_type()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001314 self.crates.append(crate)
1315
1316 def find_warning_owners(self):
1317 """For each warning file, find its owner crate."""
1318 missing_owner = False
1319 for f in self.warning_files:
1320 cargo_dir = '' # find lowest crate, with longest path
1321 owner = None # owner crate of this warning
1322 for c in self.crates:
1323 if (f.startswith(c.cargo_dir + '/') and
1324 len(cargo_dir) < len(c.cargo_dir)):
1325 cargo_dir = c.cargo_dir
1326 owner = c
1327 if owner:
1328 owner.has_warning = True
1329 else:
1330 missing_owner = True
1331 if missing_owner and os.path.exists('Cargo.toml'):
1332 # owner is the root cargo, with empty cargo_dir
1333 for c in self.crates:
1334 if not c.cargo_dir:
1335 c.has_warning = True
1336
1337 def rustc_command(self, n, rustc_line, line, outf_name):
1338 """Process a rustc command line from cargo -vv output."""
1339 # cargo build -vv output can have multiple lines for a rustc command
1340 # due to '\n' in strings for environment variables.
1341 # strip removes leading spaces and '\n' at the end
1342 new_rustc = (rustc_line.strip() + line) if rustc_line else line
1343 # Use an heuristic to detect the completions of a multi-line command.
1344 # This might fail for some very rare case, but easy to fix manually.
1345 if not line.endswith('`\n') or (new_rustc.count('`') % 2) != 0:
1346 return new_rustc
1347 if RUSTC_VV_CMD_ARGS.match(new_rustc):
1348 args = RUSTC_VV_CMD_ARGS.match(new_rustc).group(1)
1349 self.add_crate(Crate(self, outf_name).parse(n, args))
1350 else:
1351 self.assert_empty_vv_line(new_rustc)
1352 return ''
1353
1354 def cc_ar_command(self, n, groups, outf_name):
1355 pkg = groups.group(1)
1356 line = groups.group(3)
1357 if groups.group(2) == 'cc':
1358 self.add_cc_object(CCObject(self, outf_name).parse(pkg, n, line))
1359 else:
1360 self.add_ar_object(ARObject(self, outf_name).parse(pkg, n, line))
1361
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001362 def append_to_bp(self, line):
1363 self.init_bp_file('Android.bp')
1364 with open('Android.bp', 'a') as outf:
1365 outf.write(line)
1366
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001367 def assert_empty_vv_line(self, line):
1368 if line: # report error if line is not empty
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001369 self.append_to_bp('ERROR -vv line: ' + line)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001370 return ''
1371
1372 def parse(self, inf, outf_name):
1373 """Parse rustc and warning messages in inf, return a list of Crates."""
1374 n = 0 # line number
1375 prev_warning = False # true if the previous line was warning: ...
1376 rustc_line = '' # previous line(s) matching RUSTC_VV_PAT
1377 for line in inf:
1378 n += 1
1379 if line.startswith('warning: '):
1380 prev_warning = True
1381 rustc_line = self.assert_empty_vv_line(rustc_line)
1382 continue
1383 new_rustc = ''
1384 if RUSTC_PAT.match(line):
1385 args_line = RUSTC_PAT.match(line).group(1)
1386 self.add_crate(Crate(self, outf_name).parse(n, args_line))
1387 self.assert_empty_vv_line(rustc_line)
1388 elif rustc_line or RUSTC_VV_PAT.match(line):
1389 new_rustc = self.rustc_command(n, rustc_line, line, outf_name)
1390 elif CC_AR_VV_PAT.match(line):
1391 self.cc_ar_command(n, CC_AR_VV_PAT.match(line), outf_name)
1392 elif prev_warning and WARNING_FILE_PAT.match(line):
1393 self.assert_empty_vv_line(rustc_line)
1394 fpath = WARNING_FILE_PAT.match(line).group(1)
1395 if fpath[0] != '/': # ignore absolute path
1396 self.warning_files.add(fpath)
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001397 elif line.startswith('error: ') or line.startswith('error[E'):
1398 self.errors += line
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001399 prev_warning = False
1400 rustc_line = new_rustc
1401 self.find_warning_owners()
1402
1403
1404def parse_args():
1405 """Parse main arguments."""
1406 parser = argparse.ArgumentParser('cargo2android')
1407 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001408 '--add_workspace',
1409 action='store_true',
1410 default=False,
1411 help=('append [workspace] to Cargo.toml before calling cargo,' +
1412 ' to treat current directory as root of package source;' +
1413 ' otherwise the relative source file path in generated' +
1414 ' .bp file will be from the parent directory.'))
1415 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001416 '--cargo',
1417 action='append',
1418 metavar='args_string',
1419 help=('extra cargo build -v args in a string, ' +
1420 'each --cargo flag calls cargo build -v once'))
1421 parser.add_argument(
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001422 '--cargo_bin',
1423 type=str,
1424 help='use cargo in the cargo_bin directory instead of the prebuilt one')
1425 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001426 '--debug',
1427 action='store_true',
1428 default=False,
1429 help='dump debug info into Android.bp')
1430 parser.add_argument(
1431 '--dependencies',
1432 action='store_true',
1433 default=False,
1434 help='dump debug info of dependent crates')
1435 parser.add_argument(
1436 '--device',
1437 action='store_true',
1438 default=False,
1439 help='run cargo also for a default device target')
1440 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001441 '--features',
1442 type=str,
1443 help=('pass features to cargo build, ' +
1444 'empty string means no default features'))
1445 parser.add_argument(
1446 '--global_defaults',
1447 type=str,
1448 help='add a defaults name to every module')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001449 parser.add_argument(
1450 '--host-first-multilib',
1451 action='store_true',
1452 default=False,
1453 help=('add a compile_multilib:"first" property ' +
1454 'to Android.bp host modules.'))
1455 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001456 '--no-host',
1457 action='store_true',
1458 default=False,
1459 help='do not run cargo for the host; only for the device target')
1460 parser.add_argument(
1461 '--no-subdir',
1462 action='store_true',
1463 default=False,
1464 help='do not output anything for sub-directories')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001465 parser.add_argument(
1466 '--onefile',
1467 action='store_true',
1468 default=False,
1469 help=('output all into one ./Android.bp, default will generate ' +
1470 'one Android.bp per Cargo.toml in subdirectories'))
1471 parser.add_argument(
1472 '--run',
1473 action='store_true',
1474 default=False,
1475 help='run it, default is dry-run')
1476 parser.add_argument('--rustflags', type=str, help='passing flags to rustc')
1477 parser.add_argument(
1478 '--skipcargo',
1479 action='store_true',
1480 default=False,
1481 help='skip cargo command, parse cargo.out, and generate Android.bp')
1482 parser.add_argument(
1483 '--tests',
1484 action='store_true',
1485 default=False,
1486 help='run cargo build --tests after normal build')
1487 parser.add_argument(
1488 '--verbose',
1489 action='store_true',
1490 default=False,
1491 help='echo executed commands')
1492 parser.add_argument(
1493 '--vv',
1494 action='store_true',
1495 default=False,
1496 help='run cargo with -vv instead of default -v')
1497 return parser.parse_args()
1498
1499
1500def main():
1501 args = parse_args()
1502 if not args.run: # default is dry-run
1503 print(DRY_RUN_NOTE)
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001504 Runner(args).run_cargo().gen_bp().dump_test_mapping_files()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001505
1506
1507if __name__ == '__main__':
1508 main()