blob: 6af3cde951cc3c5e0aaa6338c1bb326c16dd7ef8 [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 Hsieh776f6a12020-07-22 14:16:54 -0700373 if not (flag.startswith('debuginfo=') or
Chih-Hung Hsieh63459ed2020-08-26 11:51:15 -0700374 flag.startswith('embed-bitcode=') or
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700375 flag.startswith('extra-filename=') or
376 flag.startswith('incremental=') or
377 flag.startswith('metadata=') or
378 flag == 'prefer-dynamic'):
379 self.codegens.append(flag)
380
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800381 def parse(self, line_num, line):
382 """Find important rustc arguments to convert to Android.bp properties."""
383 self.line_num = line_num
384 self.line = line
385 args = line.split() # Loop through every argument of rustc.
386 i = 0
387 while i < len(args):
388 arg = args[i]
389 if arg == '--crate-name':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700390 i += 1
391 self.crate_name = args[i]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800392 elif arg == '--crate-type':
393 i += 1
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700394 # cargo calls rustc with multiple --crate-type flags.
395 # rustc can accept:
396 # --crate-type [bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]
397 self.crate_types.append(args[i])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800398 elif arg == '--test':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700399 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800400 elif arg == '--target':
401 i += 1
402 self.target = args[i]
403 elif arg == '--cfg':
404 i += 1
405 if args[i].startswith('\'feature='):
406 self.features.append(unquote(args[i].replace('\'feature=', '')[:-1]))
407 else:
408 self.cfgs.append(args[i])
409 elif arg == '--extern':
410 i += 1
411 extern_names = re.sub('=/[^ ]*/deps/', ' = ', args[i])
412 self.externs.append(extern_names)
413 self.core_externs.append(re.sub(' = .*', '', extern_names))
414 elif arg == '-C': # codegen options
415 i += 1
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700416 self.add_codegens_flag(args[i])
417 elif arg.startswith('-C'):
418 # cargo has been passing "-C <xyz>" flag to rustc,
419 # but newer cargo could pass '-Cembed-bitcode=no' to rustc.
420 self.add_codegens_flag(arg[2:])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800421 elif arg == '--cap-lints':
422 i += 1
423 self.cap_lints = args[i]
424 elif arg == '-L':
425 i += 1
426 if args[i].startswith('dependency=') and args[i].endswith('/deps'):
427 if '/' + TARGET_TMP + '/' in args[i]:
428 self.root_pkg = re.sub(
429 '^.*/', '', re.sub('/' + TARGET_TMP + '/.*/deps$', '', args[i]))
430 else:
431 self.root_pkg = re.sub('^.*/', '',
432 re.sub('/[^/]+/[^/]+/deps$', '', args[i]))
433 self.root_pkg = remove_version_suffix(self.root_pkg)
434 elif arg == '-l':
435 i += 1
436 if args[i].startswith('static='):
437 self.static_libs.append(re.sub('static=', '', args[i]))
438 elif args[i].startswith('dylib='):
439 self.shared_libs.append(re.sub('dylib=', '', args[i]))
440 else:
441 self.shared_libs.append(args[i])
442 elif arg == '--out-dir' or arg == '--color': # ignored
443 i += 1
444 elif arg.startswith('--error-format=') or arg.startswith('--json='):
445 _ = arg # ignored
446 elif arg.startswith('--emit='):
447 self.emit_list = arg.replace('--emit=', '')
448 elif arg.startswith('--edition='):
449 self.edition = arg.replace('--edition=', '')
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700450 elif not arg.startswith('-'):
451 # shorten imported crate main source paths like $HOME/.cargo/
452 # registry/src/github.com-1ecc6299db9ec823/memchr-2.3.3/src/lib.rs
453 self.main_src = re.sub(r'^/[^ ]*/registry/src/', '.../', args[i])
454 self.main_src = re.sub(r'^\.\.\./github.com-[0-9a-f]*/', '.../',
455 self.main_src)
456 self.find_cargo_dir()
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700457 if self.cargo_dir: # for a subdirectory
458 if self.runner.args.no_subdir: # all .bp content to /dev/null
459 self.outf_name = '/dev/null'
460 elif not self.runner.args.onefile:
461 # Write to Android.bp in the subdirectory with Cargo.toml.
462 self.outf_name = self.cargo_dir + '/Android.bp'
463 self.main_src = self.main_src[len(self.cargo_dir) + 1:]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800464 else:
465 self.errors += 'ERROR: unknown ' + arg + '\n'
466 i += 1
467 if not self.crate_name:
468 self.errors += 'ERROR: missing --crate-name\n'
469 if not self.main_src:
470 self.errors += 'ERROR: missing main source file\n'
471 else:
472 self.srcs.append(self.main_src)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700473 if not self.crate_types:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800474 # Treat "--cfg test" as "--test"
475 if 'test' in self.cfgs:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700476 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800477 else:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700478 self.errors += 'ERROR: missing --crate-type or --test\n'
479 elif len(self.crate_types) > 1:
480 if 'test' in self.crate_types:
481 self.errors += 'ERROR: cannot handle both --crate-type and --test\n'
482 if 'lib' in self.crate_types and 'rlib' in self.crate_types:
483 self.errors += 'ERROR: cannot generate both lib and rlib crate types\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800484 if not self.root_pkg:
485 self.root_pkg = self.crate_name
486 if self.target:
487 self.device_supported = True
488 self.host_supported = True # assume host supported for all builds
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700489 if self.runner.args.no_host: # unless --no-host was specified
490 self.host_supported = False
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800491 self.cfgs = sorted(set(self.cfgs))
492 self.features = sorted(set(self.features))
493 self.codegens = sorted(set(self.codegens))
494 self.externs = sorted(set(self.externs))
495 self.core_externs = sorted(set(self.core_externs))
496 self.static_libs = sorted(set(self.static_libs))
497 self.shared_libs = sorted(set(self.shared_libs))
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700498 self.crate_types = sorted(set(self.crate_types))
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800499 self.decide_module_type()
500 self.module_name = altered_name(self.stem)
501 return self
502
503 def dump_line(self):
504 self.write('\n// Line ' + str(self.line_num) + ' ' + self.line)
505
506 def feature_list(self):
507 """Return a string of main_src + "feature_list"."""
508 pkg = self.main_src
509 if pkg.startswith('.../'): # keep only the main package name
510 pkg = re.sub('/.*', '', pkg[4:])
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700511 elif pkg.startswith('/'): # use relative path for a local package
512 pkg = os.path.relpath(pkg)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800513 if not self.features:
514 return pkg
515 return pkg + ' "' + ','.join(self.features) + '"'
516
517 def dump_skip_crate(self, kind):
518 if self.debug:
519 self.write('\n// IGNORED: ' + kind + ' ' + self.main_src)
520 return self
521
522 def skip_crate(self):
523 """Return crate_name or a message if this crate should be skipped."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700524 if (is_build_crate_name(self.crate_name) or
525 self.crate_name in EXCLUDED_CRATES):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800526 return self.crate_name
527 if is_dependent_file_path(self.main_src):
528 return 'dependent crate'
529 return ''
530
531 def dump(self):
532 """Dump all error/debug/module code to the output .bp file."""
533 self.runner.init_bp_file(self.outf_name)
534 with open(self.outf_name, 'a') as outf:
535 self.outf = outf
536 if self.errors:
537 self.dump_line()
538 self.write(self.errors)
539 elif self.skip_crate():
540 self.dump_skip_crate(self.skip_crate())
541 else:
542 if self.debug:
543 self.dump_debug_info()
544 self.dump_android_module()
545
546 def dump_debug_info(self):
547 """Dump parsed data, when cargo2android is called with --debug."""
548
549 def dump(name, value):
550 self.write('//%12s = %s' % (name, value))
551
552 def opt_dump(name, value):
553 if value:
554 dump(name, value)
555
556 def dump_list(fmt, values):
557 for v in values:
558 self.write(fmt % v)
559
560 self.dump_line()
561 dump('module_name', self.module_name)
562 dump('crate_name', self.crate_name)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700563 dump('crate_types', self.crate_types)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800564 dump('main_src', self.main_src)
565 dump('has_warning', self.has_warning)
566 dump('for_host', self.host_supported)
567 dump('for_device', self.device_supported)
568 dump('module_type', self.module_type)
569 opt_dump('target', self.target)
570 opt_dump('edition', self.edition)
571 opt_dump('emit_list', self.emit_list)
572 opt_dump('cap_lints', self.cap_lints)
573 dump_list('// cfg = %s', self.cfgs)
574 dump_list('// cfg = \'feature "%s"\'', self.features)
575 # TODO(chh): escape quotes in self.features, but not in other dump_list
576 dump_list('// codegen = %s', self.codegens)
577 dump_list('// externs = %s', self.externs)
578 dump_list('// -l static = %s', self.static_libs)
579 dump_list('// -l (dylib) = %s', self.shared_libs)
580
581 def dump_android_module(self):
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700582 """Dump one or more Android module definition, depending on crate_types."""
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700583 if len(self.crate_types) == 1:
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700584 self.dump_single_type_android_module()
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700585 return
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700586 if 'test' in self.crate_types:
587 self.write('\nERROR: multiple crate types cannot include test type')
588 return
589 # Dump one Android module per crate_type.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700590 for crate_type in self.crate_types:
591 self.decide_one_module_type(crate_type)
592 self.dump_one_android_module(crate_type)
593
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700594 def build_default_name(self):
595 """Return a short and readable name for the rust_defaults module."""
596 # Choices: (1) root_pkg + '_defaults',
597 # (2) root_pkg + '_defaults_' + crate_name
598 # (3) root_pkg + '_defaults_' + main_src_basename_path
599 # (4) root_pkg + '_defaults_' + a_positive_sequence_number
600 name1 = altered_defaults(self.root_pkg) + '_defaults'
601 if self.runner.try_claim_module_name(name1, self):
602 return name1
603 name2 = name1 + '_' + self.crate_name
604 if self.runner.try_claim_module_name(name2, self):
605 return name2
606 name3 = name1 + '_' + self.main_src_basename_path()
607 if self.runner.try_claim_module_name(name3, self):
608 return name3
609 return self.runner.claim_module_name(name1, self, 0)
610
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700611 def dump_defaults_module(self):
612 """Dump a rust_defaults module to be shared by other modules."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700613 name = self.build_default_name()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700614 self.defaults = name
615 self.write('\nrust_defaults {')
616 self.write(' name: "' + name + '",')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700617 if self.runner.args.global_defaults:
618 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700619 self.write(' crate_name: "' + self.crate_name + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700620 if len(self.srcs) == 1: # only one source file; share it in defaults
621 self.default_srcs = True
622 if self.has_warning and not self.cap_lints:
623 self.write(' // has rustc warnings')
624 self.write(' srcs: ["' + self.main_src + '"],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700625 if 'test' in self.crate_types:
626 self.write(' test_suites: ["general-tests"],')
627 self.write(' auto_gen_config: true,')
628 self.dump_edition_flags_libs()
629 self.write('}')
630
631 def dump_single_type_android_module(self):
632 """Dump one simple Android module, which has only one crate_type."""
633 crate_type = self.crate_types[0]
634 if crate_type != 'test':
635 # do not change self.stem or self.module_name
636 self.dump_one_android_module(crate_type)
637 return
638 # Dump one test module per source file, and separate host and device tests.
639 # crate_type == 'test'
640 if (self.host_supported and self.device_supported) or len(self.srcs) > 1:
641 self.srcs = sorted(set(self.srcs))
642 self.dump_defaults_module()
643 saved_srcs = self.srcs
644 for src in saved_srcs:
645 self.srcs = [src]
646 saved_device_supported = self.device_supported
647 saved_host_supported = self.host_supported
648 saved_main_src = self.main_src
649 self.main_src = src
650 if saved_host_supported:
651 self.device_supported = False
652 self.host_supported = True
653 self.module_name = self.test_module_name()
654 self.decide_one_module_type(crate_type)
655 self.dump_one_android_module(crate_type)
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700656 self.runner.add_test(self.outf_name, self.module_name, True)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700657 if saved_device_supported:
658 self.device_supported = True
659 self.host_supported = False
660 self.module_name = self.test_module_name()
661 self.decide_one_module_type(crate_type)
662 self.dump_one_android_module(crate_type)
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700663 self.runner.add_test(self.outf_name, self.module_name, False)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700664 self.host_supported = saved_host_supported
665 self.device_supported = saved_device_supported
666 self.main_src = saved_main_src
667 self.srcs = saved_srcs
668
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700669 def dump_one_android_module(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800670 """Dump one Android module definition."""
671 if not self.module_type:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700672 self.write('\nERROR: unknown crate_type ' + crate_type)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800673 return
674 self.write('\n' + self.module_type + ' {')
675 self.dump_android_core_properties()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700676 if not self.defaults:
677 self.dump_edition_flags_libs()
678 if self.runner.args.host_first_multilib and self.host_supported and crate_type != 'test':
679 self.write(' compile_multilib: "first",')
680 self.write('}')
681
682 def dump_android_flags(self):
683 """Dump Android module flags property."""
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700684 if not self.cfgs and not self.codegens and not self.cap_lints:
685 return
686 self.write(' flags: [')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800687 if self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700688 self.write(' "--cap-lints ' + self.cap_lints + '",')
689 cfg_fmt = '"--cfg %s"'
690 codegens_fmt = '"-C %s"'
691 self.dump_android_property_list_items(cfg_fmt, self.cfgs)
692 self.dump_android_property_list_items(codegens_fmt, self.codegens)
693 self.write(' ],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700694
695 def dump_edition_flags_libs(self):
696 if self.edition:
697 self.write(' edition: "' + self.edition + '",')
698 self.dump_android_property_list('features', '"%s"', self.features)
699 self.dump_android_flags()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800700 if self.externs:
701 self.dump_android_externs()
702 self.dump_android_property_list('static_libs', '"lib%s"', self.static_libs)
703 self.dump_android_property_list('shared_libs', '"lib%s"', self.shared_libs)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800704
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700705 def main_src_basename_path(self):
706 return re.sub('/', '_', re.sub('.rs$', '', self.main_src))
707
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800708 def test_module_name(self):
709 """Return a unique name for a test module."""
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700710 # root_pkg+(_host|_device) + '_test_'+source_file_name
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700711 suffix = self.main_src_basename_path()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700712 host_device = '_host'
713 if self.device_supported:
714 host_device = '_device'
715 return self.root_pkg + host_device + '_test_' + suffix
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800716
717 def decide_module_type(self):
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700718 # Use the first crate type for the default/first module.
719 crate_type = self.crate_types[0] if self.crate_types else ''
720 self.decide_one_module_type(crate_type)
721
722 def decide_one_module_type(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800723 """Decide which Android module type to use."""
724 host = '' if self.device_supported else '_host'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700725 if crate_type == 'bin': # rust_binary[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800726 self.module_type = 'rust_binary' + host
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700727 # In rare cases like protobuf-codegen, the output binary name must
728 # be renamed to use as a plugin for protoc.
729 self.stem = altered_stem(self.crate_name)
730 self.module_name = altered_name(self.crate_name)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700731 elif crate_type == 'lib': # rust_library[_host]
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700732 # TODO(chh): should this be rust_library[_host]?
733 # Assuming that Cargo.toml do not use both 'lib' and 'rlib',
734 # because we map them both to rlib.
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700735 self.module_type = 'rust_library' + host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800736 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700737 self.module_name = altered_name(self.stem)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700738 elif crate_type == 'rlib': # rust_library[_host]
739 self.module_type = 'rust_library' + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700740 self.stem = 'lib' + self.crate_name
741 self.module_name = altered_name(self.stem)
742 elif crate_type == 'dylib': # rust_library[_host]_dylib
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800743 self.module_type = 'rust_library' + host + '_dylib'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700744 self.stem = 'lib' + self.crate_name
745 self.module_name = altered_name(self.stem) + '_dylib'
746 elif crate_type == 'cdylib': # rust_library[_host]_shared
747 self.module_type = 'rust_library' + host + '_shared'
748 self.stem = 'lib' + self.crate_name
749 self.module_name = altered_name(self.stem) + '_shared'
750 elif crate_type == 'staticlib': # rust_library[_host]_static
751 self.module_type = 'rust_library' + host + '_static'
752 self.stem = 'lib' + self.crate_name
753 self.module_name = altered_name(self.stem) + '_static'
754 elif crate_type == 'test': # rust_test[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800755 self.module_type = 'rust_test' + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700756 # Before do_merge, stem name is based on the --crate-name parameter.
757 # and test module name is based on stem.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800758 self.stem = self.test_module_name()
759 # self.stem will be changed after merging with other tests.
760 # self.stem is NOT used for final test binary name.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700761 # rust_test uses each source file base name as part of output file name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700762 # In do_merge, this function is called again, with a module_name.
763 # We make sure that the module name is unique in each package.
764 if self.module_name:
765 # Cargo uses "-C extra-filename=..." and "-C metadata=..." to add
766 # different suffixes and distinguish multiple tests of the same
767 # crate name. We ignore -C and use claim_module_name to get
768 # unique sequential suffix.
769 self.module_name = self.runner.claim_module_name(
770 self.module_name, self, 0)
771 # Now the module name is unique, stem should also match and unique.
772 self.stem = self.module_name
773 elif crate_type == 'proc-macro': # rust_proc_macro
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800774 self.module_type = 'rust_proc_macro'
775 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700776 self.module_name = altered_name(self.stem)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800777 else: # unknown module type, rust_prebuilt_dylib? rust_library[_host]?
778 self.module_type = ''
779 self.stem = ''
780
781 def dump_android_property_list_items(self, fmt, values):
782 for v in values:
783 # fmt has quotes, so we need escape_quotes(v)
784 self.write(' ' + (fmt % escape_quotes(v)) + ',')
785
786 def dump_android_property_list(self, name, fmt, values):
787 if values:
788 self.write(' ' + name + ': [')
789 self.dump_android_property_list_items(fmt, values)
790 self.write(' ],')
791
792 def dump_android_core_properties(self):
793 """Dump the module header, name, stem, etc."""
794 self.write(' name: "' + self.module_name + '",')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700795 # see properties shared by dump_defaults_module
796 if self.defaults:
797 self.write(' defaults: ["' + self.defaults + '"],')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700798 elif self.runner.args.global_defaults:
799 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800800 if self.stem != self.module_name:
801 self.write(' stem: "' + self.stem + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700802 if self.has_warning and not self.cap_lints and not self.default_srcs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700803 self.write(' // has rustc warnings')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800804 if self.host_supported and self.device_supported:
805 self.write(' host_supported: true,')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700806 if not self.defaults:
807 self.write(' crate_name: "' + self.crate_name + '",')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800808 if len(self.srcs) > 1:
809 self.srcs = sorted(set(self.srcs))
810 self.dump_android_property_list('srcs', '"%s"', self.srcs)
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700811 elif not self.default_srcs:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800812 self.write(' srcs: ["' + self.main_src + '"],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700813 if 'test' in self.crate_types and not self.defaults:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800814 # self.root_pkg can have multiple test modules, with different *_tests[n]
815 # names, but their executables can all be installed under the same _tests
816 # directory. When built from Cargo.toml, all tests should have different
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700817 # file or crate names. So we used (root_pkg + '_tests') name as the
818 # relative_install_path.
819 # However, some package like 'slab' can have non-mergeable tests that
820 # must be separated by different module names. So, here we no longer
821 # emit relative_install_path.
822 # self.write(' relative_install_path: "' + self.root_pkg + '_tests",')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800823 self.write(' test_suites: ["general-tests"],')
824 self.write(' auto_gen_config: true,')
825
826 def dump_android_externs(self):
827 """Dump the dependent rlibs and dylibs property."""
828 so_libs = list()
829 rust_libs = ''
830 deps_libname = re.compile('^.* = lib(.*)-[0-9a-f]*.(rlib|so|rmeta)$')
831 for lib in self.externs:
832 # normal value of lib: "libc = liblibc-*.rlib"
833 # strange case in rand crate: "getrandom_package = libgetrandom-*.rlib"
834 # we should use "libgetrandom", not "lib" + "getrandom_package"
835 groups = deps_libname.match(lib)
836 if groups is not None:
837 lib_name = groups.group(1)
838 else:
839 lib_name = re.sub(' .*$', '', lib)
840 if lib.endswith('.rlib') or lib.endswith('.rmeta'):
841 # On MacOS .rmeta is used when Linux uses .rlib or .rmeta.
842 rust_libs += ' "' + altered_name('lib' + lib_name) + '",\n'
843 elif lib.endswith('.so'):
844 so_libs.append(lib_name)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700845 elif lib != 'proc_macro': # --extern proc_macro is special and ignored
846 rust_libs += ' // ERROR: unknown type of lib ' + lib + '\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800847 if rust_libs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700848 self.write(' rustlibs: [\n' + rust_libs + ' ],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800849 # Are all dependent .so files proc_macros?
850 # TODO(chh): Separate proc_macros and dylib.
851 self.dump_android_property_list('proc_macros', '"lib%s"', so_libs)
852
853
854class ARObject(object):
855 """Information of an "ar" link command."""
856
857 def __init__(self, runner, outf_name):
858 # Remembered global runner and its members.
859 self.runner = runner
860 self.pkg = ''
861 self.outf_name = outf_name # path to Android.bp
862 # "ar" arguments
863 self.line_num = 1
864 self.line = ''
865 self.flags = '' # e.g. "crs"
866 self.lib = '' # e.g. "/.../out/lib*.a"
867 self.objs = list() # e.g. "/.../out/.../*.o"
868
869 def parse(self, pkg, line_num, args_line):
870 """Collect ar obj/lib file names."""
871 self.pkg = pkg
872 self.line_num = line_num
873 self.line = args_line
874 args = args_line.split()
875 num_args = len(args)
876 if num_args < 3:
877 print('ERROR: "ar" command has too few arguments', args_line)
878 else:
879 self.flags = unquote(args[0])
880 self.lib = unquote(args[1])
881 self.objs = sorted(set(map(unquote, args[2:])))
882 return self
883
884 def write(self, s):
885 self.outf.write(s + '\n')
886
887 def dump_debug_info(self):
888 self.write('\n// Line ' + str(self.line_num) + ' "ar" ' + self.line)
889 self.write('// ar_object for %12s' % self.pkg)
890 self.write('// flags = %s' % self.flags)
891 self.write('// lib = %s' % short_out_name(self.pkg, self.lib))
892 for o in self.objs:
893 self.write('// obj = %s' % short_out_name(self.pkg, o))
894
895 def dump_android_lib(self):
896 """Write cc_library_static into Android.bp."""
897 self.write('\ncc_library_static {')
898 self.write(' name: "' + file_base_name(self.lib) + '",')
899 self.write(' host_supported: true,')
900 if self.flags != 'crs':
901 self.write(' // ar flags = %s' % self.flags)
902 if self.pkg not in self.runner.pkg_obj2cc:
903 self.write(' ERROR: cannot find source files.\n}')
904 return
905 self.write(' srcs: [')
906 obj2cc = self.runner.pkg_obj2cc[self.pkg]
907 # Note: wflags are ignored.
908 dflags = list()
909 fflags = list()
910 for obj in self.objs:
911 self.write(' "' + short_out_name(self.pkg, obj2cc[obj].src) + '",')
912 # TODO(chh): union of dflags and flags of all obj
913 # Now, just a temporary hack that uses the last obj's flags
914 dflags = obj2cc[obj].dflags
915 fflags = obj2cc[obj].fflags
916 self.write(' ],')
917 self.write(' cflags: [')
918 self.write(' "-O3",') # TODO(chh): is this default correct?
919 self.write(' "-Wno-error",')
920 for x in fflags:
921 self.write(' "-f' + x + '",')
922 for x in dflags:
923 self.write(' "-D' + x + '",')
924 self.write(' ],')
925 self.write('}')
926
927 def dump(self):
928 """Dump error/debug/module info to the output .bp file."""
929 self.runner.init_bp_file(self.outf_name)
930 with open(self.outf_name, 'a') as outf:
931 self.outf = outf
932 if self.runner.args.debug:
933 self.dump_debug_info()
934 self.dump_android_lib()
935
936
937class CCObject(object):
938 """Information of a "cc" compilation command."""
939
940 def __init__(self, runner, outf_name):
941 # Remembered global runner and its members.
942 self.runner = runner
943 self.pkg = ''
944 self.outf_name = outf_name # path to Android.bp
945 # "cc" arguments
946 self.line_num = 1
947 self.line = ''
948 self.src = ''
949 self.obj = ''
950 self.dflags = list() # -D flags
951 self.fflags = list() # -f flags
952 self.iflags = list() # -I flags
953 self.wflags = list() # -W flags
954 self.other_args = list()
955
956 def parse(self, pkg, line_num, args_line):
957 """Collect cc compilation flags and src/out file names."""
958 self.pkg = pkg
959 self.line_num = line_num
960 self.line = args_line
961 args = args_line.split()
962 i = 0
963 while i < len(args):
964 arg = args[i]
965 if arg == '"-c"':
966 i += 1
967 if args[i].startswith('"-o'):
968 # ring-0.13.5 dumps: ... "-c" "-o/.../*.o" ".../*.c"
969 self.obj = unquote(args[i])[2:]
970 i += 1
971 self.src = unquote(args[i])
972 else:
973 self.src = unquote(args[i])
974 elif arg == '"-o"':
975 i += 1
976 self.obj = unquote(args[i])
977 elif arg == '"-I"':
978 i += 1
979 self.iflags.append(unquote(args[i]))
980 elif arg.startswith('"-D'):
981 self.dflags.append(unquote(args[i])[2:])
982 elif arg.startswith('"-f'):
983 self.fflags.append(unquote(args[i])[2:])
984 elif arg.startswith('"-W'):
985 self.wflags.append(unquote(args[i])[2:])
986 elif not (arg.startswith('"-O') or arg == '"-m64"' or arg == '"-g"' or
987 arg == '"-g3"'):
988 # ignore -O -m64 -g
989 self.other_args.append(unquote(args[i]))
990 i += 1
991 self.dflags = sorted(set(self.dflags))
992 self.fflags = sorted(set(self.fflags))
993 # self.wflags is not sorted because some are order sensitive
994 # and we ignore them anyway.
995 if self.pkg not in self.runner.pkg_obj2cc:
996 self.runner.pkg_obj2cc[self.pkg] = {}
997 self.runner.pkg_obj2cc[self.pkg][self.obj] = self
998 return self
999
1000 def write(self, s):
1001 self.outf.write(s + '\n')
1002
1003 def dump_debug_flags(self, name, flags):
1004 self.write('// ' + name + ':')
1005 for f in flags:
1006 self.write('// %s' % f)
1007
1008 def dump(self):
1009 """Dump only error/debug info to the output .bp file."""
1010 if not self.runner.args.debug:
1011 return
1012 self.runner.init_bp_file(self.outf_name)
1013 with open(self.outf_name, 'a') as outf:
1014 self.outf = outf
1015 self.write('\n// Line ' + str(self.line_num) + ' "cc" ' + self.line)
1016 self.write('// cc_object for %12s' % self.pkg)
1017 self.write('// src = %s' % short_out_name(self.pkg, self.src))
1018 self.write('// obj = %s' % short_out_name(self.pkg, self.obj))
1019 self.dump_debug_flags('-I flags', self.iflags)
1020 self.dump_debug_flags('-D flags', self.dflags)
1021 self.dump_debug_flags('-f flags', self.fflags)
1022 self.dump_debug_flags('-W flags', self.wflags)
1023 if self.other_args:
1024 self.dump_debug_flags('other args', self.other_args)
1025
1026
1027class Runner(object):
1028 """Main class to parse cargo -v output and print Android module definitions."""
1029
1030 def __init__(self, args):
1031 self.bp_files = set() # Remember all output Android.bp files.
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001032 self.test_mappings = {} # Map from Android.bp file path to TestMapping.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001033 self.root_pkg = '' # name of package in ./Cargo.toml
1034 # Saved flags, modes, and data.
1035 self.args = args
1036 self.dry_run = not args.run
1037 self.skip_cargo = args.skipcargo
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001038 self.cargo_path = './cargo' # path to cargo, will be set later
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001039 # All cc/ar objects, crates, dependencies, and warning files
1040 self.cc_objects = list()
1041 self.pkg_obj2cc = {}
1042 # pkg_obj2cc[cc_object[i].pkg][cc_objects[i].obj] = cc_objects[i]
1043 self.ar_objects = list()
1044 self.crates = list()
1045 self.dependencies = list() # dependent and build script crates
1046 self.warning_files = set()
1047 # Keep a unique mapping from (module name) to crate
1048 self.name_owners = {}
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001049 # Save and dump all errors from cargo to Android.bp.
1050 self.errors = ''
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001051 self.setup_cargo_path()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001052 # Default action is cargo clean, followed by build or user given actions.
1053 if args.cargo:
1054 self.cargo = ['clean'] + args.cargo
1055 else:
1056 self.cargo = ['clean', 'build']
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001057 if args.no_host: # do not run "cargo build" for host
1058 self.cargo = ['clean']
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001059 default_target = '--target x86_64-unknown-linux-gnu'
1060 if args.device:
1061 self.cargo.append('build ' + default_target)
1062 if args.tests:
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001063 if not args.no_host:
1064 self.cargo.append('build --tests')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001065 self.cargo.append('build --tests ' + default_target)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001066 elif args.tests and not args.no_host:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001067 self.cargo.append('build --tests')
1068
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001069 def setup_cargo_path(self):
1070 """Find cargo in the --cargo_bin or prebuilt rust bin directory."""
1071 if self.args.cargo_bin:
1072 self.cargo_path = os.path.join(self.args.cargo_bin, 'cargo')
1073 if not os.path.isfile(self.cargo_path):
1074 sys.exit('ERROR: cannot find cargo in ' + self.args.cargo_bin)
1075 print('WARNING: using cargo in ' + self.args.cargo_bin)
1076 return
1077 # We have only tested this on Linux.
1078 if platform.system() != 'Linux':
1079 sys.exit('ERROR: this script has only been tested on Linux with cargo.')
1080 # Assuming that this script is in development/scripts.
1081 my_dir = os.path.dirname(os.path.abspath(__file__))
1082 linux_dir = os.path.join(my_dir, '..', '..',
1083 'prebuilts', 'rust', 'linux-x86')
1084 if not os.path.isdir(linux_dir):
1085 sys.exit('ERROR: cannot find directory ' + linux_dir)
1086 rust_version = self.find_rust_version(my_dir, linux_dir)
1087 cargo_bin = os.path.join(linux_dir, rust_version, 'bin')
1088 self.cargo_path = os.path.join(cargo_bin, 'cargo')
1089 if not os.path.isfile(self.cargo_path):
1090 sys.exit('ERROR: cannot find cargo in ' + cargo_bin
1091 + '; please try --cargo_bin= flag.')
1092 return
1093
1094 def find_rust_version(self, my_dir, linux_dir):
1095 """Use my script directory, find prebuilt rust version."""
1096 # First look up build/soong/rust/config/global.go.
1097 path2global = os.path.join(my_dir, '..', '..',
1098 'build', 'soong', 'rust', 'config', 'global.go')
1099 if os.path.isfile(path2global):
1100 # try to find: RustDefaultVersion = "1.44.0"
1101 version_pat = re.compile(
1102 r'\s*RustDefaultVersion\s*=\s*"([0-9]+\.[0-9]+\.[0-9]+)".*$')
1103 with open(path2global, 'r') as inf:
1104 for line in inf:
1105 result = version_pat.match(line)
1106 if result:
1107 return result.group(1)
1108 print('WARNING: cannot find RustDefaultVersion in ' + path2global)
1109 # Otherwise, find the newest (largest) version number in linux_dir.
1110 rust_version = (0, 0, 0) # the prebuilt version to use
1111 version_pat = re.compile(r'([0-9]+)\.([0-9]+)\.([0-9]+)$')
1112 for dir_name in os.listdir(linux_dir):
1113 result = version_pat.match(dir_name)
1114 if not result:
1115 continue
1116 version = (result.group(1), result.group(2), result.group(3))
1117 if version > rust_version:
1118 rust_version = version
1119 return '.'.join(rust_version)
1120
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001121 def init_bp_file(self, name):
1122 if name not in self.bp_files:
1123 self.bp_files.add(name)
1124 with open(name, 'w') as outf:
Andrew Walbran80e90be2020-06-09 14:33:18 +01001125 outf.write(ANDROID_BP_HEADER.format(args=' '.join(sys.argv[1:])))
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001126
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001127 def dump_test_mapping_files(self):
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001128 """Dump all TEST_MAPPING files."""
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001129 if self.dry_run:
1130 print('Dry-run skip dump of TEST_MAPPING')
1131 else:
1132 for bp_file_name in self.test_mappings:
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001133 if bp_file_name != '/dev/null':
1134 name = os.path.join(os.path.dirname(bp_file_name), 'TEST_MAPPING')
1135 self.test_mappings[bp_file_name].dump(name)
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001136 return self
1137
1138 def add_test(self, bp_file_name, test_name, host):
1139 if bp_file_name not in self.test_mappings:
1140 self.test_mappings[bp_file_name] = TestMapping()
1141 mapping = self.test_mappings[bp_file_name]
1142 mapping.add_test(test_name, host)
1143
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001144 def try_claim_module_name(self, name, owner):
1145 """Reserve and return True if it has not been reserved yet."""
1146 if name not in self.name_owners or owner == self.name_owners[name]:
1147 self.name_owners[name] = owner
1148 return True
1149 return False
1150
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001151 def claim_module_name(self, prefix, owner, counter):
1152 """Return prefix if not owned yet, otherwise, prefix+str(counter)."""
1153 while True:
1154 name = prefix
1155 if counter > 0:
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001156 name += '_' + str(counter)
1157 if self.try_claim_module_name(name, owner):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001158 return name
1159 counter += 1
1160
1161 def find_root_pkg(self):
1162 """Read name of [package] in ./Cargo.toml."""
1163 if not os.path.exists('./Cargo.toml'):
1164 return
1165 with open('./Cargo.toml', 'r') as inf:
1166 pkg_section = re.compile(r'^ *\[package\]')
1167 name = re.compile('^ *name *= * "([^"]*)"')
1168 in_pkg = False
1169 for line in inf:
1170 if in_pkg:
1171 if name.match(line):
1172 self.root_pkg = name.match(line).group(1)
1173 break
1174 else:
1175 in_pkg = pkg_section.match(line) is not None
1176
1177 def run_cargo(self):
1178 """Calls cargo -v and save its output to ./cargo.out."""
1179 if self.skip_cargo:
1180 return self
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001181 cargo_toml = './Cargo.toml'
1182 cargo_out = './cargo.out'
1183 if not os.access(cargo_toml, os.R_OK):
1184 print('ERROR: Cannot find or read', cargo_toml)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001185 return self
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001186 if not self.dry_run and os.path.exists(cargo_out):
1187 os.remove(cargo_out)
1188 cmd_tail = ' --target-dir ' + TARGET_TMP + ' >> ' + cargo_out + ' 2>&1'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001189 # set up search PATH for cargo to find the correct rustc
1190 saved_path = os.environ['PATH']
1191 os.environ['PATH'] = os.path.dirname(self.cargo_path) + ':' + saved_path
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001192 # Add [workspace] to Cargo.toml if it is not there.
1193 added_workspace = False
1194 if self.args.add_workspace:
1195 with open(cargo_toml, 'r') as in_file:
1196 cargo_toml_lines = in_file.readlines()
1197 found_workspace = '[workspace]\n' in cargo_toml_lines
1198 if found_workspace:
1199 print('### WARNING: found [workspace] in Cargo.toml')
1200 else:
1201 with open(cargo_toml, 'a') as out_file:
1202 out_file.write('[workspace]\n')
1203 added_workspace = True
1204 if self.args.verbose:
1205 print('### INFO: added [workspace] to Cargo.toml')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001206 for c in self.cargo:
1207 features = ''
Chih-Hung Hsieh6c8d52f2020-03-30 18:28:52 -07001208 if c != 'clean':
1209 if self.args.features is not None:
1210 features = ' --no-default-features'
1211 if self.args.features:
1212 features += ' --features ' + self.args.features
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001213 cmd_v_flag = ' -vv ' if self.args.vv else ' -v '
1214 cmd = self.cargo_path + cmd_v_flag
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001215 cmd += c + features + cmd_tail
1216 if self.args.rustflags and c != 'clean':
1217 cmd = 'RUSTFLAGS="' + self.args.rustflags + '" ' + cmd
1218 if self.dry_run:
1219 print('Dry-run skip:', cmd)
1220 else:
1221 if self.args.verbose:
1222 print('Running:', cmd)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001223 with open(cargo_out, 'a') as out_file:
1224 out_file.write('### Running: ' + cmd + '\n')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001225 os.system(cmd)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001226 if added_workspace: # restore original Cargo.toml
1227 with open(cargo_toml, 'w') as out_file:
1228 out_file.writelines(cargo_toml_lines)
1229 if self.args.verbose:
1230 print('### INFO: restored original Cargo.toml')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001231 os.environ['PATH'] = saved_path
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001232 return self
1233
1234 def dump_dependencies(self):
1235 """Append dependencies and their features to Android.bp."""
1236 if not self.dependencies:
1237 return
1238 dependent_list = list()
1239 for c in self.dependencies:
1240 dependent_list.append(c.feature_list())
1241 sorted_dependencies = sorted(set(dependent_list))
1242 self.init_bp_file('Android.bp')
1243 with open('Android.bp', 'a') as outf:
1244 outf.write('\n// dependent_library ["feature_list"]\n')
1245 for s in sorted_dependencies:
1246 outf.write('// ' + s + '\n')
1247
1248 def dump_pkg_obj2cc(self):
1249 """Dump debug info of the pkg_obj2cc map."""
1250 if not self.args.debug:
1251 return
1252 self.init_bp_file('Android.bp')
1253 with open('Android.bp', 'a') as outf:
1254 sorted_pkgs = sorted(self.pkg_obj2cc.keys())
1255 for pkg in sorted_pkgs:
1256 if not self.pkg_obj2cc[pkg]:
1257 continue
1258 outf.write('\n// obj => src for %s\n' % pkg)
1259 obj2cc = self.pkg_obj2cc[pkg]
1260 for obj in sorted(obj2cc.keys()):
1261 outf.write('// ' + short_out_name(pkg, obj) + ' => ' +
1262 short_out_name(pkg, obj2cc[obj].src) + '\n')
1263
1264 def gen_bp(self):
1265 """Parse cargo.out and generate Android.bp files."""
1266 if self.dry_run:
1267 print('Dry-run skip: read', CARGO_OUT, 'write Android.bp')
1268 elif os.path.exists(CARGO_OUT):
1269 self.find_root_pkg()
1270 with open(CARGO_OUT, 'r') as cargo_out:
1271 self.parse(cargo_out, 'Android.bp')
1272 self.crates.sort(key=get_module_name)
1273 for obj in self.cc_objects:
1274 obj.dump()
1275 self.dump_pkg_obj2cc()
1276 for crate in self.crates:
1277 crate.dump()
1278 dumped_libs = set()
1279 for lib in self.ar_objects:
1280 if lib.pkg == self.root_pkg:
1281 lib_name = file_base_name(lib.lib)
1282 if lib_name not in dumped_libs:
1283 dumped_libs.add(lib_name)
1284 lib.dump()
1285 if self.args.dependencies and self.dependencies:
1286 self.dump_dependencies()
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001287 if self.errors:
1288 self.append_to_bp('\nErrors in ' + CARGO_OUT + ':\n' + self.errors)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001289 return self
1290
1291 def add_ar_object(self, obj):
1292 self.ar_objects.append(obj)
1293
1294 def add_cc_object(self, obj):
1295 self.cc_objects.append(obj)
1296
1297 def add_crate(self, crate):
1298 """Merge crate with someone in crates, or append to it. Return crates."""
1299 if crate.skip_crate():
1300 if self.args.debug: # include debug info of all crates
1301 self.crates.append(crate)
1302 if self.args.dependencies: # include only dependent crates
1303 if (is_dependent_file_path(crate.main_src) and
1304 not is_build_crate_name(crate.crate_name)):
1305 self.dependencies.append(crate)
1306 else:
1307 for c in self.crates:
1308 if c.merge(crate, 'Android.bp'):
1309 return
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001310 # If not merged, decide module type and name now.
1311 crate.decide_module_type()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001312 self.crates.append(crate)
1313
1314 def find_warning_owners(self):
1315 """For each warning file, find its owner crate."""
1316 missing_owner = False
1317 for f in self.warning_files:
1318 cargo_dir = '' # find lowest crate, with longest path
1319 owner = None # owner crate of this warning
1320 for c in self.crates:
1321 if (f.startswith(c.cargo_dir + '/') and
1322 len(cargo_dir) < len(c.cargo_dir)):
1323 cargo_dir = c.cargo_dir
1324 owner = c
1325 if owner:
1326 owner.has_warning = True
1327 else:
1328 missing_owner = True
1329 if missing_owner and os.path.exists('Cargo.toml'):
1330 # owner is the root cargo, with empty cargo_dir
1331 for c in self.crates:
1332 if not c.cargo_dir:
1333 c.has_warning = True
1334
1335 def rustc_command(self, n, rustc_line, line, outf_name):
1336 """Process a rustc command line from cargo -vv output."""
1337 # cargo build -vv output can have multiple lines for a rustc command
1338 # due to '\n' in strings for environment variables.
1339 # strip removes leading spaces and '\n' at the end
1340 new_rustc = (rustc_line.strip() + line) if rustc_line else line
1341 # Use an heuristic to detect the completions of a multi-line command.
1342 # This might fail for some very rare case, but easy to fix manually.
1343 if not line.endswith('`\n') or (new_rustc.count('`') % 2) != 0:
1344 return new_rustc
1345 if RUSTC_VV_CMD_ARGS.match(new_rustc):
1346 args = RUSTC_VV_CMD_ARGS.match(new_rustc).group(1)
1347 self.add_crate(Crate(self, outf_name).parse(n, args))
1348 else:
1349 self.assert_empty_vv_line(new_rustc)
1350 return ''
1351
1352 def cc_ar_command(self, n, groups, outf_name):
1353 pkg = groups.group(1)
1354 line = groups.group(3)
1355 if groups.group(2) == 'cc':
1356 self.add_cc_object(CCObject(self, outf_name).parse(pkg, n, line))
1357 else:
1358 self.add_ar_object(ARObject(self, outf_name).parse(pkg, n, line))
1359
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001360 def append_to_bp(self, line):
1361 self.init_bp_file('Android.bp')
1362 with open('Android.bp', 'a') as outf:
1363 outf.write(line)
1364
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001365 def assert_empty_vv_line(self, line):
1366 if line: # report error if line is not empty
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001367 self.append_to_bp('ERROR -vv line: ' + line)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001368 return ''
1369
1370 def parse(self, inf, outf_name):
1371 """Parse rustc and warning messages in inf, return a list of Crates."""
1372 n = 0 # line number
1373 prev_warning = False # true if the previous line was warning: ...
1374 rustc_line = '' # previous line(s) matching RUSTC_VV_PAT
1375 for line in inf:
1376 n += 1
1377 if line.startswith('warning: '):
1378 prev_warning = True
1379 rustc_line = self.assert_empty_vv_line(rustc_line)
1380 continue
1381 new_rustc = ''
1382 if RUSTC_PAT.match(line):
1383 args_line = RUSTC_PAT.match(line).group(1)
1384 self.add_crate(Crate(self, outf_name).parse(n, args_line))
1385 self.assert_empty_vv_line(rustc_line)
1386 elif rustc_line or RUSTC_VV_PAT.match(line):
1387 new_rustc = self.rustc_command(n, rustc_line, line, outf_name)
1388 elif CC_AR_VV_PAT.match(line):
1389 self.cc_ar_command(n, CC_AR_VV_PAT.match(line), outf_name)
1390 elif prev_warning and WARNING_FILE_PAT.match(line):
1391 self.assert_empty_vv_line(rustc_line)
1392 fpath = WARNING_FILE_PAT.match(line).group(1)
1393 if fpath[0] != '/': # ignore absolute path
1394 self.warning_files.add(fpath)
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001395 elif line.startswith('error: ') or line.startswith('error[E'):
1396 self.errors += line
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001397 prev_warning = False
1398 rustc_line = new_rustc
1399 self.find_warning_owners()
1400
1401
1402def parse_args():
1403 """Parse main arguments."""
1404 parser = argparse.ArgumentParser('cargo2android')
1405 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001406 '--add_workspace',
1407 action='store_true',
1408 default=False,
1409 help=('append [workspace] to Cargo.toml before calling cargo,' +
1410 ' to treat current directory as root of package source;' +
1411 ' otherwise the relative source file path in generated' +
1412 ' .bp file will be from the parent directory.'))
1413 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001414 '--cargo',
1415 action='append',
1416 metavar='args_string',
1417 help=('extra cargo build -v args in a string, ' +
1418 'each --cargo flag calls cargo build -v once'))
1419 parser.add_argument(
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001420 '--cargo_bin',
1421 type=str,
1422 help='use cargo in the cargo_bin directory instead of the prebuilt one')
1423 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001424 '--debug',
1425 action='store_true',
1426 default=False,
1427 help='dump debug info into Android.bp')
1428 parser.add_argument(
1429 '--dependencies',
1430 action='store_true',
1431 default=False,
1432 help='dump debug info of dependent crates')
1433 parser.add_argument(
1434 '--device',
1435 action='store_true',
1436 default=False,
1437 help='run cargo also for a default device target')
1438 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001439 '--features',
1440 type=str,
1441 help=('pass features to cargo build, ' +
1442 'empty string means no default features'))
1443 parser.add_argument(
1444 '--global_defaults',
1445 type=str,
1446 help='add a defaults name to every module')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001447 parser.add_argument(
1448 '--host-first-multilib',
1449 action='store_true',
1450 default=False,
1451 help=('add a compile_multilib:"first" property ' +
1452 'to Android.bp host modules.'))
1453 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001454 '--no-host',
1455 action='store_true',
1456 default=False,
1457 help='do not run cargo for the host; only for the device target')
1458 parser.add_argument(
1459 '--no-subdir',
1460 action='store_true',
1461 default=False,
1462 help='do not output anything for sub-directories')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001463 parser.add_argument(
1464 '--onefile',
1465 action='store_true',
1466 default=False,
1467 help=('output all into one ./Android.bp, default will generate ' +
1468 'one Android.bp per Cargo.toml in subdirectories'))
1469 parser.add_argument(
1470 '--run',
1471 action='store_true',
1472 default=False,
1473 help='run it, default is dry-run')
1474 parser.add_argument('--rustflags', type=str, help='passing flags to rustc')
1475 parser.add_argument(
1476 '--skipcargo',
1477 action='store_true',
1478 default=False,
1479 help='skip cargo command, parse cargo.out, and generate Android.bp')
1480 parser.add_argument(
1481 '--tests',
1482 action='store_true',
1483 default=False,
1484 help='run cargo build --tests after normal build')
1485 parser.add_argument(
1486 '--verbose',
1487 action='store_true',
1488 default=False,
1489 help='echo executed commands')
1490 parser.add_argument(
1491 '--vv',
1492 action='store_true',
1493 default=False,
1494 help='run cargo with -vv instead of default -v')
1495 return parser.parse_args()
1496
1497
1498def main():
1499 args = parse_args()
1500 if not args.run: # default is dry-run
1501 print(DRY_RUN_NOTE)
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001502 Runner(args).run_cargo().gen_bp().dump_test_mapping_files()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001503
1504
1505if __name__ == '__main__':
1506 main()