blob: 2a4bfdf7153d935055710d29dbaf4c973a7a94e8 [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 Galenson308f3522021-09-30 14:13:45 -0700134# Rust package name with suffix -d1.d2.d3(+.*)?.
135VERSION_SUFFIX_PAT = re.compile(r'^(.*)-[0-9]+\.[0-9]+\.[0-9]+(?:\+.*)?$')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800136
Matthew Maurer062709c2021-08-17 11:27:36 -0700137# Crate types corresponding to a C ABI library
138C_LIBRARY_CRATE_TYPES = ['staticlib', 'cdylib']
139# Crate types corresponding to a Rust ABI library
140RUST_LIBRARY_CRATE_TYPES = ['lib', 'rlib', 'dylib']
141# Crate types corresponding to a library
142LIBRARY_CRATE_TYPES = C_LIBRARY_CRATE_TYPES + RUST_LIBRARY_CRATE_TYPES
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800143
144def altered_name(name):
145 return RENAME_MAP[name] if (name in RENAME_MAP) else name
146
147
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700148def altered_stem(name):
149 return RENAME_STEM_MAP[name] if (name in RENAME_STEM_MAP) else name
150
151
152def altered_defaults(name):
153 return RENAME_DEFAULTS_MAP[name] if (name in RENAME_DEFAULTS_MAP) else name
154
155
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800156def is_build_crate_name(name):
157 # We added special prefix to build script crate names.
158 return name.startswith('build_script_')
159
160
161def is_dependent_file_path(path):
162 # Absolute or dependent '.../' paths are not main files of this crate.
163 return path.startswith('/') or path.startswith('.../')
164
165
166def get_module_name(crate): # to sort crates in a list
167 return crate.module_name
168
169
170def pkg2crate_name(s):
171 return s.replace('-', '_').replace('.', '_')
172
173
174def file_base_name(path):
175 return os.path.splitext(os.path.basename(path))[0]
176
177
178def test_base_name(path):
179 return pkg2crate_name(file_base_name(path))
180
181
182def unquote(s): # remove quotes around str
183 if s and len(s) > 1 and s[0] == '"' and s[-1] == '"':
184 return s[1:-1]
185 return s
186
187
188def remove_version_suffix(s): # remove -d1.d2.d3 suffix
189 if VERSION_SUFFIX_PAT.match(s):
190 return VERSION_SUFFIX_PAT.match(s).group(1)
191 return s
192
193
194def short_out_name(pkg, s): # replace /.../pkg-*/out/* with .../out/*
195 return re.sub('^/.*/' + pkg + '-[0-9a-f]*/out/', '.../out/', s)
196
197
198def escape_quotes(s): # replace '"' with '\\"'
199 return s.replace('"', '\\"')
200
201
202class Crate(object):
203 """Information of a Rust crate to collect/emit for an Android.bp module."""
204
205 def __init__(self, runner, outf_name):
206 # Remembered global runner and its members.
207 self.runner = runner
208 self.debug = runner.args.debug
209 self.cargo_dir = '' # directory of my Cargo.toml
210 self.outf_name = outf_name # path to Android.bp
211 self.outf = None # open file handle of outf_name during dump*
212 # Variants/results that could be merged from multiple rustc lines.
213 self.host_supported = False
214 self.device_supported = False
215 self.has_warning = False
216 # Android module properties derived from rustc parameters.
217 self.module_name = '' # unique in Android build system
218 self.module_type = '' # rust_{binary,library,test}[_host] etc.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700219 self.defaults = '' # rust_defaults used by rust_test* modules
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700220 self.default_srcs = False # use 'srcs' defined in self.defaults
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800221 self.root_pkg = '' # parent package name of a sub/test packge, from -L
222 self.srcs = list() # main_src or merged multiple source files
223 self.stem = '' # real base name of output file
224 # Kept parsed status
225 self.errors = '' # all errors found during parsing
226 self.line_num = 1 # runner told input source line number
227 self.line = '' # original rustc command line parameters
228 # Parameters collected from rustc command line.
229 self.crate_name = '' # follows --crate-name
230 self.main_src = '' # follows crate_name parameter, shortened
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700231 self.crate_types = list() # follows --crate-type
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800232 self.cfgs = list() # follows --cfg, without feature= prefix
233 self.features = list() # follows --cfg, name in 'feature="..."'
234 self.codegens = list() # follows -C, some ignored
235 self.externs = list() # follows --extern
236 self.core_externs = list() # first part of self.externs elements
237 self.static_libs = list() # e.g. -l static=host_cpuid
238 self.shared_libs = list() # e.g. -l dylib=wayland-client, -l z
239 self.cap_lints = '' # follows --cap-lints
240 self.emit_list = '' # e.g., --emit=dep-info,metadata,link
241 self.edition = '2015' # rustc default, e.g., --edition=2018
242 self.target = '' # follows --target
Ivan Lozanocc660f12021-08-11 16:49:46 -0400243 self.cargo_env_compat = True
244 self.cargo_pkg_version = '' # value extracted from Cargo.toml version field
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800245
246 def write(self, s):
247 # convenient way to output one line at a time with EOL.
248 self.outf.write(s + '\n')
249
250 def same_flags(self, other):
251 # host_supported, device_supported, has_warning are not compared but merged
252 # target is not compared, to merge different target/host modules
253 # externs is not compared; only core_externs is compared
254 return (not self.errors and not other.errors and
255 self.edition == other.edition and
256 self.cap_lints == other.cap_lints and
257 self.emit_list == other.emit_list and
258 self.core_externs == other.core_externs and
259 self.codegens == other.codegens and
260 self.features == other.features and
261 self.static_libs == other.static_libs and
262 self.shared_libs == other.shared_libs and self.cfgs == other.cfgs)
263
264 def merge_host_device(self, other):
265 """Returns true if attributes are the same except host/device support."""
266 return (self.crate_name == other.crate_name and
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700267 self.crate_types == other.crate_types and
268 self.main_src == other.main_src and
269 # before merge, each test module has an unique module name and stem
270 (self.stem == other.stem or self.crate_types == ['test']) and
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800271 self.root_pkg == other.root_pkg and not self.skip_crate() and
272 self.same_flags(other))
273
274 def merge_test(self, other):
275 """Returns true if self and other are tests of same root_pkg."""
276 # Before merger, each test has its own crate_name.
277 # A merged test uses its source file base name as output file name,
278 # so a test is mergeable only if its base name equals to its crate name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700279 return (self.crate_types == other.crate_types and
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700280 self.crate_types == ['test'] and self.root_pkg == other.root_pkg and
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800281 not self.skip_crate() and
282 other.crate_name == test_base_name(other.main_src) and
283 (len(self.srcs) > 1 or
284 (self.crate_name == test_base_name(self.main_src)) and
285 self.host_supported == other.host_supported and
286 self.device_supported == other.device_supported) and
287 self.same_flags(other))
288
289 def merge(self, other, outf_name):
290 """Try to merge crate into self."""
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700291 # Cargo build --tests could recompile a library for tests.
292 # We need to merge such duplicated calls to rustc, with
293 # the algorithm in merge_host_device.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800294 should_merge_host_device = self.merge_host_device(other)
295 should_merge_test = False
296 if not should_merge_host_device:
297 should_merge_test = self.merge_test(other)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800298 if should_merge_host_device or should_merge_test:
299 self.runner.init_bp_file(outf_name)
300 with open(outf_name, 'a') as outf: # to write debug info
301 self.outf = outf
302 other.outf = outf
303 self.do_merge(other, should_merge_test)
304 return True
305 return False
306
307 def do_merge(self, other, should_merge_test):
308 """Merge attributes of other to self."""
309 if self.debug:
310 self.write('\n// Before merge definition (1):')
311 self.dump_debug_info()
312 self.write('\n// Before merge definition (2):')
313 other.dump_debug_info()
314 # Merge properties of other to self.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800315 self.has_warning = self.has_warning or other.has_warning
316 if not self.target: # okay to keep only the first target triple
317 self.target = other.target
318 # decide_module_type sets up default self.stem,
319 # which can be changed if self is a merged test module.
320 self.decide_module_type()
321 if should_merge_test:
Joel Galenson7cfc6fe2021-10-14 13:09:32 -0700322 if (self.runner.should_ignore_test(self.main_src)
323 and not self.runner.should_ignore_test(other.main_src)):
Joel Galenson57fa23a2021-07-15 10:47:35 -0700324 self.main_src = other.main_src
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800325 self.srcs.append(other.main_src)
326 # use a short unique name as the merged module name.
327 prefix = self.root_pkg + '_tests'
328 self.module_name = self.runner.claim_module_name(prefix, self, 0)
329 self.stem = self.module_name
330 # This normalized root_pkg name although might be the same
331 # as other module's crate_name, it is not actually used for
332 # output file name. A merged test module always have multiple
333 # source files and each source file base name is used as
334 # its output file name.
335 self.crate_name = pkg2crate_name(self.root_pkg)
336 if self.debug:
337 self.write('\n// After merge definition (1):')
338 self.dump_debug_info()
339
340 def find_cargo_dir(self):
341 """Deepest directory with Cargo.toml and contains the main_src."""
342 if not is_dependent_file_path(self.main_src):
343 dir_name = os.path.dirname(self.main_src)
344 while dir_name:
345 if os.path.exists(dir_name + '/Cargo.toml'):
346 self.cargo_dir = dir_name
347 return
348 dir_name = os.path.dirname(dir_name)
349
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700350 def add_codegens_flag(self, flag):
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700351 """Ignore options not used in Android."""
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700352 # 'prefer-dynamic' does not work with common flag -C lto
Chih-Hung Hsieh63459ed2020-08-26 11:51:15 -0700353 # 'embed-bitcode' is ignored; we might control LTO with other .bp flag
Chih-Hung Hsieh6c13b722020-09-11 21:24:03 -0700354 # 'codegen-units' is set in Android global config or by default
355 if not (flag.startswith('codegen-units=') or
356 flag.startswith('debuginfo=') or
Chih-Hung Hsieh63459ed2020-08-26 11:51:15 -0700357 flag.startswith('embed-bitcode=') or
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700358 flag.startswith('extra-filename=') or
359 flag.startswith('incremental=') or
360 flag.startswith('metadata=') or
361 flag == 'prefer-dynamic'):
362 self.codegens.append(flag)
363
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800364 def parse(self, line_num, line):
365 """Find important rustc arguments to convert to Android.bp properties."""
366 self.line_num = line_num
367 self.line = line
368 args = line.split() # Loop through every argument of rustc.
369 i = 0
370 while i < len(args):
371 arg = args[i]
372 if arg == '--crate-name':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700373 i += 1
374 self.crate_name = args[i]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800375 elif arg == '--crate-type':
376 i += 1
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700377 # cargo calls rustc with multiple --crate-type flags.
378 # rustc can accept:
379 # --crate-type [bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]
380 self.crate_types.append(args[i])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800381 elif arg == '--test':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700382 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800383 elif arg == '--target':
384 i += 1
385 self.target = args[i]
386 elif arg == '--cfg':
387 i += 1
388 if args[i].startswith('\'feature='):
389 self.features.append(unquote(args[i].replace('\'feature=', '')[:-1]))
390 else:
391 self.cfgs.append(args[i])
392 elif arg == '--extern':
393 i += 1
394 extern_names = re.sub('=/[^ ]*/deps/', ' = ', args[i])
395 self.externs.append(extern_names)
396 self.core_externs.append(re.sub(' = .*', '', extern_names))
397 elif arg == '-C': # codegen options
398 i += 1
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700399 self.add_codegens_flag(args[i])
400 elif arg.startswith('-C'):
401 # cargo has been passing "-C <xyz>" flag to rustc,
402 # but newer cargo could pass '-Cembed-bitcode=no' to rustc.
403 self.add_codegens_flag(arg[2:])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800404 elif arg == '--cap-lints':
405 i += 1
406 self.cap_lints = args[i]
407 elif arg == '-L':
408 i += 1
409 if args[i].startswith('dependency=') and args[i].endswith('/deps'):
410 if '/' + TARGET_TMP + '/' in args[i]:
411 self.root_pkg = re.sub(
412 '^.*/', '', re.sub('/' + TARGET_TMP + '/.*/deps$', '', args[i]))
413 else:
414 self.root_pkg = re.sub('^.*/', '',
415 re.sub('/[^/]+/[^/]+/deps$', '', args[i]))
416 self.root_pkg = remove_version_suffix(self.root_pkg)
417 elif arg == '-l':
418 i += 1
419 if args[i].startswith('static='):
420 self.static_libs.append(re.sub('static=', '', args[i]))
421 elif args[i].startswith('dylib='):
422 self.shared_libs.append(re.sub('dylib=', '', args[i]))
423 else:
424 self.shared_libs.append(args[i])
425 elif arg == '--out-dir' or arg == '--color': # ignored
426 i += 1
427 elif arg.startswith('--error-format=') or arg.startswith('--json='):
428 _ = arg # ignored
429 elif arg.startswith('--emit='):
430 self.emit_list = arg.replace('--emit=', '')
431 elif arg.startswith('--edition='):
432 self.edition = arg.replace('--edition=', '')
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700433 elif not arg.startswith('-'):
434 # shorten imported crate main source paths like $HOME/.cargo/
435 # registry/src/github.com-1ecc6299db9ec823/memchr-2.3.3/src/lib.rs
436 self.main_src = re.sub(r'^/[^ ]*/registry/src/', '.../', args[i])
437 self.main_src = re.sub(r'^\.\.\./github.com-[0-9a-f]*/', '.../',
438 self.main_src)
439 self.find_cargo_dir()
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700440 if self.cargo_dir: # for a subdirectory
441 if self.runner.args.no_subdir: # all .bp content to /dev/null
442 self.outf_name = '/dev/null'
443 elif not self.runner.args.onefile:
444 # Write to Android.bp in the subdirectory with Cargo.toml.
445 self.outf_name = self.cargo_dir + '/Android.bp'
446 self.main_src = self.main_src[len(self.cargo_dir) + 1:]
Ivan Lozanocc660f12021-08-11 16:49:46 -0400447
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800448 else:
449 self.errors += 'ERROR: unknown ' + arg + '\n'
450 i += 1
451 if not self.crate_name:
452 self.errors += 'ERROR: missing --crate-name\n'
453 if not self.main_src:
454 self.errors += 'ERROR: missing main source file\n'
455 else:
456 self.srcs.append(self.main_src)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700457 if not self.crate_types:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800458 # Treat "--cfg test" as "--test"
459 if 'test' in self.cfgs:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700460 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800461 else:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700462 self.errors += 'ERROR: missing --crate-type or --test\n'
463 elif len(self.crate_types) > 1:
464 if 'test' in self.crate_types:
465 self.errors += 'ERROR: cannot handle both --crate-type and --test\n'
466 if 'lib' in self.crate_types and 'rlib' in self.crate_types:
467 self.errors += 'ERROR: cannot generate both lib and rlib crate types\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800468 if not self.root_pkg:
469 self.root_pkg = self.crate_name
Ivan Lozano26aa1c32021-08-16 11:20:32 -0400470
471 # get the package version from running cargo metadata
Joel Galenson69ba8072021-08-16 11:31:29 -0700472 if not self.runner.args.no_pkg_vers and not self.skip_crate():
Ivan Lozano26aa1c32021-08-16 11:20:32 -0400473 self.get_pkg_version()
474
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700475 self.device_supported = self.runner.args.device
476 self.host_supported = not self.runner.args.no_host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800477 self.cfgs = sorted(set(self.cfgs))
478 self.features = sorted(set(self.features))
479 self.codegens = sorted(set(self.codegens))
480 self.externs = sorted(set(self.externs))
481 self.core_externs = sorted(set(self.core_externs))
482 self.static_libs = sorted(set(self.static_libs))
483 self.shared_libs = sorted(set(self.shared_libs))
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700484 self.crate_types = sorted(set(self.crate_types))
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800485 self.decide_module_type()
486 self.module_name = altered_name(self.stem)
487 return self
488
Ivan Lozano26aa1c32021-08-16 11:20:32 -0400489 def get_pkg_version(self):
490 """Attempt to retrieve the package version from the Cargo.toml
491
492 If there is only one package, use its version. Otherwise, try to
493 match the emitted `--crate_name` arg against the package name.
494
495 This may fail in cases where multiple packages are defined (workspaces)
496 and where the package name does not match the emitted crate_name
497 (e.g. [lib.name] is set).
498 """
Joel Galenson69ba8072021-08-16 11:31:29 -0700499 cargo_metadata = subprocess.run([self.runner.cargo_path, 'metadata', '--no-deps',
500 '--format-version', '1'],
Joel Galensonc5186502021-08-16 11:22:47 -0700501 cwd=os.path.abspath(self.cargo_dir),
502 stdout=subprocess.PIPE)
Ivan Lozano26aa1c32021-08-16 11:20:32 -0400503 if cargo_metadata.returncode:
504 self.errors += ('ERROR: unable to get cargo metadata for package version; ' +
505 'return code ' + cargo_metadata.returncode + '\n')
506 else:
507 metadata_json = json.loads(cargo_metadata.stdout)
508 if len(metadata_json['packages']) > 1:
509 for package in metadata_json['packages']:
510 # package names may contain '-', but is changed to '_' in the crate_name
511 if package['name'].replace('-','_') == self.crate_name:
512 self.cargo_pkg_version = package['version']
513 break
514 else:
515 self.cargo_pkg_version = metadata_json['packages'][0]['version']
516
517 if not self.cargo_pkg_version:
518 self.errors += ('ERROR: Unable to retrieve package version; ' +
519 'to disable, run with arg "--no-pkg-vers"\n')
520
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800521 def dump_line(self):
522 self.write('\n// Line ' + str(self.line_num) + ' ' + self.line)
523
524 def feature_list(self):
525 """Return a string of main_src + "feature_list"."""
526 pkg = self.main_src
527 if pkg.startswith('.../'): # keep only the main package name
528 pkg = re.sub('/.*', '', pkg[4:])
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700529 elif pkg.startswith('/'): # use relative path for a local package
530 pkg = os.path.relpath(pkg)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800531 if not self.features:
532 return pkg
533 return pkg + ' "' + ','.join(self.features) + '"'
534
535 def dump_skip_crate(self, kind):
536 if self.debug:
537 self.write('\n// IGNORED: ' + kind + ' ' + self.main_src)
538 return self
539
540 def skip_crate(self):
541 """Return crate_name or a message if this crate should be skipped."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700542 if (is_build_crate_name(self.crate_name) or
543 self.crate_name in EXCLUDED_CRATES):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800544 return self.crate_name
545 if is_dependent_file_path(self.main_src):
546 return 'dependent crate'
547 return ''
548
549 def dump(self):
550 """Dump all error/debug/module code to the output .bp file."""
551 self.runner.init_bp_file(self.outf_name)
552 with open(self.outf_name, 'a') as outf:
553 self.outf = outf
554 if self.errors:
555 self.dump_line()
556 self.write(self.errors)
557 elif self.skip_crate():
558 self.dump_skip_crate(self.skip_crate())
559 else:
560 if self.debug:
561 self.dump_debug_info()
562 self.dump_android_module()
563
564 def dump_debug_info(self):
565 """Dump parsed data, when cargo2android is called with --debug."""
566
567 def dump(name, value):
568 self.write('//%12s = %s' % (name, value))
569
570 def opt_dump(name, value):
571 if value:
572 dump(name, value)
573
574 def dump_list(fmt, values):
575 for v in values:
576 self.write(fmt % v)
577
578 self.dump_line()
579 dump('module_name', self.module_name)
580 dump('crate_name', self.crate_name)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700581 dump('crate_types', self.crate_types)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800582 dump('main_src', self.main_src)
583 dump('has_warning', self.has_warning)
584 dump('for_host', self.host_supported)
585 dump('for_device', self.device_supported)
586 dump('module_type', self.module_type)
587 opt_dump('target', self.target)
588 opt_dump('edition', self.edition)
589 opt_dump('emit_list', self.emit_list)
590 opt_dump('cap_lints', self.cap_lints)
591 dump_list('// cfg = %s', self.cfgs)
592 dump_list('// cfg = \'feature "%s"\'', self.features)
593 # TODO(chh): escape quotes in self.features, but not in other dump_list
594 dump_list('// codegen = %s', self.codegens)
595 dump_list('// externs = %s', self.externs)
596 dump_list('// -l static = %s', self.static_libs)
597 dump_list('// -l (dylib) = %s', self.shared_libs)
598
599 def dump_android_module(self):
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700600 """Dump one or more Android module definition, depending on crate_types."""
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700601 if len(self.crate_types) == 1:
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700602 self.dump_single_type_android_module()
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700603 return
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700604 if 'test' in self.crate_types:
605 self.write('\nERROR: multiple crate types cannot include test type')
606 return
607 # Dump one Android module per crate_type.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700608 for crate_type in self.crate_types:
609 self.decide_one_module_type(crate_type)
610 self.dump_one_android_module(crate_type)
611
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700612 def build_default_name(self):
613 """Return a short and readable name for the rust_defaults module."""
Joel Galensond37d7e62021-07-13 09:03:01 -0700614 # Choices: (1) root_pkg + '_test'? + '_defaults',
615 # (2) root_pkg + '_test'? + '_defaults_' + crate_name
616 # (3) root_pkg + '_test'? + '_defaults_' + main_src_basename_path
617 # (4) root_pkg + '_test'? + '_defaults_' + a_positive_sequence_number
618 test = "_test" if self.crate_types == ['test'] else ""
619 name1 = altered_defaults(self.root_pkg) + test + '_defaults'
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700620 if self.runner.try_claim_module_name(name1, self):
621 return name1
622 name2 = name1 + '_' + self.crate_name
623 if self.runner.try_claim_module_name(name2, self):
624 return name2
625 name3 = name1 + '_' + self.main_src_basename_path()
626 if self.runner.try_claim_module_name(name3, self):
627 return name3
628 return self.runner.claim_module_name(name1, self, 0)
629
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700630 def dump_srcs_list(self):
631 """Dump the srcs list, for defaults or regular modules."""
632 if len(self.srcs) > 1:
633 srcs = sorted(set(self.srcs)) # make a copy and dedup
634 else:
635 srcs = [self.main_src]
636 copy_out = self.runner.copy_out_module_name()
637 if copy_out:
638 srcs.append(':' + copy_out)
639 self.dump_android_property_list('srcs', '"%s"', srcs)
640
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700641 def dump_defaults_module(self):
642 """Dump a rust_defaults module to be shared by other modules."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700643 name = self.build_default_name()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700644 self.defaults = name
645 self.write('\nrust_defaults {')
646 self.write(' name: "' + name + '",')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700647 if self.runner.args.global_defaults:
648 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700649 self.write(' crate_name: "' + self.crate_name + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700650 if len(self.srcs) == 1: # only one source file; share it in defaults
651 self.default_srcs = True
652 if self.has_warning and not self.cap_lints:
653 self.write(' // has rustc warnings')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700654 self.dump_srcs_list()
Joel Galensonb06c42a2021-08-31 14:28:48 -0700655 if self.cargo_env_compat:
656 self.write(' cargo_env_compat: true,')
Joel Galenson94b8a8d2021-09-28 11:53:51 -0700657 if not self.runner.args.no_pkg_vers:
658 self.write(' cargo_pkg_version: "' + self.cargo_pkg_version + '",')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700659 if 'test' in self.crate_types:
660 self.write(' test_suites: ["general-tests"],')
661 self.write(' auto_gen_config: true,')
662 self.dump_edition_flags_libs()
Joel Galensone4f53882021-07-19 11:14:55 -0700663 if 'test' in self.crate_types and len(self.srcs) == 1:
664 self.dump_test_data()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700665 self.write('}')
666
667 def dump_single_type_android_module(self):
668 """Dump one simple Android module, which has only one crate_type."""
669 crate_type = self.crate_types[0]
670 if crate_type != 'test':
671 # do not change self.stem or self.module_name
672 self.dump_one_android_module(crate_type)
673 return
674 # Dump one test module per source file, and separate host and device tests.
675 # crate_type == 'test'
Joel Galenson7cfc6fe2021-10-14 13:09:32 -0700676 self.srcs = [src for src in self.srcs if not self.runner.should_ignore_test(src)]
Joel Galensonf6b3c912021-06-03 16:00:54 -0700677 if ((self.host_supported and self.device_supported and len(self.srcs) > 0) or
678 len(self.srcs) > 1):
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700679 self.srcs = sorted(set(self.srcs))
680 self.dump_defaults_module()
681 saved_srcs = self.srcs
682 for src in saved_srcs:
683 self.srcs = [src]
684 saved_device_supported = self.device_supported
685 saved_host_supported = self.host_supported
686 saved_main_src = self.main_src
687 self.main_src = src
688 if saved_host_supported:
689 self.device_supported = False
690 self.host_supported = True
691 self.module_name = self.test_module_name()
692 self.decide_one_module_type(crate_type)
693 self.dump_one_android_module(crate_type)
694 if saved_device_supported:
695 self.device_supported = True
696 self.host_supported = False
697 self.module_name = self.test_module_name()
698 self.decide_one_module_type(crate_type)
699 self.dump_one_android_module(crate_type)
700 self.host_supported = saved_host_supported
701 self.device_supported = saved_device_supported
702 self.main_src = saved_main_src
703 self.srcs = saved_srcs
704
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700705 def dump_one_android_module(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800706 """Dump one Android module definition."""
707 if not self.module_type:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700708 self.write('\nERROR: unknown crate_type ' + crate_type)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800709 return
710 self.write('\n' + self.module_type + ' {')
711 self.dump_android_core_properties()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700712 if not self.defaults:
713 self.dump_edition_flags_libs()
714 if self.runner.args.host_first_multilib and self.host_supported and crate_type != 'test':
715 self.write(' compile_multilib: "first",')
Matthew Maurer062709c2021-08-17 11:27:36 -0700716 if self.runner.args.exported_c_header_dir and crate_type in C_LIBRARY_CRATE_TYPES:
717 self.write(' include_dirs: [')
718 for header_dir in self.runner.args.exported_c_header_dir:
719 self.write(' "%s",' % header_dir)
720 self.write(' ],')
Matthew Maurer9e4b7812021-08-16 14:21:01 -0700721 if self.runner.args.apex_available and crate_type in LIBRARY_CRATE_TYPES:
Joel Galensond9c4de62021-04-23 10:26:40 -0700722 self.write(' apex_available: [')
723 for apex in self.runner.args.apex_available:
724 self.write(' "%s",' % apex)
725 self.write(' ],')
Matthew Maurer70182e42021-08-17 13:53:52 -0700726 if crate_type != 'test':
727 if self.runner.args.native_bridge_supported:
728 self.write(' native_bridge_supported: true,')
729 if self.runner.args.product_available:
730 self.write(' product_available: true,')
731 if self.runner.args.recovery_available:
732 self.write(' recovery_available: true,')
733 if self.runner.args.vendor_available:
734 self.write(' vendor_available: true,')
735 if self.runner.args.vendor_ramdisk_available:
736 self.write(' vendor_ramdisk_available: true,')
737 if self.runner.args.ramdisk_available:
738 self.write(' ramdisk_available: true,')
Matthew Maurer9e4b7812021-08-16 14:21:01 -0700739 if self.runner.args.min_sdk_version and crate_type in LIBRARY_CRATE_TYPES:
Joel Galensond9c4de62021-04-23 10:26:40 -0700740 self.write(' min_sdk_version: "%s",' % self.runner.args.min_sdk_version)
Joel Galensone4f53882021-07-19 11:14:55 -0700741 if crate_type == 'test' and not self.default_srcs:
742 self.dump_test_data()
Joel Galenson5664f2a2021-06-10 10:13:49 -0700743 if self.runner.args.add_module_block:
744 with open(self.runner.args.add_module_block, 'r') as f:
745 self.write(' %s,' % f.read().replace('\n', '\n '))
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700746 self.write('}')
747
748 def dump_android_flags(self):
749 """Dump Android module flags property."""
ThiƩbaud Weksteena5a728b2021-04-08 14:23:49 +0200750 if not self.codegens and not self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700751 return
752 self.write(' flags: [')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800753 if self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700754 self.write(' "--cap-lints ' + self.cap_lints + '",')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700755 codegens_fmt = '"-C %s"'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700756 self.dump_android_property_list_items(codegens_fmt, self.codegens)
757 self.write(' ],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700758
759 def dump_edition_flags_libs(self):
760 if self.edition:
761 self.write(' edition: "' + self.edition + '",')
762 self.dump_android_property_list('features', '"%s"', self.features)
Joel Galenson3d6d1e72021-06-07 15:00:24 -0700763 cfgs = [cfg for cfg in self.cfgs if not cfg in self.runner.args.cfg_blocklist]
764 self.dump_android_property_list('cfgs', '"%s"', cfgs)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700765 self.dump_android_flags()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800766 if self.externs:
767 self.dump_android_externs()
Joel Galenson12467e52021-07-12 14:33:28 -0700768 all_static_libs = [lib for lib in self.static_libs if not lib in self.runner.args.lib_blocklist]
769 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 -0700770 self.dump_android_property_list('static_libs', '"lib%s"', static_libs)
Joel Galenson12467e52021-07-12 14:33:28 -0700771 whole_static_libs = [lib for lib in all_static_libs if lib in self.runner.args.whole_static_libs]
772 self.dump_android_property_list('whole_static_libs', '"lib%s"', whole_static_libs)
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700773 shared_libs = [lib for lib in self.shared_libs if not lib in self.runner.args.lib_blocklist]
774 self.dump_android_property_list('shared_libs', '"lib%s"', shared_libs)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800775
Joel Galensone4f53882021-07-19 11:14:55 -0700776 def dump_test_data(self):
777 data = [data for (name, data) in map(lambda kv: kv.split('=', 1), self.runner.args.test_data)
778 if self.srcs == [name]]
779 if data:
780 self.dump_android_property_list('data', '"%s"', data)
781
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700782 def main_src_basename_path(self):
783 return re.sub('/', '_', re.sub('.rs$', '', self.main_src))
784
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800785 def test_module_name(self):
786 """Return a unique name for a test module."""
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700787 # root_pkg+(_host|_device) + '_test_'+source_file_name
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700788 suffix = self.main_src_basename_path()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700789 host_device = '_host'
790 if self.device_supported:
791 host_device = '_device'
792 return self.root_pkg + host_device + '_test_' + suffix
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800793
794 def decide_module_type(self):
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700795 # Use the first crate type for the default/first module.
796 crate_type = self.crate_types[0] if self.crate_types else ''
797 self.decide_one_module_type(crate_type)
798
799 def decide_one_module_type(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800800 """Decide which Android module type to use."""
801 host = '' if self.device_supported else '_host'
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700802 rlib = '_rlib' if self.runner.args.force_rlib else ''
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700803 if crate_type == 'bin': # rust_binary[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800804 self.module_type = 'rust_binary' + host
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700805 # In rare cases like protobuf-codegen, the output binary name must
806 # be renamed to use as a plugin for protoc.
807 self.stem = altered_stem(self.crate_name)
808 self.module_name = altered_name(self.crate_name)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700809 elif crate_type == 'lib': # rust_library[_host]
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700810 # TODO(chh): should this be rust_library[_host]?
811 # Assuming that Cargo.toml do not use both 'lib' and 'rlib',
812 # because we map them both to rlib.
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700813 self.module_type = 'rust_library' + rlib + host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800814 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700815 self.module_name = altered_name(self.stem)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700816 elif crate_type == 'rlib': # rust_library[_host]
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700817 self.module_type = 'rust_library' + rlib + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700818 self.stem = 'lib' + self.crate_name
819 self.module_name = altered_name(self.stem)
820 elif crate_type == 'dylib': # rust_library[_host]_dylib
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800821 self.module_type = 'rust_library' + host + '_dylib'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700822 self.stem = 'lib' + self.crate_name
823 self.module_name = altered_name(self.stem) + '_dylib'
824 elif crate_type == 'cdylib': # rust_library[_host]_shared
Ivan Lozano0c057ad2020-12-15 10:41:26 -0500825 self.module_type = 'rust_ffi' + host + '_shared'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700826 self.stem = 'lib' + self.crate_name
827 self.module_name = altered_name(self.stem) + '_shared'
828 elif crate_type == 'staticlib': # rust_library[_host]_static
Ivan Lozano0c057ad2020-12-15 10:41:26 -0500829 self.module_type = 'rust_ffi' + host + '_static'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700830 self.stem = 'lib' + self.crate_name
831 self.module_name = altered_name(self.stem) + '_static'
832 elif crate_type == 'test': # rust_test[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800833 self.module_type = 'rust_test' + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700834 # Before do_merge, stem name is based on the --crate-name parameter.
835 # and test module name is based on stem.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800836 self.stem = self.test_module_name()
837 # self.stem will be changed after merging with other tests.
838 # self.stem is NOT used for final test binary name.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700839 # rust_test uses each source file base name as part of output file name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700840 # In do_merge, this function is called again, with a module_name.
841 # We make sure that the module name is unique in each package.
842 if self.module_name:
843 # Cargo uses "-C extra-filename=..." and "-C metadata=..." to add
844 # different suffixes and distinguish multiple tests of the same
845 # crate name. We ignore -C and use claim_module_name to get
846 # unique sequential suffix.
847 self.module_name = self.runner.claim_module_name(
848 self.module_name, self, 0)
849 # Now the module name is unique, stem should also match and unique.
850 self.stem = self.module_name
851 elif crate_type == 'proc-macro': # rust_proc_macro
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800852 self.module_type = 'rust_proc_macro'
853 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700854 self.module_name = altered_name(self.stem)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800855 else: # unknown module type, rust_prebuilt_dylib? rust_library[_host]?
856 self.module_type = ''
857 self.stem = ''
858
859 def dump_android_property_list_items(self, fmt, values):
860 for v in values:
861 # fmt has quotes, so we need escape_quotes(v)
862 self.write(' ' + (fmt % escape_quotes(v)) + ',')
863
864 def dump_android_property_list(self, name, fmt, values):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700865 if not values:
866 return
867 if len(values) > 1:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800868 self.write(' ' + name + ': [')
869 self.dump_android_property_list_items(fmt, values)
870 self.write(' ],')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700871 else:
872 self.write(' ' + name + ': [' +
873 (fmt % escape_quotes(values[0])) + '],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800874
875 def dump_android_core_properties(self):
876 """Dump the module header, name, stem, etc."""
877 self.write(' name: "' + self.module_name + '",')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700878 # see properties shared by dump_defaults_module
879 if self.defaults:
880 self.write(' defaults: ["' + self.defaults + '"],')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700881 elif self.runner.args.global_defaults:
882 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800883 if self.stem != self.module_name:
884 self.write(' stem: "' + self.stem + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700885 if self.has_warning and not self.cap_lints and not self.default_srcs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700886 self.write(' // has rustc warnings')
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700887 if self.host_supported and self.device_supported and self.module_type != 'rust_proc_macro':
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800888 self.write(' host_supported: true,')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700889 if not self.defaults:
890 self.write(' crate_name: "' + self.crate_name + '",')
Ivan Lozanocc660f12021-08-11 16:49:46 -0400891 if not self.defaults and self.cargo_env_compat:
892 self.write(' cargo_env_compat: true,')
Joel Galenson94b8a8d2021-09-28 11:53:51 -0700893 if not self.runner.args.no_pkg_vers:
894 self.write(' cargo_pkg_version: "' + self.cargo_pkg_version + '",')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700895 if not self.default_srcs:
896 self.dump_srcs_list()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700897 if 'test' in self.crate_types and not self.defaults:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800898 # self.root_pkg can have multiple test modules, with different *_tests[n]
899 # names, but their executables can all be installed under the same _tests
900 # directory. When built from Cargo.toml, all tests should have different
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700901 # file or crate names. So we used (root_pkg + '_tests') name as the
902 # relative_install_path.
903 # However, some package like 'slab' can have non-mergeable tests that
904 # must be separated by different module names. So, here we no longer
905 # emit relative_install_path.
906 # self.write(' relative_install_path: "' + self.root_pkg + '_tests",')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800907 self.write(' test_suites: ["general-tests"],')
908 self.write(' auto_gen_config: true,')
Joel Galensone261a152021-01-12 11:31:53 -0800909 if 'test' in self.crate_types and self.host_supported:
910 self.write(' test_options: {')
911 self.write(' unit_test: true,')
912 self.write(' },')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800913
914 def dump_android_externs(self):
915 """Dump the dependent rlibs and dylibs property."""
916 so_libs = list()
917 rust_libs = ''
918 deps_libname = re.compile('^.* = lib(.*)-[0-9a-f]*.(rlib|so|rmeta)$')
919 for lib in self.externs:
920 # normal value of lib: "libc = liblibc-*.rlib"
921 # strange case in rand crate: "getrandom_package = libgetrandom-*.rlib"
922 # we should use "libgetrandom", not "lib" + "getrandom_package"
923 groups = deps_libname.match(lib)
924 if groups is not None:
925 lib_name = groups.group(1)
926 else:
927 lib_name = re.sub(' .*$', '', lib)
Joel Galenson97e414a2021-05-27 09:42:32 -0700928 if lib_name in self.runner.args.dependency_blocklist:
929 continue
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800930 if lib.endswith('.rlib') or lib.endswith('.rmeta'):
931 # On MacOS .rmeta is used when Linux uses .rlib or .rmeta.
932 rust_libs += ' "' + altered_name('lib' + lib_name) + '",\n'
933 elif lib.endswith('.so'):
934 so_libs.append(lib_name)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700935 elif lib != 'proc_macro': # --extern proc_macro is special and ignored
936 rust_libs += ' // ERROR: unknown type of lib ' + lib + '\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800937 if rust_libs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700938 self.write(' rustlibs: [\n' + rust_libs + ' ],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800939 # Are all dependent .so files proc_macros?
940 # TODO(chh): Separate proc_macros and dylib.
941 self.dump_android_property_list('proc_macros', '"lib%s"', so_libs)
942
943
944class ARObject(object):
945 """Information of an "ar" link command."""
946
947 def __init__(self, runner, outf_name):
948 # Remembered global runner and its members.
949 self.runner = runner
950 self.pkg = ''
951 self.outf_name = outf_name # path to Android.bp
952 # "ar" arguments
953 self.line_num = 1
954 self.line = ''
955 self.flags = '' # e.g. "crs"
956 self.lib = '' # e.g. "/.../out/lib*.a"
957 self.objs = list() # e.g. "/.../out/.../*.o"
958
959 def parse(self, pkg, line_num, args_line):
960 """Collect ar obj/lib file names."""
961 self.pkg = pkg
962 self.line_num = line_num
963 self.line = args_line
964 args = args_line.split()
965 num_args = len(args)
966 if num_args < 3:
967 print('ERROR: "ar" command has too few arguments', args_line)
968 else:
969 self.flags = unquote(args[0])
970 self.lib = unquote(args[1])
971 self.objs = sorted(set(map(unquote, args[2:])))
972 return self
973
974 def write(self, s):
975 self.outf.write(s + '\n')
976
977 def dump_debug_info(self):
978 self.write('\n// Line ' + str(self.line_num) + ' "ar" ' + self.line)
979 self.write('// ar_object for %12s' % self.pkg)
980 self.write('// flags = %s' % self.flags)
981 self.write('// lib = %s' % short_out_name(self.pkg, self.lib))
982 for o in self.objs:
983 self.write('// obj = %s' % short_out_name(self.pkg, o))
984
985 def dump_android_lib(self):
986 """Write cc_library_static into Android.bp."""
987 self.write('\ncc_library_static {')
988 self.write(' name: "' + file_base_name(self.lib) + '",')
989 self.write(' host_supported: true,')
990 if self.flags != 'crs':
991 self.write(' // ar flags = %s' % self.flags)
992 if self.pkg not in self.runner.pkg_obj2cc:
993 self.write(' ERROR: cannot find source files.\n}')
994 return
995 self.write(' srcs: [')
996 obj2cc = self.runner.pkg_obj2cc[self.pkg]
997 # Note: wflags are ignored.
998 dflags = list()
999 fflags = list()
1000 for obj in self.objs:
1001 self.write(' "' + short_out_name(self.pkg, obj2cc[obj].src) + '",')
1002 # TODO(chh): union of dflags and flags of all obj
1003 # Now, just a temporary hack that uses the last obj's flags
1004 dflags = obj2cc[obj].dflags
1005 fflags = obj2cc[obj].fflags
1006 self.write(' ],')
1007 self.write(' cflags: [')
1008 self.write(' "-O3",') # TODO(chh): is this default correct?
1009 self.write(' "-Wno-error",')
1010 for x in fflags:
1011 self.write(' "-f' + x + '",')
1012 for x in dflags:
1013 self.write(' "-D' + x + '",')
1014 self.write(' ],')
1015 self.write('}')
1016
1017 def dump(self):
1018 """Dump error/debug/module info to the output .bp file."""
1019 self.runner.init_bp_file(self.outf_name)
1020 with open(self.outf_name, 'a') as outf:
1021 self.outf = outf
1022 if self.runner.args.debug:
1023 self.dump_debug_info()
1024 self.dump_android_lib()
1025
1026
1027class CCObject(object):
1028 """Information of a "cc" compilation command."""
1029
1030 def __init__(self, runner, outf_name):
1031 # Remembered global runner and its members.
1032 self.runner = runner
1033 self.pkg = ''
1034 self.outf_name = outf_name # path to Android.bp
1035 # "cc" arguments
1036 self.line_num = 1
1037 self.line = ''
1038 self.src = ''
1039 self.obj = ''
1040 self.dflags = list() # -D flags
1041 self.fflags = list() # -f flags
1042 self.iflags = list() # -I flags
1043 self.wflags = list() # -W flags
1044 self.other_args = list()
1045
1046 def parse(self, pkg, line_num, args_line):
1047 """Collect cc compilation flags and src/out file names."""
1048 self.pkg = pkg
1049 self.line_num = line_num
1050 self.line = args_line
1051 args = args_line.split()
1052 i = 0
1053 while i < len(args):
1054 arg = args[i]
1055 if arg == '"-c"':
1056 i += 1
1057 if args[i].startswith('"-o'):
1058 # ring-0.13.5 dumps: ... "-c" "-o/.../*.o" ".../*.c"
1059 self.obj = unquote(args[i])[2:]
1060 i += 1
1061 self.src = unquote(args[i])
1062 else:
1063 self.src = unquote(args[i])
1064 elif arg == '"-o"':
1065 i += 1
1066 self.obj = unquote(args[i])
1067 elif arg == '"-I"':
1068 i += 1
1069 self.iflags.append(unquote(args[i]))
1070 elif arg.startswith('"-D'):
1071 self.dflags.append(unquote(args[i])[2:])
1072 elif arg.startswith('"-f'):
1073 self.fflags.append(unquote(args[i])[2:])
1074 elif arg.startswith('"-W'):
1075 self.wflags.append(unquote(args[i])[2:])
1076 elif not (arg.startswith('"-O') or arg == '"-m64"' or arg == '"-g"' or
1077 arg == '"-g3"'):
1078 # ignore -O -m64 -g
1079 self.other_args.append(unquote(args[i]))
1080 i += 1
1081 self.dflags = sorted(set(self.dflags))
1082 self.fflags = sorted(set(self.fflags))
1083 # self.wflags is not sorted because some are order sensitive
1084 # and we ignore them anyway.
1085 if self.pkg not in self.runner.pkg_obj2cc:
1086 self.runner.pkg_obj2cc[self.pkg] = {}
1087 self.runner.pkg_obj2cc[self.pkg][self.obj] = self
1088 return self
1089
1090 def write(self, s):
1091 self.outf.write(s + '\n')
1092
1093 def dump_debug_flags(self, name, flags):
1094 self.write('// ' + name + ':')
1095 for f in flags:
1096 self.write('// %s' % f)
1097
1098 def dump(self):
1099 """Dump only error/debug info to the output .bp file."""
1100 if not self.runner.args.debug:
1101 return
1102 self.runner.init_bp_file(self.outf_name)
1103 with open(self.outf_name, 'a') as outf:
1104 self.outf = outf
1105 self.write('\n// Line ' + str(self.line_num) + ' "cc" ' + self.line)
1106 self.write('// cc_object for %12s' % self.pkg)
1107 self.write('// src = %s' % short_out_name(self.pkg, self.src))
1108 self.write('// obj = %s' % short_out_name(self.pkg, self.obj))
1109 self.dump_debug_flags('-I flags', self.iflags)
1110 self.dump_debug_flags('-D flags', self.dflags)
1111 self.dump_debug_flags('-f flags', self.fflags)
1112 self.dump_debug_flags('-W flags', self.wflags)
1113 if self.other_args:
1114 self.dump_debug_flags('other args', self.other_args)
1115
1116
1117class Runner(object):
1118 """Main class to parse cargo -v output and print Android module definitions."""
1119
1120 def __init__(self, args):
1121 self.bp_files = set() # Remember all output Android.bp files.
1122 self.root_pkg = '' # name of package in ./Cargo.toml
1123 # Saved flags, modes, and data.
1124 self.args = args
1125 self.dry_run = not args.run
1126 self.skip_cargo = args.skipcargo
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001127 self.cargo_path = './cargo' # path to cargo, will be set later
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001128 self.checked_out_files = False # to check only once
1129 self.build_out_files = [] # output files generated by build.rs
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001130 # All cc/ar objects, crates, dependencies, and warning files
1131 self.cc_objects = list()
1132 self.pkg_obj2cc = {}
1133 # pkg_obj2cc[cc_object[i].pkg][cc_objects[i].obj] = cc_objects[i]
1134 self.ar_objects = list()
1135 self.crates = list()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001136 self.warning_files = set()
1137 # Keep a unique mapping from (module name) to crate
1138 self.name_owners = {}
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001139 # Save and dump all errors from cargo to Android.bp.
1140 self.errors = ''
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001141 self.setup_cargo_path()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001142 # Default action is cargo clean, followed by build or user given actions.
1143 if args.cargo:
1144 self.cargo = ['clean'] + args.cargo
1145 else:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001146 default_target = '--target x86_64-unknown-linux-gnu'
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -07001147 # Use the same target for both host and default device builds.
1148 # Same target is used as default in host x86_64 Android compilation.
1149 # Note: b/169872957, prebuilt cargo failed to build vsock
1150 # on x86_64-unknown-linux-musl systems.
1151 self.cargo = ['clean', 'build ' + default_target]
1152 if args.tests:
1153 self.cargo.append('build --tests ' + default_target)
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001154 self.empty_tests = set()
1155 self.empty_unittests = False
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001156
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001157 def setup_cargo_path(self):
1158 """Find cargo in the --cargo_bin or prebuilt rust bin directory."""
1159 if self.args.cargo_bin:
1160 self.cargo_path = os.path.join(self.args.cargo_bin, 'cargo')
1161 if not os.path.isfile(self.cargo_path):
1162 sys.exit('ERROR: cannot find cargo in ' + self.args.cargo_bin)
1163 print('WARNING: using cargo in ' + self.args.cargo_bin)
1164 return
1165 # We have only tested this on Linux.
1166 if platform.system() != 'Linux':
1167 sys.exit('ERROR: this script has only been tested on Linux with cargo.')
1168 # Assuming that this script is in development/scripts.
1169 my_dir = os.path.dirname(os.path.abspath(__file__))
1170 linux_dir = os.path.join(my_dir, '..', '..',
1171 'prebuilts', 'rust', 'linux-x86')
1172 if not os.path.isdir(linux_dir):
1173 sys.exit('ERROR: cannot find directory ' + linux_dir)
1174 rust_version = self.find_rust_version(my_dir, linux_dir)
1175 cargo_bin = os.path.join(linux_dir, rust_version, 'bin')
1176 self.cargo_path = os.path.join(cargo_bin, 'cargo')
1177 if not os.path.isfile(self.cargo_path):
1178 sys.exit('ERROR: cannot find cargo in ' + cargo_bin
1179 + '; please try --cargo_bin= flag.')
1180 return
1181
1182 def find_rust_version(self, my_dir, linux_dir):
1183 """Use my script directory, find prebuilt rust version."""
1184 # First look up build/soong/rust/config/global.go.
1185 path2global = os.path.join(my_dir, '..', '..',
1186 'build', 'soong', 'rust', 'config', 'global.go')
1187 if os.path.isfile(path2global):
1188 # try to find: RustDefaultVersion = "1.44.0"
1189 version_pat = re.compile(
1190 r'\s*RustDefaultVersion\s*=\s*"([0-9]+\.[0-9]+\.[0-9]+)".*$')
1191 with open(path2global, 'r') as inf:
1192 for line in inf:
1193 result = version_pat.match(line)
1194 if result:
1195 return result.group(1)
1196 print('WARNING: cannot find RustDefaultVersion in ' + path2global)
1197 # Otherwise, find the newest (largest) version number in linux_dir.
1198 rust_version = (0, 0, 0) # the prebuilt version to use
1199 version_pat = re.compile(r'([0-9]+)\.([0-9]+)\.([0-9]+)$')
1200 for dir_name in os.listdir(linux_dir):
1201 result = version_pat.match(dir_name)
1202 if not result:
1203 continue
1204 version = (result.group(1), result.group(2), result.group(3))
1205 if version > rust_version:
1206 rust_version = version
1207 return '.'.join(rust_version)
1208
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001209 def find_out_files(self):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001210 # list1 has build.rs output for normal crates
1211 list1 = glob.glob(TARGET_TMP + '/*/*/build/' + self.root_pkg + '-*/out/*')
1212 # list2 has build.rs output for proc-macro crates
1213 list2 = glob.glob(TARGET_TMP + '/*/build/' + self.root_pkg + '-*/out/*')
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001214 return list1 + list2
1215
1216 def copy_out_files(self):
1217 """Copy build.rs output files to ./out and set up build_out_files."""
1218 if self.checked_out_files:
1219 return
1220 self.checked_out_files = True
1221 cargo_out_files = self.find_out_files()
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001222 out_files = set()
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001223 if cargo_out_files:
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001224 os.makedirs('out', exist_ok=True)
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001225 for path in cargo_out_files:
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001226 file_name = path.split('/')[-1]
1227 out_files.add(file_name)
1228 shutil.copy(path, 'out/' + file_name)
1229 self.build_out_files = sorted(out_files)
1230
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001231 def has_used_out_dir(self):
1232 """Returns true if env!("OUT_DIR") is found."""
1233 return 0 == os.system('grep -rl --exclude build.rs --include \\*.rs' +
1234 ' \'env!("OUT_DIR")\' * > /dev/null')
1235
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001236 def copy_out_module_name(self):
1237 if self.args.copy_out and self.build_out_files:
1238 return 'copy_' + self.root_pkg + '_build_out'
1239 else:
1240 return ''
1241
Haibo Huang0f72c952021-03-19 11:34:15 -07001242 def read_license(self, name):
1243 if not os.path.isfile(name):
1244 return ''
1245 license = ''
1246 with open(name, 'r') as intf:
1247 line = intf.readline()
1248 # Firstly skip ANDROID_BP_HEADER
1249 while line.startswith('//'):
1250 line = intf.readline()
Joel Galensond9d13b82021-04-05 11:27:55 -07001251 # Read all lines until we see a rust_* or genrule rule.
1252 while line != '' and not (line.startswith('rust_') or line.startswith('genrule {')):
Haibo Huang0f72c952021-03-19 11:34:15 -07001253 license += line
1254 line = intf.readline()
1255 return license.strip()
1256
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001257 def dump_copy_out_module(self, outf):
1258 """Output the genrule module to copy out/* to $(genDir)."""
1259 copy_out = self.copy_out_module_name()
1260 if not copy_out:
1261 return
1262 outf.write('\ngenrule {\n')
1263 outf.write(' name: "' + copy_out + '",\n')
1264 outf.write(' srcs: ["out/*"],\n')
1265 outf.write(' cmd: "cp $(in) $(genDir)",\n')
1266 if len(self.build_out_files) > 1:
1267 outf.write(' out: [\n')
1268 for f in self.build_out_files:
1269 outf.write(' "' + f + '",\n')
1270 outf.write(' ],\n')
1271 else:
1272 outf.write(' out: ["' + self.build_out_files[0] + '"],\n')
1273 outf.write('}\n')
1274
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001275 def init_bp_file(self, name):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001276 # name could be Android.bp or sub_dir_path/Android.bp
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001277 if name not in self.bp_files:
1278 self.bp_files.add(name)
Haibo Huang0f72c952021-03-19 11:34:15 -07001279 license_section = self.read_license(name)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001280 with open(name, 'w') as outf:
Joel Galensonc3bfaf82021-08-18 09:39:36 -07001281 outf.write(ANDROID_BP_HEADER.format(args=' '.join(sys.argv[1:])))
Haibo Huang0f72c952021-03-19 11:34:15 -07001282 outf.write('\n')
1283 outf.write(license_section)
1284 outf.write('\n')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001285 # at most one copy_out module per .bp file
1286 self.dump_copy_out_module(outf)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001287
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001288 def try_claim_module_name(self, name, owner):
1289 """Reserve and return True if it has not been reserved yet."""
1290 if name not in self.name_owners or owner == self.name_owners[name]:
1291 self.name_owners[name] = owner
1292 return True
1293 return False
1294
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001295 def claim_module_name(self, prefix, owner, counter):
1296 """Return prefix if not owned yet, otherwise, prefix+str(counter)."""
1297 while True:
1298 name = prefix
1299 if counter > 0:
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001300 name += '_' + str(counter)
1301 if self.try_claim_module_name(name, owner):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001302 return name
1303 counter += 1
1304
1305 def find_root_pkg(self):
1306 """Read name of [package] in ./Cargo.toml."""
1307 if not os.path.exists('./Cargo.toml'):
1308 return
1309 with open('./Cargo.toml', 'r') as inf:
1310 pkg_section = re.compile(r'^ *\[package\]')
1311 name = re.compile('^ *name *= * "([^"]*)"')
1312 in_pkg = False
1313 for line in inf:
1314 if in_pkg:
1315 if name.match(line):
1316 self.root_pkg = name.match(line).group(1)
1317 break
1318 else:
1319 in_pkg = pkg_section.match(line) is not None
1320
1321 def run_cargo(self):
1322 """Calls cargo -v and save its output to ./cargo.out."""
1323 if self.skip_cargo:
1324 return self
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001325 cargo_toml = './Cargo.toml'
1326 cargo_out = './cargo.out'
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001327 # Do not use Cargo.lock, because .bp rules are designed to
1328 # run with "latest" crates avaialable on Android.
1329 cargo_lock = './Cargo.lock'
1330 cargo_lock_saved = './cargo.lock.saved'
1331 had_cargo_lock = os.path.exists(cargo_lock)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001332 if not os.access(cargo_toml, os.R_OK):
1333 print('ERROR: Cannot find or read', cargo_toml)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001334 return self
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001335 if not self.dry_run:
1336 if os.path.exists(cargo_out):
1337 os.remove(cargo_out)
1338 if not self.args.use_cargo_lock and had_cargo_lock: # save it
1339 os.rename(cargo_lock, cargo_lock_saved)
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001340 cmd_tail_target = ' --target-dir ' + TARGET_TMP
1341 cmd_tail_redir = ' >> ' + cargo_out + ' 2>&1'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001342 # set up search PATH for cargo to find the correct rustc
1343 saved_path = os.environ['PATH']
1344 os.environ['PATH'] = os.path.dirname(self.cargo_path) + ':' + saved_path
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001345 # Add [workspace] to Cargo.toml if it is not there.
1346 added_workspace = False
1347 if self.args.add_workspace:
1348 with open(cargo_toml, 'r') as in_file:
1349 cargo_toml_lines = in_file.readlines()
1350 found_workspace = '[workspace]\n' in cargo_toml_lines
1351 if found_workspace:
1352 print('### WARNING: found [workspace] in Cargo.toml')
1353 else:
1354 with open(cargo_toml, 'a') as out_file:
1355 out_file.write('[workspace]\n')
1356 added_workspace = True
1357 if self.args.verbose:
1358 print('### INFO: added [workspace] to Cargo.toml')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001359 for c in self.cargo:
1360 features = ''
Chih-Hung Hsieh6c8d52f2020-03-30 18:28:52 -07001361 if c != 'clean':
1362 if self.args.features is not None:
1363 features = ' --no-default-features'
1364 if self.args.features:
1365 features += ' --features ' + self.args.features
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001366 cmd_v_flag = ' -vv ' if self.args.vv else ' -v '
1367 cmd = self.cargo_path + cmd_v_flag
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001368 cmd += c + features + cmd_tail_target + cmd_tail_redir
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001369 if self.args.rustflags and c != 'clean':
1370 cmd = 'RUSTFLAGS="' + self.args.rustflags + '" ' + cmd
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001371 self.run_cmd(cmd, cargo_out)
1372 if self.args.tests:
1373 cmd = self.cargo_path + ' test' + features + cmd_tail_target + ' -- --list' + cmd_tail_redir
1374 self.run_cmd(cmd, cargo_out)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001375 if added_workspace: # restore original Cargo.toml
1376 with open(cargo_toml, 'w') as out_file:
1377 out_file.writelines(cargo_toml_lines)
1378 if self.args.verbose:
1379 print('### INFO: restored original Cargo.toml')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001380 os.environ['PATH'] = saved_path
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001381 if not self.dry_run:
1382 if not had_cargo_lock: # restore to no Cargo.lock state
1383 os.remove(cargo_lock)
1384 elif not self.args.use_cargo_lock: # restore saved Cargo.lock
1385 os.rename(cargo_lock_saved, cargo_lock)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001386 return self
1387
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001388 def run_cmd(self, cmd, cargo_out):
1389 if self.dry_run:
1390 print('Dry-run skip:', cmd)
1391 else:
1392 if self.args.verbose:
1393 print('Running:', cmd)
1394 with open(cargo_out, 'a') as out_file:
1395 out_file.write('### Running: ' + cmd + '\n')
1396 ret = os.system(cmd)
1397 if ret != 0:
1398 print('*** There was an error while running cargo. ' +
1399 'See the cargo.out file for details.')
1400
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001401 def dump_pkg_obj2cc(self):
1402 """Dump debug info of the pkg_obj2cc map."""
1403 if not self.args.debug:
1404 return
1405 self.init_bp_file('Android.bp')
1406 with open('Android.bp', 'a') as outf:
1407 sorted_pkgs = sorted(self.pkg_obj2cc.keys())
1408 for pkg in sorted_pkgs:
1409 if not self.pkg_obj2cc[pkg]:
1410 continue
1411 outf.write('\n// obj => src for %s\n' % pkg)
1412 obj2cc = self.pkg_obj2cc[pkg]
1413 for obj in sorted(obj2cc.keys()):
1414 outf.write('// ' + short_out_name(pkg, obj) + ' => ' +
1415 short_out_name(pkg, obj2cc[obj].src) + '\n')
1416
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001417 def apply_patch(self):
1418 """Apply local patch file if it is given."""
1419 if self.args.patch:
1420 if self.dry_run:
1421 print('Dry-run skip patch file:', self.args.patch)
1422 else:
1423 if not os.path.exists(self.args.patch):
1424 self.append_to_bp('ERROR cannot find patch file: ' + self.args.patch)
1425 return self
1426 if self.args.verbose:
1427 print('### INFO: applying local patch file:', self.args.patch)
Joel Galenson7e8247e2021-05-20 18:51:42 -07001428 subprocess.run(['patch', '-s', '--no-backup-if-mismatch', './Android.bp',
1429 self.args.patch], check=True)
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001430 return self
1431
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001432 def gen_bp(self):
1433 """Parse cargo.out and generate Android.bp files."""
1434 if self.dry_run:
1435 print('Dry-run skip: read', CARGO_OUT, 'write Android.bp')
1436 elif os.path.exists(CARGO_OUT):
1437 self.find_root_pkg()
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001438 if self.args.copy_out:
1439 self.copy_out_files()
1440 elif self.find_out_files() and self.has_used_out_dir():
1441 print('WARNING: ' + self.root_pkg + ' has cargo output files; ' +
1442 'please rerun with the --copy-out flag.')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001443 with open(CARGO_OUT, 'r') as cargo_out:
1444 self.parse(cargo_out, 'Android.bp')
1445 self.crates.sort(key=get_module_name)
1446 for obj in self.cc_objects:
1447 obj.dump()
1448 self.dump_pkg_obj2cc()
1449 for crate in self.crates:
1450 crate.dump()
1451 dumped_libs = set()
1452 for lib in self.ar_objects:
1453 if lib.pkg == self.root_pkg:
1454 lib_name = file_base_name(lib.lib)
1455 if lib_name not in dumped_libs:
1456 dumped_libs.add(lib_name)
1457 lib.dump()
Joel Galenson5664f2a2021-06-10 10:13:49 -07001458 if self.args.add_toplevel_block:
1459 with open(self.args.add_toplevel_block, 'r') as f:
1460 self.append_to_bp('\n' + f.read() + '\n')
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001461 if self.errors:
Joel Galenson3f42f802021-04-07 12:42:17 -07001462 self.append_to_bp('\n' + ERRORS_LINE + '\n' + self.errors)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001463 return self
1464
1465 def add_ar_object(self, obj):
1466 self.ar_objects.append(obj)
1467
1468 def add_cc_object(self, obj):
1469 self.cc_objects.append(obj)
1470
1471 def add_crate(self, crate):
1472 """Merge crate with someone in crates, or append to it. Return crates."""
1473 if crate.skip_crate():
1474 if self.args.debug: # include debug info of all crates
1475 self.crates.append(crate)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001476 else:
1477 for c in self.crates:
1478 if c.merge(crate, 'Android.bp'):
1479 return
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001480 # If not merged, decide module type and name now.
1481 crate.decide_module_type()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001482 self.crates.append(crate)
1483
1484 def find_warning_owners(self):
1485 """For each warning file, find its owner crate."""
1486 missing_owner = False
1487 for f in self.warning_files:
1488 cargo_dir = '' # find lowest crate, with longest path
1489 owner = None # owner crate of this warning
1490 for c in self.crates:
1491 if (f.startswith(c.cargo_dir + '/') and
1492 len(cargo_dir) < len(c.cargo_dir)):
1493 cargo_dir = c.cargo_dir
1494 owner = c
1495 if owner:
1496 owner.has_warning = True
1497 else:
1498 missing_owner = True
1499 if missing_owner and os.path.exists('Cargo.toml'):
1500 # owner is the root cargo, with empty cargo_dir
1501 for c in self.crates:
1502 if not c.cargo_dir:
1503 c.has_warning = True
1504
1505 def rustc_command(self, n, rustc_line, line, outf_name):
1506 """Process a rustc command line from cargo -vv output."""
1507 # cargo build -vv output can have multiple lines for a rustc command
1508 # due to '\n' in strings for environment variables.
1509 # strip removes leading spaces and '\n' at the end
1510 new_rustc = (rustc_line.strip() + line) if rustc_line else line
1511 # Use an heuristic to detect the completions of a multi-line command.
1512 # This might fail for some very rare case, but easy to fix manually.
1513 if not line.endswith('`\n') or (new_rustc.count('`') % 2) != 0:
1514 return new_rustc
1515 if RUSTC_VV_CMD_ARGS.match(new_rustc):
1516 args = RUSTC_VV_CMD_ARGS.match(new_rustc).group(1)
1517 self.add_crate(Crate(self, outf_name).parse(n, args))
1518 else:
1519 self.assert_empty_vv_line(new_rustc)
1520 return ''
1521
1522 def cc_ar_command(self, n, groups, outf_name):
1523 pkg = groups.group(1)
1524 line = groups.group(3)
1525 if groups.group(2) == 'cc':
1526 self.add_cc_object(CCObject(self, outf_name).parse(pkg, n, line))
1527 else:
1528 self.add_ar_object(ARObject(self, outf_name).parse(pkg, n, line))
1529
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001530 def append_to_bp(self, line):
1531 self.init_bp_file('Android.bp')
1532 with open('Android.bp', 'a') as outf:
1533 outf.write(line)
1534
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001535 def assert_empty_vv_line(self, line):
1536 if line: # report error if line is not empty
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001537 self.append_to_bp('ERROR -vv line: ' + line)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001538 return ''
1539
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001540 def add_empty_test(self, name):
1541 if name == 'unittests':
1542 self.empty_unittests = True
1543 else:
1544 self.empty_tests.add(name)
1545
1546 def should_ignore_test(self, src):
1547 # cargo test outputs the source file for integration tests but "unittests"
1548 # for unit tests. To figure out to which crate this corresponds, we check
1549 # if the current source file is the main source of a non-test crate, e.g.,
1550 # a library or a binary.
1551 return (src in self.args.test_blocklist or src in self.empty_tests
1552 or (self.empty_unittests
1553 and src in [c.main_src for c in self.crates if c.crate_types != ['test']]))
1554
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001555 def parse(self, inf, outf_name):
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001556 """Parse rustc, test, and warning messages in inf, return a list of Crates."""
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001557 n = 0 # line number
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001558 # We read the file in two passes, where the first simply checks for empty tests.
1559 # Otherwise we would add and merge tests before seeing they're empty.
1560 cur_test_name = None
1561 for line in inf:
1562 if CARGO_TEST_LIST_START_PAT.match(line):
1563 cur_test_name = CARGO_TEST_LIST_START_PAT.match(line).group(1)
1564 elif cur_test_name and CARGO_TEST_LIST_END_PAT.match(line):
1565 match = CARGO_TEST_LIST_END_PAT.match(line)
1566 if int(match.group(1)) + int(match.group(2)) == 0:
1567 self.add_empty_test(cur_test_name)
1568 cur_test_name = None
1569 inf.seek(0)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001570 prev_warning = False # true if the previous line was warning: ...
1571 rustc_line = '' # previous line(s) matching RUSTC_VV_PAT
1572 for line in inf:
1573 n += 1
1574 if line.startswith('warning: '):
1575 prev_warning = True
1576 rustc_line = self.assert_empty_vv_line(rustc_line)
1577 continue
1578 new_rustc = ''
1579 if RUSTC_PAT.match(line):
1580 args_line = RUSTC_PAT.match(line).group(1)
1581 self.add_crate(Crate(self, outf_name).parse(n, args_line))
1582 self.assert_empty_vv_line(rustc_line)
1583 elif rustc_line or RUSTC_VV_PAT.match(line):
1584 new_rustc = self.rustc_command(n, rustc_line, line, outf_name)
1585 elif CC_AR_VV_PAT.match(line):
1586 self.cc_ar_command(n, CC_AR_VV_PAT.match(line), outf_name)
1587 elif prev_warning and WARNING_FILE_PAT.match(line):
1588 self.assert_empty_vv_line(rustc_line)
1589 fpath = WARNING_FILE_PAT.match(line).group(1)
1590 if fpath[0] != '/': # ignore absolute path
1591 self.warning_files.add(fpath)
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001592 elif line.startswith('error: ') or line.startswith('error[E'):
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001593 if not self.args.ignore_cargo_errors:
1594 self.errors += line
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001595 prev_warning = False
1596 rustc_line = new_rustc
1597 self.find_warning_owners()
1598
1599
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001600def get_parser():
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001601 """Parse main arguments."""
1602 parser = argparse.ArgumentParser('cargo2android')
1603 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001604 '--add_workspace',
1605 action='store_true',
1606 default=False,
1607 help=('append [workspace] to Cargo.toml before calling cargo,' +
1608 ' to treat current directory as root of package source;' +
1609 ' otherwise the relative source file path in generated' +
1610 ' .bp file will be from the parent directory.'))
1611 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001612 '--cargo',
1613 action='append',
1614 metavar='args_string',
1615 help=('extra cargo build -v args in a string, ' +
1616 'each --cargo flag calls cargo build -v once'))
1617 parser.add_argument(
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001618 '--cargo_bin',
1619 type=str,
1620 help='use cargo in the cargo_bin directory instead of the prebuilt one')
1621 parser.add_argument(
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001622 '--copy-out',
1623 action='store_true',
1624 default=False,
1625 help=('only for root directory, ' +
1626 'copy build.rs output to ./out/* and add a genrule to copy ' +
1627 './out/* to genrule output; for crates with code pattern: ' +
1628 'include!(concat!(env!("OUT_DIR"), "/<some_file>.rs"))'))
1629 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001630 '--debug',
1631 action='store_true',
1632 default=False,
1633 help='dump debug info into Android.bp')
1634 parser.add_argument(
1635 '--dependencies',
1636 action='store_true',
1637 default=False,
Joel Galenson833848c2021-08-17 10:50:42 -07001638 help='Deprecated. Has no effect.')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001639 parser.add_argument(
1640 '--device',
1641 action='store_true',
1642 default=False,
1643 help='run cargo also for a default device target')
1644 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001645 '--features',
1646 type=str,
1647 help=('pass features to cargo build, ' +
1648 'empty string means no default features'))
1649 parser.add_argument(
1650 '--global_defaults',
1651 type=str,
1652 help='add a defaults name to every module')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001653 parser.add_argument(
1654 '--host-first-multilib',
1655 action='store_true',
1656 default=False,
1657 help=('add a compile_multilib:"first" property ' +
1658 'to Android.bp host modules.'))
1659 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001660 '--ignore-cargo-errors',
1661 action='store_true',
1662 default=False,
1663 help='do not append cargo/rustc error messages to Android.bp')
1664 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001665 '--no-host',
1666 action='store_true',
1667 default=False,
1668 help='do not run cargo for the host; only for the device target')
1669 parser.add_argument(
1670 '--no-subdir',
1671 action='store_true',
1672 default=False,
1673 help='do not output anything for sub-directories')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001674 parser.add_argument(
1675 '--onefile',
1676 action='store_true',
1677 default=False,
1678 help=('output all into one ./Android.bp, default will generate ' +
1679 'one Android.bp per Cargo.toml in subdirectories'))
1680 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001681 '--patch',
1682 type=str,
1683 help='apply the given patch file to generated ./Android.bp')
1684 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001685 '--run',
1686 action='store_true',
1687 default=False,
1688 help='run it, default is dry-run')
1689 parser.add_argument('--rustflags', type=str, help='passing flags to rustc')
1690 parser.add_argument(
1691 '--skipcargo',
1692 action='store_true',
1693 default=False,
1694 help='skip cargo command, parse cargo.out, and generate Android.bp')
1695 parser.add_argument(
1696 '--tests',
1697 action='store_true',
1698 default=False,
1699 help='run cargo build --tests after normal build')
1700 parser.add_argument(
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001701 '--use-cargo-lock',
1702 action='store_true',
1703 default=False,
1704 help=('run cargo build with existing Cargo.lock ' +
1705 '(used when some latest dependent crates failed)'))
1706 parser.add_argument(
Matthew Maurer062709c2021-08-17 11:27:36 -07001707 '--exported_c_header_dir',
1708 nargs='*',
1709 help='Directories with headers to export for C usage'
1710 )
1711 parser.add_argument(
Joel Galensond9c4de62021-04-23 10:26:40 -07001712 '--min-sdk-version',
1713 type=str,
1714 help='Minimum SDK version')
1715 parser.add_argument(
1716 '--apex-available',
1717 nargs='*',
1718 help='Mark the main library as apex_available with the given apexes.')
1719 parser.add_argument(
Matthew Maurerac677252021-08-13 15:52:52 -07001720 '--native-bridge-supported',
1721 action='store_true',
1722 default=False,
1723 help='Mark the main library as native_bridge_supported.')
1724 parser.add_argument(
1725 '--product-available',
1726 action='store_true',
1727 default=False,
1728 help='Mark the main library as product_available.')
1729 parser.add_argument(
1730 '--recovery-available',
1731 action='store_true',
1732 default=False,
1733 help='Mark the main library as recovery_available.')
1734 parser.add_argument(
Ivan Lozano91920862021-07-19 10:49:08 -04001735 '--vendor-available',
1736 action='store_true',
1737 default=False,
1738 help='Mark the main library as vendor_available.')
1739 parser.add_argument(
1740 '--vendor-ramdisk-available',
1741 action='store_true',
1742 default=False,
1743 help='Mark the main library as vendor_ramdisk_available.')
1744 parser.add_argument(
Matthew Maurerac677252021-08-13 15:52:52 -07001745 '--ramdisk-available',
1746 action='store_true',
1747 default=False,
1748 help='Mark the main library as ramdisk_available.')
1749 parser.add_argument(
Joel Galensoncb5f2f02021-06-08 14:47:55 -07001750 '--force-rlib',
1751 action='store_true',
1752 default=False,
1753 help='Make the main library an rlib.')
1754 parser.add_argument(
Joel Galenson12467e52021-07-12 14:33:28 -07001755 '--whole-static-libs',
1756 nargs='*',
1757 default=[],
1758 help='Make the given libraries (without lib prefixes) whole_static_libs.')
1759 parser.add_argument(
Ivan Lozano26aa1c32021-08-16 11:20:32 -04001760 '--no-pkg-vers',
1761 action='store_true',
1762 default=False,
1763 help='Do not attempt to determine the package version automatically.')
1764 parser.add_argument(
Joel Galensone4f53882021-07-19 11:14:55 -07001765 '--test-data',
1766 nargs='*',
1767 default=[],
1768 help=('Add the given file to the given test\'s data property. ' +
1769 'Usage: test-path=data-path'))
1770 parser.add_argument(
Joel Galenson97e414a2021-05-27 09:42:32 -07001771 '--dependency-blocklist',
1772 nargs='*',
1773 default=[],
Joel Galenson12467e52021-07-12 14:33:28 -07001774 help='Do not emit the given dependencies (without lib prefixes).')
Joel Galenson97e414a2021-05-27 09:42:32 -07001775 parser.add_argument(
Joel Galensoncb5f2f02021-06-08 14:47:55 -07001776 '--lib-blocklist',
1777 nargs='*',
1778 default=[],
Joel Galenson12467e52021-07-12 14:33:28 -07001779 help='Do not emit the given C libraries as dependencies (without lib prefixes).')
Joel Galensoncb5f2f02021-06-08 14:47:55 -07001780 parser.add_argument(
Joel Galensonf6b3c912021-06-03 16:00:54 -07001781 '--test-blocklist',
1782 nargs='*',
1783 default=[],
1784 help=('Do not emit the given tests. ' +
1785 'Pass the path to the test file to exclude.'))
1786 parser.add_argument(
Joel Galenson3d6d1e72021-06-07 15:00:24 -07001787 '--cfg-blocklist',
1788 nargs='*',
1789 default=[],
1790 help='Do not emit the given cfg.')
1791 parser.add_argument(
Joel Galenson5664f2a2021-06-10 10:13:49 -07001792 '--add-toplevel-block',
1793 type=str,
1794 help='Add the contents of the given file to the top level of the Android.bp.')
1795 parser.add_argument(
1796 '--add-module-block',
1797 type=str,
1798 help='Add the contents of the given file to the main module.')
1799 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 Galensonc3bfaf82021-08-18 09:39:36 -07001843 and arg != 'config'):
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()