blob: ddb7e860c1cf9919561eb57d981d2ce33b06e3bb [file] [log] [blame]
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001#!/usr/bin/env python3
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08002#
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 Hsieh35ca4bc2020-07-10 16:49:51 -070042If there are rustc warning messages, this script will add
43a warning comment to the owner crate module in Android.bp.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080044"""
45
46from __future__ import print_function
47
48import argparse
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -070049import glob
Joel Galenson0fbdafe2021-04-21 16:33:33 -070050import json
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080051import os
52import os.path
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -070053import platform
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080054import re
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -070055import shutil
Joel Galenson7e8247e2021-05-20 18:51:42 -070056import subprocess
Andrew Walbran80e90be2020-06-09 14:33:18 +010057import sys
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080058
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -070059# Some Rust packages include extra unwanted crates.
60# This set contains all such excluded crate names.
61EXCLUDED_CRATES = set(['protobuf_bin_gen_rust_do_not_use'])
62
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080063RENAME_MAP = {
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -070064 # This map includes all changes to the default rust module names
65 # to resolve name conflicts, avoid confusion, or work as plugin.
Jason Macnak051340d2021-09-04 11:04:26 -070066 'libash': 'libash_rust',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080067 'libbacktrace': 'libbacktrace_rust',
Andrew Walbrane51f1042020-08-11 16:42:48 +010068 'libbase': 'libbase_rust',
Luke Huanga1371af2021-06-29 18:04:40 +080069 'libbase64': 'libbase64_rust',
Victor Hsieh21bea792020-12-04 10:59:16 -080070 'libfuse': 'libfuse_rust',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080071 'libgcc': 'libgcc_rust',
72 'liblog': 'liblog_rust',
Chih-Hung Hsieh07119862020-07-24 15:34:06 -070073 'libminijail': 'libminijail_rust',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080074 'libsync': 'libsync_rust',
75 'libx86_64': 'libx86_64_rust',
Jooyung Hana427c9b2021-07-16 08:53:14 +090076 'libxml': 'libxml_rust',
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -070077 'protoc_gen_rust': 'protoc-gen-rust',
78}
79
80RENAME_STEM_MAP = {
81 # This map includes all changes to the default rust module stem names,
82 # which is used for output files when different from the module name.
83 'protoc_gen_rust': 'protoc-gen-rust',
84}
85
86RENAME_DEFAULTS_MAP = {
87 # This map includes all changes to the default prefix of rust_default
88 # module names, to avoid conflict with existing Android modules.
89 'libc': 'rust_libc',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080090}
91
92# Header added to all generated Android.bp files.
Joel Galenson56446742021-02-18 08:27:48 -080093ANDROID_BP_HEADER = (
94 '// This file is generated by cargo2android.py {args}.\n' +
95 '// Do not modify this file as changes will be overridden on upgrade.\n')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080096
97CARGO_OUT = 'cargo.out' # Name of file to keep cargo build -v output.
98
Joel Galenson3f42f802021-04-07 12:42:17 -070099# This should be kept in sync with tools/external_updater/crates_updater.py.
100ERRORS_LINE = 'Errors in ' + CARGO_OUT + ':'
101
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800102TARGET_TMP = 'target.tmp' # Name of temporary output directory.
103
104# Message to be displayed when this script is called without the --run flag.
105DRY_RUN_NOTE = (
106 'Dry-run: This script uses ./' + TARGET_TMP + ' for output directory,\n' +
107 'runs cargo clean, runs cargo build -v, saves output to ./cargo.out,\n' +
108 'and writes to Android.bp in the current and subdirectories.\n\n' +
109 'To do do all of the above, use the --run flag.\n' +
110 'See --help for other flags, and more usage notes in this script.\n')
111
112# Cargo -v output of a call to rustc.
113RUSTC_PAT = re.compile('^ +Running `rustc (.*)`$')
114
115# Cargo -vv output of a call to rustc could be split into multiple lines.
116# Assume that the first line will contain some CARGO_* env definition.
117RUSTC_VV_PAT = re.compile('^ +Running `.*CARGO_.*=.*$')
118# The combined -vv output rustc command line pattern.
119RUSTC_VV_CMD_ARGS = re.compile('^ *Running `.*CARGO_.*=.* rustc (.*)`$')
120
121# Cargo -vv output of a "cc" or "ar" command; all in one line.
122CC_AR_VV_PAT = re.compile(r'^\[([^ ]*)[^\]]*\] running:? "(cc|ar)" (.*)$')
123# Some package, such as ring-0.13.5, has pattern '... running "cc"'.
124
125# Rustc output of file location path pattern for a warning message.
126WARNING_FILE_PAT = re.compile('^ *--> ([^:]*):[0-9]+')
127
Joel Galenson7cfc6fe2021-10-14 13:09:32 -0700128# cargo test --list output of the start of running a binary.
129CARGO_TEST_LIST_START_PAT = re.compile('^\s*Running (.*) \(.*\)$')
130
131# cargo test --list output of the end of running a binary.
132CARGO_TEST_LIST_END_PAT = re.compile('^(\d+) tests, (\d+) benchmarks$')
133
Joel Galenson56b5f4b2021-11-30 10:02:57 -0800134CARGO2ANDROID_RUNNING_PAT = re.compile('^### Running: .*$')
135
Joel Galenson308f3522021-09-30 14:13:45 -0700136# Rust package name with suffix -d1.d2.d3(+.*)?.
137VERSION_SUFFIX_PAT = re.compile(r'^(.*)-[0-9]+\.[0-9]+\.[0-9]+(?:\+.*)?$')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800138
Matthew Maurer062709c2021-08-17 11:27:36 -0700139# Crate types corresponding to a C ABI library
140C_LIBRARY_CRATE_TYPES = ['staticlib', 'cdylib']
141# Crate types corresponding to a Rust ABI library
142RUST_LIBRARY_CRATE_TYPES = ['lib', 'rlib', 'dylib']
143# Crate types corresponding to a library
144LIBRARY_CRATE_TYPES = C_LIBRARY_CRATE_TYPES + RUST_LIBRARY_CRATE_TYPES
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800145
146def altered_name(name):
147 return RENAME_MAP[name] if (name in RENAME_MAP) else name
148
149
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700150def altered_stem(name):
151 return RENAME_STEM_MAP[name] if (name in RENAME_STEM_MAP) else name
152
153
154def altered_defaults(name):
155 return RENAME_DEFAULTS_MAP[name] if (name in RENAME_DEFAULTS_MAP) else name
156
157
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800158def is_build_crate_name(name):
159 # We added special prefix to build script crate names.
160 return name.startswith('build_script_')
161
162
163def is_dependent_file_path(path):
164 # Absolute or dependent '.../' paths are not main files of this crate.
165 return path.startswith('/') or path.startswith('.../')
166
167
168def get_module_name(crate): # to sort crates in a list
169 return crate.module_name
170
171
172def pkg2crate_name(s):
173 return s.replace('-', '_').replace('.', '_')
174
175
176def file_base_name(path):
177 return os.path.splitext(os.path.basename(path))[0]
178
179
180def test_base_name(path):
181 return pkg2crate_name(file_base_name(path))
182
183
184def unquote(s): # remove quotes around str
185 if s and len(s) > 1 and s[0] == '"' and s[-1] == '"':
186 return s[1:-1]
187 return s
188
189
190def remove_version_suffix(s): # remove -d1.d2.d3 suffix
191 if VERSION_SUFFIX_PAT.match(s):
192 return VERSION_SUFFIX_PAT.match(s).group(1)
193 return s
194
195
196def short_out_name(pkg, s): # replace /.../pkg-*/out/* with .../out/*
197 return re.sub('^/.*/' + pkg + '-[0-9a-f]*/out/', '.../out/', s)
198
199
200def escape_quotes(s): # replace '"' with '\\"'
201 return s.replace('"', '\\"')
202
203
204class Crate(object):
205 """Information of a Rust crate to collect/emit for an Android.bp module."""
206
207 def __init__(self, runner, outf_name):
208 # Remembered global runner and its members.
209 self.runner = runner
210 self.debug = runner.args.debug
211 self.cargo_dir = '' # directory of my Cargo.toml
212 self.outf_name = outf_name # path to Android.bp
213 self.outf = None # open file handle of outf_name during dump*
214 # Variants/results that could be merged from multiple rustc lines.
215 self.host_supported = False
216 self.device_supported = False
217 self.has_warning = False
218 # Android module properties derived from rustc parameters.
219 self.module_name = '' # unique in Android build system
220 self.module_type = '' # rust_{binary,library,test}[_host] etc.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700221 self.defaults = '' # rust_defaults used by rust_test* modules
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700222 self.default_srcs = False # use 'srcs' defined in self.defaults
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800223 self.root_pkg = '' # parent package name of a sub/test packge, from -L
224 self.srcs = list() # main_src or merged multiple source files
225 self.stem = '' # real base name of output file
226 # Kept parsed status
227 self.errors = '' # all errors found during parsing
228 self.line_num = 1 # runner told input source line number
229 self.line = '' # original rustc command line parameters
230 # Parameters collected from rustc command line.
231 self.crate_name = '' # follows --crate-name
232 self.main_src = '' # follows crate_name parameter, shortened
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700233 self.crate_types = list() # follows --crate-type
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800234 self.cfgs = list() # follows --cfg, without feature= prefix
235 self.features = list() # follows --cfg, name in 'feature="..."'
236 self.codegens = list() # follows -C, some ignored
237 self.externs = list() # follows --extern
238 self.core_externs = list() # first part of self.externs elements
239 self.static_libs = list() # e.g. -l static=host_cpuid
240 self.shared_libs = list() # e.g. -l dylib=wayland-client, -l z
241 self.cap_lints = '' # follows --cap-lints
242 self.emit_list = '' # e.g., --emit=dep-info,metadata,link
243 self.edition = '2015' # rustc default, e.g., --edition=2018
244 self.target = '' # follows --target
Ivan Lozanocc660f12021-08-11 16:49:46 -0400245 self.cargo_env_compat = True
246 self.cargo_pkg_version = '' # value extracted from Cargo.toml version field
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800247
248 def write(self, s):
249 # convenient way to output one line at a time with EOL.
250 self.outf.write(s + '\n')
251
252 def same_flags(self, other):
253 # host_supported, device_supported, has_warning are not compared but merged
254 # target is not compared, to merge different target/host modules
255 # externs is not compared; only core_externs is compared
256 return (not self.errors and not other.errors and
257 self.edition == other.edition and
258 self.cap_lints == other.cap_lints and
259 self.emit_list == other.emit_list and
260 self.core_externs == other.core_externs and
261 self.codegens == other.codegens and
262 self.features == other.features and
263 self.static_libs == other.static_libs and
264 self.shared_libs == other.shared_libs and self.cfgs == other.cfgs)
265
266 def merge_host_device(self, other):
267 """Returns true if attributes are the same except host/device support."""
268 return (self.crate_name == other.crate_name and
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700269 self.crate_types == other.crate_types and
270 self.main_src == other.main_src and
271 # before merge, each test module has an unique module name and stem
272 (self.stem == other.stem or self.crate_types == ['test']) and
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800273 self.root_pkg == other.root_pkg and not self.skip_crate() and
274 self.same_flags(other))
275
276 def merge_test(self, other):
277 """Returns true if self and other are tests of same root_pkg."""
278 # Before merger, each test has its own crate_name.
279 # A merged test uses its source file base name as output file name,
280 # so a test is mergeable only if its base name equals to its crate name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700281 return (self.crate_types == other.crate_types and
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700282 self.crate_types == ['test'] and self.root_pkg == other.root_pkg and
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800283 not self.skip_crate() and
284 other.crate_name == test_base_name(other.main_src) and
285 (len(self.srcs) > 1 or
286 (self.crate_name == test_base_name(self.main_src)) and
287 self.host_supported == other.host_supported and
288 self.device_supported == other.device_supported) and
289 self.same_flags(other))
290
291 def merge(self, other, outf_name):
292 """Try to merge crate into self."""
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700293 # Cargo build --tests could recompile a library for tests.
294 # We need to merge such duplicated calls to rustc, with
295 # the algorithm in merge_host_device.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800296 should_merge_host_device = self.merge_host_device(other)
297 should_merge_test = False
298 if not should_merge_host_device:
299 should_merge_test = self.merge_test(other)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800300 if should_merge_host_device or should_merge_test:
301 self.runner.init_bp_file(outf_name)
302 with open(outf_name, 'a') as outf: # to write debug info
303 self.outf = outf
304 other.outf = outf
305 self.do_merge(other, should_merge_test)
306 return True
307 return False
308
309 def do_merge(self, other, should_merge_test):
310 """Merge attributes of other to self."""
311 if self.debug:
312 self.write('\n// Before merge definition (1):')
313 self.dump_debug_info()
314 self.write('\n// Before merge definition (2):')
315 other.dump_debug_info()
316 # Merge properties of other to self.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800317 self.has_warning = self.has_warning or other.has_warning
318 if not self.target: # okay to keep only the first target triple
319 self.target = other.target
320 # decide_module_type sets up default self.stem,
321 # which can be changed if self is a merged test module.
322 self.decide_module_type()
323 if should_merge_test:
Joel Galenson7cfc6fe2021-10-14 13:09:32 -0700324 if (self.runner.should_ignore_test(self.main_src)
325 and not self.runner.should_ignore_test(other.main_src)):
Joel Galenson57fa23a2021-07-15 10:47:35 -0700326 self.main_src = other.main_src
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800327 self.srcs.append(other.main_src)
328 # use a short unique name as the merged module name.
329 prefix = self.root_pkg + '_tests'
330 self.module_name = self.runner.claim_module_name(prefix, self, 0)
331 self.stem = self.module_name
332 # This normalized root_pkg name although might be the same
333 # as other module's crate_name, it is not actually used for
334 # output file name. A merged test module always have multiple
335 # source files and each source file base name is used as
336 # its output file name.
337 self.crate_name = pkg2crate_name(self.root_pkg)
338 if self.debug:
339 self.write('\n// After merge definition (1):')
340 self.dump_debug_info()
341
342 def find_cargo_dir(self):
343 """Deepest directory with Cargo.toml and contains the main_src."""
344 if not is_dependent_file_path(self.main_src):
345 dir_name = os.path.dirname(self.main_src)
346 while dir_name:
347 if os.path.exists(dir_name + '/Cargo.toml'):
348 self.cargo_dir = dir_name
349 return
350 dir_name = os.path.dirname(dir_name)
351
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700352 def add_codegens_flag(self, flag):
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700353 """Ignore options not used in Android."""
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700354 # 'prefer-dynamic' does not work with common flag -C lto
Chih-Hung Hsieh63459ed2020-08-26 11:51:15 -0700355 # 'embed-bitcode' is ignored; we might control LTO with other .bp flag
Chih-Hung Hsieh6c13b722020-09-11 21:24:03 -0700356 # 'codegen-units' is set in Android global config or by default
357 if not (flag.startswith('codegen-units=') or
358 flag.startswith('debuginfo=') or
Chih-Hung Hsieh63459ed2020-08-26 11:51:15 -0700359 flag.startswith('embed-bitcode=') or
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700360 flag.startswith('extra-filename=') or
361 flag.startswith('incremental=') or
362 flag.startswith('metadata=') or
363 flag == 'prefer-dynamic'):
364 self.codegens.append(flag)
365
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800366 def parse(self, line_num, line):
367 """Find important rustc arguments to convert to Android.bp properties."""
368 self.line_num = line_num
369 self.line = line
370 args = line.split() # Loop through every argument of rustc.
371 i = 0
372 while i < len(args):
373 arg = args[i]
374 if arg == '--crate-name':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700375 i += 1
376 self.crate_name = args[i]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800377 elif arg == '--crate-type':
378 i += 1
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700379 # cargo calls rustc with multiple --crate-type flags.
380 # rustc can accept:
381 # --crate-type [bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]
382 self.crate_types.append(args[i])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800383 elif arg == '--test':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700384 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800385 elif arg == '--target':
386 i += 1
387 self.target = args[i]
388 elif arg == '--cfg':
389 i += 1
390 if args[i].startswith('\'feature='):
391 self.features.append(unquote(args[i].replace('\'feature=', '')[:-1]))
392 else:
393 self.cfgs.append(args[i])
394 elif arg == '--extern':
395 i += 1
396 extern_names = re.sub('=/[^ ]*/deps/', ' = ', args[i])
397 self.externs.append(extern_names)
398 self.core_externs.append(re.sub(' = .*', '', extern_names))
399 elif arg == '-C': # codegen options
400 i += 1
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700401 self.add_codegens_flag(args[i])
402 elif arg.startswith('-C'):
403 # cargo has been passing "-C <xyz>" flag to rustc,
404 # but newer cargo could pass '-Cembed-bitcode=no' to rustc.
405 self.add_codegens_flag(arg[2:])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800406 elif arg == '--cap-lints':
407 i += 1
408 self.cap_lints = args[i]
409 elif arg == '-L':
410 i += 1
411 if args[i].startswith('dependency=') and args[i].endswith('/deps'):
412 if '/' + TARGET_TMP + '/' in args[i]:
413 self.root_pkg = re.sub(
414 '^.*/', '', re.sub('/' + TARGET_TMP + '/.*/deps$', '', args[i]))
415 else:
416 self.root_pkg = re.sub('^.*/', '',
417 re.sub('/[^/]+/[^/]+/deps$', '', args[i]))
418 self.root_pkg = remove_version_suffix(self.root_pkg)
419 elif arg == '-l':
420 i += 1
421 if args[i].startswith('static='):
422 self.static_libs.append(re.sub('static=', '', args[i]))
423 elif args[i].startswith('dylib='):
424 self.shared_libs.append(re.sub('dylib=', '', args[i]))
425 else:
426 self.shared_libs.append(args[i])
427 elif arg == '--out-dir' or arg == '--color': # ignored
428 i += 1
429 elif arg.startswith('--error-format=') or arg.startswith('--json='):
430 _ = arg # ignored
431 elif arg.startswith('--emit='):
432 self.emit_list = arg.replace('--emit=', '')
433 elif arg.startswith('--edition='):
434 self.edition = arg.replace('--edition=', '')
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700435 elif not arg.startswith('-'):
436 # shorten imported crate main source paths like $HOME/.cargo/
437 # registry/src/github.com-1ecc6299db9ec823/memchr-2.3.3/src/lib.rs
438 self.main_src = re.sub(r'^/[^ ]*/registry/src/', '.../', args[i])
439 self.main_src = re.sub(r'^\.\.\./github.com-[0-9a-f]*/', '.../',
440 self.main_src)
441 self.find_cargo_dir()
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700442 if self.cargo_dir: # for a subdirectory
443 if self.runner.args.no_subdir: # all .bp content to /dev/null
444 self.outf_name = '/dev/null'
445 elif not self.runner.args.onefile:
446 # Write to Android.bp in the subdirectory with Cargo.toml.
447 self.outf_name = self.cargo_dir + '/Android.bp'
448 self.main_src = self.main_src[len(self.cargo_dir) + 1:]
Ivan Lozanocc660f12021-08-11 16:49:46 -0400449
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800450 else:
451 self.errors += 'ERROR: unknown ' + arg + '\n'
452 i += 1
453 if not self.crate_name:
454 self.errors += 'ERROR: missing --crate-name\n'
455 if not self.main_src:
456 self.errors += 'ERROR: missing main source file\n'
457 else:
458 self.srcs.append(self.main_src)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700459 if not self.crate_types:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800460 # Treat "--cfg test" as "--test"
461 if 'test' in self.cfgs:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700462 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800463 else:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700464 self.errors += 'ERROR: missing --crate-type or --test\n'
465 elif len(self.crate_types) > 1:
466 if 'test' in self.crate_types:
467 self.errors += 'ERROR: cannot handle both --crate-type and --test\n'
468 if 'lib' in self.crate_types and 'rlib' in self.crate_types:
469 self.errors += 'ERROR: cannot generate both lib and rlib crate types\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800470 if not self.root_pkg:
471 self.root_pkg = self.crate_name
Ivan Lozano26aa1c32021-08-16 11:20:32 -0400472
473 # get the package version from running cargo metadata
Joel Galenson69ba8072021-08-16 11:31:29 -0700474 if not self.runner.args.no_pkg_vers and not self.skip_crate():
Ivan Lozano26aa1c32021-08-16 11:20:32 -0400475 self.get_pkg_version()
476
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700477 self.device_supported = self.runner.args.device
478 self.host_supported = not self.runner.args.no_host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800479 self.cfgs = sorted(set(self.cfgs))
480 self.features = sorted(set(self.features))
481 self.codegens = sorted(set(self.codegens))
482 self.externs = sorted(set(self.externs))
483 self.core_externs = sorted(set(self.core_externs))
484 self.static_libs = sorted(set(self.static_libs))
485 self.shared_libs = sorted(set(self.shared_libs))
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700486 self.crate_types = sorted(set(self.crate_types))
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800487 self.decide_module_type()
488 self.module_name = altered_name(self.stem)
489 return self
490
Ivan Lozano26aa1c32021-08-16 11:20:32 -0400491 def get_pkg_version(self):
492 """Attempt to retrieve the package version from the Cargo.toml
493
494 If there is only one package, use its version. Otherwise, try to
495 match the emitted `--crate_name` arg against the package name.
496
497 This may fail in cases where multiple packages are defined (workspaces)
498 and where the package name does not match the emitted crate_name
499 (e.g. [lib.name] is set).
500 """
Joel Galenson69ba8072021-08-16 11:31:29 -0700501 cargo_metadata = subprocess.run([self.runner.cargo_path, 'metadata', '--no-deps',
502 '--format-version', '1'],
Joel Galensonc5186502021-08-16 11:22:47 -0700503 cwd=os.path.abspath(self.cargo_dir),
504 stdout=subprocess.PIPE)
Ivan Lozano26aa1c32021-08-16 11:20:32 -0400505 if cargo_metadata.returncode:
506 self.errors += ('ERROR: unable to get cargo metadata for package version; ' +
507 'return code ' + cargo_metadata.returncode + '\n')
508 else:
509 metadata_json = json.loads(cargo_metadata.stdout)
510 if len(metadata_json['packages']) > 1:
511 for package in metadata_json['packages']:
512 # package names may contain '-', but is changed to '_' in the crate_name
513 if package['name'].replace('-','_') == self.crate_name:
514 self.cargo_pkg_version = package['version']
515 break
516 else:
517 self.cargo_pkg_version = metadata_json['packages'][0]['version']
518
519 if not self.cargo_pkg_version:
520 self.errors += ('ERROR: Unable to retrieve package version; ' +
521 'to disable, run with arg "--no-pkg-vers"\n')
522
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800523 def dump_line(self):
524 self.write('\n// Line ' + str(self.line_num) + ' ' + self.line)
525
526 def feature_list(self):
527 """Return a string of main_src + "feature_list"."""
528 pkg = self.main_src
529 if pkg.startswith('.../'): # keep only the main package name
530 pkg = re.sub('/.*', '', pkg[4:])
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700531 elif pkg.startswith('/'): # use relative path for a local package
532 pkg = os.path.relpath(pkg)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800533 if not self.features:
534 return pkg
535 return pkg + ' "' + ','.join(self.features) + '"'
536
537 def dump_skip_crate(self, kind):
538 if self.debug:
539 self.write('\n// IGNORED: ' + kind + ' ' + self.main_src)
540 return self
541
542 def skip_crate(self):
543 """Return crate_name or a message if this crate should be skipped."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700544 if (is_build_crate_name(self.crate_name) or
545 self.crate_name in EXCLUDED_CRATES):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800546 return self.crate_name
547 if is_dependent_file_path(self.main_src):
548 return 'dependent crate'
549 return ''
550
551 def dump(self):
552 """Dump all error/debug/module code to the output .bp file."""
553 self.runner.init_bp_file(self.outf_name)
554 with open(self.outf_name, 'a') as outf:
555 self.outf = outf
556 if self.errors:
557 self.dump_line()
558 self.write(self.errors)
559 elif self.skip_crate():
560 self.dump_skip_crate(self.skip_crate())
561 else:
562 if self.debug:
563 self.dump_debug_info()
564 self.dump_android_module()
565
566 def dump_debug_info(self):
567 """Dump parsed data, when cargo2android is called with --debug."""
568
569 def dump(name, value):
570 self.write('//%12s = %s' % (name, value))
571
572 def opt_dump(name, value):
573 if value:
574 dump(name, value)
575
576 def dump_list(fmt, values):
577 for v in values:
578 self.write(fmt % v)
579
580 self.dump_line()
581 dump('module_name', self.module_name)
582 dump('crate_name', self.crate_name)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700583 dump('crate_types', self.crate_types)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800584 dump('main_src', self.main_src)
585 dump('has_warning', self.has_warning)
586 dump('for_host', self.host_supported)
587 dump('for_device', self.device_supported)
588 dump('module_type', self.module_type)
589 opt_dump('target', self.target)
590 opt_dump('edition', self.edition)
591 opt_dump('emit_list', self.emit_list)
592 opt_dump('cap_lints', self.cap_lints)
593 dump_list('// cfg = %s', self.cfgs)
594 dump_list('// cfg = \'feature "%s"\'', self.features)
595 # TODO(chh): escape quotes in self.features, but not in other dump_list
596 dump_list('// codegen = %s', self.codegens)
597 dump_list('// externs = %s', self.externs)
598 dump_list('// -l static = %s', self.static_libs)
599 dump_list('// -l (dylib) = %s', self.shared_libs)
600
601 def dump_android_module(self):
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700602 """Dump one or more Android module definition, depending on crate_types."""
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700603 if len(self.crate_types) == 1:
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700604 self.dump_single_type_android_module()
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700605 return
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700606 if 'test' in self.crate_types:
607 self.write('\nERROR: multiple crate types cannot include test type')
608 return
609 # Dump one Android module per crate_type.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700610 for crate_type in self.crate_types:
611 self.decide_one_module_type(crate_type)
612 self.dump_one_android_module(crate_type)
613
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700614 def build_default_name(self):
615 """Return a short and readable name for the rust_defaults module."""
Joel Galensond37d7e62021-07-13 09:03:01 -0700616 # Choices: (1) root_pkg + '_test'? + '_defaults',
617 # (2) root_pkg + '_test'? + '_defaults_' + crate_name
618 # (3) root_pkg + '_test'? + '_defaults_' + main_src_basename_path
619 # (4) root_pkg + '_test'? + '_defaults_' + a_positive_sequence_number
620 test = "_test" if self.crate_types == ['test'] else ""
621 name1 = altered_defaults(self.root_pkg) + test + '_defaults'
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700622 if self.runner.try_claim_module_name(name1, self):
623 return name1
624 name2 = name1 + '_' + self.crate_name
625 if self.runner.try_claim_module_name(name2, self):
626 return name2
627 name3 = name1 + '_' + self.main_src_basename_path()
628 if self.runner.try_claim_module_name(name3, self):
629 return name3
630 return self.runner.claim_module_name(name1, self, 0)
631
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700632 def dump_srcs_list(self):
633 """Dump the srcs list, for defaults or regular modules."""
634 if len(self.srcs) > 1:
635 srcs = sorted(set(self.srcs)) # make a copy and dedup
636 else:
637 srcs = [self.main_src]
638 copy_out = self.runner.copy_out_module_name()
639 if copy_out:
640 srcs.append(':' + copy_out)
641 self.dump_android_property_list('srcs', '"%s"', srcs)
642
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700643 def dump_defaults_module(self):
644 """Dump a rust_defaults module to be shared by other modules."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700645 name = self.build_default_name()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700646 self.defaults = name
647 self.write('\nrust_defaults {')
648 self.write(' name: "' + name + '",')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700649 if self.runner.args.global_defaults:
650 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700651 self.write(' crate_name: "' + self.crate_name + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700652 if len(self.srcs) == 1: # only one source file; share it in defaults
653 self.default_srcs = True
654 if self.has_warning and not self.cap_lints:
655 self.write(' // has rustc warnings')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700656 self.dump_srcs_list()
Joel Galensonb06c42a2021-08-31 14:28:48 -0700657 if self.cargo_env_compat:
658 self.write(' cargo_env_compat: true,')
Joel Galenson94b8a8d2021-09-28 11:53:51 -0700659 if not self.runner.args.no_pkg_vers:
660 self.write(' cargo_pkg_version: "' + self.cargo_pkg_version + '",')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700661 if 'test' in self.crate_types:
662 self.write(' test_suites: ["general-tests"],')
663 self.write(' auto_gen_config: true,')
664 self.dump_edition_flags_libs()
Joel Galensone4f53882021-07-19 11:14:55 -0700665 if 'test' in self.crate_types and len(self.srcs) == 1:
666 self.dump_test_data()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700667 self.write('}')
668
669 def dump_single_type_android_module(self):
670 """Dump one simple Android module, which has only one crate_type."""
671 crate_type = self.crate_types[0]
672 if crate_type != 'test':
673 # do not change self.stem or self.module_name
674 self.dump_one_android_module(crate_type)
675 return
Joel Galenson54d65532021-08-31 14:08:05 -0700676 # Dump one test module per source file.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700677 # crate_type == 'test'
Joel Galenson7cfc6fe2021-10-14 13:09:32 -0700678 self.srcs = [src for src in self.srcs if not self.runner.should_ignore_test(src)]
Joel Galenson54d65532021-08-31 14:08:05 -0700679 if len(self.srcs) > 1:
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700680 self.srcs = sorted(set(self.srcs))
681 self.dump_defaults_module()
682 saved_srcs = self.srcs
683 for src in saved_srcs:
684 self.srcs = [src]
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700685 saved_main_src = self.main_src
686 self.main_src = src
Joel Galenson54d65532021-08-31 14:08:05 -0700687 self.module_name = self.test_module_name()
688 self.decide_one_module_type(crate_type)
689 self.dump_one_android_module(crate_type)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700690 self.main_src = saved_main_src
691 self.srcs = saved_srcs
692
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700693 def dump_one_android_module(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800694 """Dump one Android module definition."""
695 if not self.module_type:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700696 self.write('\nERROR: unknown crate_type ' + crate_type)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800697 return
698 self.write('\n' + self.module_type + ' {')
699 self.dump_android_core_properties()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700700 if not self.defaults:
701 self.dump_edition_flags_libs()
702 if self.runner.args.host_first_multilib and self.host_supported and crate_type != 'test':
703 self.write(' compile_multilib: "first",')
Matthew Maurer062709c2021-08-17 11:27:36 -0700704 if self.runner.args.exported_c_header_dir and crate_type in C_LIBRARY_CRATE_TYPES:
705 self.write(' include_dirs: [')
706 for header_dir in self.runner.args.exported_c_header_dir:
707 self.write(' "%s",' % header_dir)
708 self.write(' ],')
Matthew Maurer9e4b7812021-08-16 14:21:01 -0700709 if self.runner.args.apex_available and crate_type in LIBRARY_CRATE_TYPES:
Joel Galensond9c4de62021-04-23 10:26:40 -0700710 self.write(' apex_available: [')
711 for apex in self.runner.args.apex_available:
712 self.write(' "%s",' % apex)
713 self.write(' ],')
Matthew Maurer70182e42021-08-17 13:53:52 -0700714 if crate_type != 'test':
715 if self.runner.args.native_bridge_supported:
716 self.write(' native_bridge_supported: true,')
717 if self.runner.args.product_available:
718 self.write(' product_available: true,')
719 if self.runner.args.recovery_available:
720 self.write(' recovery_available: true,')
721 if self.runner.args.vendor_available:
722 self.write(' vendor_available: true,')
723 if self.runner.args.vendor_ramdisk_available:
724 self.write(' vendor_ramdisk_available: true,')
725 if self.runner.args.ramdisk_available:
726 self.write(' ramdisk_available: true,')
Matthew Maurer9e4b7812021-08-16 14:21:01 -0700727 if self.runner.args.min_sdk_version and crate_type in LIBRARY_CRATE_TYPES:
Joel Galensond9c4de62021-04-23 10:26:40 -0700728 self.write(' min_sdk_version: "%s",' % self.runner.args.min_sdk_version)
Joel Galensone4f53882021-07-19 11:14:55 -0700729 if crate_type == 'test' and not self.default_srcs:
730 self.dump_test_data()
Joel Galenson5664f2a2021-06-10 10:13:49 -0700731 if self.runner.args.add_module_block:
732 with open(self.runner.args.add_module_block, 'r') as f:
733 self.write(' %s,' % f.read().replace('\n', '\n '))
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700734 self.write('}')
735
736 def dump_android_flags(self):
737 """Dump Android module flags property."""
ThiƩbaud Weksteena5a728b2021-04-08 14:23:49 +0200738 if not self.codegens and not self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700739 return
740 self.write(' flags: [')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800741 if self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700742 self.write(' "--cap-lints ' + self.cap_lints + '",')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700743 codegens_fmt = '"-C %s"'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700744 self.dump_android_property_list_items(codegens_fmt, self.codegens)
745 self.write(' ],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700746
747 def dump_edition_flags_libs(self):
748 if self.edition:
749 self.write(' edition: "' + self.edition + '",')
750 self.dump_android_property_list('features', '"%s"', self.features)
Joel Galenson3d6d1e72021-06-07 15:00:24 -0700751 cfgs = [cfg for cfg in self.cfgs if not cfg in self.runner.args.cfg_blocklist]
752 self.dump_android_property_list('cfgs', '"%s"', cfgs)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700753 self.dump_android_flags()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800754 if self.externs:
755 self.dump_android_externs()
Joel Galenson12467e52021-07-12 14:33:28 -0700756 all_static_libs = [lib for lib in self.static_libs if not lib in self.runner.args.lib_blocklist]
757 static_libs = [lib for lib in all_static_libs if not lib in self.runner.args.whole_static_libs]
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700758 self.dump_android_property_list('static_libs', '"lib%s"', static_libs)
Joel Galenson12467e52021-07-12 14:33:28 -0700759 whole_static_libs = [lib for lib in all_static_libs if lib in self.runner.args.whole_static_libs]
760 self.dump_android_property_list('whole_static_libs', '"lib%s"', whole_static_libs)
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700761 shared_libs = [lib for lib in self.shared_libs if not lib in self.runner.args.lib_blocklist]
762 self.dump_android_property_list('shared_libs', '"lib%s"', shared_libs)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800763
Joel Galensone4f53882021-07-19 11:14:55 -0700764 def dump_test_data(self):
765 data = [data for (name, data) in map(lambda kv: kv.split('=', 1), self.runner.args.test_data)
766 if self.srcs == [name]]
767 if data:
768 self.dump_android_property_list('data', '"%s"', data)
769
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700770 def main_src_basename_path(self):
771 return re.sub('/', '_', re.sub('.rs$', '', self.main_src))
772
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800773 def test_module_name(self):
774 """Return a unique name for a test module."""
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700775 # root_pkg+(_host|_device) + '_test_'+source_file_name
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700776 suffix = self.main_src_basename_path()
Joel Galenson54d65532021-08-31 14:08:05 -0700777 return self.root_pkg + '_test_' + suffix
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800778
779 def decide_module_type(self):
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700780 # Use the first crate type for the default/first module.
781 crate_type = self.crate_types[0] if self.crate_types else ''
782 self.decide_one_module_type(crate_type)
783
784 def decide_one_module_type(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800785 """Decide which Android module type to use."""
786 host = '' if self.device_supported else '_host'
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700787 rlib = '_rlib' if self.runner.args.force_rlib else ''
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700788 if crate_type == 'bin': # rust_binary[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800789 self.module_type = 'rust_binary' + host
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700790 # In rare cases like protobuf-codegen, the output binary name must
791 # be renamed to use as a plugin for protoc.
792 self.stem = altered_stem(self.crate_name)
793 self.module_name = altered_name(self.crate_name)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700794 elif crate_type == 'lib': # rust_library[_host]
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700795 # TODO(chh): should this be rust_library[_host]?
796 # Assuming that Cargo.toml do not use both 'lib' and 'rlib',
797 # because we map them both to rlib.
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700798 self.module_type = 'rust_library' + rlib + host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800799 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700800 self.module_name = altered_name(self.stem)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700801 elif crate_type == 'rlib': # rust_library[_host]
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700802 self.module_type = 'rust_library' + rlib + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700803 self.stem = 'lib' + self.crate_name
804 self.module_name = altered_name(self.stem)
805 elif crate_type == 'dylib': # rust_library[_host]_dylib
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800806 self.module_type = 'rust_library' + host + '_dylib'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700807 self.stem = 'lib' + self.crate_name
808 self.module_name = altered_name(self.stem) + '_dylib'
809 elif crate_type == 'cdylib': # rust_library[_host]_shared
Ivan Lozano0c057ad2020-12-15 10:41:26 -0500810 self.module_type = 'rust_ffi' + host + '_shared'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700811 self.stem = 'lib' + self.crate_name
812 self.module_name = altered_name(self.stem) + '_shared'
813 elif crate_type == 'staticlib': # rust_library[_host]_static
Ivan Lozano0c057ad2020-12-15 10:41:26 -0500814 self.module_type = 'rust_ffi' + host + '_static'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700815 self.stem = 'lib' + self.crate_name
816 self.module_name = altered_name(self.stem) + '_static'
817 elif crate_type == 'test': # rust_test[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800818 self.module_type = 'rust_test' + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700819 # Before do_merge, stem name is based on the --crate-name parameter.
820 # and test module name is based on stem.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800821 self.stem = self.test_module_name()
822 # self.stem will be changed after merging with other tests.
823 # self.stem is NOT used for final test binary name.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700824 # rust_test uses each source file base name as part of output file name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700825 # In do_merge, this function is called again, with a module_name.
826 # We make sure that the module name is unique in each package.
827 if self.module_name:
828 # Cargo uses "-C extra-filename=..." and "-C metadata=..." to add
829 # different suffixes and distinguish multiple tests of the same
830 # crate name. We ignore -C and use claim_module_name to get
831 # unique sequential suffix.
832 self.module_name = self.runner.claim_module_name(
833 self.module_name, self, 0)
834 # Now the module name is unique, stem should also match and unique.
835 self.stem = self.module_name
836 elif crate_type == 'proc-macro': # rust_proc_macro
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800837 self.module_type = 'rust_proc_macro'
838 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700839 self.module_name = altered_name(self.stem)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800840 else: # unknown module type, rust_prebuilt_dylib? rust_library[_host]?
841 self.module_type = ''
842 self.stem = ''
843
844 def dump_android_property_list_items(self, fmt, values):
845 for v in values:
846 # fmt has quotes, so we need escape_quotes(v)
847 self.write(' ' + (fmt % escape_quotes(v)) + ',')
848
849 def dump_android_property_list(self, name, fmt, values):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700850 if not values:
851 return
852 if len(values) > 1:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800853 self.write(' ' + name + ': [')
854 self.dump_android_property_list_items(fmt, values)
855 self.write(' ],')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700856 else:
857 self.write(' ' + name + ': [' +
858 (fmt % escape_quotes(values[0])) + '],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800859
860 def dump_android_core_properties(self):
861 """Dump the module header, name, stem, etc."""
862 self.write(' name: "' + self.module_name + '",')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700863 # see properties shared by dump_defaults_module
864 if self.defaults:
865 self.write(' defaults: ["' + self.defaults + '"],')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700866 elif self.runner.args.global_defaults:
867 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800868 if self.stem != self.module_name:
869 self.write(' stem: "' + self.stem + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700870 if self.has_warning and not self.cap_lints and not self.default_srcs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700871 self.write(' // has rustc warnings')
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700872 if self.host_supported and self.device_supported and self.module_type != 'rust_proc_macro':
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800873 self.write(' host_supported: true,')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700874 if not self.defaults:
875 self.write(' crate_name: "' + self.crate_name + '",')
Ivan Lozanocc660f12021-08-11 16:49:46 -0400876 if not self.defaults and self.cargo_env_compat:
877 self.write(' cargo_env_compat: true,')
Joel Galenson94b8a8d2021-09-28 11:53:51 -0700878 if not self.runner.args.no_pkg_vers:
879 self.write(' cargo_pkg_version: "' + self.cargo_pkg_version + '",')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700880 if not self.default_srcs:
881 self.dump_srcs_list()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700882 if 'test' in self.crate_types and not self.defaults:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800883 # self.root_pkg can have multiple test modules, with different *_tests[n]
884 # names, but their executables can all be installed under the same _tests
885 # directory. When built from Cargo.toml, all tests should have different
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700886 # file or crate names. So we used (root_pkg + '_tests') name as the
887 # relative_install_path.
888 # However, some package like 'slab' can have non-mergeable tests that
889 # must be separated by different module names. So, here we no longer
890 # emit relative_install_path.
891 # self.write(' relative_install_path: "' + self.root_pkg + '_tests",')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800892 self.write(' test_suites: ["general-tests"],')
893 self.write(' auto_gen_config: true,')
Joel Galensone261a152021-01-12 11:31:53 -0800894 if 'test' in self.crate_types and self.host_supported:
895 self.write(' test_options: {')
896 self.write(' unit_test: true,')
897 self.write(' },')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800898
899 def dump_android_externs(self):
900 """Dump the dependent rlibs and dylibs property."""
901 so_libs = list()
902 rust_libs = ''
903 deps_libname = re.compile('^.* = lib(.*)-[0-9a-f]*.(rlib|so|rmeta)$')
904 for lib in self.externs:
905 # normal value of lib: "libc = liblibc-*.rlib"
906 # strange case in rand crate: "getrandom_package = libgetrandom-*.rlib"
907 # we should use "libgetrandom", not "lib" + "getrandom_package"
908 groups = deps_libname.match(lib)
909 if groups is not None:
910 lib_name = groups.group(1)
911 else:
912 lib_name = re.sub(' .*$', '', lib)
Joel Galenson97e414a2021-05-27 09:42:32 -0700913 if lib_name in self.runner.args.dependency_blocklist:
914 continue
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800915 if lib.endswith('.rlib') or lib.endswith('.rmeta'):
916 # On MacOS .rmeta is used when Linux uses .rlib or .rmeta.
917 rust_libs += ' "' + altered_name('lib' + lib_name) + '",\n'
918 elif lib.endswith('.so'):
919 so_libs.append(lib_name)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700920 elif lib != 'proc_macro': # --extern proc_macro is special and ignored
921 rust_libs += ' // ERROR: unknown type of lib ' + lib + '\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800922 if rust_libs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700923 self.write(' rustlibs: [\n' + rust_libs + ' ],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800924 # Are all dependent .so files proc_macros?
925 # TODO(chh): Separate proc_macros and dylib.
926 self.dump_android_property_list('proc_macros', '"lib%s"', so_libs)
927
928
929class ARObject(object):
930 """Information of an "ar" link command."""
931
932 def __init__(self, runner, outf_name):
933 # Remembered global runner and its members.
934 self.runner = runner
935 self.pkg = ''
936 self.outf_name = outf_name # path to Android.bp
937 # "ar" arguments
938 self.line_num = 1
939 self.line = ''
940 self.flags = '' # e.g. "crs"
941 self.lib = '' # e.g. "/.../out/lib*.a"
942 self.objs = list() # e.g. "/.../out/.../*.o"
943
944 def parse(self, pkg, line_num, args_line):
945 """Collect ar obj/lib file names."""
946 self.pkg = pkg
947 self.line_num = line_num
948 self.line = args_line
949 args = args_line.split()
950 num_args = len(args)
951 if num_args < 3:
952 print('ERROR: "ar" command has too few arguments', args_line)
953 else:
954 self.flags = unquote(args[0])
955 self.lib = unquote(args[1])
956 self.objs = sorted(set(map(unquote, args[2:])))
957 return self
958
959 def write(self, s):
960 self.outf.write(s + '\n')
961
962 def dump_debug_info(self):
963 self.write('\n// Line ' + str(self.line_num) + ' "ar" ' + self.line)
964 self.write('// ar_object for %12s' % self.pkg)
965 self.write('// flags = %s' % self.flags)
966 self.write('// lib = %s' % short_out_name(self.pkg, self.lib))
967 for o in self.objs:
968 self.write('// obj = %s' % short_out_name(self.pkg, o))
969
970 def dump_android_lib(self):
971 """Write cc_library_static into Android.bp."""
972 self.write('\ncc_library_static {')
973 self.write(' name: "' + file_base_name(self.lib) + '",')
974 self.write(' host_supported: true,')
975 if self.flags != 'crs':
976 self.write(' // ar flags = %s' % self.flags)
977 if self.pkg not in self.runner.pkg_obj2cc:
978 self.write(' ERROR: cannot find source files.\n}')
979 return
980 self.write(' srcs: [')
981 obj2cc = self.runner.pkg_obj2cc[self.pkg]
982 # Note: wflags are ignored.
983 dflags = list()
984 fflags = list()
985 for obj in self.objs:
986 self.write(' "' + short_out_name(self.pkg, obj2cc[obj].src) + '",')
987 # TODO(chh): union of dflags and flags of all obj
988 # Now, just a temporary hack that uses the last obj's flags
989 dflags = obj2cc[obj].dflags
990 fflags = obj2cc[obj].fflags
991 self.write(' ],')
992 self.write(' cflags: [')
993 self.write(' "-O3",') # TODO(chh): is this default correct?
994 self.write(' "-Wno-error",')
995 for x in fflags:
996 self.write(' "-f' + x + '",')
997 for x in dflags:
998 self.write(' "-D' + x + '",')
999 self.write(' ],')
1000 self.write('}')
1001
1002 def dump(self):
1003 """Dump error/debug/module info to the output .bp file."""
1004 self.runner.init_bp_file(self.outf_name)
1005 with open(self.outf_name, 'a') as outf:
1006 self.outf = outf
1007 if self.runner.args.debug:
1008 self.dump_debug_info()
1009 self.dump_android_lib()
1010
1011
1012class CCObject(object):
1013 """Information of a "cc" compilation command."""
1014
1015 def __init__(self, runner, outf_name):
1016 # Remembered global runner and its members.
1017 self.runner = runner
1018 self.pkg = ''
1019 self.outf_name = outf_name # path to Android.bp
1020 # "cc" arguments
1021 self.line_num = 1
1022 self.line = ''
1023 self.src = ''
1024 self.obj = ''
1025 self.dflags = list() # -D flags
1026 self.fflags = list() # -f flags
1027 self.iflags = list() # -I flags
1028 self.wflags = list() # -W flags
1029 self.other_args = list()
1030
1031 def parse(self, pkg, line_num, args_line):
1032 """Collect cc compilation flags and src/out file names."""
1033 self.pkg = pkg
1034 self.line_num = line_num
1035 self.line = args_line
1036 args = args_line.split()
1037 i = 0
1038 while i < len(args):
1039 arg = args[i]
1040 if arg == '"-c"':
1041 i += 1
1042 if args[i].startswith('"-o'):
1043 # ring-0.13.5 dumps: ... "-c" "-o/.../*.o" ".../*.c"
1044 self.obj = unquote(args[i])[2:]
1045 i += 1
1046 self.src = unquote(args[i])
1047 else:
1048 self.src = unquote(args[i])
1049 elif arg == '"-o"':
1050 i += 1
1051 self.obj = unquote(args[i])
1052 elif arg == '"-I"':
1053 i += 1
1054 self.iflags.append(unquote(args[i]))
1055 elif arg.startswith('"-D'):
1056 self.dflags.append(unquote(args[i])[2:])
1057 elif arg.startswith('"-f'):
1058 self.fflags.append(unquote(args[i])[2:])
1059 elif arg.startswith('"-W'):
1060 self.wflags.append(unquote(args[i])[2:])
1061 elif not (arg.startswith('"-O') or arg == '"-m64"' or arg == '"-g"' or
1062 arg == '"-g3"'):
1063 # ignore -O -m64 -g
1064 self.other_args.append(unquote(args[i]))
1065 i += 1
1066 self.dflags = sorted(set(self.dflags))
1067 self.fflags = sorted(set(self.fflags))
1068 # self.wflags is not sorted because some are order sensitive
1069 # and we ignore them anyway.
1070 if self.pkg not in self.runner.pkg_obj2cc:
1071 self.runner.pkg_obj2cc[self.pkg] = {}
1072 self.runner.pkg_obj2cc[self.pkg][self.obj] = self
1073 return self
1074
1075 def write(self, s):
1076 self.outf.write(s + '\n')
1077
1078 def dump_debug_flags(self, name, flags):
1079 self.write('// ' + name + ':')
1080 for f in flags:
1081 self.write('// %s' % f)
1082
1083 def dump(self):
1084 """Dump only error/debug info to the output .bp file."""
1085 if not self.runner.args.debug:
1086 return
1087 self.runner.init_bp_file(self.outf_name)
1088 with open(self.outf_name, 'a') as outf:
1089 self.outf = outf
1090 self.write('\n// Line ' + str(self.line_num) + ' "cc" ' + self.line)
1091 self.write('// cc_object for %12s' % self.pkg)
1092 self.write('// src = %s' % short_out_name(self.pkg, self.src))
1093 self.write('// obj = %s' % short_out_name(self.pkg, self.obj))
1094 self.dump_debug_flags('-I flags', self.iflags)
1095 self.dump_debug_flags('-D flags', self.dflags)
1096 self.dump_debug_flags('-f flags', self.fflags)
1097 self.dump_debug_flags('-W flags', self.wflags)
1098 if self.other_args:
1099 self.dump_debug_flags('other args', self.other_args)
1100
1101
1102class Runner(object):
1103 """Main class to parse cargo -v output and print Android module definitions."""
1104
1105 def __init__(self, args):
1106 self.bp_files = set() # Remember all output Android.bp files.
1107 self.root_pkg = '' # name of package in ./Cargo.toml
1108 # Saved flags, modes, and data.
1109 self.args = args
1110 self.dry_run = not args.run
1111 self.skip_cargo = args.skipcargo
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001112 self.cargo_path = './cargo' # path to cargo, will be set later
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001113 self.checked_out_files = False # to check only once
1114 self.build_out_files = [] # output files generated by build.rs
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001115 # All cc/ar objects, crates, dependencies, and warning files
1116 self.cc_objects = list()
1117 self.pkg_obj2cc = {}
1118 # pkg_obj2cc[cc_object[i].pkg][cc_objects[i].obj] = cc_objects[i]
1119 self.ar_objects = list()
1120 self.crates = list()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001121 self.warning_files = set()
1122 # Keep a unique mapping from (module name) to crate
1123 self.name_owners = {}
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001124 # Save and dump all errors from cargo to Android.bp.
1125 self.errors = ''
Joel Galenson56b5f4b2021-11-30 10:02:57 -08001126 self.test_errors = ''
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001127 self.setup_cargo_path()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001128 # Default action is cargo clean, followed by build or user given actions.
1129 if args.cargo:
1130 self.cargo = ['clean'] + args.cargo
1131 else:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001132 default_target = '--target x86_64-unknown-linux-gnu'
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -07001133 # Use the same target for both host and default device builds.
1134 # Same target is used as default in host x86_64 Android compilation.
1135 # Note: b/169872957, prebuilt cargo failed to build vsock
1136 # on x86_64-unknown-linux-musl systems.
1137 self.cargo = ['clean', 'build ' + default_target]
1138 if args.tests:
1139 self.cargo.append('build --tests ' + default_target)
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001140 self.empty_tests = set()
1141 self.empty_unittests = False
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001142
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001143 def setup_cargo_path(self):
1144 """Find cargo in the --cargo_bin or prebuilt rust bin directory."""
1145 if self.args.cargo_bin:
1146 self.cargo_path = os.path.join(self.args.cargo_bin, 'cargo')
1147 if not os.path.isfile(self.cargo_path):
1148 sys.exit('ERROR: cannot find cargo in ' + self.args.cargo_bin)
1149 print('WARNING: using cargo in ' + self.args.cargo_bin)
1150 return
1151 # We have only tested this on Linux.
1152 if platform.system() != 'Linux':
1153 sys.exit('ERROR: this script has only been tested on Linux with cargo.')
1154 # Assuming that this script is in development/scripts.
1155 my_dir = os.path.dirname(os.path.abspath(__file__))
1156 linux_dir = os.path.join(my_dir, '..', '..',
1157 'prebuilts', 'rust', 'linux-x86')
1158 if not os.path.isdir(linux_dir):
1159 sys.exit('ERROR: cannot find directory ' + linux_dir)
1160 rust_version = self.find_rust_version(my_dir, linux_dir)
1161 cargo_bin = os.path.join(linux_dir, rust_version, 'bin')
1162 self.cargo_path = os.path.join(cargo_bin, 'cargo')
1163 if not os.path.isfile(self.cargo_path):
1164 sys.exit('ERROR: cannot find cargo in ' + cargo_bin
1165 + '; please try --cargo_bin= flag.')
1166 return
1167
1168 def find_rust_version(self, my_dir, linux_dir):
1169 """Use my script directory, find prebuilt rust version."""
1170 # First look up build/soong/rust/config/global.go.
1171 path2global = os.path.join(my_dir, '..', '..',
1172 'build', 'soong', 'rust', 'config', 'global.go')
1173 if os.path.isfile(path2global):
1174 # try to find: RustDefaultVersion = "1.44.0"
1175 version_pat = re.compile(
1176 r'\s*RustDefaultVersion\s*=\s*"([0-9]+\.[0-9]+\.[0-9]+)".*$')
1177 with open(path2global, 'r') as inf:
1178 for line in inf:
1179 result = version_pat.match(line)
1180 if result:
1181 return result.group(1)
1182 print('WARNING: cannot find RustDefaultVersion in ' + path2global)
1183 # Otherwise, find the newest (largest) version number in linux_dir.
1184 rust_version = (0, 0, 0) # the prebuilt version to use
1185 version_pat = re.compile(r'([0-9]+)\.([0-9]+)\.([0-9]+)$')
1186 for dir_name in os.listdir(linux_dir):
1187 result = version_pat.match(dir_name)
1188 if not result:
1189 continue
1190 version = (result.group(1), result.group(2), result.group(3))
1191 if version > rust_version:
1192 rust_version = version
1193 return '.'.join(rust_version)
1194
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001195 def find_out_files(self):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001196 # list1 has build.rs output for normal crates
1197 list1 = glob.glob(TARGET_TMP + '/*/*/build/' + self.root_pkg + '-*/out/*')
1198 # list2 has build.rs output for proc-macro crates
1199 list2 = glob.glob(TARGET_TMP + '/*/build/' + self.root_pkg + '-*/out/*')
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001200 return list1 + list2
1201
1202 def copy_out_files(self):
1203 """Copy build.rs output files to ./out and set up build_out_files."""
1204 if self.checked_out_files:
1205 return
1206 self.checked_out_files = True
1207 cargo_out_files = self.find_out_files()
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001208 out_files = set()
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001209 if cargo_out_files:
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001210 os.makedirs('out', exist_ok=True)
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001211 for path in cargo_out_files:
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001212 file_name = path.split('/')[-1]
1213 out_files.add(file_name)
1214 shutil.copy(path, 'out/' + file_name)
1215 self.build_out_files = sorted(out_files)
1216
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001217 def has_used_out_dir(self):
1218 """Returns true if env!("OUT_DIR") is found."""
1219 return 0 == os.system('grep -rl --exclude build.rs --include \\*.rs' +
1220 ' \'env!("OUT_DIR")\' * > /dev/null')
1221
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001222 def copy_out_module_name(self):
1223 if self.args.copy_out and self.build_out_files:
1224 return 'copy_' + self.root_pkg + '_build_out'
1225 else:
1226 return ''
1227
Haibo Huang0f72c952021-03-19 11:34:15 -07001228 def read_license(self, name):
1229 if not os.path.isfile(name):
1230 return ''
1231 license = ''
1232 with open(name, 'r') as intf:
1233 line = intf.readline()
1234 # Firstly skip ANDROID_BP_HEADER
1235 while line.startswith('//'):
1236 line = intf.readline()
Joel Galensond9d13b82021-04-05 11:27:55 -07001237 # Read all lines until we see a rust_* or genrule rule.
1238 while line != '' and not (line.startswith('rust_') or line.startswith('genrule {')):
Haibo Huang0f72c952021-03-19 11:34:15 -07001239 license += line
1240 line = intf.readline()
1241 return license.strip()
1242
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001243 def dump_copy_out_module(self, outf):
1244 """Output the genrule module to copy out/* to $(genDir)."""
1245 copy_out = self.copy_out_module_name()
1246 if not copy_out:
1247 return
1248 outf.write('\ngenrule {\n')
1249 outf.write(' name: "' + copy_out + '",\n')
1250 outf.write(' srcs: ["out/*"],\n')
1251 outf.write(' cmd: "cp $(in) $(genDir)",\n')
1252 if len(self.build_out_files) > 1:
1253 outf.write(' out: [\n')
1254 for f in self.build_out_files:
1255 outf.write(' "' + f + '",\n')
1256 outf.write(' ],\n')
1257 else:
1258 outf.write(' out: ["' + self.build_out_files[0] + '"],\n')
1259 outf.write('}\n')
1260
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001261 def init_bp_file(self, name):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001262 # name could be Android.bp or sub_dir_path/Android.bp
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001263 if name not in self.bp_files:
1264 self.bp_files.add(name)
Haibo Huang0f72c952021-03-19 11:34:15 -07001265 license_section = self.read_license(name)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001266 with open(name, 'w') as outf:
Joel Galenson769c3c32021-11-24 15:04:29 -08001267 print_args = sys.argv[1:].copy()
1268 if '--cargo_bin' in print_args:
1269 index = print_args.index('--cargo_bin')
1270 del print_args[index:index+2]
1271 outf.write(ANDROID_BP_HEADER.format(args=' '.join(print_args)))
Haibo Huang0f72c952021-03-19 11:34:15 -07001272 outf.write('\n')
1273 outf.write(license_section)
1274 outf.write('\n')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001275 # at most one copy_out module per .bp file
1276 self.dump_copy_out_module(outf)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001277
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001278 def try_claim_module_name(self, name, owner):
1279 """Reserve and return True if it has not been reserved yet."""
1280 if name not in self.name_owners or owner == self.name_owners[name]:
1281 self.name_owners[name] = owner
1282 return True
1283 return False
1284
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001285 def claim_module_name(self, prefix, owner, counter):
1286 """Return prefix if not owned yet, otherwise, prefix+str(counter)."""
1287 while True:
1288 name = prefix
1289 if counter > 0:
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001290 name += '_' + str(counter)
1291 if self.try_claim_module_name(name, owner):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001292 return name
1293 counter += 1
1294
1295 def find_root_pkg(self):
1296 """Read name of [package] in ./Cargo.toml."""
1297 if not os.path.exists('./Cargo.toml'):
1298 return
1299 with open('./Cargo.toml', 'r') as inf:
1300 pkg_section = re.compile(r'^ *\[package\]')
1301 name = re.compile('^ *name *= * "([^"]*)"')
1302 in_pkg = False
1303 for line in inf:
1304 if in_pkg:
1305 if name.match(line):
1306 self.root_pkg = name.match(line).group(1)
1307 break
1308 else:
1309 in_pkg = pkg_section.match(line) is not None
1310
1311 def run_cargo(self):
1312 """Calls cargo -v and save its output to ./cargo.out."""
1313 if self.skip_cargo:
1314 return self
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001315 cargo_toml = './Cargo.toml'
1316 cargo_out = './cargo.out'
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001317 # Do not use Cargo.lock, because .bp rules are designed to
1318 # run with "latest" crates avaialable on Android.
1319 cargo_lock = './Cargo.lock'
1320 cargo_lock_saved = './cargo.lock.saved'
1321 had_cargo_lock = os.path.exists(cargo_lock)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001322 if not os.access(cargo_toml, os.R_OK):
1323 print('ERROR: Cannot find or read', cargo_toml)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001324 return self
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001325 if not self.dry_run:
1326 if os.path.exists(cargo_out):
1327 os.remove(cargo_out)
1328 if not self.args.use_cargo_lock and had_cargo_lock: # save it
1329 os.rename(cargo_lock, cargo_lock_saved)
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001330 cmd_tail_target = ' --target-dir ' + TARGET_TMP
1331 cmd_tail_redir = ' >> ' + cargo_out + ' 2>&1'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001332 # set up search PATH for cargo to find the correct rustc
1333 saved_path = os.environ['PATH']
1334 os.environ['PATH'] = os.path.dirname(self.cargo_path) + ':' + saved_path
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001335 # Add [workspace] to Cargo.toml if it is not there.
1336 added_workspace = False
1337 if self.args.add_workspace:
1338 with open(cargo_toml, 'r') as in_file:
1339 cargo_toml_lines = in_file.readlines()
1340 found_workspace = '[workspace]\n' in cargo_toml_lines
1341 if found_workspace:
1342 print('### WARNING: found [workspace] in Cargo.toml')
1343 else:
1344 with open(cargo_toml, 'a') as out_file:
1345 out_file.write('[workspace]\n')
1346 added_workspace = True
1347 if self.args.verbose:
1348 print('### INFO: added [workspace] to Cargo.toml')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001349 for c in self.cargo:
1350 features = ''
Chih-Hung Hsieh6c8d52f2020-03-30 18:28:52 -07001351 if c != 'clean':
1352 if self.args.features is not None:
1353 features = ' --no-default-features'
1354 if self.args.features:
1355 features += ' --features ' + self.args.features
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001356 cmd_v_flag = ' -vv ' if self.args.vv else ' -v '
1357 cmd = self.cargo_path + cmd_v_flag
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001358 cmd += c + features + cmd_tail_target + cmd_tail_redir
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001359 if self.args.rustflags and c != 'clean':
Joel Galenson903a0f82021-11-24 11:09:36 -08001360 cmd = 'RUSTFLAGS="' + self.args.rustflags + '" ' + cmd
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001361 self.run_cmd(cmd, cargo_out)
1362 if self.args.tests:
1363 cmd = self.cargo_path + ' test' + features + cmd_tail_target + ' -- --list' + cmd_tail_redir
1364 self.run_cmd(cmd, cargo_out)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001365 if added_workspace: # restore original Cargo.toml
1366 with open(cargo_toml, 'w') as out_file:
1367 out_file.writelines(cargo_toml_lines)
1368 if self.args.verbose:
1369 print('### INFO: restored original Cargo.toml')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001370 os.environ['PATH'] = saved_path
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001371 if not self.dry_run:
1372 if not had_cargo_lock: # restore to no Cargo.lock state
1373 os.remove(cargo_lock)
1374 elif not self.args.use_cargo_lock: # restore saved Cargo.lock
1375 os.rename(cargo_lock_saved, cargo_lock)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001376 return self
1377
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001378 def run_cmd(self, cmd, cargo_out):
1379 if self.dry_run:
1380 print('Dry-run skip:', cmd)
1381 else:
1382 if self.args.verbose:
1383 print('Running:', cmd)
1384 with open(cargo_out, 'a') as out_file:
1385 out_file.write('### Running: ' + cmd + '\n')
1386 ret = os.system(cmd)
1387 if ret != 0:
1388 print('*** There was an error while running cargo. ' +
1389 'See the cargo.out file for details.')
1390
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001391 def dump_pkg_obj2cc(self):
1392 """Dump debug info of the pkg_obj2cc map."""
1393 if not self.args.debug:
1394 return
1395 self.init_bp_file('Android.bp')
1396 with open('Android.bp', 'a') as outf:
1397 sorted_pkgs = sorted(self.pkg_obj2cc.keys())
1398 for pkg in sorted_pkgs:
1399 if not self.pkg_obj2cc[pkg]:
1400 continue
1401 outf.write('\n// obj => src for %s\n' % pkg)
1402 obj2cc = self.pkg_obj2cc[pkg]
1403 for obj in sorted(obj2cc.keys()):
1404 outf.write('// ' + short_out_name(pkg, obj) + ' => ' +
1405 short_out_name(pkg, obj2cc[obj].src) + '\n')
1406
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001407 def apply_patch(self):
1408 """Apply local patch file if it is given."""
1409 if self.args.patch:
1410 if self.dry_run:
1411 print('Dry-run skip patch file:', self.args.patch)
1412 else:
1413 if not os.path.exists(self.args.patch):
1414 self.append_to_bp('ERROR cannot find patch file: ' + self.args.patch)
1415 return self
1416 if self.args.verbose:
1417 print('### INFO: applying local patch file:', self.args.patch)
Joel Galenson7e8247e2021-05-20 18:51:42 -07001418 subprocess.run(['patch', '-s', '--no-backup-if-mismatch', './Android.bp',
1419 self.args.patch], check=True)
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001420 return self
1421
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001422 def gen_bp(self):
1423 """Parse cargo.out and generate Android.bp files."""
1424 if self.dry_run:
1425 print('Dry-run skip: read', CARGO_OUT, 'write Android.bp')
1426 elif os.path.exists(CARGO_OUT):
1427 self.find_root_pkg()
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001428 if self.args.copy_out:
1429 self.copy_out_files()
1430 elif self.find_out_files() and self.has_used_out_dir():
1431 print('WARNING: ' + self.root_pkg + ' has cargo output files; ' +
1432 'please rerun with the --copy-out flag.')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001433 with open(CARGO_OUT, 'r') as cargo_out:
1434 self.parse(cargo_out, 'Android.bp')
1435 self.crates.sort(key=get_module_name)
1436 for obj in self.cc_objects:
1437 obj.dump()
1438 self.dump_pkg_obj2cc()
1439 for crate in self.crates:
1440 crate.dump()
1441 dumped_libs = set()
1442 for lib in self.ar_objects:
1443 if lib.pkg == self.root_pkg:
1444 lib_name = file_base_name(lib.lib)
1445 if lib_name not in dumped_libs:
1446 dumped_libs.add(lib_name)
1447 lib.dump()
Joel Galenson5664f2a2021-06-10 10:13:49 -07001448 if self.args.add_toplevel_block:
1449 with open(self.args.add_toplevel_block, 'r') as f:
1450 self.append_to_bp('\n' + f.read() + '\n')
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001451 if self.errors:
Joel Galenson3f42f802021-04-07 12:42:17 -07001452 self.append_to_bp('\n' + ERRORS_LINE + '\n' + self.errors)
Joel Galenson56b5f4b2021-11-30 10:02:57 -08001453 if self.test_errors:
1454 self.append_to_bp('\n// Errors when listing tests:\n' + self.test_errors)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001455 return self
1456
1457 def add_ar_object(self, obj):
1458 self.ar_objects.append(obj)
1459
1460 def add_cc_object(self, obj):
1461 self.cc_objects.append(obj)
1462
1463 def add_crate(self, crate):
1464 """Merge crate with someone in crates, or append to it. Return crates."""
1465 if crate.skip_crate():
1466 if self.args.debug: # include debug info of all crates
1467 self.crates.append(crate)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001468 else:
1469 for c in self.crates:
1470 if c.merge(crate, 'Android.bp'):
1471 return
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001472 # If not merged, decide module type and name now.
1473 crate.decide_module_type()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001474 self.crates.append(crate)
1475
1476 def find_warning_owners(self):
1477 """For each warning file, find its owner crate."""
1478 missing_owner = False
1479 for f in self.warning_files:
1480 cargo_dir = '' # find lowest crate, with longest path
1481 owner = None # owner crate of this warning
1482 for c in self.crates:
1483 if (f.startswith(c.cargo_dir + '/') and
1484 len(cargo_dir) < len(c.cargo_dir)):
1485 cargo_dir = c.cargo_dir
1486 owner = c
1487 if owner:
1488 owner.has_warning = True
1489 else:
1490 missing_owner = True
1491 if missing_owner and os.path.exists('Cargo.toml'):
1492 # owner is the root cargo, with empty cargo_dir
1493 for c in self.crates:
1494 if not c.cargo_dir:
1495 c.has_warning = True
1496
1497 def rustc_command(self, n, rustc_line, line, outf_name):
1498 """Process a rustc command line from cargo -vv output."""
1499 # cargo build -vv output can have multiple lines for a rustc command
1500 # due to '\n' in strings for environment variables.
1501 # strip removes leading spaces and '\n' at the end
1502 new_rustc = (rustc_line.strip() + line) if rustc_line else line
1503 # Use an heuristic to detect the completions of a multi-line command.
1504 # This might fail for some very rare case, but easy to fix manually.
1505 if not line.endswith('`\n') or (new_rustc.count('`') % 2) != 0:
1506 return new_rustc
1507 if RUSTC_VV_CMD_ARGS.match(new_rustc):
1508 args = RUSTC_VV_CMD_ARGS.match(new_rustc).group(1)
1509 self.add_crate(Crate(self, outf_name).parse(n, args))
1510 else:
1511 self.assert_empty_vv_line(new_rustc)
1512 return ''
1513
1514 def cc_ar_command(self, n, groups, outf_name):
1515 pkg = groups.group(1)
1516 line = groups.group(3)
1517 if groups.group(2) == 'cc':
1518 self.add_cc_object(CCObject(self, outf_name).parse(pkg, n, line))
1519 else:
1520 self.add_ar_object(ARObject(self, outf_name).parse(pkg, n, line))
1521
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001522 def append_to_bp(self, line):
1523 self.init_bp_file('Android.bp')
1524 with open('Android.bp', 'a') as outf:
1525 outf.write(line)
1526
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001527 def assert_empty_vv_line(self, line):
1528 if line: # report error if line is not empty
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001529 self.append_to_bp('ERROR -vv line: ' + line)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001530 return ''
1531
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001532 def add_empty_test(self, name):
1533 if name == 'unittests':
1534 self.empty_unittests = True
1535 else:
1536 self.empty_tests.add(name)
1537
1538 def should_ignore_test(self, src):
1539 # cargo test outputs the source file for integration tests but "unittests"
1540 # for unit tests. To figure out to which crate this corresponds, we check
1541 # if the current source file is the main source of a non-test crate, e.g.,
1542 # a library or a binary.
1543 return (src in self.args.test_blocklist or src in self.empty_tests
1544 or (self.empty_unittests
1545 and src in [c.main_src for c in self.crates if c.crate_types != ['test']]))
1546
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001547 def parse(self, inf, outf_name):
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001548 """Parse rustc, test, and warning messages in inf, return a list of Crates."""
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001549 n = 0 # line number
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001550 # We read the file in two passes, where the first simply checks for empty tests.
1551 # Otherwise we would add and merge tests before seeing they're empty.
1552 cur_test_name = None
1553 for line in inf:
1554 if CARGO_TEST_LIST_START_PAT.match(line):
1555 cur_test_name = CARGO_TEST_LIST_START_PAT.match(line).group(1)
1556 elif cur_test_name and CARGO_TEST_LIST_END_PAT.match(line):
1557 match = CARGO_TEST_LIST_END_PAT.match(line)
1558 if int(match.group(1)) + int(match.group(2)) == 0:
1559 self.add_empty_test(cur_test_name)
1560 cur_test_name = None
1561 inf.seek(0)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001562 prev_warning = False # true if the previous line was warning: ...
1563 rustc_line = '' # previous line(s) matching RUSTC_VV_PAT
Joel Galenson56b5f4b2021-11-30 10:02:57 -08001564 in_tests = False
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001565 for line in inf:
1566 n += 1
1567 if line.startswith('warning: '):
1568 prev_warning = True
1569 rustc_line = self.assert_empty_vv_line(rustc_line)
1570 continue
1571 new_rustc = ''
1572 if RUSTC_PAT.match(line):
1573 args_line = RUSTC_PAT.match(line).group(1)
1574 self.add_crate(Crate(self, outf_name).parse(n, args_line))
1575 self.assert_empty_vv_line(rustc_line)
1576 elif rustc_line or RUSTC_VV_PAT.match(line):
1577 new_rustc = self.rustc_command(n, rustc_line, line, outf_name)
1578 elif CC_AR_VV_PAT.match(line):
1579 self.cc_ar_command(n, CC_AR_VV_PAT.match(line), outf_name)
1580 elif prev_warning and WARNING_FILE_PAT.match(line):
1581 self.assert_empty_vv_line(rustc_line)
1582 fpath = WARNING_FILE_PAT.match(line).group(1)
1583 if fpath[0] != '/': # ignore absolute path
1584 self.warning_files.add(fpath)
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001585 elif line.startswith('error: ') or line.startswith('error[E'):
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001586 if not self.args.ignore_cargo_errors:
Joel Galenson56b5f4b2021-11-30 10:02:57 -08001587 if in_tests:
1588 self.test_errors += '// ' + line
1589 else:
1590 self.errors += line
1591 elif CARGO2ANDROID_RUNNING_PAT.match(line):
1592 in_tests = "cargo test" in line and "--list" in line
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001593 prev_warning = False
1594 rustc_line = new_rustc
1595 self.find_warning_owners()
1596
1597
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001598def get_parser():
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001599 """Parse main arguments."""
1600 parser = argparse.ArgumentParser('cargo2android')
1601 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001602 '--add_workspace',
1603 action='store_true',
1604 default=False,
1605 help=('append [workspace] to Cargo.toml before calling cargo,' +
1606 ' to treat current directory as root of package source;' +
1607 ' otherwise the relative source file path in generated' +
1608 ' .bp file will be from the parent directory.'))
1609 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001610 '--cargo',
1611 action='append',
1612 metavar='args_string',
1613 help=('extra cargo build -v args in a string, ' +
1614 'each --cargo flag calls cargo build -v once'))
1615 parser.add_argument(
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001616 '--cargo_bin',
1617 type=str,
1618 help='use cargo in the cargo_bin directory instead of the prebuilt one')
1619 parser.add_argument(
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001620 '--copy-out',
1621 action='store_true',
1622 default=False,
1623 help=('only for root directory, ' +
1624 'copy build.rs output to ./out/* and add a genrule to copy ' +
1625 './out/* to genrule output; for crates with code pattern: ' +
1626 'include!(concat!(env!("OUT_DIR"), "/<some_file>.rs"))'))
1627 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001628 '--debug',
1629 action='store_true',
1630 default=False,
1631 help='dump debug info into Android.bp')
1632 parser.add_argument(
1633 '--dependencies',
1634 action='store_true',
1635 default=False,
Joel Galenson833848c2021-08-17 10:50:42 -07001636 help='Deprecated. Has no effect.')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001637 parser.add_argument(
1638 '--device',
1639 action='store_true',
1640 default=False,
1641 help='run cargo also for a default device target')
1642 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001643 '--features',
1644 type=str,
1645 help=('pass features to cargo build, ' +
1646 'empty string means no default features'))
1647 parser.add_argument(
1648 '--global_defaults',
1649 type=str,
1650 help='add a defaults name to every module')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001651 parser.add_argument(
1652 '--host-first-multilib',
1653 action='store_true',
1654 default=False,
1655 help=('add a compile_multilib:"first" property ' +
1656 'to Android.bp host modules.'))
1657 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001658 '--ignore-cargo-errors',
1659 action='store_true',
1660 default=False,
1661 help='do not append cargo/rustc error messages to Android.bp')
1662 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001663 '--no-host',
1664 action='store_true',
1665 default=False,
1666 help='do not run cargo for the host; only for the device target')
1667 parser.add_argument(
1668 '--no-subdir',
1669 action='store_true',
1670 default=False,
1671 help='do not output anything for sub-directories')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001672 parser.add_argument(
1673 '--onefile',
1674 action='store_true',
1675 default=False,
1676 help=('output all into one ./Android.bp, default will generate ' +
1677 'one Android.bp per Cargo.toml in subdirectories'))
1678 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001679 '--patch',
1680 type=str,
1681 help='apply the given patch file to generated ./Android.bp')
1682 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001683 '--run',
1684 action='store_true',
1685 default=False,
1686 help='run it, default is dry-run')
1687 parser.add_argument('--rustflags', type=str, help='passing flags to rustc')
1688 parser.add_argument(
1689 '--skipcargo',
1690 action='store_true',
1691 default=False,
1692 help='skip cargo command, parse cargo.out, and generate Android.bp')
1693 parser.add_argument(
1694 '--tests',
1695 action='store_true',
1696 default=False,
1697 help='run cargo build --tests after normal build')
1698 parser.add_argument(
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001699 '--use-cargo-lock',
1700 action='store_true',
1701 default=False,
1702 help=('run cargo build with existing Cargo.lock ' +
1703 '(used when some latest dependent crates failed)'))
1704 parser.add_argument(
Matthew Maurer062709c2021-08-17 11:27:36 -07001705 '--exported_c_header_dir',
1706 nargs='*',
1707 help='Directories with headers to export for C usage'
1708 )
1709 parser.add_argument(
Joel Galensond9c4de62021-04-23 10:26:40 -07001710 '--min-sdk-version',
1711 type=str,
1712 help='Minimum SDK version')
1713 parser.add_argument(
1714 '--apex-available',
1715 nargs='*',
1716 help='Mark the main library as apex_available with the given apexes.')
1717 parser.add_argument(
Matthew Maurerac677252021-08-13 15:52:52 -07001718 '--native-bridge-supported',
1719 action='store_true',
1720 default=False,
1721 help='Mark the main library as native_bridge_supported.')
1722 parser.add_argument(
1723 '--product-available',
1724 action='store_true',
1725 default=False,
1726 help='Mark the main library as product_available.')
1727 parser.add_argument(
1728 '--recovery-available',
1729 action='store_true',
1730 default=False,
1731 help='Mark the main library as recovery_available.')
1732 parser.add_argument(
Ivan Lozano91920862021-07-19 10:49:08 -04001733 '--vendor-available',
1734 action='store_true',
1735 default=False,
1736 help='Mark the main library as vendor_available.')
1737 parser.add_argument(
1738 '--vendor-ramdisk-available',
1739 action='store_true',
1740 default=False,
1741 help='Mark the main library as vendor_ramdisk_available.')
1742 parser.add_argument(
Matthew Maurerac677252021-08-13 15:52:52 -07001743 '--ramdisk-available',
1744 action='store_true',
1745 default=False,
1746 help='Mark the main library as ramdisk_available.')
1747 parser.add_argument(
Joel Galensoncb5f2f02021-06-08 14:47:55 -07001748 '--force-rlib',
1749 action='store_true',
1750 default=False,
1751 help='Make the main library an rlib.')
1752 parser.add_argument(
Joel Galenson12467e52021-07-12 14:33:28 -07001753 '--whole-static-libs',
1754 nargs='*',
1755 default=[],
1756 help='Make the given libraries (without lib prefixes) whole_static_libs.')
1757 parser.add_argument(
Ivan Lozano26aa1c32021-08-16 11:20:32 -04001758 '--no-pkg-vers',
1759 action='store_true',
1760 default=False,
1761 help='Do not attempt to determine the package version automatically.')
1762 parser.add_argument(
Joel Galensone4f53882021-07-19 11:14:55 -07001763 '--test-data',
1764 nargs='*',
1765 default=[],
1766 help=('Add the given file to the given test\'s data property. ' +
1767 'Usage: test-path=data-path'))
1768 parser.add_argument(
Joel Galenson97e414a2021-05-27 09:42:32 -07001769 '--dependency-blocklist',
1770 nargs='*',
1771 default=[],
Joel Galenson12467e52021-07-12 14:33:28 -07001772 help='Do not emit the given dependencies (without lib prefixes).')
Joel Galenson97e414a2021-05-27 09:42:32 -07001773 parser.add_argument(
Joel Galensoncb5f2f02021-06-08 14:47:55 -07001774 '--lib-blocklist',
1775 nargs='*',
1776 default=[],
Joel Galenson12467e52021-07-12 14:33:28 -07001777 help='Do not emit the given C libraries as dependencies (without lib prefixes).')
Joel Galensoncb5f2f02021-06-08 14:47:55 -07001778 parser.add_argument(
Joel Galensonf6b3c912021-06-03 16:00:54 -07001779 '--test-blocklist',
1780 nargs='*',
1781 default=[],
1782 help=('Do not emit the given tests. ' +
1783 'Pass the path to the test file to exclude.'))
1784 parser.add_argument(
Joel Galenson3d6d1e72021-06-07 15:00:24 -07001785 '--cfg-blocklist',
1786 nargs='*',
1787 default=[],
1788 help='Do not emit the given cfg.')
1789 parser.add_argument(
Joel Galenson5664f2a2021-06-10 10:13:49 -07001790 '--add-toplevel-block',
1791 type=str,
Joel Galenson493c26a2021-10-18 15:54:41 -07001792 help=('Add the contents of the given file to the top level of the Android.bp. ' +
1793 'The filename should start with cargo2android to work with the updater.'))
Joel Galenson5664f2a2021-06-10 10:13:49 -07001794 parser.add_argument(
1795 '--add-module-block',
1796 type=str,
Joel Galenson493c26a2021-10-18 15:54:41 -07001797 help=('Add the contents of the given file to the main module. '+
1798 'The filename should start with cargo2android to work with the updater.'))
Joel Galenson5664f2a2021-06-10 10:13:49 -07001799 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001800 '--verbose',
1801 action='store_true',
1802 default=False,
1803 help='echo executed commands')
1804 parser.add_argument(
1805 '--vv',
1806 action='store_true',
1807 default=False,
1808 help='run cargo with -vv instead of default -v')
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001809 parser.add_argument(
1810 '--dump-config-and-exit',
1811 type=str,
1812 help=('Dump command-line arguments (minus this flag) to a config file and exit. ' +
1813 'This is intended to help migrate from command line options to config files.'))
1814 parser.add_argument(
1815 '--config',
1816 type=str,
1817 help=('Load command-line options from the given config file. ' +
1818 'Options in this file will override those passed on the command line.'))
1819 return parser
1820
1821
1822def parse_args(parser):
1823 """Parses command-line options."""
1824 args = parser.parse_args()
1825 # Use the values specified in a config file if one was found.
1826 if args.config:
1827 with open(args.config, 'r') as f:
1828 config = json.load(f)
1829 args_dict = vars(args)
1830 for arg in config:
1831 args_dict[arg.replace('-', '_')] = config[arg]
1832 return args
1833
1834
1835def dump_config(parser, args):
1836 """Writes the non-default command-line options to the specified file."""
1837 args_dict = vars(args)
1838 # Filter out the arguments that have their default value.
Joel Galenson367360c2021-04-29 14:31:43 -07001839 # Also filter certain "temporary" arguments.
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001840 non_default_args = {}
1841 for arg in args_dict:
Joel Galenson9a82ad92021-08-17 17:52:04 -07001842 if (args_dict[arg] != parser.get_default(arg) and arg != 'dump_config_and_exit'
Joel Galenson769c3c32021-11-24 15:04:29 -08001843 and arg != 'config' and arg != 'cargo_bin'):
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001844 non_default_args[arg.replace('_', '-')] = args_dict[arg]
1845 # Write to the specified file.
1846 with open(args.dump_config_and_exit, 'w') as f:
1847 json.dump(non_default_args, f, indent=2, sort_keys=True)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001848
1849
1850def main():
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001851 parser = get_parser()
1852 args = parse_args(parser)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001853 if not args.run: # default is dry-run
1854 print(DRY_RUN_NOTE)
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001855 if args.dump_config_and_exit:
1856 dump_config(parser, args)
1857 else:
ThiƩbaud Weksteen198e93f2021-07-02 14:49:19 +02001858 Runner(args).run_cargo().gen_bp().apply_patch()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001859
1860
1861if __name__ == '__main__':
1862 main()