blob: ebee1e906f37cde08e4392eb45335f644a47f46a [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
Joel Galenson4f4ac2f2021-11-22 15:22:45 -0800355 # 'lto' is used in Android, but it's set by the build system
Chih-Hung Hsieh6c13b722020-09-11 21:24:03 -0700356 if not (flag.startswith('codegen-units=') or
357 flag.startswith('debuginfo=') or
Chih-Hung Hsieh63459ed2020-08-26 11:51:15 -0700358 flag.startswith('embed-bitcode=') or
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700359 flag.startswith('extra-filename=') or
360 flag.startswith('incremental=') or
361 flag.startswith('metadata=') or
Joel Galenson4f4ac2f2021-11-22 15:22:45 -0800362 flag.startswith('lto=') or
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700363 flag == 'prefer-dynamic'):
364 self.codegens.append(flag)
365
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800366 def parse(self, line_num, line):
367 """Find important rustc arguments to convert to Android.bp properties."""
368 self.line_num = line_num
369 self.line = line
370 args = line.split() # Loop through every argument of rustc.
371 i = 0
372 while i < len(args):
373 arg = args[i]
374 if arg == '--crate-name':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700375 i += 1
376 self.crate_name = args[i]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800377 elif arg == '--crate-type':
378 i += 1
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700379 # cargo calls rustc with multiple --crate-type flags.
380 # rustc can accept:
381 # --crate-type [bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]
382 self.crate_types.append(args[i])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800383 elif arg == '--test':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700384 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800385 elif arg == '--target':
386 i += 1
387 self.target = args[i]
388 elif arg == '--cfg':
389 i += 1
390 if args[i].startswith('\'feature='):
391 self.features.append(unquote(args[i].replace('\'feature=', '')[:-1]))
392 else:
393 self.cfgs.append(args[i])
394 elif arg == '--extern':
395 i += 1
396 extern_names = re.sub('=/[^ ]*/deps/', ' = ', args[i])
397 self.externs.append(extern_names)
398 self.core_externs.append(re.sub(' = .*', '', extern_names))
399 elif arg == '-C': # codegen options
400 i += 1
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700401 self.add_codegens_flag(args[i])
402 elif arg.startswith('-C'):
403 # cargo has been passing "-C <xyz>" flag to rustc,
404 # but newer cargo could pass '-Cembed-bitcode=no' to rustc.
405 self.add_codegens_flag(arg[2:])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800406 elif arg == '--cap-lints':
407 i += 1
408 self.cap_lints = args[i]
409 elif arg == '-L':
410 i += 1
411 if args[i].startswith('dependency=') and args[i].endswith('/deps'):
412 if '/' + TARGET_TMP + '/' in args[i]:
413 self.root_pkg = re.sub(
414 '^.*/', '', re.sub('/' + TARGET_TMP + '/.*/deps$', '', args[i]))
415 else:
416 self.root_pkg = re.sub('^.*/', '',
417 re.sub('/[^/]+/[^/]+/deps$', '', args[i]))
418 self.root_pkg = remove_version_suffix(self.root_pkg)
419 elif arg == '-l':
420 i += 1
421 if args[i].startswith('static='):
422 self.static_libs.append(re.sub('static=', '', args[i]))
423 elif args[i].startswith('dylib='):
424 self.shared_libs.append(re.sub('dylib=', '', args[i]))
425 else:
426 self.shared_libs.append(args[i])
427 elif arg == '--out-dir' or arg == '--color': # ignored
428 i += 1
429 elif arg.startswith('--error-format=') or arg.startswith('--json='):
430 _ = arg # ignored
431 elif arg.startswith('--emit='):
432 self.emit_list = arg.replace('--emit=', '')
433 elif arg.startswith('--edition='):
434 self.edition = arg.replace('--edition=', '')
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700435 elif not arg.startswith('-'):
436 # shorten imported crate main source paths like $HOME/.cargo/
437 # registry/src/github.com-1ecc6299db9ec823/memchr-2.3.3/src/lib.rs
438 self.main_src = re.sub(r'^/[^ ]*/registry/src/', '.../', args[i])
439 self.main_src = re.sub(r'^\.\.\./github.com-[0-9a-f]*/', '.../',
440 self.main_src)
441 self.find_cargo_dir()
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700442 if self.cargo_dir: # for a subdirectory
443 if self.runner.args.no_subdir: # all .bp content to /dev/null
444 self.outf_name = '/dev/null'
445 elif not self.runner.args.onefile:
446 # Write to Android.bp in the subdirectory with Cargo.toml.
447 self.outf_name = self.cargo_dir + '/Android.bp'
448 self.main_src = self.main_src[len(self.cargo_dir) + 1:]
Ivan Lozanocc660f12021-08-11 16:49:46 -0400449
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800450 else:
451 self.errors += 'ERROR: unknown ' + arg + '\n'
452 i += 1
453 if not self.crate_name:
454 self.errors += 'ERROR: missing --crate-name\n'
455 if not self.main_src:
456 self.errors += 'ERROR: missing main source file\n'
457 else:
458 self.srcs.append(self.main_src)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700459 if not self.crate_types:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800460 # Treat "--cfg test" as "--test"
461 if 'test' in self.cfgs:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700462 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800463 else:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700464 self.errors += 'ERROR: missing --crate-type or --test\n'
465 elif len(self.crate_types) > 1:
466 if 'test' in self.crate_types:
467 self.errors += 'ERROR: cannot handle both --crate-type and --test\n'
468 if 'lib' in self.crate_types and 'rlib' in self.crate_types:
469 self.errors += 'ERROR: cannot generate both lib and rlib crate types\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800470 if not self.root_pkg:
471 self.root_pkg = self.crate_name
Ivan Lozano26aa1c32021-08-16 11:20:32 -0400472
473 # get the package version from running cargo metadata
Joel Galenson69ba8072021-08-16 11:31:29 -0700474 if not self.runner.args.no_pkg_vers and not self.skip_crate():
Ivan Lozano26aa1c32021-08-16 11:20:32 -0400475 self.get_pkg_version()
476
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700477 self.device_supported = self.runner.args.device
478 self.host_supported = not self.runner.args.no_host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800479 self.cfgs = sorted(set(self.cfgs))
480 self.features = sorted(set(self.features))
481 self.codegens = sorted(set(self.codegens))
482 self.externs = sorted(set(self.externs))
483 self.core_externs = sorted(set(self.core_externs))
484 self.static_libs = sorted(set(self.static_libs))
485 self.shared_libs = sorted(set(self.shared_libs))
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700486 self.crate_types = sorted(set(self.crate_types))
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800487 self.decide_module_type()
488 self.module_name = altered_name(self.stem)
489 return self
490
Ivan Lozano26aa1c32021-08-16 11:20:32 -0400491 def get_pkg_version(self):
492 """Attempt to retrieve the package version from the Cargo.toml
493
494 If there is only one package, use its version. Otherwise, try to
495 match the emitted `--crate_name` arg against the package name.
496
497 This may fail in cases where multiple packages are defined (workspaces)
498 and where the package name does not match the emitted crate_name
499 (e.g. [lib.name] is set).
500 """
Joel Galenson69ba8072021-08-16 11:31:29 -0700501 cargo_metadata = subprocess.run([self.runner.cargo_path, 'metadata', '--no-deps',
502 '--format-version', '1'],
Joel Galensonc5186502021-08-16 11:22:47 -0700503 cwd=os.path.abspath(self.cargo_dir),
504 stdout=subprocess.PIPE)
Ivan Lozano26aa1c32021-08-16 11:20:32 -0400505 if cargo_metadata.returncode:
506 self.errors += ('ERROR: unable to get cargo metadata for package version; ' +
507 'return code ' + cargo_metadata.returncode + '\n')
508 else:
509 metadata_json = json.loads(cargo_metadata.stdout)
510 if len(metadata_json['packages']) > 1:
511 for package in metadata_json['packages']:
512 # package names may contain '-', but is changed to '_' in the crate_name
513 if package['name'].replace('-','_') == self.crate_name:
514 self.cargo_pkg_version = package['version']
515 break
516 else:
517 self.cargo_pkg_version = metadata_json['packages'][0]['version']
518
519 if not self.cargo_pkg_version:
520 self.errors += ('ERROR: Unable to retrieve package version; ' +
521 'to disable, run with arg "--no-pkg-vers"\n')
522
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800523 def dump_line(self):
524 self.write('\n// Line ' + str(self.line_num) + ' ' + self.line)
525
526 def feature_list(self):
527 """Return a string of main_src + "feature_list"."""
528 pkg = self.main_src
529 if pkg.startswith('.../'): # keep only the main package name
530 pkg = re.sub('/.*', '', pkg[4:])
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700531 elif pkg.startswith('/'): # use relative path for a local package
532 pkg = os.path.relpath(pkg)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800533 if not self.features:
534 return pkg
535 return pkg + ' "' + ','.join(self.features) + '"'
536
537 def dump_skip_crate(self, kind):
538 if self.debug:
539 self.write('\n// IGNORED: ' + kind + ' ' + self.main_src)
540 return self
541
542 def skip_crate(self):
543 """Return crate_name or a message if this crate should be skipped."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700544 if (is_build_crate_name(self.crate_name) or
545 self.crate_name in EXCLUDED_CRATES):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800546 return self.crate_name
547 if is_dependent_file_path(self.main_src):
548 return 'dependent crate'
549 return ''
550
551 def dump(self):
552 """Dump all error/debug/module code to the output .bp file."""
553 self.runner.init_bp_file(self.outf_name)
554 with open(self.outf_name, 'a') as outf:
555 self.outf = outf
556 if self.errors:
557 self.dump_line()
558 self.write(self.errors)
559 elif self.skip_crate():
560 self.dump_skip_crate(self.skip_crate())
561 else:
562 if self.debug:
563 self.dump_debug_info()
564 self.dump_android_module()
565
566 def dump_debug_info(self):
567 """Dump parsed data, when cargo2android is called with --debug."""
568
569 def dump(name, value):
570 self.write('//%12s = %s' % (name, value))
571
572 def opt_dump(name, value):
573 if value:
574 dump(name, value)
575
576 def dump_list(fmt, values):
577 for v in values:
578 self.write(fmt % v)
579
580 self.dump_line()
581 dump('module_name', self.module_name)
582 dump('crate_name', self.crate_name)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700583 dump('crate_types', self.crate_types)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800584 dump('main_src', self.main_src)
585 dump('has_warning', self.has_warning)
586 dump('for_host', self.host_supported)
587 dump('for_device', self.device_supported)
588 dump('module_type', self.module_type)
589 opt_dump('target', self.target)
590 opt_dump('edition', self.edition)
591 opt_dump('emit_list', self.emit_list)
592 opt_dump('cap_lints', self.cap_lints)
593 dump_list('// cfg = %s', self.cfgs)
594 dump_list('// cfg = \'feature "%s"\'', self.features)
595 # TODO(chh): escape quotes in self.features, but not in other dump_list
596 dump_list('// codegen = %s', self.codegens)
597 dump_list('// externs = %s', self.externs)
598 dump_list('// -l static = %s', self.static_libs)
599 dump_list('// -l (dylib) = %s', self.shared_libs)
600
601 def dump_android_module(self):
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700602 """Dump one or more Android module definition, depending on crate_types."""
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700603 if len(self.crate_types) == 1:
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700604 self.dump_single_type_android_module()
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700605 return
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700606 if 'test' in self.crate_types:
607 self.write('\nERROR: multiple crate types cannot include test type')
608 return
609 # Dump one Android module per crate_type.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700610 for crate_type in self.crate_types:
611 self.decide_one_module_type(crate_type)
612 self.dump_one_android_module(crate_type)
613
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700614 def build_default_name(self):
615 """Return a short and readable name for the rust_defaults module."""
Joel Galensond37d7e62021-07-13 09:03:01 -0700616 # Choices: (1) root_pkg + '_test'? + '_defaults',
617 # (2) root_pkg + '_test'? + '_defaults_' + crate_name
618 # (3) root_pkg + '_test'? + '_defaults_' + main_src_basename_path
619 # (4) root_pkg + '_test'? + '_defaults_' + a_positive_sequence_number
620 test = "_test" if self.crate_types == ['test'] else ""
621 name1 = altered_defaults(self.root_pkg) + test + '_defaults'
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700622 if self.runner.try_claim_module_name(name1, self):
623 return name1
624 name2 = name1 + '_' + self.crate_name
625 if self.runner.try_claim_module_name(name2, self):
626 return name2
627 name3 = name1 + '_' + self.main_src_basename_path()
628 if self.runner.try_claim_module_name(name3, self):
629 return name3
630 return self.runner.claim_module_name(name1, self, 0)
631
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700632 def dump_srcs_list(self):
633 """Dump the srcs list, for defaults or regular modules."""
634 if len(self.srcs) > 1:
635 srcs = sorted(set(self.srcs)) # make a copy and dedup
636 else:
637 srcs = [self.main_src]
638 copy_out = self.runner.copy_out_module_name()
639 if copy_out:
640 srcs.append(':' + copy_out)
641 self.dump_android_property_list('srcs', '"%s"', srcs)
642
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700643 def dump_defaults_module(self):
644 """Dump a rust_defaults module to be shared by other modules."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700645 name = self.build_default_name()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700646 self.defaults = name
647 self.write('\nrust_defaults {')
648 self.write(' name: "' + name + '",')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700649 if self.runner.args.global_defaults:
650 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700651 self.write(' crate_name: "' + self.crate_name + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700652 if len(self.srcs) == 1: # only one source file; share it in defaults
653 self.default_srcs = True
654 if self.has_warning and not self.cap_lints:
655 self.write(' // has rustc warnings')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700656 self.dump_srcs_list()
Joel Galensonb06c42a2021-08-31 14:28:48 -0700657 if self.cargo_env_compat:
658 self.write(' cargo_env_compat: true,')
Joel Galenson94b8a8d2021-09-28 11:53:51 -0700659 if not self.runner.args.no_pkg_vers:
660 self.write(' cargo_pkg_version: "' + self.cargo_pkg_version + '",')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700661 if 'test' in self.crate_types:
662 self.write(' test_suites: ["general-tests"],')
663 self.write(' auto_gen_config: true,')
664 self.dump_edition_flags_libs()
Joel Galensone4f53882021-07-19 11:14:55 -0700665 if 'test' in self.crate_types and len(self.srcs) == 1:
666 self.dump_test_data()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700667 self.write('}')
668
669 def dump_single_type_android_module(self):
670 """Dump one simple Android module, which has only one crate_type."""
671 crate_type = self.crate_types[0]
672 if crate_type != 'test':
673 # do not change self.stem or self.module_name
674 self.dump_one_android_module(crate_type)
675 return
676 # Dump one test module per source file, and separate host and device tests.
677 # crate_type == 'test'
Joel Galenson7cfc6fe2021-10-14 13:09:32 -0700678 self.srcs = [src for src in self.srcs if not self.runner.should_ignore_test(src)]
Joel Galensonf6b3c912021-06-03 16:00:54 -0700679 if ((self.host_supported and self.device_supported and len(self.srcs) > 0) or
680 len(self.srcs) > 1):
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700681 self.srcs = sorted(set(self.srcs))
682 self.dump_defaults_module()
683 saved_srcs = self.srcs
684 for src in saved_srcs:
685 self.srcs = [src]
686 saved_device_supported = self.device_supported
687 saved_host_supported = self.host_supported
688 saved_main_src = self.main_src
689 self.main_src = src
690 if saved_host_supported:
691 self.device_supported = False
692 self.host_supported = True
693 self.module_name = self.test_module_name()
694 self.decide_one_module_type(crate_type)
695 self.dump_one_android_module(crate_type)
696 if saved_device_supported:
697 self.device_supported = True
698 self.host_supported = False
699 self.module_name = self.test_module_name()
700 self.decide_one_module_type(crate_type)
701 self.dump_one_android_module(crate_type)
702 self.host_supported = saved_host_supported
703 self.device_supported = saved_device_supported
704 self.main_src = saved_main_src
705 self.srcs = saved_srcs
706
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700707 def dump_one_android_module(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800708 """Dump one Android module definition."""
709 if not self.module_type:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700710 self.write('\nERROR: unknown crate_type ' + crate_type)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800711 return
712 self.write('\n' + self.module_type + ' {')
713 self.dump_android_core_properties()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700714 if not self.defaults:
715 self.dump_edition_flags_libs()
716 if self.runner.args.host_first_multilib and self.host_supported and crate_type != 'test':
717 self.write(' compile_multilib: "first",')
Matthew Maurer062709c2021-08-17 11:27:36 -0700718 if self.runner.args.exported_c_header_dir and crate_type in C_LIBRARY_CRATE_TYPES:
719 self.write(' include_dirs: [')
720 for header_dir in self.runner.args.exported_c_header_dir:
721 self.write(' "%s",' % header_dir)
722 self.write(' ],')
Matthew Maurer9e4b7812021-08-16 14:21:01 -0700723 if self.runner.args.apex_available and crate_type in LIBRARY_CRATE_TYPES:
Joel Galensond9c4de62021-04-23 10:26:40 -0700724 self.write(' apex_available: [')
725 for apex in self.runner.args.apex_available:
726 self.write(' "%s",' % apex)
727 self.write(' ],')
Matthew Maurer70182e42021-08-17 13:53:52 -0700728 if crate_type != 'test':
729 if self.runner.args.native_bridge_supported:
730 self.write(' native_bridge_supported: true,')
731 if self.runner.args.product_available:
732 self.write(' product_available: true,')
733 if self.runner.args.recovery_available:
734 self.write(' recovery_available: true,')
735 if self.runner.args.vendor_available:
736 self.write(' vendor_available: true,')
737 if self.runner.args.vendor_ramdisk_available:
738 self.write(' vendor_ramdisk_available: true,')
739 if self.runner.args.ramdisk_available:
740 self.write(' ramdisk_available: true,')
Matthew Maurer9e4b7812021-08-16 14:21:01 -0700741 if self.runner.args.min_sdk_version and crate_type in LIBRARY_CRATE_TYPES:
Joel Galensond9c4de62021-04-23 10:26:40 -0700742 self.write(' min_sdk_version: "%s",' % self.runner.args.min_sdk_version)
Joel Galensone4f53882021-07-19 11:14:55 -0700743 if crate_type == 'test' and not self.default_srcs:
744 self.dump_test_data()
Joel Galenson5664f2a2021-06-10 10:13:49 -0700745 if self.runner.args.add_module_block:
746 with open(self.runner.args.add_module_block, 'r') as f:
747 self.write(' %s,' % f.read().replace('\n', '\n '))
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700748 self.write('}')
749
750 def dump_android_flags(self):
751 """Dump Android module flags property."""
ThiƩbaud Weksteena5a728b2021-04-08 14:23:49 +0200752 if not self.codegens and not self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700753 return
754 self.write(' flags: [')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800755 if self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700756 self.write(' "--cap-lints ' + self.cap_lints + '",')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700757 codegens_fmt = '"-C %s"'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700758 self.dump_android_property_list_items(codegens_fmt, self.codegens)
759 self.write(' ],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700760
761 def dump_edition_flags_libs(self):
762 if self.edition:
763 self.write(' edition: "' + self.edition + '",')
764 self.dump_android_property_list('features', '"%s"', self.features)
Joel Galenson3d6d1e72021-06-07 15:00:24 -0700765 cfgs = [cfg for cfg in self.cfgs if not cfg in self.runner.args.cfg_blocklist]
766 self.dump_android_property_list('cfgs', '"%s"', cfgs)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700767 self.dump_android_flags()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800768 if self.externs:
769 self.dump_android_externs()
Joel Galenson12467e52021-07-12 14:33:28 -0700770 all_static_libs = [lib for lib in self.static_libs if not lib in self.runner.args.lib_blocklist]
771 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 -0700772 self.dump_android_property_list('static_libs', '"lib%s"', static_libs)
Joel Galenson12467e52021-07-12 14:33:28 -0700773 whole_static_libs = [lib for lib in all_static_libs if lib in self.runner.args.whole_static_libs]
774 self.dump_android_property_list('whole_static_libs', '"lib%s"', whole_static_libs)
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700775 shared_libs = [lib for lib in self.shared_libs if not lib in self.runner.args.lib_blocklist]
776 self.dump_android_property_list('shared_libs', '"lib%s"', shared_libs)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800777
Joel Galensone4f53882021-07-19 11:14:55 -0700778 def dump_test_data(self):
779 data = [data for (name, data) in map(lambda kv: kv.split('=', 1), self.runner.args.test_data)
780 if self.srcs == [name]]
781 if data:
782 self.dump_android_property_list('data', '"%s"', data)
783
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700784 def main_src_basename_path(self):
785 return re.sub('/', '_', re.sub('.rs$', '', self.main_src))
786
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800787 def test_module_name(self):
788 """Return a unique name for a test module."""
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700789 # root_pkg+(_host|_device) + '_test_'+source_file_name
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700790 suffix = self.main_src_basename_path()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700791 host_device = '_host'
792 if self.device_supported:
793 host_device = '_device'
794 return self.root_pkg + host_device + '_test_' + suffix
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800795
796 def decide_module_type(self):
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700797 # Use the first crate type for the default/first module.
798 crate_type = self.crate_types[0] if self.crate_types else ''
799 self.decide_one_module_type(crate_type)
800
801 def decide_one_module_type(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800802 """Decide which Android module type to use."""
803 host = '' if self.device_supported else '_host'
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700804 rlib = '_rlib' if self.runner.args.force_rlib else ''
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700805 if crate_type == 'bin': # rust_binary[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800806 self.module_type = 'rust_binary' + host
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700807 # In rare cases like protobuf-codegen, the output binary name must
808 # be renamed to use as a plugin for protoc.
809 self.stem = altered_stem(self.crate_name)
810 self.module_name = altered_name(self.crate_name)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700811 elif crate_type == 'lib': # rust_library[_host]
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700812 # TODO(chh): should this be rust_library[_host]?
813 # Assuming that Cargo.toml do not use both 'lib' and 'rlib',
814 # because we map them both to rlib.
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700815 self.module_type = 'rust_library' + rlib + host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800816 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700817 self.module_name = altered_name(self.stem)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700818 elif crate_type == 'rlib': # rust_library[_host]
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700819 self.module_type = 'rust_library' + rlib + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700820 self.stem = 'lib' + self.crate_name
821 self.module_name = altered_name(self.stem)
822 elif crate_type == 'dylib': # rust_library[_host]_dylib
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800823 self.module_type = 'rust_library' + host + '_dylib'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700824 self.stem = 'lib' + self.crate_name
825 self.module_name = altered_name(self.stem) + '_dylib'
826 elif crate_type == 'cdylib': # rust_library[_host]_shared
Ivan Lozano0c057ad2020-12-15 10:41:26 -0500827 self.module_type = 'rust_ffi' + host + '_shared'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700828 self.stem = 'lib' + self.crate_name
829 self.module_name = altered_name(self.stem) + '_shared'
830 elif crate_type == 'staticlib': # rust_library[_host]_static
Ivan Lozano0c057ad2020-12-15 10:41:26 -0500831 self.module_type = 'rust_ffi' + host + '_static'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700832 self.stem = 'lib' + self.crate_name
833 self.module_name = altered_name(self.stem) + '_static'
834 elif crate_type == 'test': # rust_test[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800835 self.module_type = 'rust_test' + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700836 # Before do_merge, stem name is based on the --crate-name parameter.
837 # and test module name is based on stem.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800838 self.stem = self.test_module_name()
839 # self.stem will be changed after merging with other tests.
840 # self.stem is NOT used for final test binary name.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700841 # rust_test uses each source file base name as part of output file name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700842 # In do_merge, this function is called again, with a module_name.
843 # We make sure that the module name is unique in each package.
844 if self.module_name:
845 # Cargo uses "-C extra-filename=..." and "-C metadata=..." to add
846 # different suffixes and distinguish multiple tests of the same
847 # crate name. We ignore -C and use claim_module_name to get
848 # unique sequential suffix.
849 self.module_name = self.runner.claim_module_name(
850 self.module_name, self, 0)
851 # Now the module name is unique, stem should also match and unique.
852 self.stem = self.module_name
853 elif crate_type == 'proc-macro': # rust_proc_macro
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800854 self.module_type = 'rust_proc_macro'
855 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700856 self.module_name = altered_name(self.stem)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800857 else: # unknown module type, rust_prebuilt_dylib? rust_library[_host]?
858 self.module_type = ''
859 self.stem = ''
860
861 def dump_android_property_list_items(self, fmt, values):
862 for v in values:
863 # fmt has quotes, so we need escape_quotes(v)
864 self.write(' ' + (fmt % escape_quotes(v)) + ',')
865
866 def dump_android_property_list(self, name, fmt, values):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700867 if not values:
868 return
869 if len(values) > 1:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800870 self.write(' ' + name + ': [')
871 self.dump_android_property_list_items(fmt, values)
872 self.write(' ],')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700873 else:
874 self.write(' ' + name + ': [' +
875 (fmt % escape_quotes(values[0])) + '],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800876
877 def dump_android_core_properties(self):
878 """Dump the module header, name, stem, etc."""
879 self.write(' name: "' + self.module_name + '",')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700880 # see properties shared by dump_defaults_module
881 if self.defaults:
882 self.write(' defaults: ["' + self.defaults + '"],')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700883 elif self.runner.args.global_defaults:
884 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800885 if self.stem != self.module_name:
886 self.write(' stem: "' + self.stem + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700887 if self.has_warning and not self.cap_lints and not self.default_srcs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700888 self.write(' // has rustc warnings')
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700889 if self.host_supported and self.device_supported and self.module_type != 'rust_proc_macro':
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800890 self.write(' host_supported: true,')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700891 if not self.defaults:
892 self.write(' crate_name: "' + self.crate_name + '",')
Ivan Lozanocc660f12021-08-11 16:49:46 -0400893 if not self.defaults and self.cargo_env_compat:
894 self.write(' cargo_env_compat: true,')
Joel Galenson94b8a8d2021-09-28 11:53:51 -0700895 if not self.runner.args.no_pkg_vers:
896 self.write(' cargo_pkg_version: "' + self.cargo_pkg_version + '",')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700897 if not self.default_srcs:
898 self.dump_srcs_list()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700899 if 'test' in self.crate_types and not self.defaults:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800900 # self.root_pkg can have multiple test modules, with different *_tests[n]
901 # names, but their executables can all be installed under the same _tests
902 # directory. When built from Cargo.toml, all tests should have different
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700903 # file or crate names. So we used (root_pkg + '_tests') name as the
904 # relative_install_path.
905 # However, some package like 'slab' can have non-mergeable tests that
906 # must be separated by different module names. So, here we no longer
907 # emit relative_install_path.
908 # self.write(' relative_install_path: "' + self.root_pkg + '_tests",')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800909 self.write(' test_suites: ["general-tests"],')
910 self.write(' auto_gen_config: true,')
Joel Galensone261a152021-01-12 11:31:53 -0800911 if 'test' in self.crate_types and self.host_supported:
912 self.write(' test_options: {')
913 self.write(' unit_test: true,')
914 self.write(' },')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800915
916 def dump_android_externs(self):
917 """Dump the dependent rlibs and dylibs property."""
918 so_libs = list()
919 rust_libs = ''
920 deps_libname = re.compile('^.* = lib(.*)-[0-9a-f]*.(rlib|so|rmeta)$')
921 for lib in self.externs:
922 # normal value of lib: "libc = liblibc-*.rlib"
923 # strange case in rand crate: "getrandom_package = libgetrandom-*.rlib"
924 # we should use "libgetrandom", not "lib" + "getrandom_package"
925 groups = deps_libname.match(lib)
926 if groups is not None:
927 lib_name = groups.group(1)
928 else:
929 lib_name = re.sub(' .*$', '', lib)
Joel Galenson97e414a2021-05-27 09:42:32 -0700930 if lib_name in self.runner.args.dependency_blocklist:
931 continue
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800932 if lib.endswith('.rlib') or lib.endswith('.rmeta'):
933 # On MacOS .rmeta is used when Linux uses .rlib or .rmeta.
934 rust_libs += ' "' + altered_name('lib' + lib_name) + '",\n'
935 elif lib.endswith('.so'):
936 so_libs.append(lib_name)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700937 elif lib != 'proc_macro': # --extern proc_macro is special and ignored
938 rust_libs += ' // ERROR: unknown type of lib ' + lib + '\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800939 if rust_libs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700940 self.write(' rustlibs: [\n' + rust_libs + ' ],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800941 # Are all dependent .so files proc_macros?
942 # TODO(chh): Separate proc_macros and dylib.
943 self.dump_android_property_list('proc_macros', '"lib%s"', so_libs)
944
945
946class ARObject(object):
947 """Information of an "ar" link command."""
948
949 def __init__(self, runner, outf_name):
950 # Remembered global runner and its members.
951 self.runner = runner
952 self.pkg = ''
953 self.outf_name = outf_name # path to Android.bp
954 # "ar" arguments
955 self.line_num = 1
956 self.line = ''
957 self.flags = '' # e.g. "crs"
958 self.lib = '' # e.g. "/.../out/lib*.a"
959 self.objs = list() # e.g. "/.../out/.../*.o"
960
961 def parse(self, pkg, line_num, args_line):
962 """Collect ar obj/lib file names."""
963 self.pkg = pkg
964 self.line_num = line_num
965 self.line = args_line
966 args = args_line.split()
967 num_args = len(args)
968 if num_args < 3:
969 print('ERROR: "ar" command has too few arguments', args_line)
970 else:
971 self.flags = unquote(args[0])
972 self.lib = unquote(args[1])
973 self.objs = sorted(set(map(unquote, args[2:])))
974 return self
975
976 def write(self, s):
977 self.outf.write(s + '\n')
978
979 def dump_debug_info(self):
980 self.write('\n// Line ' + str(self.line_num) + ' "ar" ' + self.line)
981 self.write('// ar_object for %12s' % self.pkg)
982 self.write('// flags = %s' % self.flags)
983 self.write('// lib = %s' % short_out_name(self.pkg, self.lib))
984 for o in self.objs:
985 self.write('// obj = %s' % short_out_name(self.pkg, o))
986
987 def dump_android_lib(self):
988 """Write cc_library_static into Android.bp."""
989 self.write('\ncc_library_static {')
990 self.write(' name: "' + file_base_name(self.lib) + '",')
991 self.write(' host_supported: true,')
992 if self.flags != 'crs':
993 self.write(' // ar flags = %s' % self.flags)
994 if self.pkg not in self.runner.pkg_obj2cc:
995 self.write(' ERROR: cannot find source files.\n}')
996 return
997 self.write(' srcs: [')
998 obj2cc = self.runner.pkg_obj2cc[self.pkg]
999 # Note: wflags are ignored.
1000 dflags = list()
1001 fflags = list()
1002 for obj in self.objs:
1003 self.write(' "' + short_out_name(self.pkg, obj2cc[obj].src) + '",')
1004 # TODO(chh): union of dflags and flags of all obj
1005 # Now, just a temporary hack that uses the last obj's flags
1006 dflags = obj2cc[obj].dflags
1007 fflags = obj2cc[obj].fflags
1008 self.write(' ],')
1009 self.write(' cflags: [')
1010 self.write(' "-O3",') # TODO(chh): is this default correct?
1011 self.write(' "-Wno-error",')
1012 for x in fflags:
1013 self.write(' "-f' + x + '",')
1014 for x in dflags:
1015 self.write(' "-D' + x + '",')
1016 self.write(' ],')
1017 self.write('}')
1018
1019 def dump(self):
1020 """Dump error/debug/module info to the output .bp file."""
1021 self.runner.init_bp_file(self.outf_name)
1022 with open(self.outf_name, 'a') as outf:
1023 self.outf = outf
1024 if self.runner.args.debug:
1025 self.dump_debug_info()
1026 self.dump_android_lib()
1027
1028
1029class CCObject(object):
1030 """Information of a "cc" compilation command."""
1031
1032 def __init__(self, runner, outf_name):
1033 # Remembered global runner and its members.
1034 self.runner = runner
1035 self.pkg = ''
1036 self.outf_name = outf_name # path to Android.bp
1037 # "cc" arguments
1038 self.line_num = 1
1039 self.line = ''
1040 self.src = ''
1041 self.obj = ''
1042 self.dflags = list() # -D flags
1043 self.fflags = list() # -f flags
1044 self.iflags = list() # -I flags
1045 self.wflags = list() # -W flags
1046 self.other_args = list()
1047
1048 def parse(self, pkg, line_num, args_line):
1049 """Collect cc compilation flags and src/out file names."""
1050 self.pkg = pkg
1051 self.line_num = line_num
1052 self.line = args_line
1053 args = args_line.split()
1054 i = 0
1055 while i < len(args):
1056 arg = args[i]
1057 if arg == '"-c"':
1058 i += 1
1059 if args[i].startswith('"-o'):
1060 # ring-0.13.5 dumps: ... "-c" "-o/.../*.o" ".../*.c"
1061 self.obj = unquote(args[i])[2:]
1062 i += 1
1063 self.src = unquote(args[i])
1064 else:
1065 self.src = unquote(args[i])
1066 elif arg == '"-o"':
1067 i += 1
1068 self.obj = unquote(args[i])
1069 elif arg == '"-I"':
1070 i += 1
1071 self.iflags.append(unquote(args[i]))
1072 elif arg.startswith('"-D'):
1073 self.dflags.append(unquote(args[i])[2:])
1074 elif arg.startswith('"-f'):
1075 self.fflags.append(unquote(args[i])[2:])
1076 elif arg.startswith('"-W'):
1077 self.wflags.append(unquote(args[i])[2:])
1078 elif not (arg.startswith('"-O') or arg == '"-m64"' or arg == '"-g"' or
1079 arg == '"-g3"'):
1080 # ignore -O -m64 -g
1081 self.other_args.append(unquote(args[i]))
1082 i += 1
1083 self.dflags = sorted(set(self.dflags))
1084 self.fflags = sorted(set(self.fflags))
1085 # self.wflags is not sorted because some are order sensitive
1086 # and we ignore them anyway.
1087 if self.pkg not in self.runner.pkg_obj2cc:
1088 self.runner.pkg_obj2cc[self.pkg] = {}
1089 self.runner.pkg_obj2cc[self.pkg][self.obj] = self
1090 return self
1091
1092 def write(self, s):
1093 self.outf.write(s + '\n')
1094
1095 def dump_debug_flags(self, name, flags):
1096 self.write('// ' + name + ':')
1097 for f in flags:
1098 self.write('// %s' % f)
1099
1100 def dump(self):
1101 """Dump only error/debug info to the output .bp file."""
1102 if not self.runner.args.debug:
1103 return
1104 self.runner.init_bp_file(self.outf_name)
1105 with open(self.outf_name, 'a') as outf:
1106 self.outf = outf
1107 self.write('\n// Line ' + str(self.line_num) + ' "cc" ' + self.line)
1108 self.write('// cc_object for %12s' % self.pkg)
1109 self.write('// src = %s' % short_out_name(self.pkg, self.src))
1110 self.write('// obj = %s' % short_out_name(self.pkg, self.obj))
1111 self.dump_debug_flags('-I flags', self.iflags)
1112 self.dump_debug_flags('-D flags', self.dflags)
1113 self.dump_debug_flags('-f flags', self.fflags)
1114 self.dump_debug_flags('-W flags', self.wflags)
1115 if self.other_args:
1116 self.dump_debug_flags('other args', self.other_args)
1117
1118
1119class Runner(object):
1120 """Main class to parse cargo -v output and print Android module definitions."""
1121
1122 def __init__(self, args):
1123 self.bp_files = set() # Remember all output Android.bp files.
1124 self.root_pkg = '' # name of package in ./Cargo.toml
1125 # Saved flags, modes, and data.
1126 self.args = args
1127 self.dry_run = not args.run
1128 self.skip_cargo = args.skipcargo
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001129 self.cargo_path = './cargo' # path to cargo, will be set later
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001130 self.checked_out_files = False # to check only once
1131 self.build_out_files = [] # output files generated by build.rs
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001132 # All cc/ar objects, crates, dependencies, and warning files
1133 self.cc_objects = list()
1134 self.pkg_obj2cc = {}
1135 # pkg_obj2cc[cc_object[i].pkg][cc_objects[i].obj] = cc_objects[i]
1136 self.ar_objects = list()
1137 self.crates = list()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001138 self.warning_files = set()
1139 # Keep a unique mapping from (module name) to crate
1140 self.name_owners = {}
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001141 # Save and dump all errors from cargo to Android.bp.
1142 self.errors = ''
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001143 self.setup_cargo_path()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001144 # Default action is cargo clean, followed by build or user given actions.
1145 if args.cargo:
1146 self.cargo = ['clean'] + args.cargo
1147 else:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001148 default_target = '--target x86_64-unknown-linux-gnu'
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -07001149 # Use the same target for both host and default device builds.
1150 # Same target is used as default in host x86_64 Android compilation.
1151 # Note: b/169872957, prebuilt cargo failed to build vsock
1152 # on x86_64-unknown-linux-musl systems.
1153 self.cargo = ['clean', 'build ' + default_target]
1154 if args.tests:
1155 self.cargo.append('build --tests ' + default_target)
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001156 self.empty_tests = set()
1157 self.empty_unittests = False
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001158
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001159 def setup_cargo_path(self):
1160 """Find cargo in the --cargo_bin or prebuilt rust bin directory."""
1161 if self.args.cargo_bin:
1162 self.cargo_path = os.path.join(self.args.cargo_bin, 'cargo')
1163 if not os.path.isfile(self.cargo_path):
1164 sys.exit('ERROR: cannot find cargo in ' + self.args.cargo_bin)
1165 print('WARNING: using cargo in ' + self.args.cargo_bin)
1166 return
1167 # We have only tested this on Linux.
1168 if platform.system() != 'Linux':
1169 sys.exit('ERROR: this script has only been tested on Linux with cargo.')
1170 # Assuming that this script is in development/scripts.
1171 my_dir = os.path.dirname(os.path.abspath(__file__))
1172 linux_dir = os.path.join(my_dir, '..', '..',
1173 'prebuilts', 'rust', 'linux-x86')
1174 if not os.path.isdir(linux_dir):
1175 sys.exit('ERROR: cannot find directory ' + linux_dir)
1176 rust_version = self.find_rust_version(my_dir, linux_dir)
1177 cargo_bin = os.path.join(linux_dir, rust_version, 'bin')
1178 self.cargo_path = os.path.join(cargo_bin, 'cargo')
1179 if not os.path.isfile(self.cargo_path):
1180 sys.exit('ERROR: cannot find cargo in ' + cargo_bin
1181 + '; please try --cargo_bin= flag.')
1182 return
1183
1184 def find_rust_version(self, my_dir, linux_dir):
1185 """Use my script directory, find prebuilt rust version."""
1186 # First look up build/soong/rust/config/global.go.
1187 path2global = os.path.join(my_dir, '..', '..',
1188 'build', 'soong', 'rust', 'config', 'global.go')
1189 if os.path.isfile(path2global):
1190 # try to find: RustDefaultVersion = "1.44.0"
1191 version_pat = re.compile(
1192 r'\s*RustDefaultVersion\s*=\s*"([0-9]+\.[0-9]+\.[0-9]+)".*$')
1193 with open(path2global, 'r') as inf:
1194 for line in inf:
1195 result = version_pat.match(line)
1196 if result:
1197 return result.group(1)
1198 print('WARNING: cannot find RustDefaultVersion in ' + path2global)
1199 # Otherwise, find the newest (largest) version number in linux_dir.
1200 rust_version = (0, 0, 0) # the prebuilt version to use
1201 version_pat = re.compile(r'([0-9]+)\.([0-9]+)\.([0-9]+)$')
1202 for dir_name in os.listdir(linux_dir):
1203 result = version_pat.match(dir_name)
1204 if not result:
1205 continue
1206 version = (result.group(1), result.group(2), result.group(3))
1207 if version > rust_version:
1208 rust_version = version
1209 return '.'.join(rust_version)
1210
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001211 def find_out_files(self):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001212 # list1 has build.rs output for normal crates
1213 list1 = glob.glob(TARGET_TMP + '/*/*/build/' + self.root_pkg + '-*/out/*')
1214 # list2 has build.rs output for proc-macro crates
1215 list2 = glob.glob(TARGET_TMP + '/*/build/' + self.root_pkg + '-*/out/*')
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001216 return list1 + list2
1217
1218 def copy_out_files(self):
1219 """Copy build.rs output files to ./out and set up build_out_files."""
1220 if self.checked_out_files:
1221 return
1222 self.checked_out_files = True
1223 cargo_out_files = self.find_out_files()
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001224 out_files = set()
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001225 if cargo_out_files:
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001226 os.makedirs('out', exist_ok=True)
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001227 for path in cargo_out_files:
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001228 file_name = path.split('/')[-1]
1229 out_files.add(file_name)
1230 shutil.copy(path, 'out/' + file_name)
1231 self.build_out_files = sorted(out_files)
1232
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001233 def has_used_out_dir(self):
1234 """Returns true if env!("OUT_DIR") is found."""
1235 return 0 == os.system('grep -rl --exclude build.rs --include \\*.rs' +
1236 ' \'env!("OUT_DIR")\' * > /dev/null')
1237
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001238 def copy_out_module_name(self):
1239 if self.args.copy_out and self.build_out_files:
1240 return 'copy_' + self.root_pkg + '_build_out'
1241 else:
1242 return ''
1243
Haibo Huang0f72c952021-03-19 11:34:15 -07001244 def read_license(self, name):
1245 if not os.path.isfile(name):
1246 return ''
1247 license = ''
1248 with open(name, 'r') as intf:
1249 line = intf.readline()
1250 # Firstly skip ANDROID_BP_HEADER
1251 while line.startswith('//'):
1252 line = intf.readline()
Joel Galensond9d13b82021-04-05 11:27:55 -07001253 # Read all lines until we see a rust_* or genrule rule.
1254 while line != '' and not (line.startswith('rust_') or line.startswith('genrule {')):
Haibo Huang0f72c952021-03-19 11:34:15 -07001255 license += line
1256 line = intf.readline()
1257 return license.strip()
1258
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001259 def dump_copy_out_module(self, outf):
1260 """Output the genrule module to copy out/* to $(genDir)."""
1261 copy_out = self.copy_out_module_name()
1262 if not copy_out:
1263 return
1264 outf.write('\ngenrule {\n')
1265 outf.write(' name: "' + copy_out + '",\n')
1266 outf.write(' srcs: ["out/*"],\n')
1267 outf.write(' cmd: "cp $(in) $(genDir)",\n')
1268 if len(self.build_out_files) > 1:
1269 outf.write(' out: [\n')
1270 for f in self.build_out_files:
1271 outf.write(' "' + f + '",\n')
1272 outf.write(' ],\n')
1273 else:
1274 outf.write(' out: ["' + self.build_out_files[0] + '"],\n')
1275 outf.write('}\n')
1276
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001277 def init_bp_file(self, name):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001278 # name could be Android.bp or sub_dir_path/Android.bp
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001279 if name not in self.bp_files:
1280 self.bp_files.add(name)
Haibo Huang0f72c952021-03-19 11:34:15 -07001281 license_section = self.read_license(name)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001282 with open(name, 'w') as outf:
Joel Galensonc3bfaf82021-08-18 09:39:36 -07001283 outf.write(ANDROID_BP_HEADER.format(args=' '.join(sys.argv[1:])))
Haibo Huang0f72c952021-03-19 11:34:15 -07001284 outf.write('\n')
1285 outf.write(license_section)
1286 outf.write('\n')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001287 # at most one copy_out module per .bp file
1288 self.dump_copy_out_module(outf)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001289
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001290 def try_claim_module_name(self, name, owner):
1291 """Reserve and return True if it has not been reserved yet."""
1292 if name not in self.name_owners or owner == self.name_owners[name]:
1293 self.name_owners[name] = owner
1294 return True
1295 return False
1296
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001297 def claim_module_name(self, prefix, owner, counter):
1298 """Return prefix if not owned yet, otherwise, prefix+str(counter)."""
1299 while True:
1300 name = prefix
1301 if counter > 0:
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001302 name += '_' + str(counter)
1303 if self.try_claim_module_name(name, owner):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001304 return name
1305 counter += 1
1306
1307 def find_root_pkg(self):
1308 """Read name of [package] in ./Cargo.toml."""
1309 if not os.path.exists('./Cargo.toml'):
1310 return
1311 with open('./Cargo.toml', 'r') as inf:
1312 pkg_section = re.compile(r'^ *\[package\]')
1313 name = re.compile('^ *name *= * "([^"]*)"')
1314 in_pkg = False
1315 for line in inf:
1316 if in_pkg:
1317 if name.match(line):
1318 self.root_pkg = name.match(line).group(1)
1319 break
1320 else:
1321 in_pkg = pkg_section.match(line) is not None
1322
1323 def run_cargo(self):
1324 """Calls cargo -v and save its output to ./cargo.out."""
1325 if self.skip_cargo:
1326 return self
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001327 cargo_toml = './Cargo.toml'
1328 cargo_out = './cargo.out'
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001329 # Do not use Cargo.lock, because .bp rules are designed to
1330 # run with "latest" crates avaialable on Android.
1331 cargo_lock = './Cargo.lock'
1332 cargo_lock_saved = './cargo.lock.saved'
1333 had_cargo_lock = os.path.exists(cargo_lock)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001334 if not os.access(cargo_toml, os.R_OK):
1335 print('ERROR: Cannot find or read', cargo_toml)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001336 return self
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001337 if not self.dry_run:
1338 if os.path.exists(cargo_out):
1339 os.remove(cargo_out)
1340 if not self.args.use_cargo_lock and had_cargo_lock: # save it
1341 os.rename(cargo_lock, cargo_lock_saved)
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001342 cmd_tail_target = ' --target-dir ' + TARGET_TMP
1343 cmd_tail_redir = ' >> ' + cargo_out + ' 2>&1'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001344 # set up search PATH for cargo to find the correct rustc
1345 saved_path = os.environ['PATH']
1346 os.environ['PATH'] = os.path.dirname(self.cargo_path) + ':' + saved_path
Joel Galenson4f4ac2f2021-11-22 15:22:45 -08001347 # We need to enable lto since our test prebuilts use it
1348 saved_rustflags = os.environ.get('RUSTFLAGS', '')
1349 os.environ['RUSTFLAGS'] = '-C lto=thin -C embed-bitcode=yes ' + saved_rustflags
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001350 # Add [workspace] to Cargo.toml if it is not there.
1351 added_workspace = False
1352 if self.args.add_workspace:
1353 with open(cargo_toml, 'r') as in_file:
1354 cargo_toml_lines = in_file.readlines()
1355 found_workspace = '[workspace]\n' in cargo_toml_lines
1356 if found_workspace:
1357 print('### WARNING: found [workspace] in Cargo.toml')
1358 else:
1359 with open(cargo_toml, 'a') as out_file:
1360 out_file.write('[workspace]\n')
1361 added_workspace = True
1362 if self.args.verbose:
1363 print('### INFO: added [workspace] to Cargo.toml')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001364 for c in self.cargo:
1365 features = ''
Chih-Hung Hsieh6c8d52f2020-03-30 18:28:52 -07001366 if c != 'clean':
1367 if self.args.features is not None:
1368 features = ' --no-default-features'
1369 if self.args.features:
1370 features += ' --features ' + self.args.features
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001371 cmd_v_flag = ' -vv ' if self.args.vv else ' -v '
1372 cmd = self.cargo_path + cmd_v_flag
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001373 cmd += c + features + cmd_tail_target + cmd_tail_redir
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001374 if self.args.rustflags and c != 'clean':
Joel Galenson4f4ac2f2021-11-22 15:22:45 -08001375 cmd = 'RUSTFLAGS="' + os.environ['RUSTFLAGS'] + ' ' + self.args.rustflags + '" ' + cmd
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001376 self.run_cmd(cmd, cargo_out)
1377 if self.args.tests:
1378 cmd = self.cargo_path + ' test' + features + cmd_tail_target + ' -- --list' + cmd_tail_redir
1379 self.run_cmd(cmd, cargo_out)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001380 if added_workspace: # restore original Cargo.toml
1381 with open(cargo_toml, 'w') as out_file:
1382 out_file.writelines(cargo_toml_lines)
1383 if self.args.verbose:
1384 print('### INFO: restored original Cargo.toml')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001385 os.environ['PATH'] = saved_path
Joel Galenson4f4ac2f2021-11-22 15:22:45 -08001386 os.environ['RUSTFLAGS'] = saved_rustflags
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001387 if not self.dry_run:
1388 if not had_cargo_lock: # restore to no Cargo.lock state
1389 os.remove(cargo_lock)
1390 elif not self.args.use_cargo_lock: # restore saved Cargo.lock
1391 os.rename(cargo_lock_saved, cargo_lock)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001392 return self
1393
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001394 def run_cmd(self, cmd, cargo_out):
1395 if self.dry_run:
1396 print('Dry-run skip:', cmd)
1397 else:
1398 if self.args.verbose:
1399 print('Running:', cmd)
1400 with open(cargo_out, 'a') as out_file:
1401 out_file.write('### Running: ' + cmd + '\n')
1402 ret = os.system(cmd)
1403 if ret != 0:
1404 print('*** There was an error while running cargo. ' +
1405 'See the cargo.out file for details.')
1406
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001407 def dump_pkg_obj2cc(self):
1408 """Dump debug info of the pkg_obj2cc map."""
1409 if not self.args.debug:
1410 return
1411 self.init_bp_file('Android.bp')
1412 with open('Android.bp', 'a') as outf:
1413 sorted_pkgs = sorted(self.pkg_obj2cc.keys())
1414 for pkg in sorted_pkgs:
1415 if not self.pkg_obj2cc[pkg]:
1416 continue
1417 outf.write('\n// obj => src for %s\n' % pkg)
1418 obj2cc = self.pkg_obj2cc[pkg]
1419 for obj in sorted(obj2cc.keys()):
1420 outf.write('// ' + short_out_name(pkg, obj) + ' => ' +
1421 short_out_name(pkg, obj2cc[obj].src) + '\n')
1422
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001423 def apply_patch(self):
1424 """Apply local patch file if it is given."""
1425 if self.args.patch:
1426 if self.dry_run:
1427 print('Dry-run skip patch file:', self.args.patch)
1428 else:
1429 if not os.path.exists(self.args.patch):
1430 self.append_to_bp('ERROR cannot find patch file: ' + self.args.patch)
1431 return self
1432 if self.args.verbose:
1433 print('### INFO: applying local patch file:', self.args.patch)
Joel Galenson7e8247e2021-05-20 18:51:42 -07001434 subprocess.run(['patch', '-s', '--no-backup-if-mismatch', './Android.bp',
1435 self.args.patch], check=True)
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001436 return self
1437
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001438 def gen_bp(self):
1439 """Parse cargo.out and generate Android.bp files."""
1440 if self.dry_run:
1441 print('Dry-run skip: read', CARGO_OUT, 'write Android.bp')
1442 elif os.path.exists(CARGO_OUT):
1443 self.find_root_pkg()
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001444 if self.args.copy_out:
1445 self.copy_out_files()
1446 elif self.find_out_files() and self.has_used_out_dir():
1447 print('WARNING: ' + self.root_pkg + ' has cargo output files; ' +
1448 'please rerun with the --copy-out flag.')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001449 with open(CARGO_OUT, 'r') as cargo_out:
1450 self.parse(cargo_out, 'Android.bp')
1451 self.crates.sort(key=get_module_name)
1452 for obj in self.cc_objects:
1453 obj.dump()
1454 self.dump_pkg_obj2cc()
1455 for crate in self.crates:
1456 crate.dump()
1457 dumped_libs = set()
1458 for lib in self.ar_objects:
1459 if lib.pkg == self.root_pkg:
1460 lib_name = file_base_name(lib.lib)
1461 if lib_name not in dumped_libs:
1462 dumped_libs.add(lib_name)
1463 lib.dump()
Joel Galenson5664f2a2021-06-10 10:13:49 -07001464 if self.args.add_toplevel_block:
1465 with open(self.args.add_toplevel_block, 'r') as f:
1466 self.append_to_bp('\n' + f.read() + '\n')
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001467 if self.errors:
Joel Galenson3f42f802021-04-07 12:42:17 -07001468 self.append_to_bp('\n' + ERRORS_LINE + '\n' + self.errors)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001469 return self
1470
1471 def add_ar_object(self, obj):
1472 self.ar_objects.append(obj)
1473
1474 def add_cc_object(self, obj):
1475 self.cc_objects.append(obj)
1476
1477 def add_crate(self, crate):
1478 """Merge crate with someone in crates, or append to it. Return crates."""
1479 if crate.skip_crate():
1480 if self.args.debug: # include debug info of all crates
1481 self.crates.append(crate)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001482 else:
1483 for c in self.crates:
1484 if c.merge(crate, 'Android.bp'):
1485 return
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001486 # If not merged, decide module type and name now.
1487 crate.decide_module_type()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001488 self.crates.append(crate)
1489
1490 def find_warning_owners(self):
1491 """For each warning file, find its owner crate."""
1492 missing_owner = False
1493 for f in self.warning_files:
1494 cargo_dir = '' # find lowest crate, with longest path
1495 owner = None # owner crate of this warning
1496 for c in self.crates:
1497 if (f.startswith(c.cargo_dir + '/') and
1498 len(cargo_dir) < len(c.cargo_dir)):
1499 cargo_dir = c.cargo_dir
1500 owner = c
1501 if owner:
1502 owner.has_warning = True
1503 else:
1504 missing_owner = True
1505 if missing_owner and os.path.exists('Cargo.toml'):
1506 # owner is the root cargo, with empty cargo_dir
1507 for c in self.crates:
1508 if not c.cargo_dir:
1509 c.has_warning = True
1510
1511 def rustc_command(self, n, rustc_line, line, outf_name):
1512 """Process a rustc command line from cargo -vv output."""
1513 # cargo build -vv output can have multiple lines for a rustc command
1514 # due to '\n' in strings for environment variables.
1515 # strip removes leading spaces and '\n' at the end
1516 new_rustc = (rustc_line.strip() + line) if rustc_line else line
1517 # Use an heuristic to detect the completions of a multi-line command.
1518 # This might fail for some very rare case, but easy to fix manually.
1519 if not line.endswith('`\n') or (new_rustc.count('`') % 2) != 0:
1520 return new_rustc
1521 if RUSTC_VV_CMD_ARGS.match(new_rustc):
1522 args = RUSTC_VV_CMD_ARGS.match(new_rustc).group(1)
1523 self.add_crate(Crate(self, outf_name).parse(n, args))
1524 else:
1525 self.assert_empty_vv_line(new_rustc)
1526 return ''
1527
1528 def cc_ar_command(self, n, groups, outf_name):
1529 pkg = groups.group(1)
1530 line = groups.group(3)
1531 if groups.group(2) == 'cc':
1532 self.add_cc_object(CCObject(self, outf_name).parse(pkg, n, line))
1533 else:
1534 self.add_ar_object(ARObject(self, outf_name).parse(pkg, n, line))
1535
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001536 def append_to_bp(self, line):
1537 self.init_bp_file('Android.bp')
1538 with open('Android.bp', 'a') as outf:
1539 outf.write(line)
1540
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001541 def assert_empty_vv_line(self, line):
1542 if line: # report error if line is not empty
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001543 self.append_to_bp('ERROR -vv line: ' + line)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001544 return ''
1545
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001546 def add_empty_test(self, name):
1547 if name == 'unittests':
1548 self.empty_unittests = True
1549 else:
1550 self.empty_tests.add(name)
1551
1552 def should_ignore_test(self, src):
1553 # cargo test outputs the source file for integration tests but "unittests"
1554 # for unit tests. To figure out to which crate this corresponds, we check
1555 # if the current source file is the main source of a non-test crate, e.g.,
1556 # a library or a binary.
1557 return (src in self.args.test_blocklist or src in self.empty_tests
1558 or (self.empty_unittests
1559 and src in [c.main_src for c in self.crates if c.crate_types != ['test']]))
1560
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001561 def parse(self, inf, outf_name):
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001562 """Parse rustc, test, and warning messages in inf, return a list of Crates."""
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001563 n = 0 # line number
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001564 # We read the file in two passes, where the first simply checks for empty tests.
1565 # Otherwise we would add and merge tests before seeing they're empty.
1566 cur_test_name = None
1567 for line in inf:
1568 if CARGO_TEST_LIST_START_PAT.match(line):
1569 cur_test_name = CARGO_TEST_LIST_START_PAT.match(line).group(1)
1570 elif cur_test_name and CARGO_TEST_LIST_END_PAT.match(line):
1571 match = CARGO_TEST_LIST_END_PAT.match(line)
1572 if int(match.group(1)) + int(match.group(2)) == 0:
1573 self.add_empty_test(cur_test_name)
1574 cur_test_name = None
1575 inf.seek(0)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001576 prev_warning = False # true if the previous line was warning: ...
1577 rustc_line = '' # previous line(s) matching RUSTC_VV_PAT
1578 for line in inf:
1579 n += 1
1580 if line.startswith('warning: '):
1581 prev_warning = True
1582 rustc_line = self.assert_empty_vv_line(rustc_line)
1583 continue
1584 new_rustc = ''
1585 if RUSTC_PAT.match(line):
1586 args_line = RUSTC_PAT.match(line).group(1)
1587 self.add_crate(Crate(self, outf_name).parse(n, args_line))
1588 self.assert_empty_vv_line(rustc_line)
1589 elif rustc_line or RUSTC_VV_PAT.match(line):
1590 new_rustc = self.rustc_command(n, rustc_line, line, outf_name)
1591 elif CC_AR_VV_PAT.match(line):
1592 self.cc_ar_command(n, CC_AR_VV_PAT.match(line), outf_name)
1593 elif prev_warning and WARNING_FILE_PAT.match(line):
1594 self.assert_empty_vv_line(rustc_line)
1595 fpath = WARNING_FILE_PAT.match(line).group(1)
1596 if fpath[0] != '/': # ignore absolute path
1597 self.warning_files.add(fpath)
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001598 elif line.startswith('error: ') or line.startswith('error[E'):
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001599 if not self.args.ignore_cargo_errors:
1600 self.errors += line
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001601 prev_warning = False
1602 rustc_line = new_rustc
1603 self.find_warning_owners()
1604
1605
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001606def get_parser():
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001607 """Parse main arguments."""
1608 parser = argparse.ArgumentParser('cargo2android')
1609 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001610 '--add_workspace',
1611 action='store_true',
1612 default=False,
1613 help=('append [workspace] to Cargo.toml before calling cargo,' +
1614 ' to treat current directory as root of package source;' +
1615 ' otherwise the relative source file path in generated' +
1616 ' .bp file will be from the parent directory.'))
1617 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001618 '--cargo',
1619 action='append',
1620 metavar='args_string',
1621 help=('extra cargo build -v args in a string, ' +
1622 'each --cargo flag calls cargo build -v once'))
1623 parser.add_argument(
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001624 '--cargo_bin',
1625 type=str,
1626 help='use cargo in the cargo_bin directory instead of the prebuilt one')
1627 parser.add_argument(
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001628 '--copy-out',
1629 action='store_true',
1630 default=False,
1631 help=('only for root directory, ' +
1632 'copy build.rs output to ./out/* and add a genrule to copy ' +
1633 './out/* to genrule output; for crates with code pattern: ' +
1634 'include!(concat!(env!("OUT_DIR"), "/<some_file>.rs"))'))
1635 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001636 '--debug',
1637 action='store_true',
1638 default=False,
1639 help='dump debug info into Android.bp')
1640 parser.add_argument(
1641 '--dependencies',
1642 action='store_true',
1643 default=False,
Joel Galenson833848c2021-08-17 10:50:42 -07001644 help='Deprecated. Has no effect.')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001645 parser.add_argument(
1646 '--device',
1647 action='store_true',
1648 default=False,
1649 help='run cargo also for a default device target')
1650 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001651 '--features',
1652 type=str,
1653 help=('pass features to cargo build, ' +
1654 'empty string means no default features'))
1655 parser.add_argument(
1656 '--global_defaults',
1657 type=str,
1658 help='add a defaults name to every module')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001659 parser.add_argument(
1660 '--host-first-multilib',
1661 action='store_true',
1662 default=False,
1663 help=('add a compile_multilib:"first" property ' +
1664 'to Android.bp host modules.'))
1665 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001666 '--ignore-cargo-errors',
1667 action='store_true',
1668 default=False,
1669 help='do not append cargo/rustc error messages to Android.bp')
1670 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001671 '--no-host',
1672 action='store_true',
1673 default=False,
1674 help='do not run cargo for the host; only for the device target')
1675 parser.add_argument(
1676 '--no-subdir',
1677 action='store_true',
1678 default=False,
1679 help='do not output anything for sub-directories')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001680 parser.add_argument(
1681 '--onefile',
1682 action='store_true',
1683 default=False,
1684 help=('output all into one ./Android.bp, default will generate ' +
1685 'one Android.bp per Cargo.toml in subdirectories'))
1686 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001687 '--patch',
1688 type=str,
1689 help='apply the given patch file to generated ./Android.bp')
1690 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001691 '--run',
1692 action='store_true',
1693 default=False,
1694 help='run it, default is dry-run')
1695 parser.add_argument('--rustflags', type=str, help='passing flags to rustc')
1696 parser.add_argument(
1697 '--skipcargo',
1698 action='store_true',
1699 default=False,
1700 help='skip cargo command, parse cargo.out, and generate Android.bp')
1701 parser.add_argument(
1702 '--tests',
1703 action='store_true',
1704 default=False,
1705 help='run cargo build --tests after normal build')
1706 parser.add_argument(
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001707 '--use-cargo-lock',
1708 action='store_true',
1709 default=False,
1710 help=('run cargo build with existing Cargo.lock ' +
1711 '(used when some latest dependent crates failed)'))
1712 parser.add_argument(
Matthew Maurer062709c2021-08-17 11:27:36 -07001713 '--exported_c_header_dir',
1714 nargs='*',
1715 help='Directories with headers to export for C usage'
1716 )
1717 parser.add_argument(
Joel Galensond9c4de62021-04-23 10:26:40 -07001718 '--min-sdk-version',
1719 type=str,
1720 help='Minimum SDK version')
1721 parser.add_argument(
1722 '--apex-available',
1723 nargs='*',
1724 help='Mark the main library as apex_available with the given apexes.')
1725 parser.add_argument(
Matthew Maurerac677252021-08-13 15:52:52 -07001726 '--native-bridge-supported',
1727 action='store_true',
1728 default=False,
1729 help='Mark the main library as native_bridge_supported.')
1730 parser.add_argument(
1731 '--product-available',
1732 action='store_true',
1733 default=False,
1734 help='Mark the main library as product_available.')
1735 parser.add_argument(
1736 '--recovery-available',
1737 action='store_true',
1738 default=False,
1739 help='Mark the main library as recovery_available.')
1740 parser.add_argument(
Ivan Lozano91920862021-07-19 10:49:08 -04001741 '--vendor-available',
1742 action='store_true',
1743 default=False,
1744 help='Mark the main library as vendor_available.')
1745 parser.add_argument(
1746 '--vendor-ramdisk-available',
1747 action='store_true',
1748 default=False,
1749 help='Mark the main library as vendor_ramdisk_available.')
1750 parser.add_argument(
Matthew Maurerac677252021-08-13 15:52:52 -07001751 '--ramdisk-available',
1752 action='store_true',
1753 default=False,
1754 help='Mark the main library as ramdisk_available.')
1755 parser.add_argument(
Joel Galensoncb5f2f02021-06-08 14:47:55 -07001756 '--force-rlib',
1757 action='store_true',
1758 default=False,
1759 help='Make the main library an rlib.')
1760 parser.add_argument(
Joel Galenson12467e52021-07-12 14:33:28 -07001761 '--whole-static-libs',
1762 nargs='*',
1763 default=[],
1764 help='Make the given libraries (without lib prefixes) whole_static_libs.')
1765 parser.add_argument(
Ivan Lozano26aa1c32021-08-16 11:20:32 -04001766 '--no-pkg-vers',
1767 action='store_true',
1768 default=False,
1769 help='Do not attempt to determine the package version automatically.')
1770 parser.add_argument(
Joel Galensone4f53882021-07-19 11:14:55 -07001771 '--test-data',
1772 nargs='*',
1773 default=[],
1774 help=('Add the given file to the given test\'s data property. ' +
1775 'Usage: test-path=data-path'))
1776 parser.add_argument(
Joel Galenson97e414a2021-05-27 09:42:32 -07001777 '--dependency-blocklist',
1778 nargs='*',
1779 default=[],
Joel Galenson12467e52021-07-12 14:33:28 -07001780 help='Do not emit the given dependencies (without lib prefixes).')
Joel Galenson97e414a2021-05-27 09:42:32 -07001781 parser.add_argument(
Joel Galensoncb5f2f02021-06-08 14:47:55 -07001782 '--lib-blocklist',
1783 nargs='*',
1784 default=[],
Joel Galenson12467e52021-07-12 14:33:28 -07001785 help='Do not emit the given C libraries as dependencies (without lib prefixes).')
Joel Galensoncb5f2f02021-06-08 14:47:55 -07001786 parser.add_argument(
Joel Galensonf6b3c912021-06-03 16:00:54 -07001787 '--test-blocklist',
1788 nargs='*',
1789 default=[],
1790 help=('Do not emit the given tests. ' +
1791 'Pass the path to the test file to exclude.'))
1792 parser.add_argument(
Joel Galenson3d6d1e72021-06-07 15:00:24 -07001793 '--cfg-blocklist',
1794 nargs='*',
1795 default=[],
1796 help='Do not emit the given cfg.')
1797 parser.add_argument(
Joel Galenson5664f2a2021-06-10 10:13:49 -07001798 '--add-toplevel-block',
1799 type=str,
Joel Galenson493c26a2021-10-18 15:54:41 -07001800 help=('Add the contents of the given file to the top level of the Android.bp. ' +
1801 'The filename should start with cargo2android to work with the updater.'))
Joel Galenson5664f2a2021-06-10 10:13:49 -07001802 parser.add_argument(
1803 '--add-module-block',
1804 type=str,
Joel Galenson493c26a2021-10-18 15:54:41 -07001805 help=('Add the contents of the given file to the main module. '+
1806 'The filename should start with cargo2android to work with the updater.'))
Joel Galenson5664f2a2021-06-10 10:13:49 -07001807 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001808 '--verbose',
1809 action='store_true',
1810 default=False,
1811 help='echo executed commands')
1812 parser.add_argument(
1813 '--vv',
1814 action='store_true',
1815 default=False,
1816 help='run cargo with -vv instead of default -v')
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001817 parser.add_argument(
1818 '--dump-config-and-exit',
1819 type=str,
1820 help=('Dump command-line arguments (minus this flag) to a config file and exit. ' +
1821 'This is intended to help migrate from command line options to config files.'))
1822 parser.add_argument(
1823 '--config',
1824 type=str,
1825 help=('Load command-line options from the given config file. ' +
1826 'Options in this file will override those passed on the command line.'))
1827 return parser
1828
1829
1830def parse_args(parser):
1831 """Parses command-line options."""
1832 args = parser.parse_args()
1833 # Use the values specified in a config file if one was found.
1834 if args.config:
1835 with open(args.config, 'r') as f:
1836 config = json.load(f)
1837 args_dict = vars(args)
1838 for arg in config:
1839 args_dict[arg.replace('-', '_')] = config[arg]
1840 return args
1841
1842
1843def dump_config(parser, args):
1844 """Writes the non-default command-line options to the specified file."""
1845 args_dict = vars(args)
1846 # Filter out the arguments that have their default value.
Joel Galenson367360c2021-04-29 14:31:43 -07001847 # Also filter certain "temporary" arguments.
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001848 non_default_args = {}
1849 for arg in args_dict:
Joel Galenson9a82ad92021-08-17 17:52:04 -07001850 if (args_dict[arg] != parser.get_default(arg) and arg != 'dump_config_and_exit'
Joel Galensonc3bfaf82021-08-18 09:39:36 -07001851 and arg != 'config'):
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001852 non_default_args[arg.replace('_', '-')] = args_dict[arg]
1853 # Write to the specified file.
1854 with open(args.dump_config_and_exit, 'w') as f:
1855 json.dump(non_default_args, f, indent=2, sort_keys=True)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001856
1857
1858def main():
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001859 parser = get_parser()
1860 args = parse_args(parser)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001861 if not args.run: # default is dry-run
1862 print(DRY_RUN_NOTE)
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001863 if args.dump_config_and_exit:
1864 dump_config(parser, args)
1865 else:
ThiƩbaud Weksteen198e93f2021-07-02 14:49:19 +02001866 Runner(args).run_cargo().gen_bp().apply_patch()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001867
1868
1869if __name__ == '__main__':
1870 main()