blob: 0804620cb2e66177564210057705a6796651153c [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=', '')
Andrew Walbran0e65bba2022-01-06 17:45:33 +0000435 elif arg.startswith('\'-Aclippy'):
436 # TODO: Consider storing these to include in the Android.bp.
437 _ = arg # ignored
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700438 elif not arg.startswith('-'):
439 # shorten imported crate main source paths like $HOME/.cargo/
440 # registry/src/github.com-1ecc6299db9ec823/memchr-2.3.3/src/lib.rs
441 self.main_src = re.sub(r'^/[^ ]*/registry/src/', '.../', args[i])
442 self.main_src = re.sub(r'^\.\.\./github.com-[0-9a-f]*/', '.../',
443 self.main_src)
444 self.find_cargo_dir()
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700445 if self.cargo_dir: # for a subdirectory
446 if self.runner.args.no_subdir: # all .bp content to /dev/null
447 self.outf_name = '/dev/null'
448 elif not self.runner.args.onefile:
449 # Write to Android.bp in the subdirectory with Cargo.toml.
450 self.outf_name = self.cargo_dir + '/Android.bp'
451 self.main_src = self.main_src[len(self.cargo_dir) + 1:]
Ivan Lozanocc660f12021-08-11 16:49:46 -0400452
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800453 else:
454 self.errors += 'ERROR: unknown ' + arg + '\n'
455 i += 1
456 if not self.crate_name:
457 self.errors += 'ERROR: missing --crate-name\n'
458 if not self.main_src:
459 self.errors += 'ERROR: missing main source file\n'
460 else:
461 self.srcs.append(self.main_src)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700462 if not self.crate_types:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800463 # Treat "--cfg test" as "--test"
464 if 'test' in self.cfgs:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700465 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800466 else:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700467 self.errors += 'ERROR: missing --crate-type or --test\n'
468 elif len(self.crate_types) > 1:
469 if 'test' in self.crate_types:
470 self.errors += 'ERROR: cannot handle both --crate-type and --test\n'
471 if 'lib' in self.crate_types and 'rlib' in self.crate_types:
472 self.errors += 'ERROR: cannot generate both lib and rlib crate types\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800473 if not self.root_pkg:
474 self.root_pkg = self.crate_name
Ivan Lozano26aa1c32021-08-16 11:20:32 -0400475
476 # get the package version from running cargo metadata
Joel Galenson69ba8072021-08-16 11:31:29 -0700477 if not self.runner.args.no_pkg_vers and not self.skip_crate():
Ivan Lozano26aa1c32021-08-16 11:20:32 -0400478 self.get_pkg_version()
479
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700480 self.device_supported = self.runner.args.device
481 self.host_supported = not self.runner.args.no_host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800482 self.cfgs = sorted(set(self.cfgs))
483 self.features = sorted(set(self.features))
484 self.codegens = sorted(set(self.codegens))
485 self.externs = sorted(set(self.externs))
486 self.core_externs = sorted(set(self.core_externs))
487 self.static_libs = sorted(set(self.static_libs))
488 self.shared_libs = sorted(set(self.shared_libs))
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700489 self.crate_types = sorted(set(self.crate_types))
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800490 self.decide_module_type()
491 self.module_name = altered_name(self.stem)
492 return self
493
Ivan Lozano26aa1c32021-08-16 11:20:32 -0400494 def get_pkg_version(self):
495 """Attempt to retrieve the package version from the Cargo.toml
496
497 If there is only one package, use its version. Otherwise, try to
498 match the emitted `--crate_name` arg against the package name.
499
500 This may fail in cases where multiple packages are defined (workspaces)
501 and where the package name does not match the emitted crate_name
502 (e.g. [lib.name] is set).
503 """
Joel Galenson69ba8072021-08-16 11:31:29 -0700504 cargo_metadata = subprocess.run([self.runner.cargo_path, 'metadata', '--no-deps',
505 '--format-version', '1'],
Joel Galensonc5186502021-08-16 11:22:47 -0700506 cwd=os.path.abspath(self.cargo_dir),
507 stdout=subprocess.PIPE)
Ivan Lozano26aa1c32021-08-16 11:20:32 -0400508 if cargo_metadata.returncode:
509 self.errors += ('ERROR: unable to get cargo metadata for package version; ' +
510 'return code ' + cargo_metadata.returncode + '\n')
511 else:
512 metadata_json = json.loads(cargo_metadata.stdout)
513 if len(metadata_json['packages']) > 1:
514 for package in metadata_json['packages']:
515 # package names may contain '-', but is changed to '_' in the crate_name
516 if package['name'].replace('-','_') == self.crate_name:
517 self.cargo_pkg_version = package['version']
518 break
519 else:
520 self.cargo_pkg_version = metadata_json['packages'][0]['version']
521
522 if not self.cargo_pkg_version:
523 self.errors += ('ERROR: Unable to retrieve package version; ' +
524 'to disable, run with arg "--no-pkg-vers"\n')
525
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800526 def dump_line(self):
527 self.write('\n// Line ' + str(self.line_num) + ' ' + self.line)
528
529 def feature_list(self):
530 """Return a string of main_src + "feature_list"."""
531 pkg = self.main_src
532 if pkg.startswith('.../'): # keep only the main package name
533 pkg = re.sub('/.*', '', pkg[4:])
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700534 elif pkg.startswith('/'): # use relative path for a local package
535 pkg = os.path.relpath(pkg)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800536 if not self.features:
537 return pkg
538 return pkg + ' "' + ','.join(self.features) + '"'
539
540 def dump_skip_crate(self, kind):
541 if self.debug:
542 self.write('\n// IGNORED: ' + kind + ' ' + self.main_src)
543 return self
544
545 def skip_crate(self):
546 """Return crate_name or a message if this crate should be skipped."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700547 if (is_build_crate_name(self.crate_name) or
548 self.crate_name in EXCLUDED_CRATES):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800549 return self.crate_name
550 if is_dependent_file_path(self.main_src):
551 return 'dependent crate'
552 return ''
553
554 def dump(self):
555 """Dump all error/debug/module code to the output .bp file."""
556 self.runner.init_bp_file(self.outf_name)
557 with open(self.outf_name, 'a') as outf:
558 self.outf = outf
559 if self.errors:
560 self.dump_line()
561 self.write(self.errors)
562 elif self.skip_crate():
563 self.dump_skip_crate(self.skip_crate())
564 else:
565 if self.debug:
566 self.dump_debug_info()
567 self.dump_android_module()
568
569 def dump_debug_info(self):
570 """Dump parsed data, when cargo2android is called with --debug."""
571
572 def dump(name, value):
573 self.write('//%12s = %s' % (name, value))
574
575 def opt_dump(name, value):
576 if value:
577 dump(name, value)
578
579 def dump_list(fmt, values):
580 for v in values:
581 self.write(fmt % v)
582
583 self.dump_line()
584 dump('module_name', self.module_name)
585 dump('crate_name', self.crate_name)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700586 dump('crate_types', self.crate_types)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800587 dump('main_src', self.main_src)
588 dump('has_warning', self.has_warning)
589 dump('for_host', self.host_supported)
590 dump('for_device', self.device_supported)
591 dump('module_type', self.module_type)
592 opt_dump('target', self.target)
593 opt_dump('edition', self.edition)
594 opt_dump('emit_list', self.emit_list)
595 opt_dump('cap_lints', self.cap_lints)
596 dump_list('// cfg = %s', self.cfgs)
597 dump_list('// cfg = \'feature "%s"\'', self.features)
598 # TODO(chh): escape quotes in self.features, but not in other dump_list
599 dump_list('// codegen = %s', self.codegens)
600 dump_list('// externs = %s', self.externs)
601 dump_list('// -l static = %s', self.static_libs)
602 dump_list('// -l (dylib) = %s', self.shared_libs)
603
604 def dump_android_module(self):
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700605 """Dump one or more Android module definition, depending on crate_types."""
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700606 if len(self.crate_types) == 1:
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700607 self.dump_single_type_android_module()
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700608 return
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700609 if 'test' in self.crate_types:
610 self.write('\nERROR: multiple crate types cannot include test type')
611 return
612 # Dump one Android module per crate_type.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700613 for crate_type in self.crate_types:
614 self.decide_one_module_type(crate_type)
615 self.dump_one_android_module(crate_type)
616
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700617 def build_default_name(self):
618 """Return a short and readable name for the rust_defaults module."""
Joel Galensond37d7e62021-07-13 09:03:01 -0700619 # Choices: (1) root_pkg + '_test'? + '_defaults',
620 # (2) root_pkg + '_test'? + '_defaults_' + crate_name
621 # (3) root_pkg + '_test'? + '_defaults_' + main_src_basename_path
622 # (4) root_pkg + '_test'? + '_defaults_' + a_positive_sequence_number
623 test = "_test" if self.crate_types == ['test'] else ""
624 name1 = altered_defaults(self.root_pkg) + test + '_defaults'
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700625 if self.runner.try_claim_module_name(name1, self):
626 return name1
627 name2 = name1 + '_' + self.crate_name
628 if self.runner.try_claim_module_name(name2, self):
629 return name2
630 name3 = name1 + '_' + self.main_src_basename_path()
631 if self.runner.try_claim_module_name(name3, self):
632 return name3
633 return self.runner.claim_module_name(name1, self, 0)
634
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700635 def dump_srcs_list(self):
636 """Dump the srcs list, for defaults or regular modules."""
637 if len(self.srcs) > 1:
638 srcs = sorted(set(self.srcs)) # make a copy and dedup
639 else:
640 srcs = [self.main_src]
641 copy_out = self.runner.copy_out_module_name()
642 if copy_out:
643 srcs.append(':' + copy_out)
644 self.dump_android_property_list('srcs', '"%s"', srcs)
645
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700646 def dump_defaults_module(self):
647 """Dump a rust_defaults module to be shared by other modules."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700648 name = self.build_default_name()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700649 self.defaults = name
650 self.write('\nrust_defaults {')
651 self.write(' name: "' + name + '",')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700652 if self.runner.args.global_defaults:
653 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700654 self.write(' crate_name: "' + self.crate_name + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700655 if len(self.srcs) == 1: # only one source file; share it in defaults
656 self.default_srcs = True
657 if self.has_warning and not self.cap_lints:
658 self.write(' // has rustc warnings')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700659 self.dump_srcs_list()
Joel Galensonb06c42a2021-08-31 14:28:48 -0700660 if self.cargo_env_compat:
661 self.write(' cargo_env_compat: true,')
Joel Galenson94b8a8d2021-09-28 11:53:51 -0700662 if not self.runner.args.no_pkg_vers:
663 self.write(' cargo_pkg_version: "' + self.cargo_pkg_version + '",')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700664 if 'test' in self.crate_types:
665 self.write(' test_suites: ["general-tests"],')
666 self.write(' auto_gen_config: true,')
667 self.dump_edition_flags_libs()
Joel Galensone4f53882021-07-19 11:14:55 -0700668 if 'test' in self.crate_types and len(self.srcs) == 1:
669 self.dump_test_data()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700670 self.write('}')
671
672 def dump_single_type_android_module(self):
673 """Dump one simple Android module, which has only one crate_type."""
674 crate_type = self.crate_types[0]
675 if crate_type != 'test':
676 # do not change self.stem or self.module_name
677 self.dump_one_android_module(crate_type)
678 return
Joel Galenson54d65532021-08-31 14:08:05 -0700679 # Dump one test module per source file.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700680 # crate_type == 'test'
Joel Galenson7cfc6fe2021-10-14 13:09:32 -0700681 self.srcs = [src for src in self.srcs if not self.runner.should_ignore_test(src)]
Joel Galenson54d65532021-08-31 14:08:05 -0700682 if len(self.srcs) > 1:
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700683 self.srcs = sorted(set(self.srcs))
684 self.dump_defaults_module()
685 saved_srcs = self.srcs
686 for src in saved_srcs:
687 self.srcs = [src]
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700688 saved_main_src = self.main_src
689 self.main_src = src
Joel Galenson54d65532021-08-31 14:08:05 -0700690 self.module_name = self.test_module_name()
691 self.decide_one_module_type(crate_type)
692 self.dump_one_android_module(crate_type)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700693 self.main_src = saved_main_src
694 self.srcs = saved_srcs
695
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700696 def dump_one_android_module(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800697 """Dump one Android module definition."""
698 if not self.module_type:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700699 self.write('\nERROR: unknown crate_type ' + crate_type)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800700 return
701 self.write('\n' + self.module_type + ' {')
702 self.dump_android_core_properties()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700703 if not self.defaults:
704 self.dump_edition_flags_libs()
705 if self.runner.args.host_first_multilib and self.host_supported and crate_type != 'test':
706 self.write(' compile_multilib: "first",')
Matthew Maurer062709c2021-08-17 11:27:36 -0700707 if self.runner.args.exported_c_header_dir and crate_type in C_LIBRARY_CRATE_TYPES:
708 self.write(' include_dirs: [')
709 for header_dir in self.runner.args.exported_c_header_dir:
710 self.write(' "%s",' % header_dir)
711 self.write(' ],')
Matthew Maurer9e4b7812021-08-16 14:21:01 -0700712 if self.runner.args.apex_available and crate_type in LIBRARY_CRATE_TYPES:
Joel Galensond9c4de62021-04-23 10:26:40 -0700713 self.write(' apex_available: [')
714 for apex in self.runner.args.apex_available:
715 self.write(' "%s",' % apex)
716 self.write(' ],')
Matthew Maurer70182e42021-08-17 13:53:52 -0700717 if crate_type != 'test':
718 if self.runner.args.native_bridge_supported:
719 self.write(' native_bridge_supported: true,')
720 if self.runner.args.product_available:
721 self.write(' product_available: true,')
722 if self.runner.args.recovery_available:
723 self.write(' recovery_available: true,')
724 if self.runner.args.vendor_available:
725 self.write(' vendor_available: true,')
726 if self.runner.args.vendor_ramdisk_available:
727 self.write(' vendor_ramdisk_available: true,')
728 if self.runner.args.ramdisk_available:
729 self.write(' ramdisk_available: true,')
Matthew Maurer9e4b7812021-08-16 14:21:01 -0700730 if self.runner.args.min_sdk_version and crate_type in LIBRARY_CRATE_TYPES:
Joel Galensond9c4de62021-04-23 10:26:40 -0700731 self.write(' min_sdk_version: "%s",' % self.runner.args.min_sdk_version)
Joel Galensone4f53882021-07-19 11:14:55 -0700732 if crate_type == 'test' and not self.default_srcs:
733 self.dump_test_data()
Joel Galenson5664f2a2021-06-10 10:13:49 -0700734 if self.runner.args.add_module_block:
735 with open(self.runner.args.add_module_block, 'r') as f:
736 self.write(' %s,' % f.read().replace('\n', '\n '))
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700737 self.write('}')
738
739 def dump_android_flags(self):
740 """Dump Android module flags property."""
ThiƩbaud Weksteena5a728b2021-04-08 14:23:49 +0200741 if not self.codegens and not self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700742 return
743 self.write(' flags: [')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800744 if self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700745 self.write(' "--cap-lints ' + self.cap_lints + '",')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700746 codegens_fmt = '"-C %s"'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700747 self.dump_android_property_list_items(codegens_fmt, self.codegens)
748 self.write(' ],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700749
750 def dump_edition_flags_libs(self):
751 if self.edition:
752 self.write(' edition: "' + self.edition + '",')
753 self.dump_android_property_list('features', '"%s"', self.features)
Joel Galenson3d6d1e72021-06-07 15:00:24 -0700754 cfgs = [cfg for cfg in self.cfgs if not cfg in self.runner.args.cfg_blocklist]
755 self.dump_android_property_list('cfgs', '"%s"', cfgs)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700756 self.dump_android_flags()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800757 if self.externs:
758 self.dump_android_externs()
Joel Galenson12467e52021-07-12 14:33:28 -0700759 all_static_libs = [lib for lib in self.static_libs if not lib in self.runner.args.lib_blocklist]
760 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 -0700761 self.dump_android_property_list('static_libs', '"lib%s"', static_libs)
Joel Galenson12467e52021-07-12 14:33:28 -0700762 whole_static_libs = [lib for lib in all_static_libs if lib in self.runner.args.whole_static_libs]
763 self.dump_android_property_list('whole_static_libs', '"lib%s"', whole_static_libs)
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700764 shared_libs = [lib for lib in self.shared_libs if not lib in self.runner.args.lib_blocklist]
765 self.dump_android_property_list('shared_libs', '"lib%s"', shared_libs)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800766
Joel Galensone4f53882021-07-19 11:14:55 -0700767 def dump_test_data(self):
768 data = [data for (name, data) in map(lambda kv: kv.split('=', 1), self.runner.args.test_data)
769 if self.srcs == [name]]
770 if data:
771 self.dump_android_property_list('data', '"%s"', data)
772
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700773 def main_src_basename_path(self):
774 return re.sub('/', '_', re.sub('.rs$', '', self.main_src))
775
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800776 def test_module_name(self):
777 """Return a unique name for a test module."""
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700778 # root_pkg+(_host|_device) + '_test_'+source_file_name
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700779 suffix = self.main_src_basename_path()
Joel Galenson54d65532021-08-31 14:08:05 -0700780 return self.root_pkg + '_test_' + suffix
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800781
782 def decide_module_type(self):
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700783 # Use the first crate type for the default/first module.
784 crate_type = self.crate_types[0] if self.crate_types else ''
785 self.decide_one_module_type(crate_type)
786
787 def decide_one_module_type(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800788 """Decide which Android module type to use."""
789 host = '' if self.device_supported else '_host'
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700790 rlib = '_rlib' if self.runner.args.force_rlib else ''
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700791 if crate_type == 'bin': # rust_binary[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800792 self.module_type = 'rust_binary' + host
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700793 # In rare cases like protobuf-codegen, the output binary name must
794 # be renamed to use as a plugin for protoc.
795 self.stem = altered_stem(self.crate_name)
796 self.module_name = altered_name(self.crate_name)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700797 elif crate_type == 'lib': # rust_library[_host]
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700798 # TODO(chh): should this be rust_library[_host]?
799 # Assuming that Cargo.toml do not use both 'lib' and 'rlib',
800 # because we map them both to rlib.
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700801 self.module_type = 'rust_library' + rlib + host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800802 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700803 self.module_name = altered_name(self.stem)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700804 elif crate_type == 'rlib': # rust_library[_host]
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700805 self.module_type = 'rust_library' + rlib + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700806 self.stem = 'lib' + self.crate_name
807 self.module_name = altered_name(self.stem)
808 elif crate_type == 'dylib': # rust_library[_host]_dylib
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800809 self.module_type = 'rust_library' + host + '_dylib'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700810 self.stem = 'lib' + self.crate_name
811 self.module_name = altered_name(self.stem) + '_dylib'
812 elif crate_type == 'cdylib': # rust_library[_host]_shared
Ivan Lozano0c057ad2020-12-15 10:41:26 -0500813 self.module_type = 'rust_ffi' + host + '_shared'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700814 self.stem = 'lib' + self.crate_name
815 self.module_name = altered_name(self.stem) + '_shared'
816 elif crate_type == 'staticlib': # rust_library[_host]_static
Ivan Lozano0c057ad2020-12-15 10:41:26 -0500817 self.module_type = 'rust_ffi' + host + '_static'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700818 self.stem = 'lib' + self.crate_name
819 self.module_name = altered_name(self.stem) + '_static'
820 elif crate_type == 'test': # rust_test[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800821 self.module_type = 'rust_test' + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700822 # Before do_merge, stem name is based on the --crate-name parameter.
823 # and test module name is based on stem.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800824 self.stem = self.test_module_name()
825 # self.stem will be changed after merging with other tests.
826 # self.stem is NOT used for final test binary name.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700827 # rust_test uses each source file base name as part of output file name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700828 # In do_merge, this function is called again, with a module_name.
829 # We make sure that the module name is unique in each package.
830 if self.module_name:
831 # Cargo uses "-C extra-filename=..." and "-C metadata=..." to add
832 # different suffixes and distinguish multiple tests of the same
833 # crate name. We ignore -C and use claim_module_name to get
834 # unique sequential suffix.
835 self.module_name = self.runner.claim_module_name(
836 self.module_name, self, 0)
837 # Now the module name is unique, stem should also match and unique.
838 self.stem = self.module_name
839 elif crate_type == 'proc-macro': # rust_proc_macro
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800840 self.module_type = 'rust_proc_macro'
841 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700842 self.module_name = altered_name(self.stem)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800843 else: # unknown module type, rust_prebuilt_dylib? rust_library[_host]?
844 self.module_type = ''
845 self.stem = ''
846
847 def dump_android_property_list_items(self, fmt, values):
848 for v in values:
849 # fmt has quotes, so we need escape_quotes(v)
850 self.write(' ' + (fmt % escape_quotes(v)) + ',')
851
852 def dump_android_property_list(self, name, fmt, values):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700853 if not values:
854 return
855 if len(values) > 1:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800856 self.write(' ' + name + ': [')
857 self.dump_android_property_list_items(fmt, values)
858 self.write(' ],')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700859 else:
860 self.write(' ' + name + ': [' +
861 (fmt % escape_quotes(values[0])) + '],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800862
863 def dump_android_core_properties(self):
864 """Dump the module header, name, stem, etc."""
865 self.write(' name: "' + self.module_name + '",')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700866 # see properties shared by dump_defaults_module
867 if self.defaults:
868 self.write(' defaults: ["' + self.defaults + '"],')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700869 elif self.runner.args.global_defaults:
870 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800871 if self.stem != self.module_name:
872 self.write(' stem: "' + self.stem + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700873 if self.has_warning and not self.cap_lints and not self.default_srcs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700874 self.write(' // has rustc warnings')
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700875 if self.host_supported and self.device_supported and self.module_type != 'rust_proc_macro':
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800876 self.write(' host_supported: true,')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700877 if not self.defaults:
878 self.write(' crate_name: "' + self.crate_name + '",')
Ivan Lozanocc660f12021-08-11 16:49:46 -0400879 if not self.defaults and self.cargo_env_compat:
880 self.write(' cargo_env_compat: true,')
Joel Galenson94b8a8d2021-09-28 11:53:51 -0700881 if not self.runner.args.no_pkg_vers:
882 self.write(' cargo_pkg_version: "' + self.cargo_pkg_version + '",')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700883 if not self.default_srcs:
884 self.dump_srcs_list()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700885 if 'test' in self.crate_types and not self.defaults:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800886 # self.root_pkg can have multiple test modules, with different *_tests[n]
887 # names, but their executables can all be installed under the same _tests
888 # directory. When built from Cargo.toml, all tests should have different
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700889 # file or crate names. So we used (root_pkg + '_tests') name as the
890 # relative_install_path.
891 # However, some package like 'slab' can have non-mergeable tests that
892 # must be separated by different module names. So, here we no longer
893 # emit relative_install_path.
894 # self.write(' relative_install_path: "' + self.root_pkg + '_tests",')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800895 self.write(' test_suites: ["general-tests"],')
896 self.write(' auto_gen_config: true,')
Joel Galensone261a152021-01-12 11:31:53 -0800897 if 'test' in self.crate_types and self.host_supported:
898 self.write(' test_options: {')
899 self.write(' unit_test: true,')
900 self.write(' },')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800901
902 def dump_android_externs(self):
903 """Dump the dependent rlibs and dylibs property."""
904 so_libs = list()
905 rust_libs = ''
906 deps_libname = re.compile('^.* = lib(.*)-[0-9a-f]*.(rlib|so|rmeta)$')
907 for lib in self.externs:
908 # normal value of lib: "libc = liblibc-*.rlib"
909 # strange case in rand crate: "getrandom_package = libgetrandom-*.rlib"
910 # we should use "libgetrandom", not "lib" + "getrandom_package"
911 groups = deps_libname.match(lib)
912 if groups is not None:
913 lib_name = groups.group(1)
914 else:
915 lib_name = re.sub(' .*$', '', lib)
Joel Galenson97e414a2021-05-27 09:42:32 -0700916 if lib_name in self.runner.args.dependency_blocklist:
917 continue
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800918 if lib.endswith('.rlib') or lib.endswith('.rmeta'):
919 # On MacOS .rmeta is used when Linux uses .rlib or .rmeta.
920 rust_libs += ' "' + altered_name('lib' + lib_name) + '",\n'
921 elif lib.endswith('.so'):
922 so_libs.append(lib_name)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700923 elif lib != 'proc_macro': # --extern proc_macro is special and ignored
924 rust_libs += ' // ERROR: unknown type of lib ' + lib + '\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800925 if rust_libs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700926 self.write(' rustlibs: [\n' + rust_libs + ' ],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800927 # Are all dependent .so files proc_macros?
928 # TODO(chh): Separate proc_macros and dylib.
929 self.dump_android_property_list('proc_macros', '"lib%s"', so_libs)
930
931
932class ARObject(object):
933 """Information of an "ar" link command."""
934
935 def __init__(self, runner, outf_name):
936 # Remembered global runner and its members.
937 self.runner = runner
938 self.pkg = ''
939 self.outf_name = outf_name # path to Android.bp
940 # "ar" arguments
941 self.line_num = 1
942 self.line = ''
943 self.flags = '' # e.g. "crs"
944 self.lib = '' # e.g. "/.../out/lib*.a"
945 self.objs = list() # e.g. "/.../out/.../*.o"
946
947 def parse(self, pkg, line_num, args_line):
948 """Collect ar obj/lib file names."""
949 self.pkg = pkg
950 self.line_num = line_num
951 self.line = args_line
952 args = args_line.split()
953 num_args = len(args)
954 if num_args < 3:
955 print('ERROR: "ar" command has too few arguments', args_line)
956 else:
957 self.flags = unquote(args[0])
958 self.lib = unquote(args[1])
959 self.objs = sorted(set(map(unquote, args[2:])))
960 return self
961
962 def write(self, s):
963 self.outf.write(s + '\n')
964
965 def dump_debug_info(self):
966 self.write('\n// Line ' + str(self.line_num) + ' "ar" ' + self.line)
967 self.write('// ar_object for %12s' % self.pkg)
968 self.write('// flags = %s' % self.flags)
969 self.write('// lib = %s' % short_out_name(self.pkg, self.lib))
970 for o in self.objs:
971 self.write('// obj = %s' % short_out_name(self.pkg, o))
972
973 def dump_android_lib(self):
974 """Write cc_library_static into Android.bp."""
975 self.write('\ncc_library_static {')
976 self.write(' name: "' + file_base_name(self.lib) + '",')
977 self.write(' host_supported: true,')
978 if self.flags != 'crs':
979 self.write(' // ar flags = %s' % self.flags)
980 if self.pkg not in self.runner.pkg_obj2cc:
981 self.write(' ERROR: cannot find source files.\n}')
982 return
983 self.write(' srcs: [')
984 obj2cc = self.runner.pkg_obj2cc[self.pkg]
985 # Note: wflags are ignored.
986 dflags = list()
987 fflags = list()
988 for obj in self.objs:
989 self.write(' "' + short_out_name(self.pkg, obj2cc[obj].src) + '",')
990 # TODO(chh): union of dflags and flags of all obj
991 # Now, just a temporary hack that uses the last obj's flags
992 dflags = obj2cc[obj].dflags
993 fflags = obj2cc[obj].fflags
994 self.write(' ],')
995 self.write(' cflags: [')
996 self.write(' "-O3",') # TODO(chh): is this default correct?
997 self.write(' "-Wno-error",')
998 for x in fflags:
999 self.write(' "-f' + x + '",')
1000 for x in dflags:
1001 self.write(' "-D' + x + '",')
1002 self.write(' ],')
1003 self.write('}')
1004
1005 def dump(self):
1006 """Dump error/debug/module info to the output .bp file."""
1007 self.runner.init_bp_file(self.outf_name)
1008 with open(self.outf_name, 'a') as outf:
1009 self.outf = outf
1010 if self.runner.args.debug:
1011 self.dump_debug_info()
1012 self.dump_android_lib()
1013
1014
1015class CCObject(object):
1016 """Information of a "cc" compilation command."""
1017
1018 def __init__(self, runner, outf_name):
1019 # Remembered global runner and its members.
1020 self.runner = runner
1021 self.pkg = ''
1022 self.outf_name = outf_name # path to Android.bp
1023 # "cc" arguments
1024 self.line_num = 1
1025 self.line = ''
1026 self.src = ''
1027 self.obj = ''
1028 self.dflags = list() # -D flags
1029 self.fflags = list() # -f flags
1030 self.iflags = list() # -I flags
1031 self.wflags = list() # -W flags
1032 self.other_args = list()
1033
1034 def parse(self, pkg, line_num, args_line):
1035 """Collect cc compilation flags and src/out file names."""
1036 self.pkg = pkg
1037 self.line_num = line_num
1038 self.line = args_line
1039 args = args_line.split()
1040 i = 0
1041 while i < len(args):
1042 arg = args[i]
1043 if arg == '"-c"':
1044 i += 1
1045 if args[i].startswith('"-o'):
1046 # ring-0.13.5 dumps: ... "-c" "-o/.../*.o" ".../*.c"
1047 self.obj = unquote(args[i])[2:]
1048 i += 1
1049 self.src = unquote(args[i])
1050 else:
1051 self.src = unquote(args[i])
1052 elif arg == '"-o"':
1053 i += 1
1054 self.obj = unquote(args[i])
1055 elif arg == '"-I"':
1056 i += 1
1057 self.iflags.append(unquote(args[i]))
1058 elif arg.startswith('"-D'):
1059 self.dflags.append(unquote(args[i])[2:])
1060 elif arg.startswith('"-f'):
1061 self.fflags.append(unquote(args[i])[2:])
1062 elif arg.startswith('"-W'):
1063 self.wflags.append(unquote(args[i])[2:])
1064 elif not (arg.startswith('"-O') or arg == '"-m64"' or arg == '"-g"' or
1065 arg == '"-g3"'):
1066 # ignore -O -m64 -g
1067 self.other_args.append(unquote(args[i]))
1068 i += 1
1069 self.dflags = sorted(set(self.dflags))
1070 self.fflags = sorted(set(self.fflags))
1071 # self.wflags is not sorted because some are order sensitive
1072 # and we ignore them anyway.
1073 if self.pkg not in self.runner.pkg_obj2cc:
1074 self.runner.pkg_obj2cc[self.pkg] = {}
1075 self.runner.pkg_obj2cc[self.pkg][self.obj] = self
1076 return self
1077
1078 def write(self, s):
1079 self.outf.write(s + '\n')
1080
1081 def dump_debug_flags(self, name, flags):
1082 self.write('// ' + name + ':')
1083 for f in flags:
1084 self.write('// %s' % f)
1085
1086 def dump(self):
1087 """Dump only error/debug info to the output .bp file."""
1088 if not self.runner.args.debug:
1089 return
1090 self.runner.init_bp_file(self.outf_name)
1091 with open(self.outf_name, 'a') as outf:
1092 self.outf = outf
1093 self.write('\n// Line ' + str(self.line_num) + ' "cc" ' + self.line)
1094 self.write('// cc_object for %12s' % self.pkg)
1095 self.write('// src = %s' % short_out_name(self.pkg, self.src))
1096 self.write('// obj = %s' % short_out_name(self.pkg, self.obj))
1097 self.dump_debug_flags('-I flags', self.iflags)
1098 self.dump_debug_flags('-D flags', self.dflags)
1099 self.dump_debug_flags('-f flags', self.fflags)
1100 self.dump_debug_flags('-W flags', self.wflags)
1101 if self.other_args:
1102 self.dump_debug_flags('other args', self.other_args)
1103
1104
1105class Runner(object):
1106 """Main class to parse cargo -v output and print Android module definitions."""
1107
1108 def __init__(self, args):
1109 self.bp_files = set() # Remember all output Android.bp files.
1110 self.root_pkg = '' # name of package in ./Cargo.toml
1111 # Saved flags, modes, and data.
1112 self.args = args
1113 self.dry_run = not args.run
1114 self.skip_cargo = args.skipcargo
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001115 self.cargo_path = './cargo' # path to cargo, will be set later
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001116 self.checked_out_files = False # to check only once
1117 self.build_out_files = [] # output files generated by build.rs
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001118 # All cc/ar objects, crates, dependencies, and warning files
1119 self.cc_objects = list()
1120 self.pkg_obj2cc = {}
1121 # pkg_obj2cc[cc_object[i].pkg][cc_objects[i].obj] = cc_objects[i]
1122 self.ar_objects = list()
1123 self.crates = list()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001124 self.warning_files = set()
1125 # Keep a unique mapping from (module name) to crate
1126 self.name_owners = {}
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001127 # Save and dump all errors from cargo to Android.bp.
1128 self.errors = ''
Joel Galenson56b5f4b2021-11-30 10:02:57 -08001129 self.test_errors = ''
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001130 self.setup_cargo_path()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001131 # Default action is cargo clean, followed by build or user given actions.
1132 if args.cargo:
1133 self.cargo = ['clean'] + args.cargo
1134 else:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001135 default_target = '--target x86_64-unknown-linux-gnu'
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -07001136 # Use the same target for both host and default device builds.
1137 # Same target is used as default in host x86_64 Android compilation.
1138 # Note: b/169872957, prebuilt cargo failed to build vsock
1139 # on x86_64-unknown-linux-musl systems.
1140 self.cargo = ['clean', 'build ' + default_target]
1141 if args.tests:
1142 self.cargo.append('build --tests ' + default_target)
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001143 self.empty_tests = set()
1144 self.empty_unittests = False
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001145
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001146 def setup_cargo_path(self):
1147 """Find cargo in the --cargo_bin or prebuilt rust bin directory."""
1148 if self.args.cargo_bin:
1149 self.cargo_path = os.path.join(self.args.cargo_bin, 'cargo')
1150 if not os.path.isfile(self.cargo_path):
1151 sys.exit('ERROR: cannot find cargo in ' + self.args.cargo_bin)
1152 print('WARNING: using cargo in ' + self.args.cargo_bin)
1153 return
1154 # We have only tested this on Linux.
1155 if platform.system() != 'Linux':
1156 sys.exit('ERROR: this script has only been tested on Linux with cargo.')
1157 # Assuming that this script is in development/scripts.
1158 my_dir = os.path.dirname(os.path.abspath(__file__))
1159 linux_dir = os.path.join(my_dir, '..', '..',
1160 'prebuilts', 'rust', 'linux-x86')
1161 if not os.path.isdir(linux_dir):
1162 sys.exit('ERROR: cannot find directory ' + linux_dir)
1163 rust_version = self.find_rust_version(my_dir, linux_dir)
1164 cargo_bin = os.path.join(linux_dir, rust_version, 'bin')
1165 self.cargo_path = os.path.join(cargo_bin, 'cargo')
1166 if not os.path.isfile(self.cargo_path):
1167 sys.exit('ERROR: cannot find cargo in ' + cargo_bin
1168 + '; please try --cargo_bin= flag.')
1169 return
1170
1171 def find_rust_version(self, my_dir, linux_dir):
1172 """Use my script directory, find prebuilt rust version."""
1173 # First look up build/soong/rust/config/global.go.
1174 path2global = os.path.join(my_dir, '..', '..',
1175 'build', 'soong', 'rust', 'config', 'global.go')
1176 if os.path.isfile(path2global):
1177 # try to find: RustDefaultVersion = "1.44.0"
1178 version_pat = re.compile(
1179 r'\s*RustDefaultVersion\s*=\s*"([0-9]+\.[0-9]+\.[0-9]+)".*$')
1180 with open(path2global, 'r') as inf:
1181 for line in inf:
1182 result = version_pat.match(line)
1183 if result:
1184 return result.group(1)
1185 print('WARNING: cannot find RustDefaultVersion in ' + path2global)
1186 # Otherwise, find the newest (largest) version number in linux_dir.
1187 rust_version = (0, 0, 0) # the prebuilt version to use
1188 version_pat = re.compile(r'([0-9]+)\.([0-9]+)\.([0-9]+)$')
1189 for dir_name in os.listdir(linux_dir):
1190 result = version_pat.match(dir_name)
1191 if not result:
1192 continue
1193 version = (result.group(1), result.group(2), result.group(3))
1194 if version > rust_version:
1195 rust_version = version
1196 return '.'.join(rust_version)
1197
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001198 def find_out_files(self):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001199 # list1 has build.rs output for normal crates
1200 list1 = glob.glob(TARGET_TMP + '/*/*/build/' + self.root_pkg + '-*/out/*')
1201 # list2 has build.rs output for proc-macro crates
1202 list2 = glob.glob(TARGET_TMP + '/*/build/' + self.root_pkg + '-*/out/*')
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001203 return list1 + list2
1204
1205 def copy_out_files(self):
1206 """Copy build.rs output files to ./out and set up build_out_files."""
1207 if self.checked_out_files:
1208 return
1209 self.checked_out_files = True
1210 cargo_out_files = self.find_out_files()
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001211 out_files = set()
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001212 if cargo_out_files:
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001213 os.makedirs('out', exist_ok=True)
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001214 for path in cargo_out_files:
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001215 file_name = path.split('/')[-1]
1216 out_files.add(file_name)
1217 shutil.copy(path, 'out/' + file_name)
1218 self.build_out_files = sorted(out_files)
1219
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001220 def has_used_out_dir(self):
1221 """Returns true if env!("OUT_DIR") is found."""
1222 return 0 == os.system('grep -rl --exclude build.rs --include \\*.rs' +
1223 ' \'env!("OUT_DIR")\' * > /dev/null')
1224
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001225 def copy_out_module_name(self):
1226 if self.args.copy_out and self.build_out_files:
1227 return 'copy_' + self.root_pkg + '_build_out'
1228 else:
1229 return ''
1230
Haibo Huang0f72c952021-03-19 11:34:15 -07001231 def read_license(self, name):
1232 if not os.path.isfile(name):
1233 return ''
1234 license = ''
1235 with open(name, 'r') as intf:
1236 line = intf.readline()
1237 # Firstly skip ANDROID_BP_HEADER
1238 while line.startswith('//'):
1239 line = intf.readline()
Joel Galensond9d13b82021-04-05 11:27:55 -07001240 # Read all lines until we see a rust_* or genrule rule.
1241 while line != '' and not (line.startswith('rust_') or line.startswith('genrule {')):
Haibo Huang0f72c952021-03-19 11:34:15 -07001242 license += line
1243 line = intf.readline()
1244 return license.strip()
1245
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001246 def dump_copy_out_module(self, outf):
1247 """Output the genrule module to copy out/* to $(genDir)."""
1248 copy_out = self.copy_out_module_name()
1249 if not copy_out:
1250 return
1251 outf.write('\ngenrule {\n')
1252 outf.write(' name: "' + copy_out + '",\n')
1253 outf.write(' srcs: ["out/*"],\n')
1254 outf.write(' cmd: "cp $(in) $(genDir)",\n')
1255 if len(self.build_out_files) > 1:
1256 outf.write(' out: [\n')
1257 for f in self.build_out_files:
1258 outf.write(' "' + f + '",\n')
1259 outf.write(' ],\n')
1260 else:
1261 outf.write(' out: ["' + self.build_out_files[0] + '"],\n')
1262 outf.write('}\n')
1263
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001264 def init_bp_file(self, name):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001265 # name could be Android.bp or sub_dir_path/Android.bp
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001266 if name not in self.bp_files:
1267 self.bp_files.add(name)
Haibo Huang0f72c952021-03-19 11:34:15 -07001268 license_section = self.read_license(name)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001269 with open(name, 'w') as outf:
Joel Galenson769c3c32021-11-24 15:04:29 -08001270 print_args = sys.argv[1:].copy()
1271 if '--cargo_bin' in print_args:
1272 index = print_args.index('--cargo_bin')
1273 del print_args[index:index+2]
1274 outf.write(ANDROID_BP_HEADER.format(args=' '.join(print_args)))
Haibo Huang0f72c952021-03-19 11:34:15 -07001275 outf.write('\n')
1276 outf.write(license_section)
1277 outf.write('\n')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001278 # at most one copy_out module per .bp file
1279 self.dump_copy_out_module(outf)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001280
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001281 def try_claim_module_name(self, name, owner):
1282 """Reserve and return True if it has not been reserved yet."""
1283 if name not in self.name_owners or owner == self.name_owners[name]:
1284 self.name_owners[name] = owner
1285 return True
1286 return False
1287
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001288 def claim_module_name(self, prefix, owner, counter):
1289 """Return prefix if not owned yet, otherwise, prefix+str(counter)."""
1290 while True:
1291 name = prefix
1292 if counter > 0:
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001293 name += '_' + str(counter)
1294 if self.try_claim_module_name(name, owner):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001295 return name
1296 counter += 1
1297
1298 def find_root_pkg(self):
1299 """Read name of [package] in ./Cargo.toml."""
1300 if not os.path.exists('./Cargo.toml'):
1301 return
1302 with open('./Cargo.toml', 'r') as inf:
1303 pkg_section = re.compile(r'^ *\[package\]')
1304 name = re.compile('^ *name *= * "([^"]*)"')
1305 in_pkg = False
1306 for line in inf:
1307 if in_pkg:
1308 if name.match(line):
1309 self.root_pkg = name.match(line).group(1)
1310 break
1311 else:
1312 in_pkg = pkg_section.match(line) is not None
1313
1314 def run_cargo(self):
1315 """Calls cargo -v and save its output to ./cargo.out."""
1316 if self.skip_cargo:
1317 return self
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001318 cargo_toml = './Cargo.toml'
1319 cargo_out = './cargo.out'
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001320 # Do not use Cargo.lock, because .bp rules are designed to
1321 # run with "latest" crates avaialable on Android.
1322 cargo_lock = './Cargo.lock'
1323 cargo_lock_saved = './cargo.lock.saved'
1324 had_cargo_lock = os.path.exists(cargo_lock)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001325 if not os.access(cargo_toml, os.R_OK):
1326 print('ERROR: Cannot find or read', cargo_toml)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001327 return self
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001328 if not self.dry_run:
1329 if os.path.exists(cargo_out):
1330 os.remove(cargo_out)
1331 if not self.args.use_cargo_lock and had_cargo_lock: # save it
1332 os.rename(cargo_lock, cargo_lock_saved)
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001333 cmd_tail_target = ' --target-dir ' + TARGET_TMP
1334 cmd_tail_redir = ' >> ' + cargo_out + ' 2>&1'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001335 # set up search PATH for cargo to find the correct rustc
1336 saved_path = os.environ['PATH']
1337 os.environ['PATH'] = os.path.dirname(self.cargo_path) + ':' + saved_path
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001338 # Add [workspace] to Cargo.toml if it is not there.
1339 added_workspace = False
1340 if self.args.add_workspace:
1341 with open(cargo_toml, 'r') as in_file:
1342 cargo_toml_lines = in_file.readlines()
1343 found_workspace = '[workspace]\n' in cargo_toml_lines
1344 if found_workspace:
1345 print('### WARNING: found [workspace] in Cargo.toml')
1346 else:
1347 with open(cargo_toml, 'a') as out_file:
1348 out_file.write('[workspace]\n')
1349 added_workspace = True
1350 if self.args.verbose:
1351 print('### INFO: added [workspace] to Cargo.toml')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001352 for c in self.cargo:
1353 features = ''
Chih-Hung Hsieh6c8d52f2020-03-30 18:28:52 -07001354 if c != 'clean':
1355 if self.args.features is not None:
1356 features = ' --no-default-features'
1357 if self.args.features:
1358 features += ' --features ' + self.args.features
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001359 cmd_v_flag = ' -vv ' if self.args.vv else ' -v '
1360 cmd = self.cargo_path + cmd_v_flag
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001361 cmd += c + features + cmd_tail_target + cmd_tail_redir
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001362 if self.args.rustflags and c != 'clean':
Joel Galenson903a0f82021-11-24 11:09:36 -08001363 cmd = 'RUSTFLAGS="' + self.args.rustflags + '" ' + cmd
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001364 self.run_cmd(cmd, cargo_out)
1365 if self.args.tests:
1366 cmd = self.cargo_path + ' test' + features + cmd_tail_target + ' -- --list' + cmd_tail_redir
1367 self.run_cmd(cmd, cargo_out)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001368 if added_workspace: # restore original Cargo.toml
1369 with open(cargo_toml, 'w') as out_file:
1370 out_file.writelines(cargo_toml_lines)
1371 if self.args.verbose:
1372 print('### INFO: restored original Cargo.toml')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001373 os.environ['PATH'] = saved_path
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001374 if not self.dry_run:
1375 if not had_cargo_lock: # restore to no Cargo.lock state
Andrew Walbrana32a0482022-01-06 14:03:50 +00001376 if os.path.exists(cargo_lock):
1377 os.remove(cargo_lock)
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001378 elif not self.args.use_cargo_lock: # restore saved Cargo.lock
1379 os.rename(cargo_lock_saved, cargo_lock)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001380 return self
1381
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001382 def run_cmd(self, cmd, cargo_out):
1383 if self.dry_run:
1384 print('Dry-run skip:', cmd)
1385 else:
1386 if self.args.verbose:
1387 print('Running:', cmd)
1388 with open(cargo_out, 'a') as out_file:
1389 out_file.write('### Running: ' + cmd + '\n')
1390 ret = os.system(cmd)
1391 if ret != 0:
1392 print('*** There was an error while running cargo. ' +
1393 'See the cargo.out file for details.')
1394
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001395 def dump_pkg_obj2cc(self):
1396 """Dump debug info of the pkg_obj2cc map."""
1397 if not self.args.debug:
1398 return
1399 self.init_bp_file('Android.bp')
1400 with open('Android.bp', 'a') as outf:
1401 sorted_pkgs = sorted(self.pkg_obj2cc.keys())
1402 for pkg in sorted_pkgs:
1403 if not self.pkg_obj2cc[pkg]:
1404 continue
1405 outf.write('\n// obj => src for %s\n' % pkg)
1406 obj2cc = self.pkg_obj2cc[pkg]
1407 for obj in sorted(obj2cc.keys()):
1408 outf.write('// ' + short_out_name(pkg, obj) + ' => ' +
1409 short_out_name(pkg, obj2cc[obj].src) + '\n')
1410
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001411 def apply_patch(self):
1412 """Apply local patch file if it is given."""
1413 if self.args.patch:
1414 if self.dry_run:
1415 print('Dry-run skip patch file:', self.args.patch)
1416 else:
1417 if not os.path.exists(self.args.patch):
1418 self.append_to_bp('ERROR cannot find patch file: ' + self.args.patch)
1419 return self
1420 if self.args.verbose:
1421 print('### INFO: applying local patch file:', self.args.patch)
Joel Galenson7e8247e2021-05-20 18:51:42 -07001422 subprocess.run(['patch', '-s', '--no-backup-if-mismatch', './Android.bp',
1423 self.args.patch], check=True)
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001424 return self
1425
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001426 def gen_bp(self):
1427 """Parse cargo.out and generate Android.bp files."""
1428 if self.dry_run:
1429 print('Dry-run skip: read', CARGO_OUT, 'write Android.bp')
1430 elif os.path.exists(CARGO_OUT):
1431 self.find_root_pkg()
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001432 if self.args.copy_out:
1433 self.copy_out_files()
1434 elif self.find_out_files() and self.has_used_out_dir():
1435 print('WARNING: ' + self.root_pkg + ' has cargo output files; ' +
1436 'please rerun with the --copy-out flag.')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001437 with open(CARGO_OUT, 'r') as cargo_out:
1438 self.parse(cargo_out, 'Android.bp')
1439 self.crates.sort(key=get_module_name)
1440 for obj in self.cc_objects:
1441 obj.dump()
1442 self.dump_pkg_obj2cc()
1443 for crate in self.crates:
1444 crate.dump()
1445 dumped_libs = set()
1446 for lib in self.ar_objects:
1447 if lib.pkg == self.root_pkg:
1448 lib_name = file_base_name(lib.lib)
1449 if lib_name not in dumped_libs:
1450 dumped_libs.add(lib_name)
1451 lib.dump()
Joel Galenson5664f2a2021-06-10 10:13:49 -07001452 if self.args.add_toplevel_block:
1453 with open(self.args.add_toplevel_block, 'r') as f:
1454 self.append_to_bp('\n' + f.read() + '\n')
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001455 if self.errors:
Joel Galenson3f42f802021-04-07 12:42:17 -07001456 self.append_to_bp('\n' + ERRORS_LINE + '\n' + self.errors)
Joel Galenson56b5f4b2021-11-30 10:02:57 -08001457 if self.test_errors:
1458 self.append_to_bp('\n// Errors when listing tests:\n' + self.test_errors)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001459 return self
1460
1461 def add_ar_object(self, obj):
1462 self.ar_objects.append(obj)
1463
1464 def add_cc_object(self, obj):
1465 self.cc_objects.append(obj)
1466
1467 def add_crate(self, crate):
1468 """Merge crate with someone in crates, or append to it. Return crates."""
1469 if crate.skip_crate():
1470 if self.args.debug: # include debug info of all crates
1471 self.crates.append(crate)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001472 else:
1473 for c in self.crates:
1474 if c.merge(crate, 'Android.bp'):
1475 return
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001476 # If not merged, decide module type and name now.
1477 crate.decide_module_type()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001478 self.crates.append(crate)
1479
1480 def find_warning_owners(self):
1481 """For each warning file, find its owner crate."""
1482 missing_owner = False
1483 for f in self.warning_files:
1484 cargo_dir = '' # find lowest crate, with longest path
1485 owner = None # owner crate of this warning
1486 for c in self.crates:
1487 if (f.startswith(c.cargo_dir + '/') and
1488 len(cargo_dir) < len(c.cargo_dir)):
1489 cargo_dir = c.cargo_dir
1490 owner = c
1491 if owner:
1492 owner.has_warning = True
1493 else:
1494 missing_owner = True
1495 if missing_owner and os.path.exists('Cargo.toml'):
1496 # owner is the root cargo, with empty cargo_dir
1497 for c in self.crates:
1498 if not c.cargo_dir:
1499 c.has_warning = True
1500
1501 def rustc_command(self, n, rustc_line, line, outf_name):
1502 """Process a rustc command line from cargo -vv output."""
1503 # cargo build -vv output can have multiple lines for a rustc command
1504 # due to '\n' in strings for environment variables.
1505 # strip removes leading spaces and '\n' at the end
1506 new_rustc = (rustc_line.strip() + line) if rustc_line else line
1507 # Use an heuristic to detect the completions of a multi-line command.
1508 # This might fail for some very rare case, but easy to fix manually.
1509 if not line.endswith('`\n') or (new_rustc.count('`') % 2) != 0:
1510 return new_rustc
1511 if RUSTC_VV_CMD_ARGS.match(new_rustc):
1512 args = RUSTC_VV_CMD_ARGS.match(new_rustc).group(1)
1513 self.add_crate(Crate(self, outf_name).parse(n, args))
1514 else:
1515 self.assert_empty_vv_line(new_rustc)
1516 return ''
1517
1518 def cc_ar_command(self, n, groups, outf_name):
1519 pkg = groups.group(1)
1520 line = groups.group(3)
1521 if groups.group(2) == 'cc':
1522 self.add_cc_object(CCObject(self, outf_name).parse(pkg, n, line))
1523 else:
1524 self.add_ar_object(ARObject(self, outf_name).parse(pkg, n, line))
1525
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001526 def append_to_bp(self, line):
1527 self.init_bp_file('Android.bp')
1528 with open('Android.bp', 'a') as outf:
1529 outf.write(line)
1530
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001531 def assert_empty_vv_line(self, line):
1532 if line: # report error if line is not empty
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001533 self.append_to_bp('ERROR -vv line: ' + line)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001534 return ''
1535
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001536 def add_empty_test(self, name):
1537 if name == 'unittests':
1538 self.empty_unittests = True
1539 else:
1540 self.empty_tests.add(name)
1541
1542 def should_ignore_test(self, src):
1543 # cargo test outputs the source file for integration tests but "unittests"
1544 # for unit tests. To figure out to which crate this corresponds, we check
1545 # if the current source file is the main source of a non-test crate, e.g.,
1546 # a library or a binary.
1547 return (src in self.args.test_blocklist or src in self.empty_tests
1548 or (self.empty_unittests
1549 and src in [c.main_src for c in self.crates if c.crate_types != ['test']]))
1550
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001551 def parse(self, inf, outf_name):
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001552 """Parse rustc, test, and warning messages in inf, return a list of Crates."""
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001553 n = 0 # line number
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001554 # We read the file in two passes, where the first simply checks for empty tests.
1555 # Otherwise we would add and merge tests before seeing they're empty.
1556 cur_test_name = None
1557 for line in inf:
1558 if CARGO_TEST_LIST_START_PAT.match(line):
1559 cur_test_name = CARGO_TEST_LIST_START_PAT.match(line).group(1)
1560 elif cur_test_name and CARGO_TEST_LIST_END_PAT.match(line):
1561 match = CARGO_TEST_LIST_END_PAT.match(line)
1562 if int(match.group(1)) + int(match.group(2)) == 0:
1563 self.add_empty_test(cur_test_name)
1564 cur_test_name = None
1565 inf.seek(0)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001566 prev_warning = False # true if the previous line was warning: ...
1567 rustc_line = '' # previous line(s) matching RUSTC_VV_PAT
Joel Galenson56b5f4b2021-11-30 10:02:57 -08001568 in_tests = False
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001569 for line in inf:
1570 n += 1
1571 if line.startswith('warning: '):
1572 prev_warning = True
1573 rustc_line = self.assert_empty_vv_line(rustc_line)
1574 continue
1575 new_rustc = ''
1576 if RUSTC_PAT.match(line):
1577 args_line = RUSTC_PAT.match(line).group(1)
1578 self.add_crate(Crate(self, outf_name).parse(n, args_line))
1579 self.assert_empty_vv_line(rustc_line)
1580 elif rustc_line or RUSTC_VV_PAT.match(line):
1581 new_rustc = self.rustc_command(n, rustc_line, line, outf_name)
1582 elif CC_AR_VV_PAT.match(line):
1583 self.cc_ar_command(n, CC_AR_VV_PAT.match(line), outf_name)
1584 elif prev_warning and WARNING_FILE_PAT.match(line):
1585 self.assert_empty_vv_line(rustc_line)
1586 fpath = WARNING_FILE_PAT.match(line).group(1)
1587 if fpath[0] != '/': # ignore absolute path
1588 self.warning_files.add(fpath)
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001589 elif line.startswith('error: ') or line.startswith('error[E'):
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001590 if not self.args.ignore_cargo_errors:
Joel Galenson56b5f4b2021-11-30 10:02:57 -08001591 if in_tests:
1592 self.test_errors += '// ' + line
1593 else:
1594 self.errors += line
1595 elif CARGO2ANDROID_RUNNING_PAT.match(line):
1596 in_tests = "cargo test" in line and "--list" in line
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001597 prev_warning = False
1598 rustc_line = new_rustc
1599 self.find_warning_owners()
1600
1601
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001602def get_parser():
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001603 """Parse main arguments."""
1604 parser = argparse.ArgumentParser('cargo2android')
1605 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001606 '--add_workspace',
1607 action='store_true',
1608 default=False,
1609 help=('append [workspace] to Cargo.toml before calling cargo,' +
1610 ' to treat current directory as root of package source;' +
1611 ' otherwise the relative source file path in generated' +
1612 ' .bp file will be from the parent directory.'))
1613 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001614 '--cargo',
1615 action='append',
1616 metavar='args_string',
1617 help=('extra cargo build -v args in a string, ' +
1618 'each --cargo flag calls cargo build -v once'))
1619 parser.add_argument(
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001620 '--cargo_bin',
1621 type=str,
1622 help='use cargo in the cargo_bin directory instead of the prebuilt one')
1623 parser.add_argument(
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001624 '--copy-out',
1625 action='store_true',
1626 default=False,
1627 help=('only for root directory, ' +
1628 'copy build.rs output to ./out/* and add a genrule to copy ' +
1629 './out/* to genrule output; for crates with code pattern: ' +
1630 'include!(concat!(env!("OUT_DIR"), "/<some_file>.rs"))'))
1631 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001632 '--debug',
1633 action='store_true',
1634 default=False,
1635 help='dump debug info into Android.bp')
1636 parser.add_argument(
1637 '--dependencies',
1638 action='store_true',
1639 default=False,
Joel Galenson833848c2021-08-17 10:50:42 -07001640 help='Deprecated. Has no effect.')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001641 parser.add_argument(
1642 '--device',
1643 action='store_true',
1644 default=False,
1645 help='run cargo also for a default device target')
1646 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001647 '--features',
1648 type=str,
1649 help=('pass features to cargo build, ' +
1650 'empty string means no default features'))
1651 parser.add_argument(
1652 '--global_defaults',
1653 type=str,
1654 help='add a defaults name to every module')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001655 parser.add_argument(
1656 '--host-first-multilib',
1657 action='store_true',
1658 default=False,
1659 help=('add a compile_multilib:"first" property ' +
1660 'to Android.bp host modules.'))
1661 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001662 '--ignore-cargo-errors',
1663 action='store_true',
1664 default=False,
1665 help='do not append cargo/rustc error messages to Android.bp')
1666 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001667 '--no-host',
1668 action='store_true',
1669 default=False,
1670 help='do not run cargo for the host; only for the device target')
1671 parser.add_argument(
1672 '--no-subdir',
1673 action='store_true',
1674 default=False,
1675 help='do not output anything for sub-directories')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001676 parser.add_argument(
1677 '--onefile',
1678 action='store_true',
1679 default=False,
1680 help=('output all into one ./Android.bp, default will generate ' +
1681 'one Android.bp per Cargo.toml in subdirectories'))
1682 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001683 '--patch',
1684 type=str,
1685 help='apply the given patch file to generated ./Android.bp')
1686 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001687 '--run',
1688 action='store_true',
1689 default=False,
1690 help='run it, default is dry-run')
1691 parser.add_argument('--rustflags', type=str, help='passing flags to rustc')
1692 parser.add_argument(
1693 '--skipcargo',
1694 action='store_true',
1695 default=False,
1696 help='skip cargo command, parse cargo.out, and generate Android.bp')
1697 parser.add_argument(
1698 '--tests',
1699 action='store_true',
1700 default=False,
1701 help='run cargo build --tests after normal build')
1702 parser.add_argument(
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001703 '--use-cargo-lock',
1704 action='store_true',
1705 default=False,
1706 help=('run cargo build with existing Cargo.lock ' +
1707 '(used when some latest dependent crates failed)'))
1708 parser.add_argument(
Matthew Maurer062709c2021-08-17 11:27:36 -07001709 '--exported_c_header_dir',
1710 nargs='*',
1711 help='Directories with headers to export for C usage'
1712 )
1713 parser.add_argument(
Joel Galensond9c4de62021-04-23 10:26:40 -07001714 '--min-sdk-version',
1715 type=str,
1716 help='Minimum SDK version')
1717 parser.add_argument(
1718 '--apex-available',
1719 nargs='*',
1720 help='Mark the main library as apex_available with the given apexes.')
1721 parser.add_argument(
Matthew Maurerac677252021-08-13 15:52:52 -07001722 '--native-bridge-supported',
1723 action='store_true',
1724 default=False,
1725 help='Mark the main library as native_bridge_supported.')
1726 parser.add_argument(
1727 '--product-available',
1728 action='store_true',
1729 default=False,
1730 help='Mark the main library as product_available.')
1731 parser.add_argument(
1732 '--recovery-available',
1733 action='store_true',
1734 default=False,
1735 help='Mark the main library as recovery_available.')
1736 parser.add_argument(
Ivan Lozano91920862021-07-19 10:49:08 -04001737 '--vendor-available',
1738 action='store_true',
1739 default=False,
1740 help='Mark the main library as vendor_available.')
1741 parser.add_argument(
1742 '--vendor-ramdisk-available',
1743 action='store_true',
1744 default=False,
1745 help='Mark the main library as vendor_ramdisk_available.')
1746 parser.add_argument(
Matthew Maurerac677252021-08-13 15:52:52 -07001747 '--ramdisk-available',
1748 action='store_true',
1749 default=False,
1750 help='Mark the main library as ramdisk_available.')
1751 parser.add_argument(
Joel Galensoncb5f2f02021-06-08 14:47:55 -07001752 '--force-rlib',
1753 action='store_true',
1754 default=False,
1755 help='Make the main library an rlib.')
1756 parser.add_argument(
Joel Galenson12467e52021-07-12 14:33:28 -07001757 '--whole-static-libs',
1758 nargs='*',
1759 default=[],
1760 help='Make the given libraries (without lib prefixes) whole_static_libs.')
1761 parser.add_argument(
Ivan Lozano26aa1c32021-08-16 11:20:32 -04001762 '--no-pkg-vers',
1763 action='store_true',
1764 default=False,
1765 help='Do not attempt to determine the package version automatically.')
1766 parser.add_argument(
Joel Galensone4f53882021-07-19 11:14:55 -07001767 '--test-data',
1768 nargs='*',
1769 default=[],
1770 help=('Add the given file to the given test\'s data property. ' +
1771 'Usage: test-path=data-path'))
1772 parser.add_argument(
Joel Galenson97e414a2021-05-27 09:42:32 -07001773 '--dependency-blocklist',
1774 nargs='*',
1775 default=[],
Joel Galenson12467e52021-07-12 14:33:28 -07001776 help='Do not emit the given dependencies (without lib prefixes).')
Joel Galenson97e414a2021-05-27 09:42:32 -07001777 parser.add_argument(
Joel Galensoncb5f2f02021-06-08 14:47:55 -07001778 '--lib-blocklist',
1779 nargs='*',
1780 default=[],
Joel Galenson12467e52021-07-12 14:33:28 -07001781 help='Do not emit the given C libraries as dependencies (without lib prefixes).')
Joel Galensoncb5f2f02021-06-08 14:47:55 -07001782 parser.add_argument(
Joel Galensonf6b3c912021-06-03 16:00:54 -07001783 '--test-blocklist',
1784 nargs='*',
1785 default=[],
1786 help=('Do not emit the given tests. ' +
1787 'Pass the path to the test file to exclude.'))
1788 parser.add_argument(
Joel Galenson3d6d1e72021-06-07 15:00:24 -07001789 '--cfg-blocklist',
1790 nargs='*',
1791 default=[],
1792 help='Do not emit the given cfg.')
1793 parser.add_argument(
Joel Galenson5664f2a2021-06-10 10:13:49 -07001794 '--add-toplevel-block',
1795 type=str,
Joel Galenson493c26a2021-10-18 15:54:41 -07001796 help=('Add the contents of the given file to the top level of the Android.bp. ' +
1797 'The filename should start with cargo2android to work with the updater.'))
Joel Galenson5664f2a2021-06-10 10:13:49 -07001798 parser.add_argument(
1799 '--add-module-block',
1800 type=str,
Joel Galenson493c26a2021-10-18 15:54:41 -07001801 help=('Add the contents of the given file to the main module. '+
1802 'The filename should start with cargo2android to work with the updater.'))
Joel Galenson5664f2a2021-06-10 10:13:49 -07001803 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001804 '--verbose',
1805 action='store_true',
1806 default=False,
1807 help='echo executed commands')
1808 parser.add_argument(
1809 '--vv',
1810 action='store_true',
1811 default=False,
1812 help='run cargo with -vv instead of default -v')
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001813 parser.add_argument(
1814 '--dump-config-and-exit',
1815 type=str,
1816 help=('Dump command-line arguments (minus this flag) to a config file and exit. ' +
1817 'This is intended to help migrate from command line options to config files.'))
1818 parser.add_argument(
1819 '--config',
1820 type=str,
1821 help=('Load command-line options from the given config file. ' +
1822 'Options in this file will override those passed on the command line.'))
1823 return parser
1824
1825
1826def parse_args(parser):
1827 """Parses command-line options."""
1828 args = parser.parse_args()
1829 # Use the values specified in a config file if one was found.
1830 if args.config:
1831 with open(args.config, 'r') as f:
1832 config = json.load(f)
1833 args_dict = vars(args)
1834 for arg in config:
1835 args_dict[arg.replace('-', '_')] = config[arg]
1836 return args
1837
1838
1839def dump_config(parser, args):
1840 """Writes the non-default command-line options to the specified file."""
1841 args_dict = vars(args)
1842 # Filter out the arguments that have their default value.
Joel Galenson367360c2021-04-29 14:31:43 -07001843 # Also filter certain "temporary" arguments.
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001844 non_default_args = {}
1845 for arg in args_dict:
Joel Galenson9a82ad92021-08-17 17:52:04 -07001846 if (args_dict[arg] != parser.get_default(arg) and arg != 'dump_config_and_exit'
Joel Galenson769c3c32021-11-24 15:04:29 -08001847 and arg != 'config' and arg != 'cargo_bin'):
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001848 non_default_args[arg.replace('_', '-')] = args_dict[arg]
1849 # Write to the specified file.
1850 with open(args.dump_config_and_exit, 'w') as f:
1851 json.dump(non_default_args, f, indent=2, sort_keys=True)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001852
1853
1854def main():
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001855 parser = get_parser()
1856 args = parse_args(parser)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001857 if not args.run: # default is dry-run
1858 print(DRY_RUN_NOTE)
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001859 if args.dump_config_and_exit:
1860 dump_config(parser, args)
1861 else:
ThiƩbaud Weksteen198e93f2021-07-02 14:49:19 +02001862 Runner(args).run_cargo().gen_bp().apply_patch()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001863
1864
1865if __name__ == '__main__':
1866 main()