blob: b10a654fada0c40c545939cac51c31b5806cb751 [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
372 if not (flag.startswith('debuginfo=') or
373 flag.startswith('extra-filename=') or
374 flag.startswith('incremental=') or
375 flag.startswith('metadata=') or
376 flag == 'prefer-dynamic'):
377 self.codegens.append(flag)
378
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800379 def parse(self, line_num, line):
380 """Find important rustc arguments to convert to Android.bp properties."""
381 self.line_num = line_num
382 self.line = line
383 args = line.split() # Loop through every argument of rustc.
384 i = 0
385 while i < len(args):
386 arg = args[i]
387 if arg == '--crate-name':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700388 i += 1
389 self.crate_name = args[i]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800390 elif arg == '--crate-type':
391 i += 1
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700392 # cargo calls rustc with multiple --crate-type flags.
393 # rustc can accept:
394 # --crate-type [bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]
395 self.crate_types.append(args[i])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800396 elif arg == '--test':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700397 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800398 elif arg == '--target':
399 i += 1
400 self.target = args[i]
401 elif arg == '--cfg':
402 i += 1
403 if args[i].startswith('\'feature='):
404 self.features.append(unquote(args[i].replace('\'feature=', '')[:-1]))
405 else:
406 self.cfgs.append(args[i])
407 elif arg == '--extern':
408 i += 1
409 extern_names = re.sub('=/[^ ]*/deps/', ' = ', args[i])
410 self.externs.append(extern_names)
411 self.core_externs.append(re.sub(' = .*', '', extern_names))
412 elif arg == '-C': # codegen options
413 i += 1
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700414 self.add_codegens_flag(args[i])
415 elif arg.startswith('-C'):
416 # cargo has been passing "-C <xyz>" flag to rustc,
417 # but newer cargo could pass '-Cembed-bitcode=no' to rustc.
418 self.add_codegens_flag(arg[2:])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800419 elif arg == '--cap-lints':
420 i += 1
421 self.cap_lints = args[i]
422 elif arg == '-L':
423 i += 1
424 if args[i].startswith('dependency=') and args[i].endswith('/deps'):
425 if '/' + TARGET_TMP + '/' in args[i]:
426 self.root_pkg = re.sub(
427 '^.*/', '', re.sub('/' + TARGET_TMP + '/.*/deps$', '', args[i]))
428 else:
429 self.root_pkg = re.sub('^.*/', '',
430 re.sub('/[^/]+/[^/]+/deps$', '', args[i]))
431 self.root_pkg = remove_version_suffix(self.root_pkg)
432 elif arg == '-l':
433 i += 1
434 if args[i].startswith('static='):
435 self.static_libs.append(re.sub('static=', '', args[i]))
436 elif args[i].startswith('dylib='):
437 self.shared_libs.append(re.sub('dylib=', '', args[i]))
438 else:
439 self.shared_libs.append(args[i])
440 elif arg == '--out-dir' or arg == '--color': # ignored
441 i += 1
442 elif arg.startswith('--error-format=') or arg.startswith('--json='):
443 _ = arg # ignored
444 elif arg.startswith('--emit='):
445 self.emit_list = arg.replace('--emit=', '')
446 elif arg.startswith('--edition='):
447 self.edition = arg.replace('--edition=', '')
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700448 elif not arg.startswith('-'):
449 # shorten imported crate main source paths like $HOME/.cargo/
450 # registry/src/github.com-1ecc6299db9ec823/memchr-2.3.3/src/lib.rs
451 self.main_src = re.sub(r'^/[^ ]*/registry/src/', '.../', args[i])
452 self.main_src = re.sub(r'^\.\.\./github.com-[0-9a-f]*/', '.../',
453 self.main_src)
454 self.find_cargo_dir()
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700455 if self.cargo_dir: # for a subdirectory
456 if self.runner.args.no_subdir: # all .bp content to /dev/null
457 self.outf_name = '/dev/null'
458 elif not self.runner.args.onefile:
459 # Write to Android.bp in the subdirectory with Cargo.toml.
460 self.outf_name = self.cargo_dir + '/Android.bp'
461 self.main_src = self.main_src[len(self.cargo_dir) + 1:]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800462 else:
463 self.errors += 'ERROR: unknown ' + arg + '\n'
464 i += 1
465 if not self.crate_name:
466 self.errors += 'ERROR: missing --crate-name\n'
467 if not self.main_src:
468 self.errors += 'ERROR: missing main source file\n'
469 else:
470 self.srcs.append(self.main_src)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700471 if not self.crate_types:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800472 # Treat "--cfg test" as "--test"
473 if 'test' in self.cfgs:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700474 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800475 else:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700476 self.errors += 'ERROR: missing --crate-type or --test\n'
477 elif len(self.crate_types) > 1:
478 if 'test' in self.crate_types:
479 self.errors += 'ERROR: cannot handle both --crate-type and --test\n'
480 if 'lib' in self.crate_types and 'rlib' in self.crate_types:
481 self.errors += 'ERROR: cannot generate both lib and rlib crate types\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800482 if not self.root_pkg:
483 self.root_pkg = self.crate_name
484 if self.target:
485 self.device_supported = True
486 self.host_supported = True # assume host supported for all builds
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700487 if self.runner.args.no_host: # unless --no-host was specified
488 self.host_supported = False
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800489 self.cfgs = sorted(set(self.cfgs))
490 self.features = sorted(set(self.features))
491 self.codegens = sorted(set(self.codegens))
492 self.externs = sorted(set(self.externs))
493 self.core_externs = sorted(set(self.core_externs))
494 self.static_libs = sorted(set(self.static_libs))
495 self.shared_libs = sorted(set(self.shared_libs))
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700496 self.crate_types = sorted(set(self.crate_types))
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800497 self.decide_module_type()
498 self.module_name = altered_name(self.stem)
499 return self
500
501 def dump_line(self):
502 self.write('\n// Line ' + str(self.line_num) + ' ' + self.line)
503
504 def feature_list(self):
505 """Return a string of main_src + "feature_list"."""
506 pkg = self.main_src
507 if pkg.startswith('.../'): # keep only the main package name
508 pkg = re.sub('/.*', '', pkg[4:])
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700509 elif pkg.startswith('/'): # use relative path for a local package
510 pkg = os.path.relpath(pkg)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800511 if not self.features:
512 return pkg
513 return pkg + ' "' + ','.join(self.features) + '"'
514
515 def dump_skip_crate(self, kind):
516 if self.debug:
517 self.write('\n// IGNORED: ' + kind + ' ' + self.main_src)
518 return self
519
520 def skip_crate(self):
521 """Return crate_name or a message if this crate should be skipped."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700522 if (is_build_crate_name(self.crate_name) or
523 self.crate_name in EXCLUDED_CRATES):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800524 return self.crate_name
525 if is_dependent_file_path(self.main_src):
526 return 'dependent crate'
527 return ''
528
529 def dump(self):
530 """Dump all error/debug/module code to the output .bp file."""
531 self.runner.init_bp_file(self.outf_name)
532 with open(self.outf_name, 'a') as outf:
533 self.outf = outf
534 if self.errors:
535 self.dump_line()
536 self.write(self.errors)
537 elif self.skip_crate():
538 self.dump_skip_crate(self.skip_crate())
539 else:
540 if self.debug:
541 self.dump_debug_info()
542 self.dump_android_module()
543
544 def dump_debug_info(self):
545 """Dump parsed data, when cargo2android is called with --debug."""
546
547 def dump(name, value):
548 self.write('//%12s = %s' % (name, value))
549
550 def opt_dump(name, value):
551 if value:
552 dump(name, value)
553
554 def dump_list(fmt, values):
555 for v in values:
556 self.write(fmt % v)
557
558 self.dump_line()
559 dump('module_name', self.module_name)
560 dump('crate_name', self.crate_name)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700561 dump('crate_types', self.crate_types)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800562 dump('main_src', self.main_src)
563 dump('has_warning', self.has_warning)
564 dump('for_host', self.host_supported)
565 dump('for_device', self.device_supported)
566 dump('module_type', self.module_type)
567 opt_dump('target', self.target)
568 opt_dump('edition', self.edition)
569 opt_dump('emit_list', self.emit_list)
570 opt_dump('cap_lints', self.cap_lints)
571 dump_list('// cfg = %s', self.cfgs)
572 dump_list('// cfg = \'feature "%s"\'', self.features)
573 # TODO(chh): escape quotes in self.features, but not in other dump_list
574 dump_list('// codegen = %s', self.codegens)
575 dump_list('// externs = %s', self.externs)
576 dump_list('// -l static = %s', self.static_libs)
577 dump_list('// -l (dylib) = %s', self.shared_libs)
578
579 def dump_android_module(self):
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700580 """Dump one or more Android module definition, depending on crate_types."""
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700581 if len(self.crate_types) == 1:
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700582 self.dump_single_type_android_module()
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700583 return
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700584 if 'test' in self.crate_types:
585 self.write('\nERROR: multiple crate types cannot include test type')
586 return
587 # Dump one Android module per crate_type.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700588 for crate_type in self.crate_types:
589 self.decide_one_module_type(crate_type)
590 self.dump_one_android_module(crate_type)
591
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700592 def build_default_name(self):
593 """Return a short and readable name for the rust_defaults module."""
594 # Choices: (1) root_pkg + '_defaults',
595 # (2) root_pkg + '_defaults_' + crate_name
596 # (3) root_pkg + '_defaults_' + main_src_basename_path
597 # (4) root_pkg + '_defaults_' + a_positive_sequence_number
598 name1 = altered_defaults(self.root_pkg) + '_defaults'
599 if self.runner.try_claim_module_name(name1, self):
600 return name1
601 name2 = name1 + '_' + self.crate_name
602 if self.runner.try_claim_module_name(name2, self):
603 return name2
604 name3 = name1 + '_' + self.main_src_basename_path()
605 if self.runner.try_claim_module_name(name3, self):
606 return name3
607 return self.runner.claim_module_name(name1, self, 0)
608
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700609 def dump_defaults_module(self):
610 """Dump a rust_defaults module to be shared by other modules."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700611 name = self.build_default_name()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700612 self.defaults = name
613 self.write('\nrust_defaults {')
614 self.write(' name: "' + name + '",')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700615 if self.runner.args.global_defaults:
616 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700617 self.write(' crate_name: "' + self.crate_name + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700618 if len(self.srcs) == 1: # only one source file; share it in defaults
619 self.default_srcs = True
620 if self.has_warning and not self.cap_lints:
621 self.write(' // has rustc warnings')
622 self.write(' srcs: ["' + self.main_src + '"],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700623 if 'test' in self.crate_types:
624 self.write(' test_suites: ["general-tests"],')
625 self.write(' auto_gen_config: true,')
626 self.dump_edition_flags_libs()
627 self.write('}')
628
629 def dump_single_type_android_module(self):
630 """Dump one simple Android module, which has only one crate_type."""
631 crate_type = self.crate_types[0]
632 if crate_type != 'test':
633 # do not change self.stem or self.module_name
634 self.dump_one_android_module(crate_type)
635 return
636 # Dump one test module per source file, and separate host and device tests.
637 # crate_type == 'test'
638 if (self.host_supported and self.device_supported) or len(self.srcs) > 1:
639 self.srcs = sorted(set(self.srcs))
640 self.dump_defaults_module()
641 saved_srcs = self.srcs
642 for src in saved_srcs:
643 self.srcs = [src]
644 saved_device_supported = self.device_supported
645 saved_host_supported = self.host_supported
646 saved_main_src = self.main_src
647 self.main_src = src
648 if saved_host_supported:
649 self.device_supported = False
650 self.host_supported = True
651 self.module_name = self.test_module_name()
652 self.decide_one_module_type(crate_type)
653 self.dump_one_android_module(crate_type)
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700654 self.runner.add_test(self.outf_name, self.module_name, True)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700655 if saved_device_supported:
656 self.device_supported = True
657 self.host_supported = False
658 self.module_name = self.test_module_name()
659 self.decide_one_module_type(crate_type)
660 self.dump_one_android_module(crate_type)
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700661 self.runner.add_test(self.outf_name, self.module_name, False)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700662 self.host_supported = saved_host_supported
663 self.device_supported = saved_device_supported
664 self.main_src = saved_main_src
665 self.srcs = saved_srcs
666
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700667 def dump_one_android_module(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800668 """Dump one Android module definition."""
669 if not self.module_type:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700670 self.write('\nERROR: unknown crate_type ' + crate_type)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800671 return
672 self.write('\n' + self.module_type + ' {')
673 self.dump_android_core_properties()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700674 if not self.defaults:
675 self.dump_edition_flags_libs()
676 if self.runner.args.host_first_multilib and self.host_supported and crate_type != 'test':
677 self.write(' compile_multilib: "first",')
678 self.write('}')
679
680 def dump_android_flags(self):
681 """Dump Android module flags property."""
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700682 if not self.cfgs and not self.codegens and not self.cap_lints:
683 return
684 self.write(' flags: [')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800685 if self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700686 self.write(' "--cap-lints ' + self.cap_lints + '",')
687 cfg_fmt = '"--cfg %s"'
688 codegens_fmt = '"-C %s"'
689 self.dump_android_property_list_items(cfg_fmt, self.cfgs)
690 self.dump_android_property_list_items(codegens_fmt, self.codegens)
691 self.write(' ],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700692
693 def dump_edition_flags_libs(self):
694 if self.edition:
695 self.write(' edition: "' + self.edition + '",')
696 self.dump_android_property_list('features', '"%s"', self.features)
697 self.dump_android_flags()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800698 if self.externs:
699 self.dump_android_externs()
700 self.dump_android_property_list('static_libs', '"lib%s"', self.static_libs)
701 self.dump_android_property_list('shared_libs', '"lib%s"', self.shared_libs)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800702
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700703 def main_src_basename_path(self):
704 return re.sub('/', '_', re.sub('.rs$', '', self.main_src))
705
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800706 def test_module_name(self):
707 """Return a unique name for a test module."""
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700708 # root_pkg+(_host|_device) + '_test_'+source_file_name
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700709 suffix = self.main_src_basename_path()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700710 host_device = '_host'
711 if self.device_supported:
712 host_device = '_device'
713 return self.root_pkg + host_device + '_test_' + suffix
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800714
715 def decide_module_type(self):
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700716 # Use the first crate type for the default/first module.
717 crate_type = self.crate_types[0] if self.crate_types else ''
718 self.decide_one_module_type(crate_type)
719
720 def decide_one_module_type(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800721 """Decide which Android module type to use."""
722 host = '' if self.device_supported else '_host'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700723 if crate_type == 'bin': # rust_binary[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800724 self.module_type = 'rust_binary' + host
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700725 # In rare cases like protobuf-codegen, the output binary name must
726 # be renamed to use as a plugin for protoc.
727 self.stem = altered_stem(self.crate_name)
728 self.module_name = altered_name(self.crate_name)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700729 elif crate_type == 'lib': # rust_library[_host]
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700730 # TODO(chh): should this be rust_library[_host]?
731 # Assuming that Cargo.toml do not use both 'lib' and 'rlib',
732 # because we map them both to rlib.
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700733 self.module_type = 'rust_library' + host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800734 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700735 self.module_name = altered_name(self.stem)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700736 elif crate_type == 'rlib': # rust_library[_host]
737 self.module_type = 'rust_library' + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700738 self.stem = 'lib' + self.crate_name
739 self.module_name = altered_name(self.stem)
740 elif crate_type == 'dylib': # rust_library[_host]_dylib
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800741 self.module_type = 'rust_library' + host + '_dylib'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700742 self.stem = 'lib' + self.crate_name
743 self.module_name = altered_name(self.stem) + '_dylib'
744 elif crate_type == 'cdylib': # rust_library[_host]_shared
745 self.module_type = 'rust_library' + host + '_shared'
746 self.stem = 'lib' + self.crate_name
747 self.module_name = altered_name(self.stem) + '_shared'
748 elif crate_type == 'staticlib': # rust_library[_host]_static
749 self.module_type = 'rust_library' + host + '_static'
750 self.stem = 'lib' + self.crate_name
751 self.module_name = altered_name(self.stem) + '_static'
752 elif crate_type == 'test': # rust_test[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800753 self.module_type = 'rust_test' + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700754 # Before do_merge, stem name is based on the --crate-name parameter.
755 # and test module name is based on stem.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800756 self.stem = self.test_module_name()
757 # self.stem will be changed after merging with other tests.
758 # self.stem is NOT used for final test binary name.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700759 # rust_test uses each source file base name as part of output file name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700760 # In do_merge, this function is called again, with a module_name.
761 # We make sure that the module name is unique in each package.
762 if self.module_name:
763 # Cargo uses "-C extra-filename=..." and "-C metadata=..." to add
764 # different suffixes and distinguish multiple tests of the same
765 # crate name. We ignore -C and use claim_module_name to get
766 # unique sequential suffix.
767 self.module_name = self.runner.claim_module_name(
768 self.module_name, self, 0)
769 # Now the module name is unique, stem should also match and unique.
770 self.stem = self.module_name
771 elif crate_type == 'proc-macro': # rust_proc_macro
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800772 self.module_type = 'rust_proc_macro'
773 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700774 self.module_name = altered_name(self.stem)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800775 else: # unknown module type, rust_prebuilt_dylib? rust_library[_host]?
776 self.module_type = ''
777 self.stem = ''
778
779 def dump_android_property_list_items(self, fmt, values):
780 for v in values:
781 # fmt has quotes, so we need escape_quotes(v)
782 self.write(' ' + (fmt % escape_quotes(v)) + ',')
783
784 def dump_android_property_list(self, name, fmt, values):
785 if values:
786 self.write(' ' + name + ': [')
787 self.dump_android_property_list_items(fmt, values)
788 self.write(' ],')
789
790 def dump_android_core_properties(self):
791 """Dump the module header, name, stem, etc."""
792 self.write(' name: "' + self.module_name + '",')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700793 # see properties shared by dump_defaults_module
794 if self.defaults:
795 self.write(' defaults: ["' + self.defaults + '"],')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700796 elif self.runner.args.global_defaults:
797 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800798 if self.stem != self.module_name:
799 self.write(' stem: "' + self.stem + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700800 if self.has_warning and not self.cap_lints and not self.default_srcs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700801 self.write(' // has rustc warnings')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800802 if self.host_supported and self.device_supported:
803 self.write(' host_supported: true,')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700804 if not self.defaults:
805 self.write(' crate_name: "' + self.crate_name + '",')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800806 if len(self.srcs) > 1:
807 self.srcs = sorted(set(self.srcs))
808 self.dump_android_property_list('srcs', '"%s"', self.srcs)
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700809 elif not self.default_srcs:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800810 self.write(' srcs: ["' + self.main_src + '"],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700811 if 'test' in self.crate_types and not self.defaults:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800812 # self.root_pkg can have multiple test modules, with different *_tests[n]
813 # names, but their executables can all be installed under the same _tests
814 # directory. When built from Cargo.toml, all tests should have different
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700815 # file or crate names. So we used (root_pkg + '_tests') name as the
816 # relative_install_path.
817 # However, some package like 'slab' can have non-mergeable tests that
818 # must be separated by different module names. So, here we no longer
819 # emit relative_install_path.
820 # self.write(' relative_install_path: "' + self.root_pkg + '_tests",')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800821 self.write(' test_suites: ["general-tests"],')
822 self.write(' auto_gen_config: true,')
823
824 def dump_android_externs(self):
825 """Dump the dependent rlibs and dylibs property."""
826 so_libs = list()
827 rust_libs = ''
828 deps_libname = re.compile('^.* = lib(.*)-[0-9a-f]*.(rlib|so|rmeta)$')
829 for lib in self.externs:
830 # normal value of lib: "libc = liblibc-*.rlib"
831 # strange case in rand crate: "getrandom_package = libgetrandom-*.rlib"
832 # we should use "libgetrandom", not "lib" + "getrandom_package"
833 groups = deps_libname.match(lib)
834 if groups is not None:
835 lib_name = groups.group(1)
836 else:
837 lib_name = re.sub(' .*$', '', lib)
838 if lib.endswith('.rlib') or lib.endswith('.rmeta'):
839 # On MacOS .rmeta is used when Linux uses .rlib or .rmeta.
840 rust_libs += ' "' + altered_name('lib' + lib_name) + '",\n'
841 elif lib.endswith('.so'):
842 so_libs.append(lib_name)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700843 elif lib != 'proc_macro': # --extern proc_macro is special and ignored
844 rust_libs += ' // ERROR: unknown type of lib ' + lib + '\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800845 if rust_libs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700846 self.write(' rustlibs: [\n' + rust_libs + ' ],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800847 # Are all dependent .so files proc_macros?
848 # TODO(chh): Separate proc_macros and dylib.
849 self.dump_android_property_list('proc_macros', '"lib%s"', so_libs)
850
851
852class ARObject(object):
853 """Information of an "ar" link command."""
854
855 def __init__(self, runner, outf_name):
856 # Remembered global runner and its members.
857 self.runner = runner
858 self.pkg = ''
859 self.outf_name = outf_name # path to Android.bp
860 # "ar" arguments
861 self.line_num = 1
862 self.line = ''
863 self.flags = '' # e.g. "crs"
864 self.lib = '' # e.g. "/.../out/lib*.a"
865 self.objs = list() # e.g. "/.../out/.../*.o"
866
867 def parse(self, pkg, line_num, args_line):
868 """Collect ar obj/lib file names."""
869 self.pkg = pkg
870 self.line_num = line_num
871 self.line = args_line
872 args = args_line.split()
873 num_args = len(args)
874 if num_args < 3:
875 print('ERROR: "ar" command has too few arguments', args_line)
876 else:
877 self.flags = unquote(args[0])
878 self.lib = unquote(args[1])
879 self.objs = sorted(set(map(unquote, args[2:])))
880 return self
881
882 def write(self, s):
883 self.outf.write(s + '\n')
884
885 def dump_debug_info(self):
886 self.write('\n// Line ' + str(self.line_num) + ' "ar" ' + self.line)
887 self.write('// ar_object for %12s' % self.pkg)
888 self.write('// flags = %s' % self.flags)
889 self.write('// lib = %s' % short_out_name(self.pkg, self.lib))
890 for o in self.objs:
891 self.write('// obj = %s' % short_out_name(self.pkg, o))
892
893 def dump_android_lib(self):
894 """Write cc_library_static into Android.bp."""
895 self.write('\ncc_library_static {')
896 self.write(' name: "' + file_base_name(self.lib) + '",')
897 self.write(' host_supported: true,')
898 if self.flags != 'crs':
899 self.write(' // ar flags = %s' % self.flags)
900 if self.pkg not in self.runner.pkg_obj2cc:
901 self.write(' ERROR: cannot find source files.\n}')
902 return
903 self.write(' srcs: [')
904 obj2cc = self.runner.pkg_obj2cc[self.pkg]
905 # Note: wflags are ignored.
906 dflags = list()
907 fflags = list()
908 for obj in self.objs:
909 self.write(' "' + short_out_name(self.pkg, obj2cc[obj].src) + '",')
910 # TODO(chh): union of dflags and flags of all obj
911 # Now, just a temporary hack that uses the last obj's flags
912 dflags = obj2cc[obj].dflags
913 fflags = obj2cc[obj].fflags
914 self.write(' ],')
915 self.write(' cflags: [')
916 self.write(' "-O3",') # TODO(chh): is this default correct?
917 self.write(' "-Wno-error",')
918 for x in fflags:
919 self.write(' "-f' + x + '",')
920 for x in dflags:
921 self.write(' "-D' + x + '",')
922 self.write(' ],')
923 self.write('}')
924
925 def dump(self):
926 """Dump error/debug/module info to the output .bp file."""
927 self.runner.init_bp_file(self.outf_name)
928 with open(self.outf_name, 'a') as outf:
929 self.outf = outf
930 if self.runner.args.debug:
931 self.dump_debug_info()
932 self.dump_android_lib()
933
934
935class CCObject(object):
936 """Information of a "cc" compilation command."""
937
938 def __init__(self, runner, outf_name):
939 # Remembered global runner and its members.
940 self.runner = runner
941 self.pkg = ''
942 self.outf_name = outf_name # path to Android.bp
943 # "cc" arguments
944 self.line_num = 1
945 self.line = ''
946 self.src = ''
947 self.obj = ''
948 self.dflags = list() # -D flags
949 self.fflags = list() # -f flags
950 self.iflags = list() # -I flags
951 self.wflags = list() # -W flags
952 self.other_args = list()
953
954 def parse(self, pkg, line_num, args_line):
955 """Collect cc compilation flags and src/out file names."""
956 self.pkg = pkg
957 self.line_num = line_num
958 self.line = args_line
959 args = args_line.split()
960 i = 0
961 while i < len(args):
962 arg = args[i]
963 if arg == '"-c"':
964 i += 1
965 if args[i].startswith('"-o'):
966 # ring-0.13.5 dumps: ... "-c" "-o/.../*.o" ".../*.c"
967 self.obj = unquote(args[i])[2:]
968 i += 1
969 self.src = unquote(args[i])
970 else:
971 self.src = unquote(args[i])
972 elif arg == '"-o"':
973 i += 1
974 self.obj = unquote(args[i])
975 elif arg == '"-I"':
976 i += 1
977 self.iflags.append(unquote(args[i]))
978 elif arg.startswith('"-D'):
979 self.dflags.append(unquote(args[i])[2:])
980 elif arg.startswith('"-f'):
981 self.fflags.append(unquote(args[i])[2:])
982 elif arg.startswith('"-W'):
983 self.wflags.append(unquote(args[i])[2:])
984 elif not (arg.startswith('"-O') or arg == '"-m64"' or arg == '"-g"' or
985 arg == '"-g3"'):
986 # ignore -O -m64 -g
987 self.other_args.append(unquote(args[i]))
988 i += 1
989 self.dflags = sorted(set(self.dflags))
990 self.fflags = sorted(set(self.fflags))
991 # self.wflags is not sorted because some are order sensitive
992 # and we ignore them anyway.
993 if self.pkg not in self.runner.pkg_obj2cc:
994 self.runner.pkg_obj2cc[self.pkg] = {}
995 self.runner.pkg_obj2cc[self.pkg][self.obj] = self
996 return self
997
998 def write(self, s):
999 self.outf.write(s + '\n')
1000
1001 def dump_debug_flags(self, name, flags):
1002 self.write('// ' + name + ':')
1003 for f in flags:
1004 self.write('// %s' % f)
1005
1006 def dump(self):
1007 """Dump only error/debug info to the output .bp file."""
1008 if not self.runner.args.debug:
1009 return
1010 self.runner.init_bp_file(self.outf_name)
1011 with open(self.outf_name, 'a') as outf:
1012 self.outf = outf
1013 self.write('\n// Line ' + str(self.line_num) + ' "cc" ' + self.line)
1014 self.write('// cc_object for %12s' % self.pkg)
1015 self.write('// src = %s' % short_out_name(self.pkg, self.src))
1016 self.write('// obj = %s' % short_out_name(self.pkg, self.obj))
1017 self.dump_debug_flags('-I flags', self.iflags)
1018 self.dump_debug_flags('-D flags', self.dflags)
1019 self.dump_debug_flags('-f flags', self.fflags)
1020 self.dump_debug_flags('-W flags', self.wflags)
1021 if self.other_args:
1022 self.dump_debug_flags('other args', self.other_args)
1023
1024
1025class Runner(object):
1026 """Main class to parse cargo -v output and print Android module definitions."""
1027
1028 def __init__(self, args):
1029 self.bp_files = set() # Remember all output Android.bp files.
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001030 self.test_mappings = {} # Map from Android.bp file path to TestMapping.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001031 self.root_pkg = '' # name of package in ./Cargo.toml
1032 # Saved flags, modes, and data.
1033 self.args = args
1034 self.dry_run = not args.run
1035 self.skip_cargo = args.skipcargo
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001036 self.cargo_path = './cargo' # path to cargo, will be set later
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001037 # All cc/ar objects, crates, dependencies, and warning files
1038 self.cc_objects = list()
1039 self.pkg_obj2cc = {}
1040 # pkg_obj2cc[cc_object[i].pkg][cc_objects[i].obj] = cc_objects[i]
1041 self.ar_objects = list()
1042 self.crates = list()
1043 self.dependencies = list() # dependent and build script crates
1044 self.warning_files = set()
1045 # Keep a unique mapping from (module name) to crate
1046 self.name_owners = {}
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001047 # Save and dump all errors from cargo to Android.bp.
1048 self.errors = ''
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001049 self.setup_cargo_path()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001050 # Default action is cargo clean, followed by build or user given actions.
1051 if args.cargo:
1052 self.cargo = ['clean'] + args.cargo
1053 else:
1054 self.cargo = ['clean', 'build']
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001055 if args.no_host: # do not run "cargo build" for host
1056 self.cargo = ['clean']
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001057 default_target = '--target x86_64-unknown-linux-gnu'
1058 if args.device:
1059 self.cargo.append('build ' + default_target)
1060 if args.tests:
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001061 if not args.no_host:
1062 self.cargo.append('build --tests')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001063 self.cargo.append('build --tests ' + default_target)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001064 elif args.tests and not args.no_host:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001065 self.cargo.append('build --tests')
1066
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001067 def setup_cargo_path(self):
1068 """Find cargo in the --cargo_bin or prebuilt rust bin directory."""
1069 if self.args.cargo_bin:
1070 self.cargo_path = os.path.join(self.args.cargo_bin, 'cargo')
1071 if not os.path.isfile(self.cargo_path):
1072 sys.exit('ERROR: cannot find cargo in ' + self.args.cargo_bin)
1073 print('WARNING: using cargo in ' + self.args.cargo_bin)
1074 return
1075 # We have only tested this on Linux.
1076 if platform.system() != 'Linux':
1077 sys.exit('ERROR: this script has only been tested on Linux with cargo.')
1078 # Assuming that this script is in development/scripts.
1079 my_dir = os.path.dirname(os.path.abspath(__file__))
1080 linux_dir = os.path.join(my_dir, '..', '..',
1081 'prebuilts', 'rust', 'linux-x86')
1082 if not os.path.isdir(linux_dir):
1083 sys.exit('ERROR: cannot find directory ' + linux_dir)
1084 rust_version = self.find_rust_version(my_dir, linux_dir)
1085 cargo_bin = os.path.join(linux_dir, rust_version, 'bin')
1086 self.cargo_path = os.path.join(cargo_bin, 'cargo')
1087 if not os.path.isfile(self.cargo_path):
1088 sys.exit('ERROR: cannot find cargo in ' + cargo_bin
1089 + '; please try --cargo_bin= flag.')
1090 return
1091
1092 def find_rust_version(self, my_dir, linux_dir):
1093 """Use my script directory, find prebuilt rust version."""
1094 # First look up build/soong/rust/config/global.go.
1095 path2global = os.path.join(my_dir, '..', '..',
1096 'build', 'soong', 'rust', 'config', 'global.go')
1097 if os.path.isfile(path2global):
1098 # try to find: RustDefaultVersion = "1.44.0"
1099 version_pat = re.compile(
1100 r'\s*RustDefaultVersion\s*=\s*"([0-9]+\.[0-9]+\.[0-9]+)".*$')
1101 with open(path2global, 'r') as inf:
1102 for line in inf:
1103 result = version_pat.match(line)
1104 if result:
1105 return result.group(1)
1106 print('WARNING: cannot find RustDefaultVersion in ' + path2global)
1107 # Otherwise, find the newest (largest) version number in linux_dir.
1108 rust_version = (0, 0, 0) # the prebuilt version to use
1109 version_pat = re.compile(r'([0-9]+)\.([0-9]+)\.([0-9]+)$')
1110 for dir_name in os.listdir(linux_dir):
1111 result = version_pat.match(dir_name)
1112 if not result:
1113 continue
1114 version = (result.group(1), result.group(2), result.group(3))
1115 if version > rust_version:
1116 rust_version = version
1117 return '.'.join(rust_version)
1118
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001119 def init_bp_file(self, name):
1120 if name not in self.bp_files:
1121 self.bp_files.add(name)
1122 with open(name, 'w') as outf:
Andrew Walbran80e90be2020-06-09 14:33:18 +01001123 outf.write(ANDROID_BP_HEADER.format(args=' '.join(sys.argv[1:])))
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001124
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001125 def dump_test_mapping_files(self):
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001126 """Dump all TEST_MAPPING files."""
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001127 if self.dry_run:
1128 print('Dry-run skip dump of TEST_MAPPING')
1129 else:
1130 for bp_file_name in self.test_mappings:
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001131 if bp_file_name != '/dev/null':
1132 name = os.path.join(os.path.dirname(bp_file_name), 'TEST_MAPPING')
1133 self.test_mappings[bp_file_name].dump(name)
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001134 return self
1135
1136 def add_test(self, bp_file_name, test_name, host):
1137 if bp_file_name not in self.test_mappings:
1138 self.test_mappings[bp_file_name] = TestMapping()
1139 mapping = self.test_mappings[bp_file_name]
1140 mapping.add_test(test_name, host)
1141
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001142 def try_claim_module_name(self, name, owner):
1143 """Reserve and return True if it has not been reserved yet."""
1144 if name not in self.name_owners or owner == self.name_owners[name]:
1145 self.name_owners[name] = owner
1146 return True
1147 return False
1148
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001149 def claim_module_name(self, prefix, owner, counter):
1150 """Return prefix if not owned yet, otherwise, prefix+str(counter)."""
1151 while True:
1152 name = prefix
1153 if counter > 0:
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001154 name += '_' + str(counter)
1155 if self.try_claim_module_name(name, owner):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001156 return name
1157 counter += 1
1158
1159 def find_root_pkg(self):
1160 """Read name of [package] in ./Cargo.toml."""
1161 if not os.path.exists('./Cargo.toml'):
1162 return
1163 with open('./Cargo.toml', 'r') as inf:
1164 pkg_section = re.compile(r'^ *\[package\]')
1165 name = re.compile('^ *name *= * "([^"]*)"')
1166 in_pkg = False
1167 for line in inf:
1168 if in_pkg:
1169 if name.match(line):
1170 self.root_pkg = name.match(line).group(1)
1171 break
1172 else:
1173 in_pkg = pkg_section.match(line) is not None
1174
1175 def run_cargo(self):
1176 """Calls cargo -v and save its output to ./cargo.out."""
1177 if self.skip_cargo:
1178 return self
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001179 cargo_toml = './Cargo.toml'
1180 cargo_out = './cargo.out'
1181 if not os.access(cargo_toml, os.R_OK):
1182 print('ERROR: Cannot find or read', cargo_toml)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001183 return self
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001184 if not self.dry_run and os.path.exists(cargo_out):
1185 os.remove(cargo_out)
1186 cmd_tail = ' --target-dir ' + TARGET_TMP + ' >> ' + cargo_out + ' 2>&1'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001187 # set up search PATH for cargo to find the correct rustc
1188 saved_path = os.environ['PATH']
1189 os.environ['PATH'] = os.path.dirname(self.cargo_path) + ':' + saved_path
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001190 # Add [workspace] to Cargo.toml if it is not there.
1191 added_workspace = False
1192 if self.args.add_workspace:
1193 with open(cargo_toml, 'r') as in_file:
1194 cargo_toml_lines = in_file.readlines()
1195 found_workspace = '[workspace]\n' in cargo_toml_lines
1196 if found_workspace:
1197 print('### WARNING: found [workspace] in Cargo.toml')
1198 else:
1199 with open(cargo_toml, 'a') as out_file:
1200 out_file.write('[workspace]\n')
1201 added_workspace = True
1202 if self.args.verbose:
1203 print('### INFO: added [workspace] to Cargo.toml')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001204 for c in self.cargo:
1205 features = ''
Chih-Hung Hsieh6c8d52f2020-03-30 18:28:52 -07001206 if c != 'clean':
1207 if self.args.features is not None:
1208 features = ' --no-default-features'
1209 if self.args.features:
1210 features += ' --features ' + self.args.features
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001211 cmd_v_flag = ' -vv ' if self.args.vv else ' -v '
1212 cmd = self.cargo_path + cmd_v_flag
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001213 cmd += c + features + cmd_tail
1214 if self.args.rustflags and c != 'clean':
1215 cmd = 'RUSTFLAGS="' + self.args.rustflags + '" ' + cmd
1216 if self.dry_run:
1217 print('Dry-run skip:', cmd)
1218 else:
1219 if self.args.verbose:
1220 print('Running:', cmd)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001221 with open(cargo_out, 'a') as out_file:
1222 out_file.write('### Running: ' + cmd + '\n')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001223 os.system(cmd)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001224 if added_workspace: # restore original Cargo.toml
1225 with open(cargo_toml, 'w') as out_file:
1226 out_file.writelines(cargo_toml_lines)
1227 if self.args.verbose:
1228 print('### INFO: restored original Cargo.toml')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001229 os.environ['PATH'] = saved_path
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001230 return self
1231
1232 def dump_dependencies(self):
1233 """Append dependencies and their features to Android.bp."""
1234 if not self.dependencies:
1235 return
1236 dependent_list = list()
1237 for c in self.dependencies:
1238 dependent_list.append(c.feature_list())
1239 sorted_dependencies = sorted(set(dependent_list))
1240 self.init_bp_file('Android.bp')
1241 with open('Android.bp', 'a') as outf:
1242 outf.write('\n// dependent_library ["feature_list"]\n')
1243 for s in sorted_dependencies:
1244 outf.write('// ' + s + '\n')
1245
1246 def dump_pkg_obj2cc(self):
1247 """Dump debug info of the pkg_obj2cc map."""
1248 if not self.args.debug:
1249 return
1250 self.init_bp_file('Android.bp')
1251 with open('Android.bp', 'a') as outf:
1252 sorted_pkgs = sorted(self.pkg_obj2cc.keys())
1253 for pkg in sorted_pkgs:
1254 if not self.pkg_obj2cc[pkg]:
1255 continue
1256 outf.write('\n// obj => src for %s\n' % pkg)
1257 obj2cc = self.pkg_obj2cc[pkg]
1258 for obj in sorted(obj2cc.keys()):
1259 outf.write('// ' + short_out_name(pkg, obj) + ' => ' +
1260 short_out_name(pkg, obj2cc[obj].src) + '\n')
1261
1262 def gen_bp(self):
1263 """Parse cargo.out and generate Android.bp files."""
1264 if self.dry_run:
1265 print('Dry-run skip: read', CARGO_OUT, 'write Android.bp')
1266 elif os.path.exists(CARGO_OUT):
1267 self.find_root_pkg()
1268 with open(CARGO_OUT, 'r') as cargo_out:
1269 self.parse(cargo_out, 'Android.bp')
1270 self.crates.sort(key=get_module_name)
1271 for obj in self.cc_objects:
1272 obj.dump()
1273 self.dump_pkg_obj2cc()
1274 for crate in self.crates:
1275 crate.dump()
1276 dumped_libs = set()
1277 for lib in self.ar_objects:
1278 if lib.pkg == self.root_pkg:
1279 lib_name = file_base_name(lib.lib)
1280 if lib_name not in dumped_libs:
1281 dumped_libs.add(lib_name)
1282 lib.dump()
1283 if self.args.dependencies and self.dependencies:
1284 self.dump_dependencies()
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001285 if self.errors:
1286 self.append_to_bp('\nErrors in ' + CARGO_OUT + ':\n' + self.errors)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001287 return self
1288
1289 def add_ar_object(self, obj):
1290 self.ar_objects.append(obj)
1291
1292 def add_cc_object(self, obj):
1293 self.cc_objects.append(obj)
1294
1295 def add_crate(self, crate):
1296 """Merge crate with someone in crates, or append to it. Return crates."""
1297 if crate.skip_crate():
1298 if self.args.debug: # include debug info of all crates
1299 self.crates.append(crate)
1300 if self.args.dependencies: # include only dependent crates
1301 if (is_dependent_file_path(crate.main_src) and
1302 not is_build_crate_name(crate.crate_name)):
1303 self.dependencies.append(crate)
1304 else:
1305 for c in self.crates:
1306 if c.merge(crate, 'Android.bp'):
1307 return
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001308 # If not merged, decide module type and name now.
1309 crate.decide_module_type()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001310 self.crates.append(crate)
1311
1312 def find_warning_owners(self):
1313 """For each warning file, find its owner crate."""
1314 missing_owner = False
1315 for f in self.warning_files:
1316 cargo_dir = '' # find lowest crate, with longest path
1317 owner = None # owner crate of this warning
1318 for c in self.crates:
1319 if (f.startswith(c.cargo_dir + '/') and
1320 len(cargo_dir) < len(c.cargo_dir)):
1321 cargo_dir = c.cargo_dir
1322 owner = c
1323 if owner:
1324 owner.has_warning = True
1325 else:
1326 missing_owner = True
1327 if missing_owner and os.path.exists('Cargo.toml'):
1328 # owner is the root cargo, with empty cargo_dir
1329 for c in self.crates:
1330 if not c.cargo_dir:
1331 c.has_warning = True
1332
1333 def rustc_command(self, n, rustc_line, line, outf_name):
1334 """Process a rustc command line from cargo -vv output."""
1335 # cargo build -vv output can have multiple lines for a rustc command
1336 # due to '\n' in strings for environment variables.
1337 # strip removes leading spaces and '\n' at the end
1338 new_rustc = (rustc_line.strip() + line) if rustc_line else line
1339 # Use an heuristic to detect the completions of a multi-line command.
1340 # This might fail for some very rare case, but easy to fix manually.
1341 if not line.endswith('`\n') or (new_rustc.count('`') % 2) != 0:
1342 return new_rustc
1343 if RUSTC_VV_CMD_ARGS.match(new_rustc):
1344 args = RUSTC_VV_CMD_ARGS.match(new_rustc).group(1)
1345 self.add_crate(Crate(self, outf_name).parse(n, args))
1346 else:
1347 self.assert_empty_vv_line(new_rustc)
1348 return ''
1349
1350 def cc_ar_command(self, n, groups, outf_name):
1351 pkg = groups.group(1)
1352 line = groups.group(3)
1353 if groups.group(2) == 'cc':
1354 self.add_cc_object(CCObject(self, outf_name).parse(pkg, n, line))
1355 else:
1356 self.add_ar_object(ARObject(self, outf_name).parse(pkg, n, line))
1357
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001358 def append_to_bp(self, line):
1359 self.init_bp_file('Android.bp')
1360 with open('Android.bp', 'a') as outf:
1361 outf.write(line)
1362
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001363 def assert_empty_vv_line(self, line):
1364 if line: # report error if line is not empty
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001365 self.append_to_bp('ERROR -vv line: ' + line)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001366 return ''
1367
1368 def parse(self, inf, outf_name):
1369 """Parse rustc and warning messages in inf, return a list of Crates."""
1370 n = 0 # line number
1371 prev_warning = False # true if the previous line was warning: ...
1372 rustc_line = '' # previous line(s) matching RUSTC_VV_PAT
1373 for line in inf:
1374 n += 1
1375 if line.startswith('warning: '):
1376 prev_warning = True
1377 rustc_line = self.assert_empty_vv_line(rustc_line)
1378 continue
1379 new_rustc = ''
1380 if RUSTC_PAT.match(line):
1381 args_line = RUSTC_PAT.match(line).group(1)
1382 self.add_crate(Crate(self, outf_name).parse(n, args_line))
1383 self.assert_empty_vv_line(rustc_line)
1384 elif rustc_line or RUSTC_VV_PAT.match(line):
1385 new_rustc = self.rustc_command(n, rustc_line, line, outf_name)
1386 elif CC_AR_VV_PAT.match(line):
1387 self.cc_ar_command(n, CC_AR_VV_PAT.match(line), outf_name)
1388 elif prev_warning and WARNING_FILE_PAT.match(line):
1389 self.assert_empty_vv_line(rustc_line)
1390 fpath = WARNING_FILE_PAT.match(line).group(1)
1391 if fpath[0] != '/': # ignore absolute path
1392 self.warning_files.add(fpath)
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001393 elif line.startswith('error: ') or line.startswith('error[E'):
1394 self.errors += line
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001395 prev_warning = False
1396 rustc_line = new_rustc
1397 self.find_warning_owners()
1398
1399
1400def parse_args():
1401 """Parse main arguments."""
1402 parser = argparse.ArgumentParser('cargo2android')
1403 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001404 '--add_workspace',
1405 action='store_true',
1406 default=False,
1407 help=('append [workspace] to Cargo.toml before calling cargo,' +
1408 ' to treat current directory as root of package source;' +
1409 ' otherwise the relative source file path in generated' +
1410 ' .bp file will be from the parent directory.'))
1411 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001412 '--cargo',
1413 action='append',
1414 metavar='args_string',
1415 help=('extra cargo build -v args in a string, ' +
1416 'each --cargo flag calls cargo build -v once'))
1417 parser.add_argument(
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001418 '--cargo_bin',
1419 type=str,
1420 help='use cargo in the cargo_bin directory instead of the prebuilt one')
1421 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001422 '--debug',
1423 action='store_true',
1424 default=False,
1425 help='dump debug info into Android.bp')
1426 parser.add_argument(
1427 '--dependencies',
1428 action='store_true',
1429 default=False,
1430 help='dump debug info of dependent crates')
1431 parser.add_argument(
1432 '--device',
1433 action='store_true',
1434 default=False,
1435 help='run cargo also for a default device target')
1436 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001437 '--features',
1438 type=str,
1439 help=('pass features to cargo build, ' +
1440 'empty string means no default features'))
1441 parser.add_argument(
1442 '--global_defaults',
1443 type=str,
1444 help='add a defaults name to every module')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001445 parser.add_argument(
1446 '--host-first-multilib',
1447 action='store_true',
1448 default=False,
1449 help=('add a compile_multilib:"first" property ' +
1450 'to Android.bp host modules.'))
1451 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001452 '--no-host',
1453 action='store_true',
1454 default=False,
1455 help='do not run cargo for the host; only for the device target')
1456 parser.add_argument(
1457 '--no-subdir',
1458 action='store_true',
1459 default=False,
1460 help='do not output anything for sub-directories')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001461 parser.add_argument(
1462 '--onefile',
1463 action='store_true',
1464 default=False,
1465 help=('output all into one ./Android.bp, default will generate ' +
1466 'one Android.bp per Cargo.toml in subdirectories'))
1467 parser.add_argument(
1468 '--run',
1469 action='store_true',
1470 default=False,
1471 help='run it, default is dry-run')
1472 parser.add_argument('--rustflags', type=str, help='passing flags to rustc')
1473 parser.add_argument(
1474 '--skipcargo',
1475 action='store_true',
1476 default=False,
1477 help='skip cargo command, parse cargo.out, and generate Android.bp')
1478 parser.add_argument(
1479 '--tests',
1480 action='store_true',
1481 default=False,
1482 help='run cargo build --tests after normal build')
1483 parser.add_argument(
1484 '--verbose',
1485 action='store_true',
1486 default=False,
1487 help='echo executed commands')
1488 parser.add_argument(
1489 '--vv',
1490 action='store_true',
1491 default=False,
1492 help='run cargo with -vv instead of default -v')
1493 return parser.parse_args()
1494
1495
1496def main():
1497 args = parse_args()
1498 if not args.run: # default is dry-run
1499 print(DRY_RUN_NOTE)
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001500 Runner(args).run_cargo().gen_bp().dump_test_mapping_files()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001501
1502
1503if __name__ == '__main__':
1504 main()