blob: 7417db119141c1f5ebfbbd53ce9ef60cec8269e2 [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
51Since Android rust builds by default treat all warnings as errors,
52if there are rustc warning messages, this script will add
53deny_warnings:false to the owner crate module in Android.bp.
54"""
55
56from __future__ import print_function
57
58import argparse
59import os
60import os.path
61import re
62
63RENAME_MAP = {
64 # This map includes all changes to the default rust library module
65 # names to resolve name conflicts or avoid confusion.
66 'libbacktrace': 'libbacktrace_rust',
67 'libgcc': 'libgcc_rust',
68 'liblog': 'liblog_rust',
69 'libsync': 'libsync_rust',
70 'libx86_64': 'libx86_64_rust',
71}
72
73# Header added to all generated Android.bp files.
74ANDROID_BP_HEADER = '// This file is generated by cargo2android.py.\n'
75
76CARGO_OUT = 'cargo.out' # Name of file to keep cargo build -v output.
77
78TARGET_TMP = 'target.tmp' # Name of temporary output directory.
79
80# Message to be displayed when this script is called without the --run flag.
81DRY_RUN_NOTE = (
82 'Dry-run: This script uses ./' + TARGET_TMP + ' for output directory,\n' +
83 'runs cargo clean, runs cargo build -v, saves output to ./cargo.out,\n' +
84 'and writes to Android.bp in the current and subdirectories.\n\n' +
85 'To do do all of the above, use the --run flag.\n' +
86 'See --help for other flags, and more usage notes in this script.\n')
87
88# Cargo -v output of a call to rustc.
89RUSTC_PAT = re.compile('^ +Running `rustc (.*)`$')
90
91# Cargo -vv output of a call to rustc could be split into multiple lines.
92# Assume that the first line will contain some CARGO_* env definition.
93RUSTC_VV_PAT = re.compile('^ +Running `.*CARGO_.*=.*$')
94# The combined -vv output rustc command line pattern.
95RUSTC_VV_CMD_ARGS = re.compile('^ *Running `.*CARGO_.*=.* rustc (.*)`$')
96
97# Cargo -vv output of a "cc" or "ar" command; all in one line.
98CC_AR_VV_PAT = re.compile(r'^\[([^ ]*)[^\]]*\] running:? "(cc|ar)" (.*)$')
99# Some package, such as ring-0.13.5, has pattern '... running "cc"'.
100
101# Rustc output of file location path pattern for a warning message.
102WARNING_FILE_PAT = re.compile('^ *--> ([^:]*):[0-9]+')
103
104# Rust package name with suffix -d1.d2.d3.
105VERSION_SUFFIX_PAT = re.compile(r'^(.*)-[0-9]+\.[0-9]+\.[0-9]+$')
106
107
108def altered_name(name):
109 return RENAME_MAP[name] if (name in RENAME_MAP) else name
110
111
112def is_build_crate_name(name):
113 # We added special prefix to build script crate names.
114 return name.startswith('build_script_')
115
116
117def is_dependent_file_path(path):
118 # Absolute or dependent '.../' paths are not main files of this crate.
119 return path.startswith('/') or path.startswith('.../')
120
121
122def get_module_name(crate): # to sort crates in a list
123 return crate.module_name
124
125
126def pkg2crate_name(s):
127 return s.replace('-', '_').replace('.', '_')
128
129
130def file_base_name(path):
131 return os.path.splitext(os.path.basename(path))[0]
132
133
134def test_base_name(path):
135 return pkg2crate_name(file_base_name(path))
136
137
138def unquote(s): # remove quotes around str
139 if s and len(s) > 1 and s[0] == '"' and s[-1] == '"':
140 return s[1:-1]
141 return s
142
143
144def remove_version_suffix(s): # remove -d1.d2.d3 suffix
145 if VERSION_SUFFIX_PAT.match(s):
146 return VERSION_SUFFIX_PAT.match(s).group(1)
147 return s
148
149
150def short_out_name(pkg, s): # replace /.../pkg-*/out/* with .../out/*
151 return re.sub('^/.*/' + pkg + '-[0-9a-f]*/out/', '.../out/', s)
152
153
154def escape_quotes(s): # replace '"' with '\\"'
155 return s.replace('"', '\\"')
156
157
158class Crate(object):
159 """Information of a Rust crate to collect/emit for an Android.bp module."""
160
161 def __init__(self, runner, outf_name):
162 # Remembered global runner and its members.
163 self.runner = runner
164 self.debug = runner.args.debug
165 self.cargo_dir = '' # directory of my Cargo.toml
166 self.outf_name = outf_name # path to Android.bp
167 self.outf = None # open file handle of outf_name during dump*
168 # Variants/results that could be merged from multiple rustc lines.
169 self.host_supported = False
170 self.device_supported = False
171 self.has_warning = False
172 # Android module properties derived from rustc parameters.
173 self.module_name = '' # unique in Android build system
174 self.module_type = '' # rust_{binary,library,test}[_host] etc.
175 self.root_pkg = '' # parent package name of a sub/test packge, from -L
176 self.srcs = list() # main_src or merged multiple source files
177 self.stem = '' # real base name of output file
178 # Kept parsed status
179 self.errors = '' # all errors found during parsing
180 self.line_num = 1 # runner told input source line number
181 self.line = '' # original rustc command line parameters
182 # Parameters collected from rustc command line.
183 self.crate_name = '' # follows --crate-name
184 self.main_src = '' # follows crate_name parameter, shortened
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700185 self.crate_types = list() # follows --crate-type
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800186 self.cfgs = list() # follows --cfg, without feature= prefix
187 self.features = list() # follows --cfg, name in 'feature="..."'
188 self.codegens = list() # follows -C, some ignored
189 self.externs = list() # follows --extern
190 self.core_externs = list() # first part of self.externs elements
191 self.static_libs = list() # e.g. -l static=host_cpuid
192 self.shared_libs = list() # e.g. -l dylib=wayland-client, -l z
193 self.cap_lints = '' # follows --cap-lints
194 self.emit_list = '' # e.g., --emit=dep-info,metadata,link
195 self.edition = '2015' # rustc default, e.g., --edition=2018
196 self.target = '' # follows --target
197
198 def write(self, s):
199 # convenient way to output one line at a time with EOL.
200 self.outf.write(s + '\n')
201
202 def same_flags(self, other):
203 # host_supported, device_supported, has_warning are not compared but merged
204 # target is not compared, to merge different target/host modules
205 # externs is not compared; only core_externs is compared
206 return (not self.errors and not other.errors and
207 self.edition == other.edition and
208 self.cap_lints == other.cap_lints and
209 self.emit_list == other.emit_list and
210 self.core_externs == other.core_externs and
211 self.codegens == other.codegens and
212 self.features == other.features and
213 self.static_libs == other.static_libs and
214 self.shared_libs == other.shared_libs and self.cfgs == other.cfgs)
215
216 def merge_host_device(self, other):
217 """Returns true if attributes are the same except host/device support."""
218 return (self.crate_name == other.crate_name and
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700219 self.crate_types == other.crate_types and
220 self.main_src == other.main_src and
221 # before merge, each test module has an unique module name and stem
222 (self.stem == other.stem or self.crate_types == ['test']) and
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800223 self.root_pkg == other.root_pkg and not self.skip_crate() and
224 self.same_flags(other))
225
226 def merge_test(self, other):
227 """Returns true if self and other are tests of same root_pkg."""
228 # Before merger, each test has its own crate_name.
229 # A merged test uses its source file base name as output file name,
230 # so a test is mergeable only if its base name equals to its crate name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700231 return (self.crate_types == other.crate_types and
232 self.crate_types == ['test'] and
233 self.root_pkg == other.root_pkg and
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800234 not self.skip_crate() and
235 other.crate_name == test_base_name(other.main_src) and
236 (len(self.srcs) > 1 or
237 (self.crate_name == test_base_name(self.main_src)) and
238 self.host_supported == other.host_supported and
239 self.device_supported == other.device_supported) and
240 self.same_flags(other))
241
242 def merge(self, other, outf_name):
243 """Try to merge crate into self."""
244 should_merge_host_device = self.merge_host_device(other)
245 should_merge_test = False
246 if not should_merge_host_device:
247 should_merge_test = self.merge_test(other)
248 # A for-device test crate can be merged with its for-host version,
249 # or merged with a different test for the same host or device.
250 # Since we run cargo once for each device or host, test crates for the
251 # first device or host will be merged first. Then test crates for a
252 # different device or host should be allowed to be merged into a
253 # previously merged one, maybe for a different device or host.
254 if should_merge_host_device or should_merge_test:
255 self.runner.init_bp_file(outf_name)
256 with open(outf_name, 'a') as outf: # to write debug info
257 self.outf = outf
258 other.outf = outf
259 self.do_merge(other, should_merge_test)
260 return True
261 return False
262
263 def do_merge(self, other, should_merge_test):
264 """Merge attributes of other to self."""
265 if self.debug:
266 self.write('\n// Before merge definition (1):')
267 self.dump_debug_info()
268 self.write('\n// Before merge definition (2):')
269 other.dump_debug_info()
270 # Merge properties of other to self.
271 self.host_supported = self.host_supported or other.host_supported
272 self.device_supported = self.device_supported or other.device_supported
273 self.has_warning = self.has_warning or other.has_warning
274 if not self.target: # okay to keep only the first target triple
275 self.target = other.target
276 # decide_module_type sets up default self.stem,
277 # which can be changed if self is a merged test module.
278 self.decide_module_type()
279 if should_merge_test:
280 self.srcs.append(other.main_src)
281 # use a short unique name as the merged module name.
282 prefix = self.root_pkg + '_tests'
283 self.module_name = self.runner.claim_module_name(prefix, self, 0)
284 self.stem = self.module_name
285 # This normalized root_pkg name although might be the same
286 # as other module's crate_name, it is not actually used for
287 # output file name. A merged test module always have multiple
288 # source files and each source file base name is used as
289 # its output file name.
290 self.crate_name = pkg2crate_name(self.root_pkg)
291 if self.debug:
292 self.write('\n// After merge definition (1):')
293 self.dump_debug_info()
294
295 def find_cargo_dir(self):
296 """Deepest directory with Cargo.toml and contains the main_src."""
297 if not is_dependent_file_path(self.main_src):
298 dir_name = os.path.dirname(self.main_src)
299 while dir_name:
300 if os.path.exists(dir_name + '/Cargo.toml'):
301 self.cargo_dir = dir_name
302 return
303 dir_name = os.path.dirname(dir_name)
304
305 def parse(self, line_num, line):
306 """Find important rustc arguments to convert to Android.bp properties."""
307 self.line_num = line_num
308 self.line = line
309 args = line.split() # Loop through every argument of rustc.
310 i = 0
311 while i < len(args):
312 arg = args[i]
313 if arg == '--crate-name':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700314 i += 1
315 self.crate_name = args[i]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800316 elif arg == '--crate-type':
317 i += 1
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700318 # cargo calls rustc with multiple --crate-type flags.
319 # rustc can accept:
320 # --crate-type [bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]
321 self.crate_types.append(args[i])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800322 elif arg == '--test':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700323 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800324 elif arg == '--target':
325 i += 1
326 self.target = args[i]
327 elif arg == '--cfg':
328 i += 1
329 if args[i].startswith('\'feature='):
330 self.features.append(unquote(args[i].replace('\'feature=', '')[:-1]))
331 else:
332 self.cfgs.append(args[i])
333 elif arg == '--extern':
334 i += 1
335 extern_names = re.sub('=/[^ ]*/deps/', ' = ', args[i])
336 self.externs.append(extern_names)
337 self.core_externs.append(re.sub(' = .*', '', extern_names))
338 elif arg == '-C': # codegen options
339 i += 1
340 # ignore options not used in Android
341 if not (args[i].startswith('debuginfo=') or
342 args[i].startswith('extra-filename=') or
343 args[i].startswith('incremental=') or
344 args[i].startswith('metadata=')):
345 self.codegens.append(args[i])
346 elif arg == '--cap-lints':
347 i += 1
348 self.cap_lints = args[i]
349 elif arg == '-L':
350 i += 1
351 if args[i].startswith('dependency=') and args[i].endswith('/deps'):
352 if '/' + TARGET_TMP + '/' in args[i]:
353 self.root_pkg = re.sub(
354 '^.*/', '', re.sub('/' + TARGET_TMP + '/.*/deps$', '', args[i]))
355 else:
356 self.root_pkg = re.sub('^.*/', '',
357 re.sub('/[^/]+/[^/]+/deps$', '', args[i]))
358 self.root_pkg = remove_version_suffix(self.root_pkg)
359 elif arg == '-l':
360 i += 1
361 if args[i].startswith('static='):
362 self.static_libs.append(re.sub('static=', '', args[i]))
363 elif args[i].startswith('dylib='):
364 self.shared_libs.append(re.sub('dylib=', '', args[i]))
365 else:
366 self.shared_libs.append(args[i])
367 elif arg == '--out-dir' or arg == '--color': # ignored
368 i += 1
369 elif arg.startswith('--error-format=') or arg.startswith('--json='):
370 _ = arg # ignored
371 elif arg.startswith('--emit='):
372 self.emit_list = arg.replace('--emit=', '')
373 elif arg.startswith('--edition='):
374 self.edition = arg.replace('--edition=', '')
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700375 elif not arg.startswith('-'):
376 # shorten imported crate main source paths like $HOME/.cargo/
377 # registry/src/github.com-1ecc6299db9ec823/memchr-2.3.3/src/lib.rs
378 self.main_src = re.sub(r'^/[^ ]*/registry/src/', '.../', args[i])
379 self.main_src = re.sub(r'^\.\.\./github.com-[0-9a-f]*/', '.../',
380 self.main_src)
381 self.find_cargo_dir()
382 if self.cargo_dir and not self.runner.args.onefile:
383 # Write to Android.bp in the subdirectory with Cargo.toml.
384 self.outf_name = self.cargo_dir + '/Android.bp'
385 self.main_src = self.main_src[len(self.cargo_dir) + 1:]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800386 else:
387 self.errors += 'ERROR: unknown ' + arg + '\n'
388 i += 1
389 if not self.crate_name:
390 self.errors += 'ERROR: missing --crate-name\n'
391 if not self.main_src:
392 self.errors += 'ERROR: missing main source file\n'
393 else:
394 self.srcs.append(self.main_src)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700395 if not self.crate_types:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800396 # Treat "--cfg test" as "--test"
397 if 'test' in self.cfgs:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700398 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800399 else:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700400 self.errors += 'ERROR: missing --crate-type or --test\n'
401 elif len(self.crate_types) > 1:
402 if 'test' in self.crate_types:
403 self.errors += 'ERROR: cannot handle both --crate-type and --test\n'
404 if 'lib' in self.crate_types and 'rlib' in self.crate_types:
405 self.errors += 'ERROR: cannot generate both lib and rlib crate types\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800406 if not self.root_pkg:
407 self.root_pkg = self.crate_name
408 if self.target:
409 self.device_supported = True
410 self.host_supported = True # assume host supported for all builds
411 self.cfgs = sorted(set(self.cfgs))
412 self.features = sorted(set(self.features))
413 self.codegens = sorted(set(self.codegens))
414 self.externs = sorted(set(self.externs))
415 self.core_externs = sorted(set(self.core_externs))
416 self.static_libs = sorted(set(self.static_libs))
417 self.shared_libs = sorted(set(self.shared_libs))
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700418 self.crate_types = sorted(set(self.crate_types))
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800419 self.decide_module_type()
420 self.module_name = altered_name(self.stem)
421 return self
422
423 def dump_line(self):
424 self.write('\n// Line ' + str(self.line_num) + ' ' + self.line)
425
426 def feature_list(self):
427 """Return a string of main_src + "feature_list"."""
428 pkg = self.main_src
429 if pkg.startswith('.../'): # keep only the main package name
430 pkg = re.sub('/.*', '', pkg[4:])
431 if not self.features:
432 return pkg
433 return pkg + ' "' + ','.join(self.features) + '"'
434
435 def dump_skip_crate(self, kind):
436 if self.debug:
437 self.write('\n// IGNORED: ' + kind + ' ' + self.main_src)
438 return self
439
440 def skip_crate(self):
441 """Return crate_name or a message if this crate should be skipped."""
442 if is_build_crate_name(self.crate_name):
443 return self.crate_name
444 if is_dependent_file_path(self.main_src):
445 return 'dependent crate'
446 return ''
447
448 def dump(self):
449 """Dump all error/debug/module code to the output .bp file."""
450 self.runner.init_bp_file(self.outf_name)
451 with open(self.outf_name, 'a') as outf:
452 self.outf = outf
453 if self.errors:
454 self.dump_line()
455 self.write(self.errors)
456 elif self.skip_crate():
457 self.dump_skip_crate(self.skip_crate())
458 else:
459 if self.debug:
460 self.dump_debug_info()
461 self.dump_android_module()
462
463 def dump_debug_info(self):
464 """Dump parsed data, when cargo2android is called with --debug."""
465
466 def dump(name, value):
467 self.write('//%12s = %s' % (name, value))
468
469 def opt_dump(name, value):
470 if value:
471 dump(name, value)
472
473 def dump_list(fmt, values):
474 for v in values:
475 self.write(fmt % v)
476
477 self.dump_line()
478 dump('module_name', self.module_name)
479 dump('crate_name', self.crate_name)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700480 dump('crate_types', self.crate_types)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800481 dump('main_src', self.main_src)
482 dump('has_warning', self.has_warning)
483 dump('for_host', self.host_supported)
484 dump('for_device', self.device_supported)
485 dump('module_type', self.module_type)
486 opt_dump('target', self.target)
487 opt_dump('edition', self.edition)
488 opt_dump('emit_list', self.emit_list)
489 opt_dump('cap_lints', self.cap_lints)
490 dump_list('// cfg = %s', self.cfgs)
491 dump_list('// cfg = \'feature "%s"\'', self.features)
492 # TODO(chh): escape quotes in self.features, but not in other dump_list
493 dump_list('// codegen = %s', self.codegens)
494 dump_list('// externs = %s', self.externs)
495 dump_list('// -l static = %s', self.static_libs)
496 dump_list('// -l (dylib) = %s', self.shared_libs)
497
498 def dump_android_module(self):
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700499 # Dump one Android module per crate_type.
500 if len(self.crate_types) == 1:
501 # do not change self.stem or self.module_name
502 self.dump_one_android_module(self.crate_types[0])
503 return
504 for crate_type in self.crate_types:
505 self.decide_one_module_type(crate_type)
506 self.dump_one_android_module(crate_type)
507
508 def dump_one_android_module(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800509 """Dump one Android module definition."""
510 if not self.module_type:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700511 self.write('\nERROR: unknown crate_type ' + crate_type)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800512 return
513 self.write('\n' + self.module_type + ' {')
514 self.dump_android_core_properties()
515 if self.edition:
516 self.write(' edition: "' + self.edition + '",')
517 self.dump_android_property_list('features', '"%s"', self.features)
518 cfg_fmt = '"--cfg %s"'
519 if self.cap_lints:
520 allowed = '"--cap-lints ' + self.cap_lints + '"'
521 if not self.cfgs:
522 self.write(' flags: [' + allowed + '],')
523 else:
524 self.write(' flags: [\n ' + allowed + ',')
525 self.dump_android_property_list_items(cfg_fmt, self.cfgs)
526 self.write(' ],')
527 else:
528 self.dump_android_property_list('flags', cfg_fmt, self.cfgs)
529 if self.externs:
530 self.dump_android_externs()
531 self.dump_android_property_list('static_libs', '"lib%s"', self.static_libs)
532 self.dump_android_property_list('shared_libs', '"lib%s"', self.shared_libs)
533 self.write('}')
534
535 def test_module_name(self):
536 """Return a unique name for a test module."""
537 # root_pkg+'_tests_'+(crate_name|source_file_path)
538 suffix = self.crate_name
539 if not suffix:
540 suffix = re.sub('/', '_', re.sub('.rs$', '', self.main_src))
541 return self.root_pkg + '_tests_' + suffix
542
543 def decide_module_type(self):
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700544 # Use the first crate type for the default/first module.
545 crate_type = self.crate_types[0] if self.crate_types else ''
546 self.decide_one_module_type(crate_type)
547
548 def decide_one_module_type(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800549 """Decide which Android module type to use."""
550 host = '' if self.device_supported else '_host'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700551 if crate_type == 'bin': # rust_binary[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800552 self.module_type = 'rust_binary' + host
553 self.stem = self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700554 self.module_name = altered_name(self.stem)
555 elif crate_type == 'lib': # rust_library[_host]_rlib
556 # TODO(chh): should this be rust_library[_host]?
557 # Assuming that Cargo.toml do not use both 'lib' and 'rlib',
558 # because we map them both to rlib.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800559 self.module_type = 'rust_library' + host + '_rlib'
560 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700561 self.module_name = altered_name(self.stem)
562 elif crate_type == 'rlib': # rust_library[_host]_rlib
563 self.module_type = 'rust_library' + host + '_rlib'
564 self.stem = 'lib' + self.crate_name
565 self.module_name = altered_name(self.stem)
566 elif crate_type == 'dylib': # rust_library[_host]_dylib
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800567 self.module_type = 'rust_library' + host + '_dylib'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700568 self.stem = 'lib' + self.crate_name
569 self.module_name = altered_name(self.stem) + '_dylib'
570 elif crate_type == 'cdylib': # rust_library[_host]_shared
571 self.module_type = 'rust_library' + host + '_shared'
572 self.stem = 'lib' + self.crate_name
573 self.module_name = altered_name(self.stem) + '_shared'
574 elif crate_type == 'staticlib': # rust_library[_host]_static
575 self.module_type = 'rust_library' + host + '_static'
576 self.stem = 'lib' + self.crate_name
577 self.module_name = altered_name(self.stem) + '_static'
578 elif crate_type == 'test': # rust_test[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800579 self.module_type = 'rust_test' + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700580 # Before do_merge, stem name is based on the --crate-name parameter.
581 # and test module name is based on stem.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800582 self.stem = self.test_module_name()
583 # self.stem will be changed after merging with other tests.
584 # self.stem is NOT used for final test binary name.
585 # rust_test uses each source file base name as its output file name,
586 # unless crate_name is specified by user in Cargo.toml.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700587 # In do_merge, this function is called again, with a module_name.
588 # We make sure that the module name is unique in each package.
589 if self.module_name:
590 # Cargo uses "-C extra-filename=..." and "-C metadata=..." to add
591 # different suffixes and distinguish multiple tests of the same
592 # crate name. We ignore -C and use claim_module_name to get
593 # unique sequential suffix.
594 self.module_name = self.runner.claim_module_name(
595 self.module_name, self, 0)
596 # Now the module name is unique, stem should also match and unique.
597 self.stem = self.module_name
598 elif crate_type == 'proc-macro': # rust_proc_macro
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800599 self.module_type = 'rust_proc_macro'
600 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700601 self.module_name = altered_name(self.stem)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800602 else: # unknown module type, rust_prebuilt_dylib? rust_library[_host]?
603 self.module_type = ''
604 self.stem = ''
605
606 def dump_android_property_list_items(self, fmt, values):
607 for v in values:
608 # fmt has quotes, so we need escape_quotes(v)
609 self.write(' ' + (fmt % escape_quotes(v)) + ',')
610
611 def dump_android_property_list(self, name, fmt, values):
612 if values:
613 self.write(' ' + name + ': [')
614 self.dump_android_property_list_items(fmt, values)
615 self.write(' ],')
616
617 def dump_android_core_properties(self):
618 """Dump the module header, name, stem, etc."""
619 self.write(' name: "' + self.module_name + '",')
620 if self.stem != self.module_name:
621 self.write(' stem: "' + self.stem + '",')
622 if self.has_warning and not self.cap_lints:
623 self.write(' deny_warnings: false,')
624 if self.host_supported and self.device_supported:
625 self.write(' host_supported: true,')
626 self.write(' crate_name: "' + self.crate_name + '",')
627 if len(self.srcs) > 1:
628 self.srcs = sorted(set(self.srcs))
629 self.dump_android_property_list('srcs', '"%s"', self.srcs)
630 else:
631 self.write(' srcs: ["' + self.main_src + '"],')
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700632 if 'test' in self.crate_types:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800633 # self.root_pkg can have multiple test modules, with different *_tests[n]
634 # names, but their executables can all be installed under the same _tests
635 # directory. When built from Cargo.toml, all tests should have different
636 # file or crate names.
637 self.write(' relative_install_path: "' + self.root_pkg + '_tests",')
638 self.write(' test_suites: ["general-tests"],')
639 self.write(' auto_gen_config: true,')
640
641 def dump_android_externs(self):
642 """Dump the dependent rlibs and dylibs property."""
643 so_libs = list()
644 rust_libs = ''
645 deps_libname = re.compile('^.* = lib(.*)-[0-9a-f]*.(rlib|so|rmeta)$')
646 for lib in self.externs:
647 # normal value of lib: "libc = liblibc-*.rlib"
648 # strange case in rand crate: "getrandom_package = libgetrandom-*.rlib"
649 # we should use "libgetrandom", not "lib" + "getrandom_package"
650 groups = deps_libname.match(lib)
651 if groups is not None:
652 lib_name = groups.group(1)
653 else:
654 lib_name = re.sub(' .*$', '', lib)
655 if lib.endswith('.rlib') or lib.endswith('.rmeta'):
656 # On MacOS .rmeta is used when Linux uses .rlib or .rmeta.
657 rust_libs += ' "' + altered_name('lib' + lib_name) + '",\n'
658 elif lib.endswith('.so'):
659 so_libs.append(lib_name)
660 else:
661 rust_libs += ' // ERROR: unknown type of lib ' + lib_name + '\n'
662 if rust_libs:
663 self.write(' rlibs: [\n' + rust_libs + ' ],')
664 # Are all dependent .so files proc_macros?
665 # TODO(chh): Separate proc_macros and dylib.
666 self.dump_android_property_list('proc_macros', '"lib%s"', so_libs)
667
668
669class ARObject(object):
670 """Information of an "ar" link command."""
671
672 def __init__(self, runner, outf_name):
673 # Remembered global runner and its members.
674 self.runner = runner
675 self.pkg = ''
676 self.outf_name = outf_name # path to Android.bp
677 # "ar" arguments
678 self.line_num = 1
679 self.line = ''
680 self.flags = '' # e.g. "crs"
681 self.lib = '' # e.g. "/.../out/lib*.a"
682 self.objs = list() # e.g. "/.../out/.../*.o"
683
684 def parse(self, pkg, line_num, args_line):
685 """Collect ar obj/lib file names."""
686 self.pkg = pkg
687 self.line_num = line_num
688 self.line = args_line
689 args = args_line.split()
690 num_args = len(args)
691 if num_args < 3:
692 print('ERROR: "ar" command has too few arguments', args_line)
693 else:
694 self.flags = unquote(args[0])
695 self.lib = unquote(args[1])
696 self.objs = sorted(set(map(unquote, args[2:])))
697 return self
698
699 def write(self, s):
700 self.outf.write(s + '\n')
701
702 def dump_debug_info(self):
703 self.write('\n// Line ' + str(self.line_num) + ' "ar" ' + self.line)
704 self.write('// ar_object for %12s' % self.pkg)
705 self.write('// flags = %s' % self.flags)
706 self.write('// lib = %s' % short_out_name(self.pkg, self.lib))
707 for o in self.objs:
708 self.write('// obj = %s' % short_out_name(self.pkg, o))
709
710 def dump_android_lib(self):
711 """Write cc_library_static into Android.bp."""
712 self.write('\ncc_library_static {')
713 self.write(' name: "' + file_base_name(self.lib) + '",')
714 self.write(' host_supported: true,')
715 if self.flags != 'crs':
716 self.write(' // ar flags = %s' % self.flags)
717 if self.pkg not in self.runner.pkg_obj2cc:
718 self.write(' ERROR: cannot find source files.\n}')
719 return
720 self.write(' srcs: [')
721 obj2cc = self.runner.pkg_obj2cc[self.pkg]
722 # Note: wflags are ignored.
723 dflags = list()
724 fflags = list()
725 for obj in self.objs:
726 self.write(' "' + short_out_name(self.pkg, obj2cc[obj].src) + '",')
727 # TODO(chh): union of dflags and flags of all obj
728 # Now, just a temporary hack that uses the last obj's flags
729 dflags = obj2cc[obj].dflags
730 fflags = obj2cc[obj].fflags
731 self.write(' ],')
732 self.write(' cflags: [')
733 self.write(' "-O3",') # TODO(chh): is this default correct?
734 self.write(' "-Wno-error",')
735 for x in fflags:
736 self.write(' "-f' + x + '",')
737 for x in dflags:
738 self.write(' "-D' + x + '",')
739 self.write(' ],')
740 self.write('}')
741
742 def dump(self):
743 """Dump error/debug/module info to the output .bp file."""
744 self.runner.init_bp_file(self.outf_name)
745 with open(self.outf_name, 'a') as outf:
746 self.outf = outf
747 if self.runner.args.debug:
748 self.dump_debug_info()
749 self.dump_android_lib()
750
751
752class CCObject(object):
753 """Information of a "cc" compilation command."""
754
755 def __init__(self, runner, outf_name):
756 # Remembered global runner and its members.
757 self.runner = runner
758 self.pkg = ''
759 self.outf_name = outf_name # path to Android.bp
760 # "cc" arguments
761 self.line_num = 1
762 self.line = ''
763 self.src = ''
764 self.obj = ''
765 self.dflags = list() # -D flags
766 self.fflags = list() # -f flags
767 self.iflags = list() # -I flags
768 self.wflags = list() # -W flags
769 self.other_args = list()
770
771 def parse(self, pkg, line_num, args_line):
772 """Collect cc compilation flags and src/out file names."""
773 self.pkg = pkg
774 self.line_num = line_num
775 self.line = args_line
776 args = args_line.split()
777 i = 0
778 while i < len(args):
779 arg = args[i]
780 if arg == '"-c"':
781 i += 1
782 if args[i].startswith('"-o'):
783 # ring-0.13.5 dumps: ... "-c" "-o/.../*.o" ".../*.c"
784 self.obj = unquote(args[i])[2:]
785 i += 1
786 self.src = unquote(args[i])
787 else:
788 self.src = unquote(args[i])
789 elif arg == '"-o"':
790 i += 1
791 self.obj = unquote(args[i])
792 elif arg == '"-I"':
793 i += 1
794 self.iflags.append(unquote(args[i]))
795 elif arg.startswith('"-D'):
796 self.dflags.append(unquote(args[i])[2:])
797 elif arg.startswith('"-f'):
798 self.fflags.append(unquote(args[i])[2:])
799 elif arg.startswith('"-W'):
800 self.wflags.append(unquote(args[i])[2:])
801 elif not (arg.startswith('"-O') or arg == '"-m64"' or arg == '"-g"' or
802 arg == '"-g3"'):
803 # ignore -O -m64 -g
804 self.other_args.append(unquote(args[i]))
805 i += 1
806 self.dflags = sorted(set(self.dflags))
807 self.fflags = sorted(set(self.fflags))
808 # self.wflags is not sorted because some are order sensitive
809 # and we ignore them anyway.
810 if self.pkg not in self.runner.pkg_obj2cc:
811 self.runner.pkg_obj2cc[self.pkg] = {}
812 self.runner.pkg_obj2cc[self.pkg][self.obj] = self
813 return self
814
815 def write(self, s):
816 self.outf.write(s + '\n')
817
818 def dump_debug_flags(self, name, flags):
819 self.write('// ' + name + ':')
820 for f in flags:
821 self.write('// %s' % f)
822
823 def dump(self):
824 """Dump only error/debug info to the output .bp file."""
825 if not self.runner.args.debug:
826 return
827 self.runner.init_bp_file(self.outf_name)
828 with open(self.outf_name, 'a') as outf:
829 self.outf = outf
830 self.write('\n// Line ' + str(self.line_num) + ' "cc" ' + self.line)
831 self.write('// cc_object for %12s' % self.pkg)
832 self.write('// src = %s' % short_out_name(self.pkg, self.src))
833 self.write('// obj = %s' % short_out_name(self.pkg, self.obj))
834 self.dump_debug_flags('-I flags', self.iflags)
835 self.dump_debug_flags('-D flags', self.dflags)
836 self.dump_debug_flags('-f flags', self.fflags)
837 self.dump_debug_flags('-W flags', self.wflags)
838 if self.other_args:
839 self.dump_debug_flags('other args', self.other_args)
840
841
842class Runner(object):
843 """Main class to parse cargo -v output and print Android module definitions."""
844
845 def __init__(self, args):
846 self.bp_files = set() # Remember all output Android.bp files.
847 self.root_pkg = '' # name of package in ./Cargo.toml
848 # Saved flags, modes, and data.
849 self.args = args
850 self.dry_run = not args.run
851 self.skip_cargo = args.skipcargo
852 # All cc/ar objects, crates, dependencies, and warning files
853 self.cc_objects = list()
854 self.pkg_obj2cc = {}
855 # pkg_obj2cc[cc_object[i].pkg][cc_objects[i].obj] = cc_objects[i]
856 self.ar_objects = list()
857 self.crates = list()
858 self.dependencies = list() # dependent and build script crates
859 self.warning_files = set()
860 # Keep a unique mapping from (module name) to crate
861 self.name_owners = {}
862 # Default action is cargo clean, followed by build or user given actions.
863 if args.cargo:
864 self.cargo = ['clean'] + args.cargo
865 else:
866 self.cargo = ['clean', 'build']
867 default_target = '--target x86_64-unknown-linux-gnu'
868 if args.device:
869 self.cargo.append('build ' + default_target)
870 if args.tests:
871 self.cargo.append('build --tests')
872 self.cargo.append('build --tests ' + default_target)
873 elif args.tests:
874 self.cargo.append('build --tests')
875
876 def init_bp_file(self, name):
877 if name not in self.bp_files:
878 self.bp_files.add(name)
879 with open(name, 'w') as outf:
880 outf.write(ANDROID_BP_HEADER)
881
882 def claim_module_name(self, prefix, owner, counter):
883 """Return prefix if not owned yet, otherwise, prefix+str(counter)."""
884 while True:
885 name = prefix
886 if counter > 0:
887 name += str(counter)
888 if name not in self.name_owners:
889 self.name_owners[name] = owner
890 return name
891 if owner == self.name_owners[name]:
892 return name
893 counter += 1
894
895 def find_root_pkg(self):
896 """Read name of [package] in ./Cargo.toml."""
897 if not os.path.exists('./Cargo.toml'):
898 return
899 with open('./Cargo.toml', 'r') as inf:
900 pkg_section = re.compile(r'^ *\[package\]')
901 name = re.compile('^ *name *= * "([^"]*)"')
902 in_pkg = False
903 for line in inf:
904 if in_pkg:
905 if name.match(line):
906 self.root_pkg = name.match(line).group(1)
907 break
908 else:
909 in_pkg = pkg_section.match(line) is not None
910
911 def run_cargo(self):
912 """Calls cargo -v and save its output to ./cargo.out."""
913 if self.skip_cargo:
914 return self
915 cargo = './Cargo.toml'
916 if not os.access(cargo, os.R_OK):
917 print('ERROR: Cannot find or read', cargo)
918 return self
919 if not self.dry_run and os.path.exists('cargo.out'):
920 os.remove('cargo.out')
921 cmd_tail = ' --target-dir ' + TARGET_TMP + ' >> cargo.out 2>&1'
922 for c in self.cargo:
923 features = ''
Chih-Hung Hsieh6c8d52f2020-03-30 18:28:52 -0700924 if c != 'clean':
925 if self.args.features is not None:
926 features = ' --no-default-features'
927 if self.args.features:
928 features += ' --features ' + self.args.features
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800929 cmd = 'cargo -vv ' if self.args.vv else 'cargo -v '
930 cmd += c + features + cmd_tail
931 if self.args.rustflags and c != 'clean':
932 cmd = 'RUSTFLAGS="' + self.args.rustflags + '" ' + cmd
933 if self.dry_run:
934 print('Dry-run skip:', cmd)
935 else:
936 if self.args.verbose:
937 print('Running:', cmd)
938 with open('cargo.out', 'a') as cargo_out:
939 cargo_out.write('### Running: ' + cmd + '\n')
940 os.system(cmd)
941 return self
942
943 def dump_dependencies(self):
944 """Append dependencies and their features to Android.bp."""
945 if not self.dependencies:
946 return
947 dependent_list = list()
948 for c in self.dependencies:
949 dependent_list.append(c.feature_list())
950 sorted_dependencies = sorted(set(dependent_list))
951 self.init_bp_file('Android.bp')
952 with open('Android.bp', 'a') as outf:
953 outf.write('\n// dependent_library ["feature_list"]\n')
954 for s in sorted_dependencies:
955 outf.write('// ' + s + '\n')
956
957 def dump_pkg_obj2cc(self):
958 """Dump debug info of the pkg_obj2cc map."""
959 if not self.args.debug:
960 return
961 self.init_bp_file('Android.bp')
962 with open('Android.bp', 'a') as outf:
963 sorted_pkgs = sorted(self.pkg_obj2cc.keys())
964 for pkg in sorted_pkgs:
965 if not self.pkg_obj2cc[pkg]:
966 continue
967 outf.write('\n// obj => src for %s\n' % pkg)
968 obj2cc = self.pkg_obj2cc[pkg]
969 for obj in sorted(obj2cc.keys()):
970 outf.write('// ' + short_out_name(pkg, obj) + ' => ' +
971 short_out_name(pkg, obj2cc[obj].src) + '\n')
972
973 def gen_bp(self):
974 """Parse cargo.out and generate Android.bp files."""
975 if self.dry_run:
976 print('Dry-run skip: read', CARGO_OUT, 'write Android.bp')
977 elif os.path.exists(CARGO_OUT):
978 self.find_root_pkg()
979 with open(CARGO_OUT, 'r') as cargo_out:
980 self.parse(cargo_out, 'Android.bp')
981 self.crates.sort(key=get_module_name)
982 for obj in self.cc_objects:
983 obj.dump()
984 self.dump_pkg_obj2cc()
985 for crate in self.crates:
986 crate.dump()
987 dumped_libs = set()
988 for lib in self.ar_objects:
989 if lib.pkg == self.root_pkg:
990 lib_name = file_base_name(lib.lib)
991 if lib_name not in dumped_libs:
992 dumped_libs.add(lib_name)
993 lib.dump()
994 if self.args.dependencies and self.dependencies:
995 self.dump_dependencies()
996 return self
997
998 def add_ar_object(self, obj):
999 self.ar_objects.append(obj)
1000
1001 def add_cc_object(self, obj):
1002 self.cc_objects.append(obj)
1003
1004 def add_crate(self, crate):
1005 """Merge crate with someone in crates, or append to it. Return crates."""
1006 if crate.skip_crate():
1007 if self.args.debug: # include debug info of all crates
1008 self.crates.append(crate)
1009 if self.args.dependencies: # include only dependent crates
1010 if (is_dependent_file_path(crate.main_src) and
1011 not is_build_crate_name(crate.crate_name)):
1012 self.dependencies.append(crate)
1013 else:
1014 for c in self.crates:
1015 if c.merge(crate, 'Android.bp'):
1016 return
1017 self.crates.append(crate)
1018
1019 def find_warning_owners(self):
1020 """For each warning file, find its owner crate."""
1021 missing_owner = False
1022 for f in self.warning_files:
1023 cargo_dir = '' # find lowest crate, with longest path
1024 owner = None # owner crate of this warning
1025 for c in self.crates:
1026 if (f.startswith(c.cargo_dir + '/') and
1027 len(cargo_dir) < len(c.cargo_dir)):
1028 cargo_dir = c.cargo_dir
1029 owner = c
1030 if owner:
1031 owner.has_warning = True
1032 else:
1033 missing_owner = True
1034 if missing_owner and os.path.exists('Cargo.toml'):
1035 # owner is the root cargo, with empty cargo_dir
1036 for c in self.crates:
1037 if not c.cargo_dir:
1038 c.has_warning = True
1039
1040 def rustc_command(self, n, rustc_line, line, outf_name):
1041 """Process a rustc command line from cargo -vv output."""
1042 # cargo build -vv output can have multiple lines for a rustc command
1043 # due to '\n' in strings for environment variables.
1044 # strip removes leading spaces and '\n' at the end
1045 new_rustc = (rustc_line.strip() + line) if rustc_line else line
1046 # Use an heuristic to detect the completions of a multi-line command.
1047 # This might fail for some very rare case, but easy to fix manually.
1048 if not line.endswith('`\n') or (new_rustc.count('`') % 2) != 0:
1049 return new_rustc
1050 if RUSTC_VV_CMD_ARGS.match(new_rustc):
1051 args = RUSTC_VV_CMD_ARGS.match(new_rustc).group(1)
1052 self.add_crate(Crate(self, outf_name).parse(n, args))
1053 else:
1054 self.assert_empty_vv_line(new_rustc)
1055 return ''
1056
1057 def cc_ar_command(self, n, groups, outf_name):
1058 pkg = groups.group(1)
1059 line = groups.group(3)
1060 if groups.group(2) == 'cc':
1061 self.add_cc_object(CCObject(self, outf_name).parse(pkg, n, line))
1062 else:
1063 self.add_ar_object(ARObject(self, outf_name).parse(pkg, n, line))
1064
1065 def assert_empty_vv_line(self, line):
1066 if line: # report error if line is not empty
1067 self.init_bp_file('Android.bp')
1068 with open('Android.bp', 'a') as outf:
1069 outf.write('ERROR -vv line: ', line)
1070 return ''
1071
1072 def parse(self, inf, outf_name):
1073 """Parse rustc and warning messages in inf, return a list of Crates."""
1074 n = 0 # line number
1075 prev_warning = False # true if the previous line was warning: ...
1076 rustc_line = '' # previous line(s) matching RUSTC_VV_PAT
1077 for line in inf:
1078 n += 1
1079 if line.startswith('warning: '):
1080 prev_warning = True
1081 rustc_line = self.assert_empty_vv_line(rustc_line)
1082 continue
1083 new_rustc = ''
1084 if RUSTC_PAT.match(line):
1085 args_line = RUSTC_PAT.match(line).group(1)
1086 self.add_crate(Crate(self, outf_name).parse(n, args_line))
1087 self.assert_empty_vv_line(rustc_line)
1088 elif rustc_line or RUSTC_VV_PAT.match(line):
1089 new_rustc = self.rustc_command(n, rustc_line, line, outf_name)
1090 elif CC_AR_VV_PAT.match(line):
1091 self.cc_ar_command(n, CC_AR_VV_PAT.match(line), outf_name)
1092 elif prev_warning and WARNING_FILE_PAT.match(line):
1093 self.assert_empty_vv_line(rustc_line)
1094 fpath = WARNING_FILE_PAT.match(line).group(1)
1095 if fpath[0] != '/': # ignore absolute path
1096 self.warning_files.add(fpath)
1097 prev_warning = False
1098 rustc_line = new_rustc
1099 self.find_warning_owners()
1100
1101
1102def parse_args():
1103 """Parse main arguments."""
1104 parser = argparse.ArgumentParser('cargo2android')
1105 parser.add_argument(
1106 '--cargo',
1107 action='append',
1108 metavar='args_string',
1109 help=('extra cargo build -v args in a string, ' +
1110 'each --cargo flag calls cargo build -v once'))
1111 parser.add_argument(
1112 '--debug',
1113 action='store_true',
1114 default=False,
1115 help='dump debug info into Android.bp')
1116 parser.add_argument(
1117 '--dependencies',
1118 action='store_true',
1119 default=False,
1120 help='dump debug info of dependent crates')
1121 parser.add_argument(
1122 '--device',
1123 action='store_true',
1124 default=False,
1125 help='run cargo also for a default device target')
1126 parser.add_argument(
Chih-Hung Hsieh6c8d52f2020-03-30 18:28:52 -07001127 '--features', type=str,
1128 help=('pass features to cargo build, ' +
1129 'empty string means no default features'))
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001130 parser.add_argument(
1131 '--onefile',
1132 action='store_true',
1133 default=False,
1134 help=('output all into one ./Android.bp, default will generate ' +
1135 'one Android.bp per Cargo.toml in subdirectories'))
1136 parser.add_argument(
1137 '--run',
1138 action='store_true',
1139 default=False,
1140 help='run it, default is dry-run')
1141 parser.add_argument('--rustflags', type=str, help='passing flags to rustc')
1142 parser.add_argument(
1143 '--skipcargo',
1144 action='store_true',
1145 default=False,
1146 help='skip cargo command, parse cargo.out, and generate Android.bp')
1147 parser.add_argument(
1148 '--tests',
1149 action='store_true',
1150 default=False,
1151 help='run cargo build --tests after normal build')
1152 parser.add_argument(
1153 '--verbose',
1154 action='store_true',
1155 default=False,
1156 help='echo executed commands')
1157 parser.add_argument(
1158 '--vv',
1159 action='store_true',
1160 default=False,
1161 help='run cargo with -vv instead of default -v')
1162 return parser.parse_args()
1163
1164
1165def main():
1166 args = parse_args()
1167 if not args.run: # default is dry-run
1168 print(DRY_RUN_NOTE)
1169 Runner(args).run_cargo().gen_bp()
1170
1171
1172if __name__ == '__main__':
1173 main()