blob: 8004d2b079ecc09a1bcc7ed7a5a9f3d138f2bf03 [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
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -070030 Note that cargo build is only called once with the default target
31 x86_64-unknown-linux-gnu.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080032
33(3) To build default and test crates, for host and device, use both
34 --device and --tests flags:
35 cargo2android.py --run --device --tests
36
37 This is equivalent to using the --cargo flag to add extra builds:
38 cargo2android.py --run
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080039 --cargo "build --target x86_64-unknown-linux-gnu"
40 --cargo "build --tests --target x86_64-unknown-linux-gnu"
41
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -070042 Note that when there are test modules generated into Android.bp,
43 corresponding test entries will also be added into the TEST_MAPPING file.
44
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -070045If there are rustc warning messages, this script will add
46a warning comment to the owner crate module in Android.bp.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080047"""
48
49from __future__ import print_function
50
51import argparse
52import os
53import os.path
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -070054import platform
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080055import re
Andrew Walbran80e90be2020-06-09 14:33:18 +010056import sys
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080057
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -070058# Some Rust packages include extra unwanted crates.
59# This set contains all such excluded crate names.
60EXCLUDED_CRATES = set(['protobuf_bin_gen_rust_do_not_use'])
61
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080062RENAME_MAP = {
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -070063 # This map includes all changes to the default rust module names
64 # to resolve name conflicts, avoid confusion, or work as plugin.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080065 'libbacktrace': 'libbacktrace_rust',
Andrew Walbrane51f1042020-08-11 16:42:48 +010066 'libbase': 'libbase_rust',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080067 'libgcc': 'libgcc_rust',
68 'liblog': 'liblog_rust',
Chih-Hung Hsieh07119862020-07-24 15:34:06 -070069 'libminijail': 'libminijail_rust',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080070 'libsync': 'libsync_rust',
71 'libx86_64': 'libx86_64_rust',
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -070072 'protoc_gen_rust': 'protoc-gen-rust',
73}
74
75RENAME_STEM_MAP = {
76 # This map includes all changes to the default rust module stem names,
77 # which is used for output files when different from the module name.
78 'protoc_gen_rust': 'protoc-gen-rust',
79}
80
81RENAME_DEFAULTS_MAP = {
82 # This map includes all changes to the default prefix of rust_default
83 # module names, to avoid conflict with existing Android modules.
84 'libc': 'rust_libc',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080085}
86
87# Header added to all generated Android.bp files.
Andrew Walbran80e90be2020-06-09 14:33:18 +010088ANDROID_BP_HEADER = '// This file is generated by cargo2android.py {args}.\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080089
90CARGO_OUT = 'cargo.out' # Name of file to keep cargo build -v output.
91
92TARGET_TMP = 'target.tmp' # Name of temporary output directory.
93
94# Message to be displayed when this script is called without the --run flag.
95DRY_RUN_NOTE = (
96 'Dry-run: This script uses ./' + TARGET_TMP + ' for output directory,\n' +
97 'runs cargo clean, runs cargo build -v, saves output to ./cargo.out,\n' +
98 'and writes to Android.bp in the current and subdirectories.\n\n' +
99 'To do do all of the above, use the --run flag.\n' +
100 'See --help for other flags, and more usage notes in this script.\n')
101
102# Cargo -v output of a call to rustc.
103RUSTC_PAT = re.compile('^ +Running `rustc (.*)`$')
104
105# Cargo -vv output of a call to rustc could be split into multiple lines.
106# Assume that the first line will contain some CARGO_* env definition.
107RUSTC_VV_PAT = re.compile('^ +Running `.*CARGO_.*=.*$')
108# The combined -vv output rustc command line pattern.
109RUSTC_VV_CMD_ARGS = re.compile('^ *Running `.*CARGO_.*=.* rustc (.*)`$')
110
111# Cargo -vv output of a "cc" or "ar" command; all in one line.
112CC_AR_VV_PAT = re.compile(r'^\[([^ ]*)[^\]]*\] running:? "(cc|ar)" (.*)$')
113# Some package, such as ring-0.13.5, has pattern '... running "cc"'.
114
115# Rustc output of file location path pattern for a warning message.
116WARNING_FILE_PAT = re.compile('^ *--> ([^:]*):[0-9]+')
117
118# Rust package name with suffix -d1.d2.d3.
119VERSION_SUFFIX_PAT = re.compile(r'^(.*)-[0-9]+\.[0-9]+\.[0-9]+$')
120
121
122def altered_name(name):
123 return RENAME_MAP[name] if (name in RENAME_MAP) else name
124
125
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700126def altered_stem(name):
127 return RENAME_STEM_MAP[name] if (name in RENAME_STEM_MAP) else name
128
129
130def altered_defaults(name):
131 return RENAME_DEFAULTS_MAP[name] if (name in RENAME_DEFAULTS_MAP) else name
132
133
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800134def is_build_crate_name(name):
135 # We added special prefix to build script crate names.
136 return name.startswith('build_script_')
137
138
139def is_dependent_file_path(path):
140 # Absolute or dependent '.../' paths are not main files of this crate.
141 return path.startswith('/') or path.startswith('.../')
142
143
144def get_module_name(crate): # to sort crates in a list
145 return crate.module_name
146
147
148def pkg2crate_name(s):
149 return s.replace('-', '_').replace('.', '_')
150
151
152def file_base_name(path):
153 return os.path.splitext(os.path.basename(path))[0]
154
155
156def test_base_name(path):
157 return pkg2crate_name(file_base_name(path))
158
159
160def unquote(s): # remove quotes around str
161 if s and len(s) > 1 and s[0] == '"' and s[-1] == '"':
162 return s[1:-1]
163 return s
164
165
166def remove_version_suffix(s): # remove -d1.d2.d3 suffix
167 if VERSION_SUFFIX_PAT.match(s):
168 return VERSION_SUFFIX_PAT.match(s).group(1)
169 return s
170
171
172def short_out_name(pkg, s): # replace /.../pkg-*/out/* with .../out/*
173 return re.sub('^/.*/' + pkg + '-[0-9a-f]*/out/', '.../out/', s)
174
175
176def escape_quotes(s): # replace '"' with '\\"'
177 return s.replace('"', '\\"')
178
179
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700180class TestMapping(object):
181 """Entries for a TEST_MAPPING file."""
182
183 def __init__(self):
184 self.entries = []
185
186 def add_test(self, name, host):
187 self.entries.append((name, host))
188
189 def is_empty(self):
190 return not self.entries
191
192 def dump(self, outf_name):
193 """Append all entries into the output file."""
194 if self.is_empty():
195 return
196 with open(outf_name, 'w') as outf:
197 outf.write('// Generated by cargo2android.py for tests in Android.bp\n')
198 outf.write('{\n "presubmit": [\n')
199 is_first = True
200 for (name, host) in self.entries:
201 if not is_first: # add comma and '\n' after the previous entry
202 outf.write(',\n')
203 is_first = False
204 outf.write(' {\n "name": "' + name + '"')
205 if host:
206 outf.write(',\n "host": true\n }')
207 else:
208 outf.write('\n }')
209 outf.write('\n ]\n}\n')
210
211
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800212class Crate(object):
213 """Information of a Rust crate to collect/emit for an Android.bp module."""
214
215 def __init__(self, runner, outf_name):
216 # Remembered global runner and its members.
217 self.runner = runner
218 self.debug = runner.args.debug
219 self.cargo_dir = '' # directory of my Cargo.toml
220 self.outf_name = outf_name # path to Android.bp
221 self.outf = None # open file handle of outf_name during dump*
222 # Variants/results that could be merged from multiple rustc lines.
223 self.host_supported = False
224 self.device_supported = False
225 self.has_warning = False
226 # Android module properties derived from rustc parameters.
227 self.module_name = '' # unique in Android build system
228 self.module_type = '' # rust_{binary,library,test}[_host] etc.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700229 self.defaults = '' # rust_defaults used by rust_test* modules
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700230 self.default_srcs = False # use 'srcs' defined in self.defaults
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800231 self.root_pkg = '' # parent package name of a sub/test packge, from -L
232 self.srcs = list() # main_src or merged multiple source files
233 self.stem = '' # real base name of output file
234 # Kept parsed status
235 self.errors = '' # all errors found during parsing
236 self.line_num = 1 # runner told input source line number
237 self.line = '' # original rustc command line parameters
238 # Parameters collected from rustc command line.
239 self.crate_name = '' # follows --crate-name
240 self.main_src = '' # follows crate_name parameter, shortened
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700241 self.crate_types = list() # follows --crate-type
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800242 self.cfgs = list() # follows --cfg, without feature= prefix
243 self.features = list() # follows --cfg, name in 'feature="..."'
244 self.codegens = list() # follows -C, some ignored
245 self.externs = list() # follows --extern
246 self.core_externs = list() # first part of self.externs elements
247 self.static_libs = list() # e.g. -l static=host_cpuid
248 self.shared_libs = list() # e.g. -l dylib=wayland-client, -l z
249 self.cap_lints = '' # follows --cap-lints
250 self.emit_list = '' # e.g., --emit=dep-info,metadata,link
251 self.edition = '2015' # rustc default, e.g., --edition=2018
252 self.target = '' # follows --target
253
254 def write(self, s):
255 # convenient way to output one line at a time with EOL.
256 self.outf.write(s + '\n')
257
258 def same_flags(self, other):
259 # host_supported, device_supported, has_warning are not compared but merged
260 # target is not compared, to merge different target/host modules
261 # externs is not compared; only core_externs is compared
262 return (not self.errors and not other.errors and
263 self.edition == other.edition and
264 self.cap_lints == other.cap_lints and
265 self.emit_list == other.emit_list and
266 self.core_externs == other.core_externs and
267 self.codegens == other.codegens and
268 self.features == other.features and
269 self.static_libs == other.static_libs and
270 self.shared_libs == other.shared_libs and self.cfgs == other.cfgs)
271
272 def merge_host_device(self, other):
273 """Returns true if attributes are the same except host/device support."""
274 return (self.crate_name == other.crate_name and
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700275 self.crate_types == other.crate_types and
276 self.main_src == other.main_src and
277 # before merge, each test module has an unique module name and stem
278 (self.stem == other.stem or self.crate_types == ['test']) and
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800279 self.root_pkg == other.root_pkg and not self.skip_crate() and
280 self.same_flags(other))
281
282 def merge_test(self, other):
283 """Returns true if self and other are tests of same root_pkg."""
284 # Before merger, each test has its own crate_name.
285 # A merged test uses its source file base name as output file name,
286 # so a test is mergeable only if its base name equals to its crate name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700287 return (self.crate_types == other.crate_types and
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700288 self.crate_types == ['test'] and self.root_pkg == other.root_pkg and
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800289 not self.skip_crate() and
290 other.crate_name == test_base_name(other.main_src) and
291 (len(self.srcs) > 1 or
292 (self.crate_name == test_base_name(self.main_src)) and
293 self.host_supported == other.host_supported and
294 self.device_supported == other.device_supported) and
295 self.same_flags(other))
296
297 def merge(self, other, outf_name):
298 """Try to merge crate into self."""
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700299 # Cargo build --tests could recompile a library for tests.
300 # We need to merge such duplicated calls to rustc, with
301 # the algorithm in merge_host_device.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800302 should_merge_host_device = self.merge_host_device(other)
303 should_merge_test = False
304 if not should_merge_host_device:
305 should_merge_test = self.merge_test(other)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800306 if should_merge_host_device or should_merge_test:
307 self.runner.init_bp_file(outf_name)
308 with open(outf_name, 'a') as outf: # to write debug info
309 self.outf = outf
310 other.outf = outf
311 self.do_merge(other, should_merge_test)
312 return True
313 return False
314
315 def do_merge(self, other, should_merge_test):
316 """Merge attributes of other to self."""
317 if self.debug:
318 self.write('\n// Before merge definition (1):')
319 self.dump_debug_info()
320 self.write('\n// Before merge definition (2):')
321 other.dump_debug_info()
322 # Merge properties of other to self.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800323 self.has_warning = self.has_warning or other.has_warning
324 if not self.target: # okay to keep only the first target triple
325 self.target = other.target
326 # decide_module_type sets up default self.stem,
327 # which can be changed if self is a merged test module.
328 self.decide_module_type()
329 if should_merge_test:
330 self.srcs.append(other.main_src)
331 # use a short unique name as the merged module name.
332 prefix = self.root_pkg + '_tests'
333 self.module_name = self.runner.claim_module_name(prefix, self, 0)
334 self.stem = self.module_name
335 # This normalized root_pkg name although might be the same
336 # as other module's crate_name, it is not actually used for
337 # output file name. A merged test module always have multiple
338 # source files and each source file base name is used as
339 # its output file name.
340 self.crate_name = pkg2crate_name(self.root_pkg)
341 if self.debug:
342 self.write('\n// After merge definition (1):')
343 self.dump_debug_info()
344
345 def find_cargo_dir(self):
346 """Deepest directory with Cargo.toml and contains the main_src."""
347 if not is_dependent_file_path(self.main_src):
348 dir_name = os.path.dirname(self.main_src)
349 while dir_name:
350 if os.path.exists(dir_name + '/Cargo.toml'):
351 self.cargo_dir = dir_name
352 return
353 dir_name = os.path.dirname(dir_name)
354
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700355 def add_codegens_flag(self, flag):
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700356 """Ignore options not used in Android."""
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700357 # 'prefer-dynamic' does not work with common flag -C lto
Chih-Hung Hsieh63459ed2020-08-26 11:51:15 -0700358 # 'embed-bitcode' is ignored; we might control LTO with other .bp flag
Chih-Hung Hsieh6c13b722020-09-11 21:24:03 -0700359 # 'codegen-units' is set in Android global config or by default
360 if not (flag.startswith('codegen-units=') or
361 flag.startswith('debuginfo=') or
Chih-Hung Hsieh63459ed2020-08-26 11:51:15 -0700362 flag.startswith('embed-bitcode=') or
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700363 flag.startswith('extra-filename=') or
364 flag.startswith('incremental=') or
365 flag.startswith('metadata=') or
366 flag == 'prefer-dynamic'):
367 self.codegens.append(flag)
368
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800369 def parse(self, line_num, line):
370 """Find important rustc arguments to convert to Android.bp properties."""
371 self.line_num = line_num
372 self.line = line
373 args = line.split() # Loop through every argument of rustc.
374 i = 0
375 while i < len(args):
376 arg = args[i]
377 if arg == '--crate-name':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700378 i += 1
379 self.crate_name = args[i]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800380 elif arg == '--crate-type':
381 i += 1
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700382 # cargo calls rustc with multiple --crate-type flags.
383 # rustc can accept:
384 # --crate-type [bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]
385 self.crate_types.append(args[i])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800386 elif arg == '--test':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700387 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800388 elif arg == '--target':
389 i += 1
390 self.target = args[i]
391 elif arg == '--cfg':
392 i += 1
393 if args[i].startswith('\'feature='):
394 self.features.append(unquote(args[i].replace('\'feature=', '')[:-1]))
395 else:
396 self.cfgs.append(args[i])
397 elif arg == '--extern':
398 i += 1
399 extern_names = re.sub('=/[^ ]*/deps/', ' = ', args[i])
400 self.externs.append(extern_names)
401 self.core_externs.append(re.sub(' = .*', '', extern_names))
402 elif arg == '-C': # codegen options
403 i += 1
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700404 self.add_codegens_flag(args[i])
405 elif arg.startswith('-C'):
406 # cargo has been passing "-C <xyz>" flag to rustc,
407 # but newer cargo could pass '-Cembed-bitcode=no' to rustc.
408 self.add_codegens_flag(arg[2:])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800409 elif arg == '--cap-lints':
410 i += 1
411 self.cap_lints = args[i]
412 elif arg == '-L':
413 i += 1
414 if args[i].startswith('dependency=') and args[i].endswith('/deps'):
415 if '/' + TARGET_TMP + '/' in args[i]:
416 self.root_pkg = re.sub(
417 '^.*/', '', re.sub('/' + TARGET_TMP + '/.*/deps$', '', args[i]))
418 else:
419 self.root_pkg = re.sub('^.*/', '',
420 re.sub('/[^/]+/[^/]+/deps$', '', args[i]))
421 self.root_pkg = remove_version_suffix(self.root_pkg)
422 elif arg == '-l':
423 i += 1
424 if args[i].startswith('static='):
425 self.static_libs.append(re.sub('static=', '', args[i]))
426 elif args[i].startswith('dylib='):
427 self.shared_libs.append(re.sub('dylib=', '', args[i]))
428 else:
429 self.shared_libs.append(args[i])
430 elif arg == '--out-dir' or arg == '--color': # ignored
431 i += 1
432 elif arg.startswith('--error-format=') or arg.startswith('--json='):
433 _ = arg # ignored
434 elif arg.startswith('--emit='):
435 self.emit_list = arg.replace('--emit=', '')
436 elif arg.startswith('--edition='):
437 self.edition = arg.replace('--edition=', '')
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700438 elif not arg.startswith('-'):
439 # shorten imported crate main source paths like $HOME/.cargo/
440 # registry/src/github.com-1ecc6299db9ec823/memchr-2.3.3/src/lib.rs
441 self.main_src = re.sub(r'^/[^ ]*/registry/src/', '.../', args[i])
442 self.main_src = re.sub(r'^\.\.\./github.com-[0-9a-f]*/', '.../',
443 self.main_src)
444 self.find_cargo_dir()
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700445 if self.cargo_dir: # for a subdirectory
446 if self.runner.args.no_subdir: # all .bp content to /dev/null
447 self.outf_name = '/dev/null'
448 elif not self.runner.args.onefile:
449 # Write to Android.bp in the subdirectory with Cargo.toml.
450 self.outf_name = self.cargo_dir + '/Android.bp'
451 self.main_src = self.main_src[len(self.cargo_dir) + 1:]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800452 else:
453 self.errors += 'ERROR: unknown ' + arg + '\n'
454 i += 1
455 if not self.crate_name:
456 self.errors += 'ERROR: missing --crate-name\n'
457 if not self.main_src:
458 self.errors += 'ERROR: missing main source file\n'
459 else:
460 self.srcs.append(self.main_src)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700461 if not self.crate_types:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800462 # Treat "--cfg test" as "--test"
463 if 'test' in self.cfgs:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700464 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800465 else:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700466 self.errors += 'ERROR: missing --crate-type or --test\n'
467 elif len(self.crate_types) > 1:
468 if 'test' in self.crate_types:
469 self.errors += 'ERROR: cannot handle both --crate-type and --test\n'
470 if 'lib' in self.crate_types and 'rlib' in self.crate_types:
471 self.errors += 'ERROR: cannot generate both lib and rlib crate types\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800472 if not self.root_pkg:
473 self.root_pkg = self.crate_name
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700474 self.device_supported = self.runner.args.device
475 self.host_supported = not self.runner.args.no_host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800476 self.cfgs = sorted(set(self.cfgs))
477 self.features = sorted(set(self.features))
478 self.codegens = sorted(set(self.codegens))
479 self.externs = sorted(set(self.externs))
480 self.core_externs = sorted(set(self.core_externs))
481 self.static_libs = sorted(set(self.static_libs))
482 self.shared_libs = sorted(set(self.shared_libs))
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700483 self.crate_types = sorted(set(self.crate_types))
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800484 self.decide_module_type()
485 self.module_name = altered_name(self.stem)
486 return self
487
488 def dump_line(self):
489 self.write('\n// Line ' + str(self.line_num) + ' ' + self.line)
490
491 def feature_list(self):
492 """Return a string of main_src + "feature_list"."""
493 pkg = self.main_src
494 if pkg.startswith('.../'): # keep only the main package name
495 pkg = re.sub('/.*', '', pkg[4:])
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700496 elif pkg.startswith('/'): # use relative path for a local package
497 pkg = os.path.relpath(pkg)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800498 if not self.features:
499 return pkg
500 return pkg + ' "' + ','.join(self.features) + '"'
501
502 def dump_skip_crate(self, kind):
503 if self.debug:
504 self.write('\n// IGNORED: ' + kind + ' ' + self.main_src)
505 return self
506
507 def skip_crate(self):
508 """Return crate_name or a message if this crate should be skipped."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700509 if (is_build_crate_name(self.crate_name) or
510 self.crate_name in EXCLUDED_CRATES):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800511 return self.crate_name
512 if is_dependent_file_path(self.main_src):
513 return 'dependent crate'
514 return ''
515
516 def dump(self):
517 """Dump all error/debug/module code to the output .bp file."""
518 self.runner.init_bp_file(self.outf_name)
519 with open(self.outf_name, 'a') as outf:
520 self.outf = outf
521 if self.errors:
522 self.dump_line()
523 self.write(self.errors)
524 elif self.skip_crate():
525 self.dump_skip_crate(self.skip_crate())
526 else:
527 if self.debug:
528 self.dump_debug_info()
529 self.dump_android_module()
530
531 def dump_debug_info(self):
532 """Dump parsed data, when cargo2android is called with --debug."""
533
534 def dump(name, value):
535 self.write('//%12s = %s' % (name, value))
536
537 def opt_dump(name, value):
538 if value:
539 dump(name, value)
540
541 def dump_list(fmt, values):
542 for v in values:
543 self.write(fmt % v)
544
545 self.dump_line()
546 dump('module_name', self.module_name)
547 dump('crate_name', self.crate_name)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700548 dump('crate_types', self.crate_types)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800549 dump('main_src', self.main_src)
550 dump('has_warning', self.has_warning)
551 dump('for_host', self.host_supported)
552 dump('for_device', self.device_supported)
553 dump('module_type', self.module_type)
554 opt_dump('target', self.target)
555 opt_dump('edition', self.edition)
556 opt_dump('emit_list', self.emit_list)
557 opt_dump('cap_lints', self.cap_lints)
558 dump_list('// cfg = %s', self.cfgs)
559 dump_list('// cfg = \'feature "%s"\'', self.features)
560 # TODO(chh): escape quotes in self.features, but not in other dump_list
561 dump_list('// codegen = %s', self.codegens)
562 dump_list('// externs = %s', self.externs)
563 dump_list('// -l static = %s', self.static_libs)
564 dump_list('// -l (dylib) = %s', self.shared_libs)
565
566 def dump_android_module(self):
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700567 """Dump one or more Android module definition, depending on crate_types."""
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700568 if len(self.crate_types) == 1:
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700569 self.dump_single_type_android_module()
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700570 return
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700571 if 'test' in self.crate_types:
572 self.write('\nERROR: multiple crate types cannot include test type')
573 return
574 # Dump one Android module per crate_type.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700575 for crate_type in self.crate_types:
576 self.decide_one_module_type(crate_type)
577 self.dump_one_android_module(crate_type)
578
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700579 def build_default_name(self):
580 """Return a short and readable name for the rust_defaults module."""
581 # Choices: (1) root_pkg + '_defaults',
582 # (2) root_pkg + '_defaults_' + crate_name
583 # (3) root_pkg + '_defaults_' + main_src_basename_path
584 # (4) root_pkg + '_defaults_' + a_positive_sequence_number
585 name1 = altered_defaults(self.root_pkg) + '_defaults'
586 if self.runner.try_claim_module_name(name1, self):
587 return name1
588 name2 = name1 + '_' + self.crate_name
589 if self.runner.try_claim_module_name(name2, self):
590 return name2
591 name3 = name1 + '_' + self.main_src_basename_path()
592 if self.runner.try_claim_module_name(name3, self):
593 return name3
594 return self.runner.claim_module_name(name1, self, 0)
595
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700596 def dump_defaults_module(self):
597 """Dump a rust_defaults module to be shared by other modules."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700598 name = self.build_default_name()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700599 self.defaults = name
600 self.write('\nrust_defaults {')
601 self.write(' name: "' + name + '",')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700602 if self.runner.args.global_defaults:
603 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700604 self.write(' crate_name: "' + self.crate_name + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700605 if len(self.srcs) == 1: # only one source file; share it in defaults
606 self.default_srcs = True
607 if self.has_warning and not self.cap_lints:
608 self.write(' // has rustc warnings')
609 self.write(' srcs: ["' + self.main_src + '"],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700610 if 'test' in self.crate_types:
611 self.write(' test_suites: ["general-tests"],')
612 self.write(' auto_gen_config: true,')
613 self.dump_edition_flags_libs()
614 self.write('}')
615
616 def dump_single_type_android_module(self):
617 """Dump one simple Android module, which has only one crate_type."""
618 crate_type = self.crate_types[0]
619 if crate_type != 'test':
620 # do not change self.stem or self.module_name
621 self.dump_one_android_module(crate_type)
622 return
623 # Dump one test module per source file, and separate host and device tests.
624 # crate_type == 'test'
625 if (self.host_supported and self.device_supported) or len(self.srcs) > 1:
626 self.srcs = sorted(set(self.srcs))
627 self.dump_defaults_module()
628 saved_srcs = self.srcs
629 for src in saved_srcs:
630 self.srcs = [src]
631 saved_device_supported = self.device_supported
632 saved_host_supported = self.host_supported
633 saved_main_src = self.main_src
634 self.main_src = src
635 if saved_host_supported:
636 self.device_supported = False
637 self.host_supported = True
638 self.module_name = self.test_module_name()
639 self.decide_one_module_type(crate_type)
640 self.dump_one_android_module(crate_type)
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700641 self.runner.add_test(self.outf_name, self.module_name, True)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700642 if saved_device_supported:
643 self.device_supported = True
644 self.host_supported = False
645 self.module_name = self.test_module_name()
646 self.decide_one_module_type(crate_type)
647 self.dump_one_android_module(crate_type)
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700648 self.runner.add_test(self.outf_name, self.module_name, False)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700649 self.host_supported = saved_host_supported
650 self.device_supported = saved_device_supported
651 self.main_src = saved_main_src
652 self.srcs = saved_srcs
653
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700654 def dump_one_android_module(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800655 """Dump one Android module definition."""
656 if not self.module_type:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700657 self.write('\nERROR: unknown crate_type ' + crate_type)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800658 return
659 self.write('\n' + self.module_type + ' {')
660 self.dump_android_core_properties()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700661 if not self.defaults:
662 self.dump_edition_flags_libs()
663 if self.runner.args.host_first_multilib and self.host_supported and crate_type != 'test':
664 self.write(' compile_multilib: "first",')
665 self.write('}')
666
667 def dump_android_flags(self):
668 """Dump Android module flags property."""
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700669 if not self.cfgs and not self.codegens and not self.cap_lints:
670 return
671 self.write(' flags: [')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800672 if self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700673 self.write(' "--cap-lints ' + self.cap_lints + '",')
674 cfg_fmt = '"--cfg %s"'
675 codegens_fmt = '"-C %s"'
676 self.dump_android_property_list_items(cfg_fmt, self.cfgs)
677 self.dump_android_property_list_items(codegens_fmt, self.codegens)
678 self.write(' ],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700679
680 def dump_edition_flags_libs(self):
681 if self.edition:
682 self.write(' edition: "' + self.edition + '",')
683 self.dump_android_property_list('features', '"%s"', self.features)
684 self.dump_android_flags()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800685 if self.externs:
686 self.dump_android_externs()
687 self.dump_android_property_list('static_libs', '"lib%s"', self.static_libs)
688 self.dump_android_property_list('shared_libs', '"lib%s"', self.shared_libs)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800689
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700690 def main_src_basename_path(self):
691 return re.sub('/', '_', re.sub('.rs$', '', self.main_src))
692
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800693 def test_module_name(self):
694 """Return a unique name for a test module."""
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700695 # root_pkg+(_host|_device) + '_test_'+source_file_name
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700696 suffix = self.main_src_basename_path()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700697 host_device = '_host'
698 if self.device_supported:
699 host_device = '_device'
700 return self.root_pkg + host_device + '_test_' + suffix
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800701
702 def decide_module_type(self):
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700703 # Use the first crate type for the default/first module.
704 crate_type = self.crate_types[0] if self.crate_types else ''
705 self.decide_one_module_type(crate_type)
706
707 def decide_one_module_type(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800708 """Decide which Android module type to use."""
709 host = '' if self.device_supported else '_host'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700710 if crate_type == 'bin': # rust_binary[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800711 self.module_type = 'rust_binary' + host
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700712 # In rare cases like protobuf-codegen, the output binary name must
713 # be renamed to use as a plugin for protoc.
714 self.stem = altered_stem(self.crate_name)
715 self.module_name = altered_name(self.crate_name)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700716 elif crate_type == 'lib': # rust_library[_host]
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700717 # TODO(chh): should this be rust_library[_host]?
718 # Assuming that Cargo.toml do not use both 'lib' and 'rlib',
719 # because we map them both to rlib.
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700720 self.module_type = 'rust_library' + host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800721 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700722 self.module_name = altered_name(self.stem)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700723 elif crate_type == 'rlib': # rust_library[_host]
724 self.module_type = 'rust_library' + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700725 self.stem = 'lib' + self.crate_name
726 self.module_name = altered_name(self.stem)
727 elif crate_type == 'dylib': # rust_library[_host]_dylib
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800728 self.module_type = 'rust_library' + host + '_dylib'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700729 self.stem = 'lib' + self.crate_name
730 self.module_name = altered_name(self.stem) + '_dylib'
731 elif crate_type == 'cdylib': # rust_library[_host]_shared
732 self.module_type = 'rust_library' + host + '_shared'
733 self.stem = 'lib' + self.crate_name
734 self.module_name = altered_name(self.stem) + '_shared'
735 elif crate_type == 'staticlib': # rust_library[_host]_static
736 self.module_type = 'rust_library' + host + '_static'
737 self.stem = 'lib' + self.crate_name
738 self.module_name = altered_name(self.stem) + '_static'
739 elif crate_type == 'test': # rust_test[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800740 self.module_type = 'rust_test' + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700741 # Before do_merge, stem name is based on the --crate-name parameter.
742 # and test module name is based on stem.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800743 self.stem = self.test_module_name()
744 # self.stem will be changed after merging with other tests.
745 # self.stem is NOT used for final test binary name.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700746 # rust_test uses each source file base name as part of output file name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700747 # In do_merge, this function is called again, with a module_name.
748 # We make sure that the module name is unique in each package.
749 if self.module_name:
750 # Cargo uses "-C extra-filename=..." and "-C metadata=..." to add
751 # different suffixes and distinguish multiple tests of the same
752 # crate name. We ignore -C and use claim_module_name to get
753 # unique sequential suffix.
754 self.module_name = self.runner.claim_module_name(
755 self.module_name, self, 0)
756 # Now the module name is unique, stem should also match and unique.
757 self.stem = self.module_name
758 elif crate_type == 'proc-macro': # rust_proc_macro
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800759 self.module_type = 'rust_proc_macro'
760 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700761 self.module_name = altered_name(self.stem)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800762 else: # unknown module type, rust_prebuilt_dylib? rust_library[_host]?
763 self.module_type = ''
764 self.stem = ''
765
766 def dump_android_property_list_items(self, fmt, values):
767 for v in values:
768 # fmt has quotes, so we need escape_quotes(v)
769 self.write(' ' + (fmt % escape_quotes(v)) + ',')
770
771 def dump_android_property_list(self, name, fmt, values):
772 if values:
773 self.write(' ' + name + ': [')
774 self.dump_android_property_list_items(fmt, values)
775 self.write(' ],')
776
777 def dump_android_core_properties(self):
778 """Dump the module header, name, stem, etc."""
779 self.write(' name: "' + self.module_name + '",')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700780 # see properties shared by dump_defaults_module
781 if self.defaults:
782 self.write(' defaults: ["' + self.defaults + '"],')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700783 elif self.runner.args.global_defaults:
784 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800785 if self.stem != self.module_name:
786 self.write(' stem: "' + self.stem + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700787 if self.has_warning and not self.cap_lints and not self.default_srcs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700788 self.write(' // has rustc warnings')
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700789 if self.host_supported and self.device_supported and self.module_type != 'rust_proc_macro':
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800790 self.write(' host_supported: true,')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700791 if not self.defaults:
792 self.write(' crate_name: "' + self.crate_name + '",')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800793 if len(self.srcs) > 1:
794 self.srcs = sorted(set(self.srcs))
795 self.dump_android_property_list('srcs', '"%s"', self.srcs)
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700796 elif not self.default_srcs:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800797 self.write(' srcs: ["' + self.main_src + '"],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700798 if 'test' in self.crate_types and not self.defaults:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800799 # self.root_pkg can have multiple test modules, with different *_tests[n]
800 # names, but their executables can all be installed under the same _tests
801 # directory. When built from Cargo.toml, all tests should have different
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700802 # file or crate names. So we used (root_pkg + '_tests') name as the
803 # relative_install_path.
804 # However, some package like 'slab' can have non-mergeable tests that
805 # must be separated by different module names. So, here we no longer
806 # emit relative_install_path.
807 # self.write(' relative_install_path: "' + self.root_pkg + '_tests",')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800808 self.write(' test_suites: ["general-tests"],')
809 self.write(' auto_gen_config: true,')
810
811 def dump_android_externs(self):
812 """Dump the dependent rlibs and dylibs property."""
813 so_libs = list()
814 rust_libs = ''
815 deps_libname = re.compile('^.* = lib(.*)-[0-9a-f]*.(rlib|so|rmeta)$')
816 for lib in self.externs:
817 # normal value of lib: "libc = liblibc-*.rlib"
818 # strange case in rand crate: "getrandom_package = libgetrandom-*.rlib"
819 # we should use "libgetrandom", not "lib" + "getrandom_package"
820 groups = deps_libname.match(lib)
821 if groups is not None:
822 lib_name = groups.group(1)
823 else:
824 lib_name = re.sub(' .*$', '', lib)
825 if lib.endswith('.rlib') or lib.endswith('.rmeta'):
826 # On MacOS .rmeta is used when Linux uses .rlib or .rmeta.
827 rust_libs += ' "' + altered_name('lib' + lib_name) + '",\n'
828 elif lib.endswith('.so'):
829 so_libs.append(lib_name)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700830 elif lib != 'proc_macro': # --extern proc_macro is special and ignored
831 rust_libs += ' // ERROR: unknown type of lib ' + lib + '\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800832 if rust_libs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700833 self.write(' rustlibs: [\n' + rust_libs + ' ],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800834 # Are all dependent .so files proc_macros?
835 # TODO(chh): Separate proc_macros and dylib.
836 self.dump_android_property_list('proc_macros', '"lib%s"', so_libs)
837
838
839class ARObject(object):
840 """Information of an "ar" link command."""
841
842 def __init__(self, runner, outf_name):
843 # Remembered global runner and its members.
844 self.runner = runner
845 self.pkg = ''
846 self.outf_name = outf_name # path to Android.bp
847 # "ar" arguments
848 self.line_num = 1
849 self.line = ''
850 self.flags = '' # e.g. "crs"
851 self.lib = '' # e.g. "/.../out/lib*.a"
852 self.objs = list() # e.g. "/.../out/.../*.o"
853
854 def parse(self, pkg, line_num, args_line):
855 """Collect ar obj/lib file names."""
856 self.pkg = pkg
857 self.line_num = line_num
858 self.line = args_line
859 args = args_line.split()
860 num_args = len(args)
861 if num_args < 3:
862 print('ERROR: "ar" command has too few arguments', args_line)
863 else:
864 self.flags = unquote(args[0])
865 self.lib = unquote(args[1])
866 self.objs = sorted(set(map(unquote, args[2:])))
867 return self
868
869 def write(self, s):
870 self.outf.write(s + '\n')
871
872 def dump_debug_info(self):
873 self.write('\n// Line ' + str(self.line_num) + ' "ar" ' + self.line)
874 self.write('// ar_object for %12s' % self.pkg)
875 self.write('// flags = %s' % self.flags)
876 self.write('// lib = %s' % short_out_name(self.pkg, self.lib))
877 for o in self.objs:
878 self.write('// obj = %s' % short_out_name(self.pkg, o))
879
880 def dump_android_lib(self):
881 """Write cc_library_static into Android.bp."""
882 self.write('\ncc_library_static {')
883 self.write(' name: "' + file_base_name(self.lib) + '",')
884 self.write(' host_supported: true,')
885 if self.flags != 'crs':
886 self.write(' // ar flags = %s' % self.flags)
887 if self.pkg not in self.runner.pkg_obj2cc:
888 self.write(' ERROR: cannot find source files.\n}')
889 return
890 self.write(' srcs: [')
891 obj2cc = self.runner.pkg_obj2cc[self.pkg]
892 # Note: wflags are ignored.
893 dflags = list()
894 fflags = list()
895 for obj in self.objs:
896 self.write(' "' + short_out_name(self.pkg, obj2cc[obj].src) + '",')
897 # TODO(chh): union of dflags and flags of all obj
898 # Now, just a temporary hack that uses the last obj's flags
899 dflags = obj2cc[obj].dflags
900 fflags = obj2cc[obj].fflags
901 self.write(' ],')
902 self.write(' cflags: [')
903 self.write(' "-O3",') # TODO(chh): is this default correct?
904 self.write(' "-Wno-error",')
905 for x in fflags:
906 self.write(' "-f' + x + '",')
907 for x in dflags:
908 self.write(' "-D' + x + '",')
909 self.write(' ],')
910 self.write('}')
911
912 def dump(self):
913 """Dump error/debug/module info to the output .bp file."""
914 self.runner.init_bp_file(self.outf_name)
915 with open(self.outf_name, 'a') as outf:
916 self.outf = outf
917 if self.runner.args.debug:
918 self.dump_debug_info()
919 self.dump_android_lib()
920
921
922class CCObject(object):
923 """Information of a "cc" compilation command."""
924
925 def __init__(self, runner, outf_name):
926 # Remembered global runner and its members.
927 self.runner = runner
928 self.pkg = ''
929 self.outf_name = outf_name # path to Android.bp
930 # "cc" arguments
931 self.line_num = 1
932 self.line = ''
933 self.src = ''
934 self.obj = ''
935 self.dflags = list() # -D flags
936 self.fflags = list() # -f flags
937 self.iflags = list() # -I flags
938 self.wflags = list() # -W flags
939 self.other_args = list()
940
941 def parse(self, pkg, line_num, args_line):
942 """Collect cc compilation flags and src/out file names."""
943 self.pkg = pkg
944 self.line_num = line_num
945 self.line = args_line
946 args = args_line.split()
947 i = 0
948 while i < len(args):
949 arg = args[i]
950 if arg == '"-c"':
951 i += 1
952 if args[i].startswith('"-o'):
953 # ring-0.13.5 dumps: ... "-c" "-o/.../*.o" ".../*.c"
954 self.obj = unquote(args[i])[2:]
955 i += 1
956 self.src = unquote(args[i])
957 else:
958 self.src = unquote(args[i])
959 elif arg == '"-o"':
960 i += 1
961 self.obj = unquote(args[i])
962 elif arg == '"-I"':
963 i += 1
964 self.iflags.append(unquote(args[i]))
965 elif arg.startswith('"-D'):
966 self.dflags.append(unquote(args[i])[2:])
967 elif arg.startswith('"-f'):
968 self.fflags.append(unquote(args[i])[2:])
969 elif arg.startswith('"-W'):
970 self.wflags.append(unquote(args[i])[2:])
971 elif not (arg.startswith('"-O') or arg == '"-m64"' or arg == '"-g"' or
972 arg == '"-g3"'):
973 # ignore -O -m64 -g
974 self.other_args.append(unquote(args[i]))
975 i += 1
976 self.dflags = sorted(set(self.dflags))
977 self.fflags = sorted(set(self.fflags))
978 # self.wflags is not sorted because some are order sensitive
979 # and we ignore them anyway.
980 if self.pkg not in self.runner.pkg_obj2cc:
981 self.runner.pkg_obj2cc[self.pkg] = {}
982 self.runner.pkg_obj2cc[self.pkg][self.obj] = self
983 return self
984
985 def write(self, s):
986 self.outf.write(s + '\n')
987
988 def dump_debug_flags(self, name, flags):
989 self.write('// ' + name + ':')
990 for f in flags:
991 self.write('// %s' % f)
992
993 def dump(self):
994 """Dump only error/debug info to the output .bp file."""
995 if not self.runner.args.debug:
996 return
997 self.runner.init_bp_file(self.outf_name)
998 with open(self.outf_name, 'a') as outf:
999 self.outf = outf
1000 self.write('\n// Line ' + str(self.line_num) + ' "cc" ' + self.line)
1001 self.write('// cc_object for %12s' % self.pkg)
1002 self.write('// src = %s' % short_out_name(self.pkg, self.src))
1003 self.write('// obj = %s' % short_out_name(self.pkg, self.obj))
1004 self.dump_debug_flags('-I flags', self.iflags)
1005 self.dump_debug_flags('-D flags', self.dflags)
1006 self.dump_debug_flags('-f flags', self.fflags)
1007 self.dump_debug_flags('-W flags', self.wflags)
1008 if self.other_args:
1009 self.dump_debug_flags('other args', self.other_args)
1010
1011
1012class Runner(object):
1013 """Main class to parse cargo -v output and print Android module definitions."""
1014
1015 def __init__(self, args):
1016 self.bp_files = set() # Remember all output Android.bp files.
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001017 self.test_mappings = {} # Map from Android.bp file path to TestMapping.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001018 self.root_pkg = '' # name of package in ./Cargo.toml
1019 # Saved flags, modes, and data.
1020 self.args = args
1021 self.dry_run = not args.run
1022 self.skip_cargo = args.skipcargo
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001023 self.cargo_path = './cargo' # path to cargo, will be set later
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001024 # All cc/ar objects, crates, dependencies, and warning files
1025 self.cc_objects = list()
1026 self.pkg_obj2cc = {}
1027 # pkg_obj2cc[cc_object[i].pkg][cc_objects[i].obj] = cc_objects[i]
1028 self.ar_objects = list()
1029 self.crates = list()
1030 self.dependencies = list() # dependent and build script crates
1031 self.warning_files = set()
1032 # Keep a unique mapping from (module name) to crate
1033 self.name_owners = {}
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001034 # Save and dump all errors from cargo to Android.bp.
1035 self.errors = ''
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001036 self.setup_cargo_path()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001037 # Default action is cargo clean, followed by build or user given actions.
1038 if args.cargo:
1039 self.cargo = ['clean'] + args.cargo
1040 else:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001041 default_target = '--target x86_64-unknown-linux-gnu'
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -07001042 # Use the same target for both host and default device builds.
1043 # Same target is used as default in host x86_64 Android compilation.
1044 # Note: b/169872957, prebuilt cargo failed to build vsock
1045 # on x86_64-unknown-linux-musl systems.
1046 self.cargo = ['clean', 'build ' + default_target]
1047 if args.tests:
1048 self.cargo.append('build --tests ' + default_target)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001049
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001050 def setup_cargo_path(self):
1051 """Find cargo in the --cargo_bin or prebuilt rust bin directory."""
1052 if self.args.cargo_bin:
1053 self.cargo_path = os.path.join(self.args.cargo_bin, 'cargo')
1054 if not os.path.isfile(self.cargo_path):
1055 sys.exit('ERROR: cannot find cargo in ' + self.args.cargo_bin)
1056 print('WARNING: using cargo in ' + self.args.cargo_bin)
1057 return
1058 # We have only tested this on Linux.
1059 if platform.system() != 'Linux':
1060 sys.exit('ERROR: this script has only been tested on Linux with cargo.')
1061 # Assuming that this script is in development/scripts.
1062 my_dir = os.path.dirname(os.path.abspath(__file__))
1063 linux_dir = os.path.join(my_dir, '..', '..',
1064 'prebuilts', 'rust', 'linux-x86')
1065 if not os.path.isdir(linux_dir):
1066 sys.exit('ERROR: cannot find directory ' + linux_dir)
1067 rust_version = self.find_rust_version(my_dir, linux_dir)
1068 cargo_bin = os.path.join(linux_dir, rust_version, 'bin')
1069 self.cargo_path = os.path.join(cargo_bin, 'cargo')
1070 if not os.path.isfile(self.cargo_path):
1071 sys.exit('ERROR: cannot find cargo in ' + cargo_bin
1072 + '; please try --cargo_bin= flag.')
1073 return
1074
1075 def find_rust_version(self, my_dir, linux_dir):
1076 """Use my script directory, find prebuilt rust version."""
1077 # First look up build/soong/rust/config/global.go.
1078 path2global = os.path.join(my_dir, '..', '..',
1079 'build', 'soong', 'rust', 'config', 'global.go')
1080 if os.path.isfile(path2global):
1081 # try to find: RustDefaultVersion = "1.44.0"
1082 version_pat = re.compile(
1083 r'\s*RustDefaultVersion\s*=\s*"([0-9]+\.[0-9]+\.[0-9]+)".*$')
1084 with open(path2global, 'r') as inf:
1085 for line in inf:
1086 result = version_pat.match(line)
1087 if result:
1088 return result.group(1)
1089 print('WARNING: cannot find RustDefaultVersion in ' + path2global)
1090 # Otherwise, find the newest (largest) version number in linux_dir.
1091 rust_version = (0, 0, 0) # the prebuilt version to use
1092 version_pat = re.compile(r'([0-9]+)\.([0-9]+)\.([0-9]+)$')
1093 for dir_name in os.listdir(linux_dir):
1094 result = version_pat.match(dir_name)
1095 if not result:
1096 continue
1097 version = (result.group(1), result.group(2), result.group(3))
1098 if version > rust_version:
1099 rust_version = version
1100 return '.'.join(rust_version)
1101
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001102 def init_bp_file(self, name):
1103 if name not in self.bp_files:
1104 self.bp_files.add(name)
1105 with open(name, 'w') as outf:
Andrew Walbran80e90be2020-06-09 14:33:18 +01001106 outf.write(ANDROID_BP_HEADER.format(args=' '.join(sys.argv[1:])))
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001107
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001108 def dump_test_mapping_files(self):
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001109 """Dump all TEST_MAPPING files."""
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001110 if self.dry_run:
1111 print('Dry-run skip dump of TEST_MAPPING')
1112 else:
1113 for bp_file_name in self.test_mappings:
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001114 if bp_file_name != '/dev/null':
1115 name = os.path.join(os.path.dirname(bp_file_name), 'TEST_MAPPING')
1116 self.test_mappings[bp_file_name].dump(name)
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001117 return self
1118
1119 def add_test(self, bp_file_name, test_name, host):
1120 if bp_file_name not in self.test_mappings:
1121 self.test_mappings[bp_file_name] = TestMapping()
1122 mapping = self.test_mappings[bp_file_name]
1123 mapping.add_test(test_name, host)
1124
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001125 def try_claim_module_name(self, name, owner):
1126 """Reserve and return True if it has not been reserved yet."""
1127 if name not in self.name_owners or owner == self.name_owners[name]:
1128 self.name_owners[name] = owner
1129 return True
1130 return False
1131
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001132 def claim_module_name(self, prefix, owner, counter):
1133 """Return prefix if not owned yet, otherwise, prefix+str(counter)."""
1134 while True:
1135 name = prefix
1136 if counter > 0:
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001137 name += '_' + str(counter)
1138 if self.try_claim_module_name(name, owner):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001139 return name
1140 counter += 1
1141
1142 def find_root_pkg(self):
1143 """Read name of [package] in ./Cargo.toml."""
1144 if not os.path.exists('./Cargo.toml'):
1145 return
1146 with open('./Cargo.toml', 'r') as inf:
1147 pkg_section = re.compile(r'^ *\[package\]')
1148 name = re.compile('^ *name *= * "([^"]*)"')
1149 in_pkg = False
1150 for line in inf:
1151 if in_pkg:
1152 if name.match(line):
1153 self.root_pkg = name.match(line).group(1)
1154 break
1155 else:
1156 in_pkg = pkg_section.match(line) is not None
1157
1158 def run_cargo(self):
1159 """Calls cargo -v and save its output to ./cargo.out."""
1160 if self.skip_cargo:
1161 return self
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001162 cargo_toml = './Cargo.toml'
1163 cargo_out = './cargo.out'
1164 if not os.access(cargo_toml, os.R_OK):
1165 print('ERROR: Cannot find or read', cargo_toml)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001166 return self
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001167 if not self.dry_run and os.path.exists(cargo_out):
1168 os.remove(cargo_out)
1169 cmd_tail = ' --target-dir ' + TARGET_TMP + ' >> ' + cargo_out + ' 2>&1'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001170 # set up search PATH for cargo to find the correct rustc
1171 saved_path = os.environ['PATH']
1172 os.environ['PATH'] = os.path.dirname(self.cargo_path) + ':' + saved_path
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001173 # Add [workspace] to Cargo.toml if it is not there.
1174 added_workspace = False
1175 if self.args.add_workspace:
1176 with open(cargo_toml, 'r') as in_file:
1177 cargo_toml_lines = in_file.readlines()
1178 found_workspace = '[workspace]\n' in cargo_toml_lines
1179 if found_workspace:
1180 print('### WARNING: found [workspace] in Cargo.toml')
1181 else:
1182 with open(cargo_toml, 'a') as out_file:
1183 out_file.write('[workspace]\n')
1184 added_workspace = True
1185 if self.args.verbose:
1186 print('### INFO: added [workspace] to Cargo.toml')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001187 for c in self.cargo:
1188 features = ''
Chih-Hung Hsieh6c8d52f2020-03-30 18:28:52 -07001189 if c != 'clean':
1190 if self.args.features is not None:
1191 features = ' --no-default-features'
1192 if self.args.features:
1193 features += ' --features ' + self.args.features
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001194 cmd_v_flag = ' -vv ' if self.args.vv else ' -v '
1195 cmd = self.cargo_path + cmd_v_flag
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001196 cmd += c + features + cmd_tail
1197 if self.args.rustflags and c != 'clean':
1198 cmd = 'RUSTFLAGS="' + self.args.rustflags + '" ' + cmd
1199 if self.dry_run:
1200 print('Dry-run skip:', cmd)
1201 else:
1202 if self.args.verbose:
1203 print('Running:', cmd)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001204 with open(cargo_out, 'a') as out_file:
1205 out_file.write('### Running: ' + cmd + '\n')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001206 os.system(cmd)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001207 if added_workspace: # restore original Cargo.toml
1208 with open(cargo_toml, 'w') as out_file:
1209 out_file.writelines(cargo_toml_lines)
1210 if self.args.verbose:
1211 print('### INFO: restored original Cargo.toml')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001212 os.environ['PATH'] = saved_path
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001213 return self
1214
1215 def dump_dependencies(self):
1216 """Append dependencies and their features to Android.bp."""
1217 if not self.dependencies:
1218 return
1219 dependent_list = list()
1220 for c in self.dependencies:
1221 dependent_list.append(c.feature_list())
1222 sorted_dependencies = sorted(set(dependent_list))
1223 self.init_bp_file('Android.bp')
1224 with open('Android.bp', 'a') as outf:
1225 outf.write('\n// dependent_library ["feature_list"]\n')
1226 for s in sorted_dependencies:
1227 outf.write('// ' + s + '\n')
1228
1229 def dump_pkg_obj2cc(self):
1230 """Dump debug info of the pkg_obj2cc map."""
1231 if not self.args.debug:
1232 return
1233 self.init_bp_file('Android.bp')
1234 with open('Android.bp', 'a') as outf:
1235 sorted_pkgs = sorted(self.pkg_obj2cc.keys())
1236 for pkg in sorted_pkgs:
1237 if not self.pkg_obj2cc[pkg]:
1238 continue
1239 outf.write('\n// obj => src for %s\n' % pkg)
1240 obj2cc = self.pkg_obj2cc[pkg]
1241 for obj in sorted(obj2cc.keys()):
1242 outf.write('// ' + short_out_name(pkg, obj) + ' => ' +
1243 short_out_name(pkg, obj2cc[obj].src) + '\n')
1244
1245 def gen_bp(self):
1246 """Parse cargo.out and generate Android.bp files."""
1247 if self.dry_run:
1248 print('Dry-run skip: read', CARGO_OUT, 'write Android.bp')
1249 elif os.path.exists(CARGO_OUT):
1250 self.find_root_pkg()
1251 with open(CARGO_OUT, 'r') as cargo_out:
1252 self.parse(cargo_out, 'Android.bp')
1253 self.crates.sort(key=get_module_name)
1254 for obj in self.cc_objects:
1255 obj.dump()
1256 self.dump_pkg_obj2cc()
1257 for crate in self.crates:
1258 crate.dump()
1259 dumped_libs = set()
1260 for lib in self.ar_objects:
1261 if lib.pkg == self.root_pkg:
1262 lib_name = file_base_name(lib.lib)
1263 if lib_name not in dumped_libs:
1264 dumped_libs.add(lib_name)
1265 lib.dump()
1266 if self.args.dependencies and self.dependencies:
1267 self.dump_dependencies()
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001268 if self.errors:
1269 self.append_to_bp('\nErrors in ' + CARGO_OUT + ':\n' + self.errors)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001270 return self
1271
1272 def add_ar_object(self, obj):
1273 self.ar_objects.append(obj)
1274
1275 def add_cc_object(self, obj):
1276 self.cc_objects.append(obj)
1277
1278 def add_crate(self, crate):
1279 """Merge crate with someone in crates, or append to it. Return crates."""
1280 if crate.skip_crate():
1281 if self.args.debug: # include debug info of all crates
1282 self.crates.append(crate)
1283 if self.args.dependencies: # include only dependent crates
1284 if (is_dependent_file_path(crate.main_src) and
1285 not is_build_crate_name(crate.crate_name)):
1286 self.dependencies.append(crate)
1287 else:
1288 for c in self.crates:
1289 if c.merge(crate, 'Android.bp'):
1290 return
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001291 # If not merged, decide module type and name now.
1292 crate.decide_module_type()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001293 self.crates.append(crate)
1294
1295 def find_warning_owners(self):
1296 """For each warning file, find its owner crate."""
1297 missing_owner = False
1298 for f in self.warning_files:
1299 cargo_dir = '' # find lowest crate, with longest path
1300 owner = None # owner crate of this warning
1301 for c in self.crates:
1302 if (f.startswith(c.cargo_dir + '/') and
1303 len(cargo_dir) < len(c.cargo_dir)):
1304 cargo_dir = c.cargo_dir
1305 owner = c
1306 if owner:
1307 owner.has_warning = True
1308 else:
1309 missing_owner = True
1310 if missing_owner and os.path.exists('Cargo.toml'):
1311 # owner is the root cargo, with empty cargo_dir
1312 for c in self.crates:
1313 if not c.cargo_dir:
1314 c.has_warning = True
1315
1316 def rustc_command(self, n, rustc_line, line, outf_name):
1317 """Process a rustc command line from cargo -vv output."""
1318 # cargo build -vv output can have multiple lines for a rustc command
1319 # due to '\n' in strings for environment variables.
1320 # strip removes leading spaces and '\n' at the end
1321 new_rustc = (rustc_line.strip() + line) if rustc_line else line
1322 # Use an heuristic to detect the completions of a multi-line command.
1323 # This might fail for some very rare case, but easy to fix manually.
1324 if not line.endswith('`\n') or (new_rustc.count('`') % 2) != 0:
1325 return new_rustc
1326 if RUSTC_VV_CMD_ARGS.match(new_rustc):
1327 args = RUSTC_VV_CMD_ARGS.match(new_rustc).group(1)
1328 self.add_crate(Crate(self, outf_name).parse(n, args))
1329 else:
1330 self.assert_empty_vv_line(new_rustc)
1331 return ''
1332
1333 def cc_ar_command(self, n, groups, outf_name):
1334 pkg = groups.group(1)
1335 line = groups.group(3)
1336 if groups.group(2) == 'cc':
1337 self.add_cc_object(CCObject(self, outf_name).parse(pkg, n, line))
1338 else:
1339 self.add_ar_object(ARObject(self, outf_name).parse(pkg, n, line))
1340
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001341 def append_to_bp(self, line):
1342 self.init_bp_file('Android.bp')
1343 with open('Android.bp', 'a') as outf:
1344 outf.write(line)
1345
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001346 def assert_empty_vv_line(self, line):
1347 if line: # report error if line is not empty
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001348 self.append_to_bp('ERROR -vv line: ' + line)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001349 return ''
1350
1351 def parse(self, inf, outf_name):
1352 """Parse rustc and warning messages in inf, return a list of Crates."""
1353 n = 0 # line number
1354 prev_warning = False # true if the previous line was warning: ...
1355 rustc_line = '' # previous line(s) matching RUSTC_VV_PAT
1356 for line in inf:
1357 n += 1
1358 if line.startswith('warning: '):
1359 prev_warning = True
1360 rustc_line = self.assert_empty_vv_line(rustc_line)
1361 continue
1362 new_rustc = ''
1363 if RUSTC_PAT.match(line):
1364 args_line = RUSTC_PAT.match(line).group(1)
1365 self.add_crate(Crate(self, outf_name).parse(n, args_line))
1366 self.assert_empty_vv_line(rustc_line)
1367 elif rustc_line or RUSTC_VV_PAT.match(line):
1368 new_rustc = self.rustc_command(n, rustc_line, line, outf_name)
1369 elif CC_AR_VV_PAT.match(line):
1370 self.cc_ar_command(n, CC_AR_VV_PAT.match(line), outf_name)
1371 elif prev_warning and WARNING_FILE_PAT.match(line):
1372 self.assert_empty_vv_line(rustc_line)
1373 fpath = WARNING_FILE_PAT.match(line).group(1)
1374 if fpath[0] != '/': # ignore absolute path
1375 self.warning_files.add(fpath)
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001376 elif line.startswith('error: ') or line.startswith('error[E'):
1377 self.errors += line
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001378 prev_warning = False
1379 rustc_line = new_rustc
1380 self.find_warning_owners()
1381
1382
1383def parse_args():
1384 """Parse main arguments."""
1385 parser = argparse.ArgumentParser('cargo2android')
1386 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001387 '--add_workspace',
1388 action='store_true',
1389 default=False,
1390 help=('append [workspace] to Cargo.toml before calling cargo,' +
1391 ' to treat current directory as root of package source;' +
1392 ' otherwise the relative source file path in generated' +
1393 ' .bp file will be from the parent directory.'))
1394 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001395 '--cargo',
1396 action='append',
1397 metavar='args_string',
1398 help=('extra cargo build -v args in a string, ' +
1399 'each --cargo flag calls cargo build -v once'))
1400 parser.add_argument(
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001401 '--cargo_bin',
1402 type=str,
1403 help='use cargo in the cargo_bin directory instead of the prebuilt one')
1404 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001405 '--debug',
1406 action='store_true',
1407 default=False,
1408 help='dump debug info into Android.bp')
1409 parser.add_argument(
1410 '--dependencies',
1411 action='store_true',
1412 default=False,
1413 help='dump debug info of dependent crates')
1414 parser.add_argument(
1415 '--device',
1416 action='store_true',
1417 default=False,
1418 help='run cargo also for a default device target')
1419 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001420 '--features',
1421 type=str,
1422 help=('pass features to cargo build, ' +
1423 'empty string means no default features'))
1424 parser.add_argument(
1425 '--global_defaults',
1426 type=str,
1427 help='add a defaults name to every module')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001428 parser.add_argument(
1429 '--host-first-multilib',
1430 action='store_true',
1431 default=False,
1432 help=('add a compile_multilib:"first" property ' +
1433 'to Android.bp host modules.'))
1434 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001435 '--no-host',
1436 action='store_true',
1437 default=False,
1438 help='do not run cargo for the host; only for the device target')
1439 parser.add_argument(
1440 '--no-subdir',
1441 action='store_true',
1442 default=False,
1443 help='do not output anything for sub-directories')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001444 parser.add_argument(
1445 '--onefile',
1446 action='store_true',
1447 default=False,
1448 help=('output all into one ./Android.bp, default will generate ' +
1449 'one Android.bp per Cargo.toml in subdirectories'))
1450 parser.add_argument(
1451 '--run',
1452 action='store_true',
1453 default=False,
1454 help='run it, default is dry-run')
1455 parser.add_argument('--rustflags', type=str, help='passing flags to rustc')
1456 parser.add_argument(
1457 '--skipcargo',
1458 action='store_true',
1459 default=False,
1460 help='skip cargo command, parse cargo.out, and generate Android.bp')
1461 parser.add_argument(
1462 '--tests',
1463 action='store_true',
1464 default=False,
1465 help='run cargo build --tests after normal build')
1466 parser.add_argument(
1467 '--verbose',
1468 action='store_true',
1469 default=False,
1470 help='echo executed commands')
1471 parser.add_argument(
1472 '--vv',
1473 action='store_true',
1474 default=False,
1475 help='run cargo with -vv instead of default -v')
1476 return parser.parse_args()
1477
1478
1479def main():
1480 args = parse_args()
1481 if not args.run: # default is dry-run
1482 print(DRY_RUN_NOTE)
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001483 Runner(args).run_cargo().gen_bp().dump_test_mapping_files()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001484
1485
1486if __name__ == '__main__':
1487 main()