blob: 54d63aee6589b8981f0eb551900c7f61a0490473 [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',
75 'libgcc': 'libgcc_rust',
76 'liblog': 'liblog_rust',
Chih-Hung Hsieh07119862020-07-24 15:34:06 -070077 'libminijail': 'libminijail_rust',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080078 'libsync': 'libsync_rust',
79 'libx86_64': 'libx86_64_rust',
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -070080 'protoc_gen_rust': 'protoc-gen-rust',
81}
82
83RENAME_STEM_MAP = {
84 # This map includes all changes to the default rust module stem names,
85 # which is used for output files when different from the module name.
86 'protoc_gen_rust': 'protoc-gen-rust',
87}
88
89RENAME_DEFAULTS_MAP = {
90 # This map includes all changes to the default prefix of rust_default
91 # module names, to avoid conflict with existing Android modules.
92 'libc': 'rust_libc',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080093}
94
95# Header added to all generated Android.bp files.
Andrew Walbran80e90be2020-06-09 14:33:18 +010096ANDROID_BP_HEADER = '// This file is generated by cargo2android.py {args}.\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080097
98CARGO_OUT = 'cargo.out' # Name of file to keep cargo build -v output.
99
100TARGET_TMP = 'target.tmp' # Name of temporary output directory.
101
102# Message to be displayed when this script is called without the --run flag.
103DRY_RUN_NOTE = (
104 'Dry-run: This script uses ./' + TARGET_TMP + ' for output directory,\n' +
105 'runs cargo clean, runs cargo build -v, saves output to ./cargo.out,\n' +
106 'and writes to Android.bp in the current and subdirectories.\n\n' +
107 'To do do all of the above, use the --run flag.\n' +
108 'See --help for other flags, and more usage notes in this script.\n')
109
110# Cargo -v output of a call to rustc.
111RUSTC_PAT = re.compile('^ +Running `rustc (.*)`$')
112
113# Cargo -vv output of a call to rustc could be split into multiple lines.
114# Assume that the first line will contain some CARGO_* env definition.
115RUSTC_VV_PAT = re.compile('^ +Running `.*CARGO_.*=.*$')
116# The combined -vv output rustc command line pattern.
117RUSTC_VV_CMD_ARGS = re.compile('^ *Running `.*CARGO_.*=.* rustc (.*)`$')
118
119# Cargo -vv output of a "cc" or "ar" command; all in one line.
120CC_AR_VV_PAT = re.compile(r'^\[([^ ]*)[^\]]*\] running:? "(cc|ar)" (.*)$')
121# Some package, such as ring-0.13.5, has pattern '... running "cc"'.
122
123# Rustc output of file location path pattern for a warning message.
124WARNING_FILE_PAT = re.compile('^ *--> ([^:]*):[0-9]+')
125
126# Rust package name with suffix -d1.d2.d3.
127VERSION_SUFFIX_PAT = re.compile(r'^(.*)-[0-9]+\.[0-9]+\.[0-9]+$')
128
129
130def altered_name(name):
131 return RENAME_MAP[name] if (name in RENAME_MAP) else name
132
133
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700134def altered_stem(name):
135 return RENAME_STEM_MAP[name] if (name in RENAME_STEM_MAP) else name
136
137
138def altered_defaults(name):
139 return RENAME_DEFAULTS_MAP[name] if (name in RENAME_DEFAULTS_MAP) else name
140
141
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800142def is_build_crate_name(name):
143 # We added special prefix to build script crate names.
144 return name.startswith('build_script_')
145
146
147def is_dependent_file_path(path):
148 # Absolute or dependent '.../' paths are not main files of this crate.
149 return path.startswith('/') or path.startswith('.../')
150
151
152def get_module_name(crate): # to sort crates in a list
153 return crate.module_name
154
155
156def pkg2crate_name(s):
157 return s.replace('-', '_').replace('.', '_')
158
159
160def file_base_name(path):
161 return os.path.splitext(os.path.basename(path))[0]
162
163
164def test_base_name(path):
165 return pkg2crate_name(file_base_name(path))
166
167
168def unquote(s): # remove quotes around str
169 if s and len(s) > 1 and s[0] == '"' and s[-1] == '"':
170 return s[1:-1]
171 return s
172
173
174def remove_version_suffix(s): # remove -d1.d2.d3 suffix
175 if VERSION_SUFFIX_PAT.match(s):
176 return VERSION_SUFFIX_PAT.match(s).group(1)
177 return s
178
179
180def short_out_name(pkg, s): # replace /.../pkg-*/out/* with .../out/*
181 return re.sub('^/.*/' + pkg + '-[0-9a-f]*/out/', '.../out/', s)
182
183
184def escape_quotes(s): # replace '"' with '\\"'
185 return s.replace('"', '\\"')
186
187
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700188class TestMapping(object):
189 """Entries for a TEST_MAPPING file."""
190
191 def __init__(self):
192 self.entries = []
193
194 def add_test(self, name, host):
195 self.entries.append((name, host))
196
197 def is_empty(self):
198 return not self.entries
199
200 def dump(self, outf_name):
201 """Append all entries into the output file."""
202 if self.is_empty():
203 return
204 with open(outf_name, 'w') as outf:
205 outf.write('// Generated by cargo2android.py for tests in Android.bp\n')
206 outf.write('{\n "presubmit": [\n')
207 is_first = True
208 for (name, host) in self.entries:
209 if not is_first: # add comma and '\n' after the previous entry
210 outf.write(',\n')
211 is_first = False
212 outf.write(' {\n "name": "' + name + '"')
213 if host:
214 outf.write(',\n "host": true\n }')
215 else:
216 outf.write('\n }')
217 outf.write('\n ]\n}\n')
218
219
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800220class Crate(object):
221 """Information of a Rust crate to collect/emit for an Android.bp module."""
222
223 def __init__(self, runner, outf_name):
224 # Remembered global runner and its members.
225 self.runner = runner
226 self.debug = runner.args.debug
227 self.cargo_dir = '' # directory of my Cargo.toml
228 self.outf_name = outf_name # path to Android.bp
229 self.outf = None # open file handle of outf_name during dump*
230 # Variants/results that could be merged from multiple rustc lines.
231 self.host_supported = False
232 self.device_supported = False
233 self.has_warning = False
234 # Android module properties derived from rustc parameters.
235 self.module_name = '' # unique in Android build system
236 self.module_type = '' # rust_{binary,library,test}[_host] etc.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700237 self.defaults = '' # rust_defaults used by rust_test* modules
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700238 self.default_srcs = False # use 'srcs' defined in self.defaults
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800239 self.root_pkg = '' # parent package name of a sub/test packge, from -L
240 self.srcs = list() # main_src or merged multiple source files
241 self.stem = '' # real base name of output file
242 # Kept parsed status
243 self.errors = '' # all errors found during parsing
244 self.line_num = 1 # runner told input source line number
245 self.line = '' # original rustc command line parameters
246 # Parameters collected from rustc command line.
247 self.crate_name = '' # follows --crate-name
248 self.main_src = '' # follows crate_name parameter, shortened
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700249 self.crate_types = list() # follows --crate-type
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800250 self.cfgs = list() # follows --cfg, without feature= prefix
251 self.features = list() # follows --cfg, name in 'feature="..."'
252 self.codegens = list() # follows -C, some ignored
253 self.externs = list() # follows --extern
254 self.core_externs = list() # first part of self.externs elements
255 self.static_libs = list() # e.g. -l static=host_cpuid
256 self.shared_libs = list() # e.g. -l dylib=wayland-client, -l z
257 self.cap_lints = '' # follows --cap-lints
258 self.emit_list = '' # e.g., --emit=dep-info,metadata,link
259 self.edition = '2015' # rustc default, e.g., --edition=2018
260 self.target = '' # follows --target
261
262 def write(self, s):
263 # convenient way to output one line at a time with EOL.
264 self.outf.write(s + '\n')
265
266 def same_flags(self, other):
267 # host_supported, device_supported, has_warning are not compared but merged
268 # target is not compared, to merge different target/host modules
269 # externs is not compared; only core_externs is compared
270 return (not self.errors and not other.errors and
271 self.edition == other.edition and
272 self.cap_lints == other.cap_lints and
273 self.emit_list == other.emit_list and
274 self.core_externs == other.core_externs and
275 self.codegens == other.codegens and
276 self.features == other.features and
277 self.static_libs == other.static_libs and
278 self.shared_libs == other.shared_libs and self.cfgs == other.cfgs)
279
280 def merge_host_device(self, other):
281 """Returns true if attributes are the same except host/device support."""
282 return (self.crate_name == other.crate_name and
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700283 self.crate_types == other.crate_types and
284 self.main_src == other.main_src and
285 # before merge, each test module has an unique module name and stem
286 (self.stem == other.stem or self.crate_types == ['test']) and
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800287 self.root_pkg == other.root_pkg and not self.skip_crate() and
288 self.same_flags(other))
289
290 def merge_test(self, other):
291 """Returns true if self and other are tests of same root_pkg."""
292 # Before merger, each test has its own crate_name.
293 # A merged test uses its source file base name as output file name,
294 # so a test is mergeable only if its base name equals to its crate name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700295 return (self.crate_types == other.crate_types and
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700296 self.crate_types == ['test'] and self.root_pkg == other.root_pkg and
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800297 not self.skip_crate() and
298 other.crate_name == test_base_name(other.main_src) and
299 (len(self.srcs) > 1 or
300 (self.crate_name == test_base_name(self.main_src)) and
301 self.host_supported == other.host_supported and
302 self.device_supported == other.device_supported) and
303 self.same_flags(other))
304
305 def merge(self, other, outf_name):
306 """Try to merge crate into self."""
307 should_merge_host_device = self.merge_host_device(other)
308 should_merge_test = False
309 if not should_merge_host_device:
310 should_merge_test = self.merge_test(other)
311 # A for-device test crate can be merged with its for-host version,
312 # or merged with a different test for the same host or device.
313 # Since we run cargo once for each device or host, test crates for the
314 # first device or host will be merged first. Then test crates for a
315 # different device or host should be allowed to be merged into a
316 # previously merged one, maybe for a different device or host.
317 if should_merge_host_device or should_merge_test:
318 self.runner.init_bp_file(outf_name)
319 with open(outf_name, 'a') as outf: # to write debug info
320 self.outf = outf
321 other.outf = outf
322 self.do_merge(other, should_merge_test)
323 return True
324 return False
325
326 def do_merge(self, other, should_merge_test):
327 """Merge attributes of other to self."""
328 if self.debug:
329 self.write('\n// Before merge definition (1):')
330 self.dump_debug_info()
331 self.write('\n// Before merge definition (2):')
332 other.dump_debug_info()
333 # Merge properties of other to self.
334 self.host_supported = self.host_supported or other.host_supported
335 self.device_supported = self.device_supported or other.device_supported
336 self.has_warning = self.has_warning or other.has_warning
337 if not self.target: # okay to keep only the first target triple
338 self.target = other.target
339 # decide_module_type sets up default self.stem,
340 # which can be changed if self is a merged test module.
341 self.decide_module_type()
342 if should_merge_test:
343 self.srcs.append(other.main_src)
344 # use a short unique name as the merged module name.
345 prefix = self.root_pkg + '_tests'
346 self.module_name = self.runner.claim_module_name(prefix, self, 0)
347 self.stem = self.module_name
348 # This normalized root_pkg name although might be the same
349 # as other module's crate_name, it is not actually used for
350 # output file name. A merged test module always have multiple
351 # source files and each source file base name is used as
352 # its output file name.
353 self.crate_name = pkg2crate_name(self.root_pkg)
354 if self.debug:
355 self.write('\n// After merge definition (1):')
356 self.dump_debug_info()
357
358 def find_cargo_dir(self):
359 """Deepest directory with Cargo.toml and contains the main_src."""
360 if not is_dependent_file_path(self.main_src):
361 dir_name = os.path.dirname(self.main_src)
362 while dir_name:
363 if os.path.exists(dir_name + '/Cargo.toml'):
364 self.cargo_dir = dir_name
365 return
366 dir_name = os.path.dirname(dir_name)
367
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700368 def add_codegens_flag(self, flag):
369 # ignore options not used in Android
370 # 'prefer-dynamic' does not work with common flag -C lto
371 if not (flag.startswith('debuginfo=') or
372 flag.startswith('extra-filename=') or
373 flag.startswith('incremental=') or
374 flag.startswith('metadata=') or
375 flag == 'prefer-dynamic'):
376 self.codegens.append(flag)
377
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800378 def parse(self, line_num, line):
379 """Find important rustc arguments to convert to Android.bp properties."""
380 self.line_num = line_num
381 self.line = line
382 args = line.split() # Loop through every argument of rustc.
383 i = 0
384 while i < len(args):
385 arg = args[i]
386 if arg == '--crate-name':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700387 i += 1
388 self.crate_name = args[i]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800389 elif arg == '--crate-type':
390 i += 1
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700391 # cargo calls rustc with multiple --crate-type flags.
392 # rustc can accept:
393 # --crate-type [bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]
394 self.crate_types.append(args[i])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800395 elif arg == '--test':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700396 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800397 elif arg == '--target':
398 i += 1
399 self.target = args[i]
400 elif arg == '--cfg':
401 i += 1
402 if args[i].startswith('\'feature='):
403 self.features.append(unquote(args[i].replace('\'feature=', '')[:-1]))
404 else:
405 self.cfgs.append(args[i])
406 elif arg == '--extern':
407 i += 1
408 extern_names = re.sub('=/[^ ]*/deps/', ' = ', args[i])
409 self.externs.append(extern_names)
410 self.core_externs.append(re.sub(' = .*', '', extern_names))
411 elif arg == '-C': # codegen options
412 i += 1
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700413 self.add_codegens_flag(args[i])
414 elif arg.startswith('-C'):
415 # cargo has been passing "-C <xyz>" flag to rustc,
416 # but newer cargo could pass '-Cembed-bitcode=no' to rustc.
417 self.add_codegens_flag(arg[2:])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800418 elif arg == '--cap-lints':
419 i += 1
420 self.cap_lints = args[i]
421 elif arg == '-L':
422 i += 1
423 if args[i].startswith('dependency=') and args[i].endswith('/deps'):
424 if '/' + TARGET_TMP + '/' in args[i]:
425 self.root_pkg = re.sub(
426 '^.*/', '', re.sub('/' + TARGET_TMP + '/.*/deps$', '', args[i]))
427 else:
428 self.root_pkg = re.sub('^.*/', '',
429 re.sub('/[^/]+/[^/]+/deps$', '', args[i]))
430 self.root_pkg = remove_version_suffix(self.root_pkg)
431 elif arg == '-l':
432 i += 1
433 if args[i].startswith('static='):
434 self.static_libs.append(re.sub('static=', '', args[i]))
435 elif args[i].startswith('dylib='):
436 self.shared_libs.append(re.sub('dylib=', '', args[i]))
437 else:
438 self.shared_libs.append(args[i])
439 elif arg == '--out-dir' or arg == '--color': # ignored
440 i += 1
441 elif arg.startswith('--error-format=') or arg.startswith('--json='):
442 _ = arg # ignored
443 elif arg.startswith('--emit='):
444 self.emit_list = arg.replace('--emit=', '')
445 elif arg.startswith('--edition='):
446 self.edition = arg.replace('--edition=', '')
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700447 elif not arg.startswith('-'):
448 # shorten imported crate main source paths like $HOME/.cargo/
449 # registry/src/github.com-1ecc6299db9ec823/memchr-2.3.3/src/lib.rs
450 self.main_src = re.sub(r'^/[^ ]*/registry/src/', '.../', args[i])
451 self.main_src = re.sub(r'^\.\.\./github.com-[0-9a-f]*/', '.../',
452 self.main_src)
453 self.find_cargo_dir()
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700454 if self.cargo_dir: # for a subdirectory
455 if self.runner.args.no_subdir: # all .bp content to /dev/null
456 self.outf_name = '/dev/null'
457 elif not self.runner.args.onefile:
458 # Write to Android.bp in the subdirectory with Cargo.toml.
459 self.outf_name = self.cargo_dir + '/Android.bp'
460 self.main_src = self.main_src[len(self.cargo_dir) + 1:]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800461 else:
462 self.errors += 'ERROR: unknown ' + arg + '\n'
463 i += 1
464 if not self.crate_name:
465 self.errors += 'ERROR: missing --crate-name\n'
466 if not self.main_src:
467 self.errors += 'ERROR: missing main source file\n'
468 else:
469 self.srcs.append(self.main_src)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700470 if not self.crate_types:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800471 # Treat "--cfg test" as "--test"
472 if 'test' in self.cfgs:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700473 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800474 else:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700475 self.errors += 'ERROR: missing --crate-type or --test\n'
476 elif len(self.crate_types) > 1:
477 if 'test' in self.crate_types:
478 self.errors += 'ERROR: cannot handle both --crate-type and --test\n'
479 if 'lib' in self.crate_types and 'rlib' in self.crate_types:
480 self.errors += 'ERROR: cannot generate both lib and rlib crate types\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800481 if not self.root_pkg:
482 self.root_pkg = self.crate_name
483 if self.target:
484 self.device_supported = True
485 self.host_supported = True # assume host supported for all builds
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700486 if self.runner.args.no_host: # unless --no-host was specified
487 self.host_supported = False
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800488 self.cfgs = sorted(set(self.cfgs))
489 self.features = sorted(set(self.features))
490 self.codegens = sorted(set(self.codegens))
491 self.externs = sorted(set(self.externs))
492 self.core_externs = sorted(set(self.core_externs))
493 self.static_libs = sorted(set(self.static_libs))
494 self.shared_libs = sorted(set(self.shared_libs))
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700495 self.crate_types = sorted(set(self.crate_types))
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800496 self.decide_module_type()
497 self.module_name = altered_name(self.stem)
498 return self
499
500 def dump_line(self):
501 self.write('\n// Line ' + str(self.line_num) + ' ' + self.line)
502
503 def feature_list(self):
504 """Return a string of main_src + "feature_list"."""
505 pkg = self.main_src
506 if pkg.startswith('.../'): # keep only the main package name
507 pkg = re.sub('/.*', '', pkg[4:])
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700508 elif pkg.startswith('/'): # use relative path for a local package
509 pkg = os.path.relpath(pkg)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800510 if not self.features:
511 return pkg
512 return pkg + ' "' + ','.join(self.features) + '"'
513
514 def dump_skip_crate(self, kind):
515 if self.debug:
516 self.write('\n// IGNORED: ' + kind + ' ' + self.main_src)
517 return self
518
519 def skip_crate(self):
520 """Return crate_name or a message if this crate should be skipped."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700521 if (is_build_crate_name(self.crate_name) or
522 self.crate_name in EXCLUDED_CRATES):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800523 return self.crate_name
524 if is_dependent_file_path(self.main_src):
525 return 'dependent crate'
526 return ''
527
528 def dump(self):
529 """Dump all error/debug/module code to the output .bp file."""
530 self.runner.init_bp_file(self.outf_name)
531 with open(self.outf_name, 'a') as outf:
532 self.outf = outf
533 if self.errors:
534 self.dump_line()
535 self.write(self.errors)
536 elif self.skip_crate():
537 self.dump_skip_crate(self.skip_crate())
538 else:
539 if self.debug:
540 self.dump_debug_info()
541 self.dump_android_module()
542
543 def dump_debug_info(self):
544 """Dump parsed data, when cargo2android is called with --debug."""
545
546 def dump(name, value):
547 self.write('//%12s = %s' % (name, value))
548
549 def opt_dump(name, value):
550 if value:
551 dump(name, value)
552
553 def dump_list(fmt, values):
554 for v in values:
555 self.write(fmt % v)
556
557 self.dump_line()
558 dump('module_name', self.module_name)
559 dump('crate_name', self.crate_name)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700560 dump('crate_types', self.crate_types)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800561 dump('main_src', self.main_src)
562 dump('has_warning', self.has_warning)
563 dump('for_host', self.host_supported)
564 dump('for_device', self.device_supported)
565 dump('module_type', self.module_type)
566 opt_dump('target', self.target)
567 opt_dump('edition', self.edition)
568 opt_dump('emit_list', self.emit_list)
569 opt_dump('cap_lints', self.cap_lints)
570 dump_list('// cfg = %s', self.cfgs)
571 dump_list('// cfg = \'feature "%s"\'', self.features)
572 # TODO(chh): escape quotes in self.features, but not in other dump_list
573 dump_list('// codegen = %s', self.codegens)
574 dump_list('// externs = %s', self.externs)
575 dump_list('// -l static = %s', self.static_libs)
576 dump_list('// -l (dylib) = %s', self.shared_libs)
577
578 def dump_android_module(self):
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700579 """Dump one or more Android module definition, depending on crate_types."""
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700580 if len(self.crate_types) == 1:
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700581 self.dump_single_type_android_module()
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700582 return
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700583 if 'test' in self.crate_types:
584 self.write('\nERROR: multiple crate types cannot include test type')
585 return
586 # Dump one Android module per crate_type.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700587 for crate_type in self.crate_types:
588 self.decide_one_module_type(crate_type)
589 self.dump_one_android_module(crate_type)
590
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700591 def build_default_name(self):
592 """Return a short and readable name for the rust_defaults module."""
593 # Choices: (1) root_pkg + '_defaults',
594 # (2) root_pkg + '_defaults_' + crate_name
595 # (3) root_pkg + '_defaults_' + main_src_basename_path
596 # (4) root_pkg + '_defaults_' + a_positive_sequence_number
597 name1 = altered_defaults(self.root_pkg) + '_defaults'
598 if self.runner.try_claim_module_name(name1, self):
599 return name1
600 name2 = name1 + '_' + self.crate_name
601 if self.runner.try_claim_module_name(name2, self):
602 return name2
603 name3 = name1 + '_' + self.main_src_basename_path()
604 if self.runner.try_claim_module_name(name3, self):
605 return name3
606 return self.runner.claim_module_name(name1, self, 0)
607
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700608 def dump_defaults_module(self):
609 """Dump a rust_defaults module to be shared by other modules."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700610 name = self.build_default_name()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700611 self.defaults = name
612 self.write('\nrust_defaults {')
613 self.write(' name: "' + name + '",')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700614 if self.runner.args.global_defaults:
615 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700616 self.write(' crate_name: "' + self.crate_name + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700617 if len(self.srcs) == 1: # only one source file; share it in defaults
618 self.default_srcs = True
619 if self.has_warning and not self.cap_lints:
620 self.write(' // has rustc warnings')
621 self.write(' srcs: ["' + self.main_src + '"],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700622 if 'test' in self.crate_types:
623 self.write(' test_suites: ["general-tests"],')
624 self.write(' auto_gen_config: true,')
625 self.dump_edition_flags_libs()
626 self.write('}')
627
628 def dump_single_type_android_module(self):
629 """Dump one simple Android module, which has only one crate_type."""
630 crate_type = self.crate_types[0]
631 if crate_type != 'test':
632 # do not change self.stem or self.module_name
633 self.dump_one_android_module(crate_type)
634 return
635 # Dump one test module per source file, and separate host and device tests.
636 # crate_type == 'test'
637 if (self.host_supported and self.device_supported) or len(self.srcs) > 1:
638 self.srcs = sorted(set(self.srcs))
639 self.dump_defaults_module()
640 saved_srcs = self.srcs
641 for src in saved_srcs:
642 self.srcs = [src]
643 saved_device_supported = self.device_supported
644 saved_host_supported = self.host_supported
645 saved_main_src = self.main_src
646 self.main_src = src
647 if saved_host_supported:
648 self.device_supported = False
649 self.host_supported = True
650 self.module_name = self.test_module_name()
651 self.decide_one_module_type(crate_type)
652 self.dump_one_android_module(crate_type)
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700653 self.runner.add_test(self.outf_name, self.module_name, True)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700654 if saved_device_supported:
655 self.device_supported = True
656 self.host_supported = False
657 self.module_name = self.test_module_name()
658 self.decide_one_module_type(crate_type)
659 self.dump_one_android_module(crate_type)
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700660 self.runner.add_test(self.outf_name, self.module_name, False)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700661 self.host_supported = saved_host_supported
662 self.device_supported = saved_device_supported
663 self.main_src = saved_main_src
664 self.srcs = saved_srcs
665
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700666 def dump_one_android_module(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800667 """Dump one Android module definition."""
668 if not self.module_type:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700669 self.write('\nERROR: unknown crate_type ' + crate_type)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800670 return
671 self.write('\n' + self.module_type + ' {')
672 self.dump_android_core_properties()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700673 if not self.defaults:
674 self.dump_edition_flags_libs()
675 if self.runner.args.host_first_multilib and self.host_supported and crate_type != 'test':
676 self.write(' compile_multilib: "first",')
677 self.write('}')
678
679 def dump_android_flags(self):
680 """Dump Android module flags property."""
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700681 if not self.cfgs and not self.codegens and not self.cap_lints:
682 return
683 self.write(' flags: [')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800684 if self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700685 self.write(' "--cap-lints ' + self.cap_lints + '",')
686 cfg_fmt = '"--cfg %s"'
687 codegens_fmt = '"-C %s"'
688 self.dump_android_property_list_items(cfg_fmt, self.cfgs)
689 self.dump_android_property_list_items(codegens_fmt, self.codegens)
690 self.write(' ],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700691
692 def dump_edition_flags_libs(self):
693 if self.edition:
694 self.write(' edition: "' + self.edition + '",')
695 self.dump_android_property_list('features', '"%s"', self.features)
696 self.dump_android_flags()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800697 if self.externs:
698 self.dump_android_externs()
699 self.dump_android_property_list('static_libs', '"lib%s"', self.static_libs)
700 self.dump_android_property_list('shared_libs', '"lib%s"', self.shared_libs)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800701
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700702 def main_src_basename_path(self):
703 return re.sub('/', '_', re.sub('.rs$', '', self.main_src))
704
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800705 def test_module_name(self):
706 """Return a unique name for a test module."""
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700707 # root_pkg+(_host|_device) + '_test_'+source_file_name
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700708 suffix = self.main_src_basename_path()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700709 host_device = '_host'
710 if self.device_supported:
711 host_device = '_device'
712 return self.root_pkg + host_device + '_test_' + suffix
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800713
714 def decide_module_type(self):
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700715 # Use the first crate type for the default/first module.
716 crate_type = self.crate_types[0] if self.crate_types else ''
717 self.decide_one_module_type(crate_type)
718
719 def decide_one_module_type(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800720 """Decide which Android module type to use."""
721 host = '' if self.device_supported else '_host'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700722 if crate_type == 'bin': # rust_binary[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800723 self.module_type = 'rust_binary' + host
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700724 # In rare cases like protobuf-codegen, the output binary name must
725 # be renamed to use as a plugin for protoc.
726 self.stem = altered_stem(self.crate_name)
727 self.module_name = altered_name(self.crate_name)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700728 elif crate_type == 'lib': # rust_library[_host]
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700729 # TODO(chh): should this be rust_library[_host]?
730 # Assuming that Cargo.toml do not use both 'lib' and 'rlib',
731 # because we map them both to rlib.
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700732 self.module_type = 'rust_library' + host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800733 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700734 self.module_name = altered_name(self.stem)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700735 elif crate_type == 'rlib': # rust_library[_host]
736 self.module_type = 'rust_library' + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700737 self.stem = 'lib' + self.crate_name
738 self.module_name = altered_name(self.stem)
739 elif crate_type == 'dylib': # rust_library[_host]_dylib
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800740 self.module_type = 'rust_library' + host + '_dylib'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700741 self.stem = 'lib' + self.crate_name
742 self.module_name = altered_name(self.stem) + '_dylib'
743 elif crate_type == 'cdylib': # rust_library[_host]_shared
744 self.module_type = 'rust_library' + host + '_shared'
745 self.stem = 'lib' + self.crate_name
746 self.module_name = altered_name(self.stem) + '_shared'
747 elif crate_type == 'staticlib': # rust_library[_host]_static
748 self.module_type = 'rust_library' + host + '_static'
749 self.stem = 'lib' + self.crate_name
750 self.module_name = altered_name(self.stem) + '_static'
751 elif crate_type == 'test': # rust_test[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800752 self.module_type = 'rust_test' + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700753 # Before do_merge, stem name is based on the --crate-name parameter.
754 # and test module name is based on stem.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800755 self.stem = self.test_module_name()
756 # self.stem will be changed after merging with other tests.
757 # self.stem is NOT used for final test binary name.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700758 # rust_test uses each source file base name as part of output file name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700759 # In do_merge, this function is called again, with a module_name.
760 # We make sure that the module name is unique in each package.
761 if self.module_name:
762 # Cargo uses "-C extra-filename=..." and "-C metadata=..." to add
763 # different suffixes and distinguish multiple tests of the same
764 # crate name. We ignore -C and use claim_module_name to get
765 # unique sequential suffix.
766 self.module_name = self.runner.claim_module_name(
767 self.module_name, self, 0)
768 # Now the module name is unique, stem should also match and unique.
769 self.stem = self.module_name
770 elif crate_type == 'proc-macro': # rust_proc_macro
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800771 self.module_type = 'rust_proc_macro'
772 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700773 self.module_name = altered_name(self.stem)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800774 else: # unknown module type, rust_prebuilt_dylib? rust_library[_host]?
775 self.module_type = ''
776 self.stem = ''
777
778 def dump_android_property_list_items(self, fmt, values):
779 for v in values:
780 # fmt has quotes, so we need escape_quotes(v)
781 self.write(' ' + (fmt % escape_quotes(v)) + ',')
782
783 def dump_android_property_list(self, name, fmt, values):
784 if values:
785 self.write(' ' + name + ': [')
786 self.dump_android_property_list_items(fmt, values)
787 self.write(' ],')
788
789 def dump_android_core_properties(self):
790 """Dump the module header, name, stem, etc."""
791 self.write(' name: "' + self.module_name + '",')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700792 # see properties shared by dump_defaults_module
793 if self.defaults:
794 self.write(' defaults: ["' + self.defaults + '"],')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700795 elif self.runner.args.global_defaults:
796 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800797 if self.stem != self.module_name:
798 self.write(' stem: "' + self.stem + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700799 if self.has_warning and not self.cap_lints and not self.default_srcs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700800 self.write(' // has rustc warnings')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800801 if self.host_supported and self.device_supported:
802 self.write(' host_supported: true,')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700803 if not self.defaults:
804 self.write(' crate_name: "' + self.crate_name + '",')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800805 if len(self.srcs) > 1:
806 self.srcs = sorted(set(self.srcs))
807 self.dump_android_property_list('srcs', '"%s"', self.srcs)
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700808 elif not self.default_srcs:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800809 self.write(' srcs: ["' + self.main_src + '"],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700810 if 'test' in self.crate_types and not self.defaults:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800811 # self.root_pkg can have multiple test modules, with different *_tests[n]
812 # names, but their executables can all be installed under the same _tests
813 # directory. When built from Cargo.toml, all tests should have different
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700814 # file or crate names. So we used (root_pkg + '_tests') name as the
815 # relative_install_path.
816 # However, some package like 'slab' can have non-mergeable tests that
817 # must be separated by different module names. So, here we no longer
818 # emit relative_install_path.
819 # self.write(' relative_install_path: "' + self.root_pkg + '_tests",')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800820 self.write(' test_suites: ["general-tests"],')
821 self.write(' auto_gen_config: true,')
822
823 def dump_android_externs(self):
824 """Dump the dependent rlibs and dylibs property."""
825 so_libs = list()
826 rust_libs = ''
827 deps_libname = re.compile('^.* = lib(.*)-[0-9a-f]*.(rlib|so|rmeta)$')
828 for lib in self.externs:
829 # normal value of lib: "libc = liblibc-*.rlib"
830 # strange case in rand crate: "getrandom_package = libgetrandom-*.rlib"
831 # we should use "libgetrandom", not "lib" + "getrandom_package"
832 groups = deps_libname.match(lib)
833 if groups is not None:
834 lib_name = groups.group(1)
835 else:
836 lib_name = re.sub(' .*$', '', lib)
837 if lib.endswith('.rlib') or lib.endswith('.rmeta'):
838 # On MacOS .rmeta is used when Linux uses .rlib or .rmeta.
839 rust_libs += ' "' + altered_name('lib' + lib_name) + '",\n'
840 elif lib.endswith('.so'):
841 so_libs.append(lib_name)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700842 elif lib != 'proc_macro': # --extern proc_macro is special and ignored
843 rust_libs += ' // ERROR: unknown type of lib ' + lib + '\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800844 if rust_libs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700845 self.write(' rustlibs: [\n' + rust_libs + ' ],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800846 # Are all dependent .so files proc_macros?
847 # TODO(chh): Separate proc_macros and dylib.
848 self.dump_android_property_list('proc_macros', '"lib%s"', so_libs)
849
850
851class ARObject(object):
852 """Information of an "ar" link command."""
853
854 def __init__(self, runner, outf_name):
855 # Remembered global runner and its members.
856 self.runner = runner
857 self.pkg = ''
858 self.outf_name = outf_name # path to Android.bp
859 # "ar" arguments
860 self.line_num = 1
861 self.line = ''
862 self.flags = '' # e.g. "crs"
863 self.lib = '' # e.g. "/.../out/lib*.a"
864 self.objs = list() # e.g. "/.../out/.../*.o"
865
866 def parse(self, pkg, line_num, args_line):
867 """Collect ar obj/lib file names."""
868 self.pkg = pkg
869 self.line_num = line_num
870 self.line = args_line
871 args = args_line.split()
872 num_args = len(args)
873 if num_args < 3:
874 print('ERROR: "ar" command has too few arguments', args_line)
875 else:
876 self.flags = unquote(args[0])
877 self.lib = unquote(args[1])
878 self.objs = sorted(set(map(unquote, args[2:])))
879 return self
880
881 def write(self, s):
882 self.outf.write(s + '\n')
883
884 def dump_debug_info(self):
885 self.write('\n// Line ' + str(self.line_num) + ' "ar" ' + self.line)
886 self.write('// ar_object for %12s' % self.pkg)
887 self.write('// flags = %s' % self.flags)
888 self.write('// lib = %s' % short_out_name(self.pkg, self.lib))
889 for o in self.objs:
890 self.write('// obj = %s' % short_out_name(self.pkg, o))
891
892 def dump_android_lib(self):
893 """Write cc_library_static into Android.bp."""
894 self.write('\ncc_library_static {')
895 self.write(' name: "' + file_base_name(self.lib) + '",')
896 self.write(' host_supported: true,')
897 if self.flags != 'crs':
898 self.write(' // ar flags = %s' % self.flags)
899 if self.pkg not in self.runner.pkg_obj2cc:
900 self.write(' ERROR: cannot find source files.\n}')
901 return
902 self.write(' srcs: [')
903 obj2cc = self.runner.pkg_obj2cc[self.pkg]
904 # Note: wflags are ignored.
905 dflags = list()
906 fflags = list()
907 for obj in self.objs:
908 self.write(' "' + short_out_name(self.pkg, obj2cc[obj].src) + '",')
909 # TODO(chh): union of dflags and flags of all obj
910 # Now, just a temporary hack that uses the last obj's flags
911 dflags = obj2cc[obj].dflags
912 fflags = obj2cc[obj].fflags
913 self.write(' ],')
914 self.write(' cflags: [')
915 self.write(' "-O3",') # TODO(chh): is this default correct?
916 self.write(' "-Wno-error",')
917 for x in fflags:
918 self.write(' "-f' + x + '",')
919 for x in dflags:
920 self.write(' "-D' + x + '",')
921 self.write(' ],')
922 self.write('}')
923
924 def dump(self):
925 """Dump error/debug/module info to the output .bp file."""
926 self.runner.init_bp_file(self.outf_name)
927 with open(self.outf_name, 'a') as outf:
928 self.outf = outf
929 if self.runner.args.debug:
930 self.dump_debug_info()
931 self.dump_android_lib()
932
933
934class CCObject(object):
935 """Information of a "cc" compilation command."""
936
937 def __init__(self, runner, outf_name):
938 # Remembered global runner and its members.
939 self.runner = runner
940 self.pkg = ''
941 self.outf_name = outf_name # path to Android.bp
942 # "cc" arguments
943 self.line_num = 1
944 self.line = ''
945 self.src = ''
946 self.obj = ''
947 self.dflags = list() # -D flags
948 self.fflags = list() # -f flags
949 self.iflags = list() # -I flags
950 self.wflags = list() # -W flags
951 self.other_args = list()
952
953 def parse(self, pkg, line_num, args_line):
954 """Collect cc compilation flags and src/out file names."""
955 self.pkg = pkg
956 self.line_num = line_num
957 self.line = args_line
958 args = args_line.split()
959 i = 0
960 while i < len(args):
961 arg = args[i]
962 if arg == '"-c"':
963 i += 1
964 if args[i].startswith('"-o'):
965 # ring-0.13.5 dumps: ... "-c" "-o/.../*.o" ".../*.c"
966 self.obj = unquote(args[i])[2:]
967 i += 1
968 self.src = unquote(args[i])
969 else:
970 self.src = unquote(args[i])
971 elif arg == '"-o"':
972 i += 1
973 self.obj = unquote(args[i])
974 elif arg == '"-I"':
975 i += 1
976 self.iflags.append(unquote(args[i]))
977 elif arg.startswith('"-D'):
978 self.dflags.append(unquote(args[i])[2:])
979 elif arg.startswith('"-f'):
980 self.fflags.append(unquote(args[i])[2:])
981 elif arg.startswith('"-W'):
982 self.wflags.append(unquote(args[i])[2:])
983 elif not (arg.startswith('"-O') or arg == '"-m64"' or arg == '"-g"' or
984 arg == '"-g3"'):
985 # ignore -O -m64 -g
986 self.other_args.append(unquote(args[i]))
987 i += 1
988 self.dflags = sorted(set(self.dflags))
989 self.fflags = sorted(set(self.fflags))
990 # self.wflags is not sorted because some are order sensitive
991 # and we ignore them anyway.
992 if self.pkg not in self.runner.pkg_obj2cc:
993 self.runner.pkg_obj2cc[self.pkg] = {}
994 self.runner.pkg_obj2cc[self.pkg][self.obj] = self
995 return self
996
997 def write(self, s):
998 self.outf.write(s + '\n')
999
1000 def dump_debug_flags(self, name, flags):
1001 self.write('// ' + name + ':')
1002 for f in flags:
1003 self.write('// %s' % f)
1004
1005 def dump(self):
1006 """Dump only error/debug info to the output .bp file."""
1007 if not self.runner.args.debug:
1008 return
1009 self.runner.init_bp_file(self.outf_name)
1010 with open(self.outf_name, 'a') as outf:
1011 self.outf = outf
1012 self.write('\n// Line ' + str(self.line_num) + ' "cc" ' + self.line)
1013 self.write('// cc_object for %12s' % self.pkg)
1014 self.write('// src = %s' % short_out_name(self.pkg, self.src))
1015 self.write('// obj = %s' % short_out_name(self.pkg, self.obj))
1016 self.dump_debug_flags('-I flags', self.iflags)
1017 self.dump_debug_flags('-D flags', self.dflags)
1018 self.dump_debug_flags('-f flags', self.fflags)
1019 self.dump_debug_flags('-W flags', self.wflags)
1020 if self.other_args:
1021 self.dump_debug_flags('other args', self.other_args)
1022
1023
1024class Runner(object):
1025 """Main class to parse cargo -v output and print Android module definitions."""
1026
1027 def __init__(self, args):
1028 self.bp_files = set() # Remember all output Android.bp files.
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001029 self.test_mappings = {} # Map from Android.bp file path to TestMapping.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001030 self.root_pkg = '' # name of package in ./Cargo.toml
1031 # Saved flags, modes, and data.
1032 self.args = args
1033 self.dry_run = not args.run
1034 self.skip_cargo = args.skipcargo
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001035 self.cargo_path = './cargo' # path to cargo, will be set later
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001036 # All cc/ar objects, crates, dependencies, and warning files
1037 self.cc_objects = list()
1038 self.pkg_obj2cc = {}
1039 # pkg_obj2cc[cc_object[i].pkg][cc_objects[i].obj] = cc_objects[i]
1040 self.ar_objects = list()
1041 self.crates = list()
1042 self.dependencies = list() # dependent and build script crates
1043 self.warning_files = set()
1044 # Keep a unique mapping from (module name) to crate
1045 self.name_owners = {}
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001046 # Save and dump all errors from cargo to Android.bp.
1047 self.errors = ''
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001048 self.setup_cargo_path()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001049 # Default action is cargo clean, followed by build or user given actions.
1050 if args.cargo:
1051 self.cargo = ['clean'] + args.cargo
1052 else:
1053 self.cargo = ['clean', 'build']
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001054 if args.no_host: # do not run "cargo build" for host
1055 self.cargo = ['clean']
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001056 default_target = '--target x86_64-unknown-linux-gnu'
1057 if args.device:
1058 self.cargo.append('build ' + default_target)
1059 if args.tests:
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001060 if not args.no_host:
1061 self.cargo.append('build --tests')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001062 self.cargo.append('build --tests ' + default_target)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001063 elif args.tests and not args.no_host:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001064 self.cargo.append('build --tests')
1065
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001066 def setup_cargo_path(self):
1067 """Find cargo in the --cargo_bin or prebuilt rust bin directory."""
1068 if self.args.cargo_bin:
1069 self.cargo_path = os.path.join(self.args.cargo_bin, 'cargo')
1070 if not os.path.isfile(self.cargo_path):
1071 sys.exit('ERROR: cannot find cargo in ' + self.args.cargo_bin)
1072 print('WARNING: using cargo in ' + self.args.cargo_bin)
1073 return
1074 # We have only tested this on Linux.
1075 if platform.system() != 'Linux':
1076 sys.exit('ERROR: this script has only been tested on Linux with cargo.')
1077 # Assuming that this script is in development/scripts.
1078 my_dir = os.path.dirname(os.path.abspath(__file__))
1079 linux_dir = os.path.join(my_dir, '..', '..',
1080 'prebuilts', 'rust', 'linux-x86')
1081 if not os.path.isdir(linux_dir):
1082 sys.exit('ERROR: cannot find directory ' + linux_dir)
1083 rust_version = self.find_rust_version(my_dir, linux_dir)
1084 cargo_bin = os.path.join(linux_dir, rust_version, 'bin')
1085 self.cargo_path = os.path.join(cargo_bin, 'cargo')
1086 if not os.path.isfile(self.cargo_path):
1087 sys.exit('ERROR: cannot find cargo in ' + cargo_bin
1088 + '; please try --cargo_bin= flag.')
1089 return
1090
1091 def find_rust_version(self, my_dir, linux_dir):
1092 """Use my script directory, find prebuilt rust version."""
1093 # First look up build/soong/rust/config/global.go.
1094 path2global = os.path.join(my_dir, '..', '..',
1095 'build', 'soong', 'rust', 'config', 'global.go')
1096 if os.path.isfile(path2global):
1097 # try to find: RustDefaultVersion = "1.44.0"
1098 version_pat = re.compile(
1099 r'\s*RustDefaultVersion\s*=\s*"([0-9]+\.[0-9]+\.[0-9]+)".*$')
1100 with open(path2global, 'r') as inf:
1101 for line in inf:
1102 result = version_pat.match(line)
1103 if result:
1104 return result.group(1)
1105 print('WARNING: cannot find RustDefaultVersion in ' + path2global)
1106 # Otherwise, find the newest (largest) version number in linux_dir.
1107 rust_version = (0, 0, 0) # the prebuilt version to use
1108 version_pat = re.compile(r'([0-9]+)\.([0-9]+)\.([0-9]+)$')
1109 for dir_name in os.listdir(linux_dir):
1110 result = version_pat.match(dir_name)
1111 if not result:
1112 continue
1113 version = (result.group(1), result.group(2), result.group(3))
1114 if version > rust_version:
1115 rust_version = version
1116 return '.'.join(rust_version)
1117
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001118 def init_bp_file(self, name):
1119 if name not in self.bp_files:
1120 self.bp_files.add(name)
1121 with open(name, 'w') as outf:
Andrew Walbran80e90be2020-06-09 14:33:18 +01001122 outf.write(ANDROID_BP_HEADER.format(args=' '.join(sys.argv[1:])))
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001123
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001124 def dump_test_mapping_files(self):
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001125 """Dump all TEST_MAPPING files."""
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001126 if self.dry_run:
1127 print('Dry-run skip dump of TEST_MAPPING')
1128 else:
1129 for bp_file_name in self.test_mappings:
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001130 if bp_file_name != '/dev/null':
1131 name = os.path.join(os.path.dirname(bp_file_name), 'TEST_MAPPING')
1132 self.test_mappings[bp_file_name].dump(name)
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001133 return self
1134
1135 def add_test(self, bp_file_name, test_name, host):
1136 if bp_file_name not in self.test_mappings:
1137 self.test_mappings[bp_file_name] = TestMapping()
1138 mapping = self.test_mappings[bp_file_name]
1139 mapping.add_test(test_name, host)
1140
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001141 def try_claim_module_name(self, name, owner):
1142 """Reserve and return True if it has not been reserved yet."""
1143 if name not in self.name_owners or owner == self.name_owners[name]:
1144 self.name_owners[name] = owner
1145 return True
1146 return False
1147
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001148 def claim_module_name(self, prefix, owner, counter):
1149 """Return prefix if not owned yet, otherwise, prefix+str(counter)."""
1150 while True:
1151 name = prefix
1152 if counter > 0:
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001153 name += '_' + str(counter)
1154 if self.try_claim_module_name(name, owner):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001155 return name
1156 counter += 1
1157
1158 def find_root_pkg(self):
1159 """Read name of [package] in ./Cargo.toml."""
1160 if not os.path.exists('./Cargo.toml'):
1161 return
1162 with open('./Cargo.toml', 'r') as inf:
1163 pkg_section = re.compile(r'^ *\[package\]')
1164 name = re.compile('^ *name *= * "([^"]*)"')
1165 in_pkg = False
1166 for line in inf:
1167 if in_pkg:
1168 if name.match(line):
1169 self.root_pkg = name.match(line).group(1)
1170 break
1171 else:
1172 in_pkg = pkg_section.match(line) is not None
1173
1174 def run_cargo(self):
1175 """Calls cargo -v and save its output to ./cargo.out."""
1176 if self.skip_cargo:
1177 return self
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001178 cargo_toml = './Cargo.toml'
1179 cargo_out = './cargo.out'
1180 if not os.access(cargo_toml, os.R_OK):
1181 print('ERROR: Cannot find or read', cargo_toml)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001182 return self
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001183 if not self.dry_run and os.path.exists(cargo_out):
1184 os.remove(cargo_out)
1185 cmd_tail = ' --target-dir ' + TARGET_TMP + ' >> ' + cargo_out + ' 2>&1'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001186 # set up search PATH for cargo to find the correct rustc
1187 saved_path = os.environ['PATH']
1188 os.environ['PATH'] = os.path.dirname(self.cargo_path) + ':' + saved_path
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001189 # Add [workspace] to Cargo.toml if it is not there.
1190 added_workspace = False
1191 if self.args.add_workspace:
1192 with open(cargo_toml, 'r') as in_file:
1193 cargo_toml_lines = in_file.readlines()
1194 found_workspace = '[workspace]\n' in cargo_toml_lines
1195 if found_workspace:
1196 print('### WARNING: found [workspace] in Cargo.toml')
1197 else:
1198 with open(cargo_toml, 'a') as out_file:
1199 out_file.write('[workspace]\n')
1200 added_workspace = True
1201 if self.args.verbose:
1202 print('### INFO: added [workspace] to Cargo.toml')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001203 for c in self.cargo:
1204 features = ''
Chih-Hung Hsieh6c8d52f2020-03-30 18:28:52 -07001205 if c != 'clean':
1206 if self.args.features is not None:
1207 features = ' --no-default-features'
1208 if self.args.features:
1209 features += ' --features ' + self.args.features
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001210 cmd_v_flag = ' -vv ' if self.args.vv else ' -v '
1211 cmd = self.cargo_path + cmd_v_flag
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001212 cmd += c + features + cmd_tail
1213 if self.args.rustflags and c != 'clean':
1214 cmd = 'RUSTFLAGS="' + self.args.rustflags + '" ' + cmd
1215 if self.dry_run:
1216 print('Dry-run skip:', cmd)
1217 else:
1218 if self.args.verbose:
1219 print('Running:', cmd)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001220 with open(cargo_out, 'a') as out_file:
1221 out_file.write('### Running: ' + cmd + '\n')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001222 os.system(cmd)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001223 if added_workspace: # restore original Cargo.toml
1224 with open(cargo_toml, 'w') as out_file:
1225 out_file.writelines(cargo_toml_lines)
1226 if self.args.verbose:
1227 print('### INFO: restored original Cargo.toml')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001228 os.environ['PATH'] = saved_path
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001229 return self
1230
1231 def dump_dependencies(self):
1232 """Append dependencies and their features to Android.bp."""
1233 if not self.dependencies:
1234 return
1235 dependent_list = list()
1236 for c in self.dependencies:
1237 dependent_list.append(c.feature_list())
1238 sorted_dependencies = sorted(set(dependent_list))
1239 self.init_bp_file('Android.bp')
1240 with open('Android.bp', 'a') as outf:
1241 outf.write('\n// dependent_library ["feature_list"]\n')
1242 for s in sorted_dependencies:
1243 outf.write('// ' + s + '\n')
1244
1245 def dump_pkg_obj2cc(self):
1246 """Dump debug info of the pkg_obj2cc map."""
1247 if not self.args.debug:
1248 return
1249 self.init_bp_file('Android.bp')
1250 with open('Android.bp', 'a') as outf:
1251 sorted_pkgs = sorted(self.pkg_obj2cc.keys())
1252 for pkg in sorted_pkgs:
1253 if not self.pkg_obj2cc[pkg]:
1254 continue
1255 outf.write('\n// obj => src for %s\n' % pkg)
1256 obj2cc = self.pkg_obj2cc[pkg]
1257 for obj in sorted(obj2cc.keys()):
1258 outf.write('// ' + short_out_name(pkg, obj) + ' => ' +
1259 short_out_name(pkg, obj2cc[obj].src) + '\n')
1260
1261 def gen_bp(self):
1262 """Parse cargo.out and generate Android.bp files."""
1263 if self.dry_run:
1264 print('Dry-run skip: read', CARGO_OUT, 'write Android.bp')
1265 elif os.path.exists(CARGO_OUT):
1266 self.find_root_pkg()
1267 with open(CARGO_OUT, 'r') as cargo_out:
1268 self.parse(cargo_out, 'Android.bp')
1269 self.crates.sort(key=get_module_name)
1270 for obj in self.cc_objects:
1271 obj.dump()
1272 self.dump_pkg_obj2cc()
1273 for crate in self.crates:
1274 crate.dump()
1275 dumped_libs = set()
1276 for lib in self.ar_objects:
1277 if lib.pkg == self.root_pkg:
1278 lib_name = file_base_name(lib.lib)
1279 if lib_name not in dumped_libs:
1280 dumped_libs.add(lib_name)
1281 lib.dump()
1282 if self.args.dependencies and self.dependencies:
1283 self.dump_dependencies()
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001284 if self.errors:
1285 self.append_to_bp('\nErrors in ' + CARGO_OUT + ':\n' + self.errors)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001286 return self
1287
1288 def add_ar_object(self, obj):
1289 self.ar_objects.append(obj)
1290
1291 def add_cc_object(self, obj):
1292 self.cc_objects.append(obj)
1293
1294 def add_crate(self, crate):
1295 """Merge crate with someone in crates, or append to it. Return crates."""
1296 if crate.skip_crate():
1297 if self.args.debug: # include debug info of all crates
1298 self.crates.append(crate)
1299 if self.args.dependencies: # include only dependent crates
1300 if (is_dependent_file_path(crate.main_src) and
1301 not is_build_crate_name(crate.crate_name)):
1302 self.dependencies.append(crate)
1303 else:
1304 for c in self.crates:
1305 if c.merge(crate, 'Android.bp'):
1306 return
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001307 # If not merged, decide module type and name now.
1308 crate.decide_module_type()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001309 self.crates.append(crate)
1310
1311 def find_warning_owners(self):
1312 """For each warning file, find its owner crate."""
1313 missing_owner = False
1314 for f in self.warning_files:
1315 cargo_dir = '' # find lowest crate, with longest path
1316 owner = None # owner crate of this warning
1317 for c in self.crates:
1318 if (f.startswith(c.cargo_dir + '/') and
1319 len(cargo_dir) < len(c.cargo_dir)):
1320 cargo_dir = c.cargo_dir
1321 owner = c
1322 if owner:
1323 owner.has_warning = True
1324 else:
1325 missing_owner = True
1326 if missing_owner and os.path.exists('Cargo.toml'):
1327 # owner is the root cargo, with empty cargo_dir
1328 for c in self.crates:
1329 if not c.cargo_dir:
1330 c.has_warning = True
1331
1332 def rustc_command(self, n, rustc_line, line, outf_name):
1333 """Process a rustc command line from cargo -vv output."""
1334 # cargo build -vv output can have multiple lines for a rustc command
1335 # due to '\n' in strings for environment variables.
1336 # strip removes leading spaces and '\n' at the end
1337 new_rustc = (rustc_line.strip() + line) if rustc_line else line
1338 # Use an heuristic to detect the completions of a multi-line command.
1339 # This might fail for some very rare case, but easy to fix manually.
1340 if not line.endswith('`\n') or (new_rustc.count('`') % 2) != 0:
1341 return new_rustc
1342 if RUSTC_VV_CMD_ARGS.match(new_rustc):
1343 args = RUSTC_VV_CMD_ARGS.match(new_rustc).group(1)
1344 self.add_crate(Crate(self, outf_name).parse(n, args))
1345 else:
1346 self.assert_empty_vv_line(new_rustc)
1347 return ''
1348
1349 def cc_ar_command(self, n, groups, outf_name):
1350 pkg = groups.group(1)
1351 line = groups.group(3)
1352 if groups.group(2) == 'cc':
1353 self.add_cc_object(CCObject(self, outf_name).parse(pkg, n, line))
1354 else:
1355 self.add_ar_object(ARObject(self, outf_name).parse(pkg, n, line))
1356
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001357 def append_to_bp(self, line):
1358 self.init_bp_file('Android.bp')
1359 with open('Android.bp', 'a') as outf:
1360 outf.write(line)
1361
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001362 def assert_empty_vv_line(self, line):
1363 if line: # report error if line is not empty
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001364 self.append_to_bp('ERROR -vv line: ' + line)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001365 return ''
1366
1367 def parse(self, inf, outf_name):
1368 """Parse rustc and warning messages in inf, return a list of Crates."""
1369 n = 0 # line number
1370 prev_warning = False # true if the previous line was warning: ...
1371 rustc_line = '' # previous line(s) matching RUSTC_VV_PAT
1372 for line in inf:
1373 n += 1
1374 if line.startswith('warning: '):
1375 prev_warning = True
1376 rustc_line = self.assert_empty_vv_line(rustc_line)
1377 continue
1378 new_rustc = ''
1379 if RUSTC_PAT.match(line):
1380 args_line = RUSTC_PAT.match(line).group(1)
1381 self.add_crate(Crate(self, outf_name).parse(n, args_line))
1382 self.assert_empty_vv_line(rustc_line)
1383 elif rustc_line or RUSTC_VV_PAT.match(line):
1384 new_rustc = self.rustc_command(n, rustc_line, line, outf_name)
1385 elif CC_AR_VV_PAT.match(line):
1386 self.cc_ar_command(n, CC_AR_VV_PAT.match(line), outf_name)
1387 elif prev_warning and WARNING_FILE_PAT.match(line):
1388 self.assert_empty_vv_line(rustc_line)
1389 fpath = WARNING_FILE_PAT.match(line).group(1)
1390 if fpath[0] != '/': # ignore absolute path
1391 self.warning_files.add(fpath)
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001392 elif line.startswith('error: ') or line.startswith('error[E'):
1393 self.errors += line
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001394 prev_warning = False
1395 rustc_line = new_rustc
1396 self.find_warning_owners()
1397
1398
1399def parse_args():
1400 """Parse main arguments."""
1401 parser = argparse.ArgumentParser('cargo2android')
1402 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001403 '--add_workspace',
1404 action='store_true',
1405 default=False,
1406 help=('append [workspace] to Cargo.toml before calling cargo,' +
1407 ' to treat current directory as root of package source;' +
1408 ' otherwise the relative source file path in generated' +
1409 ' .bp file will be from the parent directory.'))
1410 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001411 '--cargo',
1412 action='append',
1413 metavar='args_string',
1414 help=('extra cargo build -v args in a string, ' +
1415 'each --cargo flag calls cargo build -v once'))
1416 parser.add_argument(
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001417 '--cargo_bin',
1418 type=str,
1419 help='use cargo in the cargo_bin directory instead of the prebuilt one')
1420 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001421 '--debug',
1422 action='store_true',
1423 default=False,
1424 help='dump debug info into Android.bp')
1425 parser.add_argument(
1426 '--dependencies',
1427 action='store_true',
1428 default=False,
1429 help='dump debug info of dependent crates')
1430 parser.add_argument(
1431 '--device',
1432 action='store_true',
1433 default=False,
1434 help='run cargo also for a default device target')
1435 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001436 '--features',
1437 type=str,
1438 help=('pass features to cargo build, ' +
1439 'empty string means no default features'))
1440 parser.add_argument(
1441 '--global_defaults',
1442 type=str,
1443 help='add a defaults name to every module')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001444 parser.add_argument(
1445 '--host-first-multilib',
1446 action='store_true',
1447 default=False,
1448 help=('add a compile_multilib:"first" property ' +
1449 'to Android.bp host modules.'))
1450 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001451 '--no-host',
1452 action='store_true',
1453 default=False,
1454 help='do not run cargo for the host; only for the device target')
1455 parser.add_argument(
1456 '--no-subdir',
1457 action='store_true',
1458 default=False,
1459 help='do not output anything for sub-directories')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001460 parser.add_argument(
1461 '--onefile',
1462 action='store_true',
1463 default=False,
1464 help=('output all into one ./Android.bp, default will generate ' +
1465 'one Android.bp per Cargo.toml in subdirectories'))
1466 parser.add_argument(
1467 '--run',
1468 action='store_true',
1469 default=False,
1470 help='run it, default is dry-run')
1471 parser.add_argument('--rustflags', type=str, help='passing flags to rustc')
1472 parser.add_argument(
1473 '--skipcargo',
1474 action='store_true',
1475 default=False,
1476 help='skip cargo command, parse cargo.out, and generate Android.bp')
1477 parser.add_argument(
1478 '--tests',
1479 action='store_true',
1480 default=False,
1481 help='run cargo build --tests after normal build')
1482 parser.add_argument(
1483 '--verbose',
1484 action='store_true',
1485 default=False,
1486 help='echo executed commands')
1487 parser.add_argument(
1488 '--vv',
1489 action='store_true',
1490 default=False,
1491 help='run cargo with -vv instead of default -v')
1492 return parser.parse_args()
1493
1494
1495def main():
1496 args = parse_args()
1497 if not args.run: # default is dry-run
1498 print(DRY_RUN_NOTE)
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001499 Runner(args).run_cargo().gen_bp().dump_test_mapping_files()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001500
1501
1502if __name__ == '__main__':
1503 main()