blob: 6764c89f20bc03c195ead1a3a9d0ef3cedf8ce40 [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
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080046import argparse
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -070047import glob
Joel Galenson0fbdafe2021-04-21 16:33:33 -070048import json
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080049import os
50import os.path
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -070051import platform
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080052import re
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -070053import shutil
Joel Galenson7e8247e2021-05-20 18:51:42 -070054import subprocess
Andrew Walbran80e90be2020-06-09 14:33:18 +010055import sys
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080056
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -070057# Some Rust packages include extra unwanted crates.
58# This set contains all such excluded crate names.
Matthew Maurerb59698e2022-09-12 13:03:32 -070059EXCLUDED_CRATES = {'protobuf_bin_gen_rust_do_not_use'}
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -070060
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080061RENAME_MAP = {
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -070062 # This map includes all changes to the default rust module names
63 # to resolve name conflicts, avoid confusion, or work as plugin.
Jason Macnak051340d2021-09-04 11:04:26 -070064 'libash': 'libash_rust',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080065 'libbacktrace': 'libbacktrace_rust',
Andrew Walbrane51f1042020-08-11 16:42:48 +010066 'libbase': 'libbase_rust',
Luke Huanga1371af2021-06-29 18:04:40 +080067 'libbase64': 'libbase64_rust',
Victor Hsieh21bea792020-12-04 10:59:16 -080068 'libfuse': 'libfuse_rust',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080069 'libgcc': 'libgcc_rust',
70 'liblog': 'liblog_rust',
Chih-Hung Hsieh07119862020-07-24 15:34:06 -070071 'libminijail': 'libminijail_rust',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080072 'libsync': 'libsync_rust',
73 'libx86_64': 'libx86_64_rust',
Jooyung Hana427c9b2021-07-16 08:53:14 +090074 'libxml': 'libxml_rust',
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -070075 'protoc_gen_rust': 'protoc-gen-rust',
76}
77
78RENAME_STEM_MAP = {
79 # This map includes all changes to the default rust module stem names,
80 # which is used for output files when different from the module name.
81 'protoc_gen_rust': 'protoc-gen-rust',
82}
83
84RENAME_DEFAULTS_MAP = {
85 # This map includes all changes to the default prefix of rust_default
86 # module names, to avoid conflict with existing Android modules.
87 'libc': 'rust_libc',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080088}
89
90# Header added to all generated Android.bp files.
Joel Galenson56446742021-02-18 08:27:48 -080091ANDROID_BP_HEADER = (
92 '// This file is generated by cargo2android.py {args}.\n' +
93 '// Do not modify this file as changes will be overridden on upgrade.\n')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080094
95CARGO_OUT = 'cargo.out' # Name of file to keep cargo build -v output.
96
Joel Galenson3f42f802021-04-07 12:42:17 -070097# This should be kept in sync with tools/external_updater/crates_updater.py.
98ERRORS_LINE = 'Errors in ' + CARGO_OUT + ':'
99
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800100TARGET_TMP = 'target.tmp' # Name of temporary output directory.
101
102# Message to be displayed when this script is called without the --run flag.
103DRY_RUN_NOTE = (
104 'Dry-run: This script uses ./' + TARGET_TMP + ' for output directory,\n' +
105 'runs cargo clean, runs cargo build -v, saves output to ./cargo.out,\n' +
106 'and writes to Android.bp in the current and subdirectories.\n\n' +
107 'To do do all of the above, use the --run flag.\n' +
108 'See --help for other flags, and more usage notes in this script.\n')
109
110# Cargo -v output of a call to rustc.
111RUSTC_PAT = re.compile('^ +Running `rustc (.*)`$')
112
113# Cargo -vv output of a call to rustc could be split into multiple lines.
114# Assume that the first line will contain some CARGO_* env definition.
115RUSTC_VV_PAT = re.compile('^ +Running `.*CARGO_.*=.*$')
116# The combined -vv output rustc command line pattern.
117RUSTC_VV_CMD_ARGS = re.compile('^ *Running `.*CARGO_.*=.* rustc (.*)`$')
118
119# Cargo -vv output of a "cc" or "ar" command; all in one line.
120CC_AR_VV_PAT = re.compile(r'^\[([^ ]*)[^\]]*\] running:? "(cc|ar)" (.*)$')
121# Some package, such as ring-0.13.5, has pattern '... running "cc"'.
122
123# Rustc output of file location path pattern for a warning message.
124WARNING_FILE_PAT = re.compile('^ *--> ([^:]*):[0-9]+')
125
Joel Galenson7cfc6fe2021-10-14 13:09:32 -0700126# cargo test --list output of the start of running a binary.
127CARGO_TEST_LIST_START_PAT = re.compile('^\s*Running (.*) \(.*\)$')
128
129# cargo test --list output of the end of running a binary.
Frederick Mayle9f0dd712022-08-17 20:34:26 +0000130CARGO_TEST_LIST_END_PAT = re.compile('^(\d+) tests?, (\d+) benchmarks$')
Joel Galenson7cfc6fe2021-10-14 13:09:32 -0700131
Joel Galenson56b5f4b2021-11-30 10:02:57 -0800132CARGO2ANDROID_RUNNING_PAT = re.compile('^### Running: .*$')
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
Pierre-ClƩment Tosidde806f2022-08-24 15:46:54 +0100183 if s and len(s) > 1 and s[0] == s[-1] and s[0] in ('"', "'"):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800184 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
Pierre-ClƩment Tosi435dfe52022-08-24 15:48:25 +0100368 args = list(map(unquote, line.split()))
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800369 i = 0
Pierre-ClƩment Tosi435dfe52022-08-24 15:48:25 +0100370 # Loop through every argument of rustc.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800371 while i < len(args):
372 arg = args[i]
373 if arg == '--crate-name':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700374 i += 1
375 self.crate_name = args[i]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800376 elif arg == '--crate-type':
377 i += 1
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700378 # cargo calls rustc with multiple --crate-type flags.
379 # rustc can accept:
380 # --crate-type [bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]
381 self.crate_types.append(args[i])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800382 elif arg == '--test':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700383 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800384 elif arg == '--target':
385 i += 1
386 self.target = args[i]
387 elif arg == '--cfg':
388 i += 1
Pierre-ClƩment Tosi435dfe52022-08-24 15:48:25 +0100389 if args[i].startswith('feature='):
390 self.features.append(unquote(args[i].replace('feature=', '')))
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800391 else:
392 self.cfgs.append(args[i])
393 elif arg == '--extern':
394 i += 1
395 extern_names = re.sub('=/[^ ]*/deps/', ' = ', args[i])
396 self.externs.append(extern_names)
397 self.core_externs.append(re.sub(' = .*', '', extern_names))
398 elif arg == '-C': # codegen options
399 i += 1
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700400 self.add_codegens_flag(args[i])
401 elif arg.startswith('-C'):
402 # cargo has been passing "-C <xyz>" flag to rustc,
403 # but newer cargo could pass '-Cembed-bitcode=no' to rustc.
404 self.add_codegens_flag(arg[2:])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800405 elif arg == '--cap-lints':
406 i += 1
407 self.cap_lints = args[i]
408 elif arg == '-L':
409 i += 1
410 if args[i].startswith('dependency=') and args[i].endswith('/deps'):
411 if '/' + TARGET_TMP + '/' in args[i]:
412 self.root_pkg = re.sub(
413 '^.*/', '', re.sub('/' + TARGET_TMP + '/.*/deps$', '', args[i]))
414 else:
415 self.root_pkg = re.sub('^.*/', '',
416 re.sub('/[^/]+/[^/]+/deps$', '', args[i]))
417 self.root_pkg = remove_version_suffix(self.root_pkg)
418 elif arg == '-l':
419 i += 1
420 if args[i].startswith('static='):
421 self.static_libs.append(re.sub('static=', '', args[i]))
422 elif args[i].startswith('dylib='):
423 self.shared_libs.append(re.sub('dylib=', '', args[i]))
424 else:
425 self.shared_libs.append(args[i])
426 elif arg == '--out-dir' or arg == '--color': # ignored
427 i += 1
428 elif arg.startswith('--error-format=') or arg.startswith('--json='):
Pierre-ClƩment Tosifd12b5f2022-08-24 16:19:02 +0100429 pass # ignored
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800430 elif arg.startswith('--emit='):
431 self.emit_list = arg.replace('--emit=', '')
432 elif arg.startswith('--edition='):
433 self.edition = arg.replace('--edition=', '')
Pierre-ClƩment Tosie5342d12022-08-24 16:15:16 +0100434 elif arg.startswith('-Aclippy') or arg.startswith('-Wclippy'):
Pierre-ClƩment Tosifd12b5f2022-08-24 16:19:02 +0100435 pass # TODO: Consider storing these to include in the Android.bp.
Pierre-ClƩment Tosie5342d12022-08-24 16:15:16 +0100436 elif arg.startswith('-W'):
437 pass # ignored
438 elif arg.startswith('-D'):
439 pass # TODO: Consider storing these to include in the Android.bp.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700440 elif not arg.startswith('-'):
441 # shorten imported crate main source paths like $HOME/.cargo/
442 # registry/src/github.com-1ecc6299db9ec823/memchr-2.3.3/src/lib.rs
443 self.main_src = re.sub(r'^/[^ ]*/registry/src/', '.../', args[i])
444 self.main_src = re.sub(r'^\.\.\./github.com-[0-9a-f]*/', '.../',
445 self.main_src)
446 self.find_cargo_dir()
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700447 if self.cargo_dir: # for a subdirectory
448 if self.runner.args.no_subdir: # all .bp content to /dev/null
449 self.outf_name = '/dev/null'
450 elif not self.runner.args.onefile:
451 # Write to Android.bp in the subdirectory with Cargo.toml.
452 self.outf_name = self.cargo_dir + '/Android.bp'
453 self.main_src = self.main_src[len(self.cargo_dir) + 1:]
Ivan Lozanocc660f12021-08-11 16:49:46 -0400454
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800455 else:
456 self.errors += 'ERROR: unknown ' + arg + '\n'
457 i += 1
458 if not self.crate_name:
459 self.errors += 'ERROR: missing --crate-name\n'
460 if not self.main_src:
461 self.errors += 'ERROR: missing main source file\n'
462 else:
463 self.srcs.append(self.main_src)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700464 if not self.crate_types:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800465 # Treat "--cfg test" as "--test"
466 if 'test' in self.cfgs:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700467 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800468 else:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700469 self.errors += 'ERROR: missing --crate-type or --test\n'
470 elif len(self.crate_types) > 1:
471 if 'test' in self.crate_types:
472 self.errors += 'ERROR: cannot handle both --crate-type and --test\n'
473 if 'lib' in self.crate_types and 'rlib' in self.crate_types:
474 self.errors += 'ERROR: cannot generate both lib and rlib crate types\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800475 if not self.root_pkg:
476 self.root_pkg = self.crate_name
Ivan Lozano26aa1c32021-08-16 11:20:32 -0400477
478 # get the package version from running cargo metadata
Joel Galenson69ba8072021-08-16 11:31:29 -0700479 if not self.runner.args.no_pkg_vers and not self.skip_crate():
Ivan Lozano26aa1c32021-08-16 11:20:32 -0400480 self.get_pkg_version()
481
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700482 self.device_supported = self.runner.args.device
483 self.host_supported = not self.runner.args.no_host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800484 self.cfgs = sorted(set(self.cfgs))
485 self.features = sorted(set(self.features))
486 self.codegens = sorted(set(self.codegens))
487 self.externs = sorted(set(self.externs))
488 self.core_externs = sorted(set(self.core_externs))
489 self.static_libs = sorted(set(self.static_libs))
490 self.shared_libs = sorted(set(self.shared_libs))
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700491 self.crate_types = sorted(set(self.crate_types))
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800492 self.decide_module_type()
493 self.module_name = altered_name(self.stem)
494 return self
495
Ivan Lozano26aa1c32021-08-16 11:20:32 -0400496 def get_pkg_version(self):
497 """Attempt to retrieve the package version from the Cargo.toml
498
499 If there is only one package, use its version. Otherwise, try to
500 match the emitted `--crate_name` arg against the package name.
501
502 This may fail in cases where multiple packages are defined (workspaces)
503 and where the package name does not match the emitted crate_name
504 (e.g. [lib.name] is set).
505 """
Joel Galenson69ba8072021-08-16 11:31:29 -0700506 cargo_metadata = subprocess.run([self.runner.cargo_path, 'metadata', '--no-deps',
507 '--format-version', '1'],
Joel Galensonc5186502021-08-16 11:22:47 -0700508 cwd=os.path.abspath(self.cargo_dir),
509 stdout=subprocess.PIPE)
Ivan Lozano26aa1c32021-08-16 11:20:32 -0400510 if cargo_metadata.returncode:
511 self.errors += ('ERROR: unable to get cargo metadata for package version; ' +
512 'return code ' + cargo_metadata.returncode + '\n')
513 else:
514 metadata_json = json.loads(cargo_metadata.stdout)
515 if len(metadata_json['packages']) > 1:
516 for package in metadata_json['packages']:
517 # package names may contain '-', but is changed to '_' in the crate_name
518 if package['name'].replace('-','_') == self.crate_name:
519 self.cargo_pkg_version = package['version']
520 break
521 else:
522 self.cargo_pkg_version = metadata_json['packages'][0]['version']
523
524 if not self.cargo_pkg_version:
525 self.errors += ('ERROR: Unable to retrieve package version; ' +
526 'to disable, run with arg "--no-pkg-vers"\n')
527
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800528 def dump_line(self):
529 self.write('\n// Line ' + str(self.line_num) + ' ' + self.line)
530
531 def feature_list(self):
532 """Return a string of main_src + "feature_list"."""
533 pkg = self.main_src
534 if pkg.startswith('.../'): # keep only the main package name
535 pkg = re.sub('/.*', '', pkg[4:])
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700536 elif pkg.startswith('/'): # use relative path for a local package
537 pkg = os.path.relpath(pkg)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800538 if not self.features:
539 return pkg
540 return pkg + ' "' + ','.join(self.features) + '"'
541
542 def dump_skip_crate(self, kind):
543 if self.debug:
544 self.write('\n// IGNORED: ' + kind + ' ' + self.main_src)
545 return self
546
547 def skip_crate(self):
548 """Return crate_name or a message if this crate should be skipped."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700549 if (is_build_crate_name(self.crate_name) or
550 self.crate_name in EXCLUDED_CRATES):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800551 return self.crate_name
552 if is_dependent_file_path(self.main_src):
553 return 'dependent crate'
554 return ''
555
556 def dump(self):
557 """Dump all error/debug/module code to the output .bp file."""
558 self.runner.init_bp_file(self.outf_name)
559 with open(self.outf_name, 'a') as outf:
560 self.outf = outf
561 if self.errors:
562 self.dump_line()
563 self.write(self.errors)
564 elif self.skip_crate():
565 self.dump_skip_crate(self.skip_crate())
566 else:
567 if self.debug:
568 self.dump_debug_info()
569 self.dump_android_module()
570
571 def dump_debug_info(self):
572 """Dump parsed data, when cargo2android is called with --debug."""
573
574 def dump(name, value):
575 self.write('//%12s = %s' % (name, value))
576
577 def opt_dump(name, value):
578 if value:
579 dump(name, value)
580
581 def dump_list(fmt, values):
582 for v in values:
583 self.write(fmt % v)
584
585 self.dump_line()
586 dump('module_name', self.module_name)
587 dump('crate_name', self.crate_name)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700588 dump('crate_types', self.crate_types)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800589 dump('main_src', self.main_src)
590 dump('has_warning', self.has_warning)
591 dump('for_host', self.host_supported)
592 dump('for_device', self.device_supported)
593 dump('module_type', self.module_type)
594 opt_dump('target', self.target)
595 opt_dump('edition', self.edition)
596 opt_dump('emit_list', self.emit_list)
597 opt_dump('cap_lints', self.cap_lints)
598 dump_list('// cfg = %s', self.cfgs)
599 dump_list('// cfg = \'feature "%s"\'', self.features)
600 # TODO(chh): escape quotes in self.features, but not in other dump_list
601 dump_list('// codegen = %s', self.codegens)
602 dump_list('// externs = %s', self.externs)
603 dump_list('// -l static = %s', self.static_libs)
604 dump_list('// -l (dylib) = %s', self.shared_libs)
605
606 def dump_android_module(self):
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700607 """Dump one or more Android module definition, depending on crate_types."""
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700608 if len(self.crate_types) == 1:
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700609 self.dump_single_type_android_module()
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700610 return
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700611 if 'test' in self.crate_types:
612 self.write('\nERROR: multiple crate types cannot include test type')
613 return
614 # Dump one Android module per crate_type.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700615 for crate_type in self.crate_types:
616 self.decide_one_module_type(crate_type)
617 self.dump_one_android_module(crate_type)
618
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700619 def build_default_name(self):
620 """Return a short and readable name for the rust_defaults module."""
Joel Galensond37d7e62021-07-13 09:03:01 -0700621 # Choices: (1) root_pkg + '_test'? + '_defaults',
622 # (2) root_pkg + '_test'? + '_defaults_' + crate_name
623 # (3) root_pkg + '_test'? + '_defaults_' + main_src_basename_path
624 # (4) root_pkg + '_test'? + '_defaults_' + a_positive_sequence_number
625 test = "_test" if self.crate_types == ['test'] else ""
626 name1 = altered_defaults(self.root_pkg) + test + '_defaults'
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700627 if self.runner.try_claim_module_name(name1, self):
628 return name1
629 name2 = name1 + '_' + self.crate_name
630 if self.runner.try_claim_module_name(name2, self):
631 return name2
632 name3 = name1 + '_' + self.main_src_basename_path()
633 if self.runner.try_claim_module_name(name3, self):
634 return name3
635 return self.runner.claim_module_name(name1, self, 0)
636
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700637 def dump_srcs_list(self):
638 """Dump the srcs list, for defaults or regular modules."""
639 if len(self.srcs) > 1:
640 srcs = sorted(set(self.srcs)) # make a copy and dedup
641 else:
642 srcs = [self.main_src]
643 copy_out = self.runner.copy_out_module_name()
644 if copy_out:
645 srcs.append(':' + copy_out)
646 self.dump_android_property_list('srcs', '"%s"', srcs)
647
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700648 def dump_defaults_module(self):
649 """Dump a rust_defaults module to be shared by other modules."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700650 name = self.build_default_name()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700651 self.defaults = name
652 self.write('\nrust_defaults {')
653 self.write(' name: "' + name + '",')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700654 if self.runner.args.global_defaults:
655 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700656 self.write(' crate_name: "' + self.crate_name + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700657 if len(self.srcs) == 1: # only one source file; share it in defaults
658 self.default_srcs = True
659 if self.has_warning and not self.cap_lints:
660 self.write(' // has rustc warnings')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700661 self.dump_srcs_list()
Joel Galensonb06c42a2021-08-31 14:28:48 -0700662 if self.cargo_env_compat:
663 self.write(' cargo_env_compat: true,')
Joel Galenson94b8a8d2021-09-28 11:53:51 -0700664 if not self.runner.args.no_pkg_vers:
665 self.write(' cargo_pkg_version: "' + self.cargo_pkg_version + '",')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700666 if 'test' in self.crate_types:
667 self.write(' test_suites: ["general-tests"],')
668 self.write(' auto_gen_config: true,')
669 self.dump_edition_flags_libs()
Joel Galensone4f53882021-07-19 11:14:55 -0700670 if 'test' in self.crate_types and len(self.srcs) == 1:
671 self.dump_test_data()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700672 self.write('}')
673
674 def dump_single_type_android_module(self):
675 """Dump one simple Android module, which has only one crate_type."""
676 crate_type = self.crate_types[0]
677 if crate_type != 'test':
678 # do not change self.stem or self.module_name
679 self.dump_one_android_module(crate_type)
680 return
Joel Galenson54d65532021-08-31 14:08:05 -0700681 # Dump one test module per source file.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700682 # crate_type == 'test'
Joel Galenson7cfc6fe2021-10-14 13:09:32 -0700683 self.srcs = [src for src in self.srcs if not self.runner.should_ignore_test(src)]
Joel Galenson54d65532021-08-31 14:08:05 -0700684 if len(self.srcs) > 1:
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700685 self.srcs = sorted(set(self.srcs))
686 self.dump_defaults_module()
687 saved_srcs = self.srcs
688 for src in saved_srcs:
689 self.srcs = [src]
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700690 saved_main_src = self.main_src
691 self.main_src = src
Joel Galenson54d65532021-08-31 14:08:05 -0700692 self.module_name = self.test_module_name()
693 self.decide_one_module_type(crate_type)
694 self.dump_one_android_module(crate_type)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700695 self.main_src = saved_main_src
696 self.srcs = saved_srcs
697
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700698 def dump_one_android_module(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800699 """Dump one Android module definition."""
700 if not self.module_type:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700701 self.write('\nERROR: unknown crate_type ' + crate_type)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800702 return
703 self.write('\n' + self.module_type + ' {')
704 self.dump_android_core_properties()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700705 if not self.defaults:
706 self.dump_edition_flags_libs()
707 if self.runner.args.host_first_multilib and self.host_supported and crate_type != 'test':
708 self.write(' compile_multilib: "first",')
Matthew Maurer062709c2021-08-17 11:27:36 -0700709 if self.runner.args.exported_c_header_dir and crate_type in C_LIBRARY_CRATE_TYPES:
710 self.write(' include_dirs: [')
711 for header_dir in self.runner.args.exported_c_header_dir:
712 self.write(' "%s",' % header_dir)
713 self.write(' ],')
Matthew Maurera9fb65d2022-06-16 13:07:12 -0700714 if crate_type in LIBRARY_CRATE_TYPES:
Joel Galensond9c4de62021-04-23 10:26:40 -0700715 self.write(' apex_available: [')
Matthew Maurera9fb65d2022-06-16 13:07:12 -0700716 if self.runner.args.apex_available is None:
717 # If apex_available is not explicitly set, make it available to all
718 # apexes.
719 self.write(' "//apex_available:platform",')
720 self.write(' "//apex_available:anyapex",')
721 else:
722 for apex in self.runner.args.apex_available:
723 self.write(' "%s",' % apex)
Joel Galensond9c4de62021-04-23 10:26:40 -0700724 self.write(' ],')
Matthew Maurer70182e42021-08-17 13:53:52 -0700725 if crate_type != 'test':
726 if self.runner.args.native_bridge_supported:
727 self.write(' native_bridge_supported: true,')
728 if self.runner.args.product_available:
729 self.write(' product_available: true,')
730 if self.runner.args.recovery_available:
731 self.write(' recovery_available: true,')
732 if self.runner.args.vendor_available:
733 self.write(' vendor_available: true,')
734 if self.runner.args.vendor_ramdisk_available:
735 self.write(' vendor_ramdisk_available: true,')
736 if self.runner.args.ramdisk_available:
737 self.write(' ramdisk_available: true,')
Matthew Maurer9e4b7812021-08-16 14:21:01 -0700738 if self.runner.args.min_sdk_version and crate_type in LIBRARY_CRATE_TYPES:
Joel Galensond9c4de62021-04-23 10:26:40 -0700739 self.write(' min_sdk_version: "%s",' % self.runner.args.min_sdk_version)
Joel Galensone4f53882021-07-19 11:14:55 -0700740 if crate_type == 'test' and not self.default_srcs:
741 self.dump_test_data()
Joel Galenson5664f2a2021-06-10 10:13:49 -0700742 if self.runner.args.add_module_block:
743 with open(self.runner.args.add_module_block, 'r') as f:
744 self.write(' %s,' % f.read().replace('\n', '\n '))
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700745 self.write('}')
746
747 def dump_android_flags(self):
748 """Dump Android module flags property."""
ThiƩbaud Weksteena5a728b2021-04-08 14:23:49 +0200749 if not self.codegens and not self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700750 return
751 self.write(' flags: [')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800752 if self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700753 self.write(' "--cap-lints ' + self.cap_lints + '",')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700754 codegens_fmt = '"-C %s"'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700755 self.dump_android_property_list_items(codegens_fmt, self.codegens)
756 self.write(' ],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700757
758 def dump_edition_flags_libs(self):
759 if self.edition:
760 self.write(' edition: "' + self.edition + '",')
761 self.dump_android_property_list('features', '"%s"', self.features)
Joel Galenson3d6d1e72021-06-07 15:00:24 -0700762 cfgs = [cfg for cfg in self.cfgs if not cfg in self.runner.args.cfg_blocklist]
763 self.dump_android_property_list('cfgs', '"%s"', cfgs)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700764 self.dump_android_flags()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800765 if self.externs:
766 self.dump_android_externs()
Joel Galenson12467e52021-07-12 14:33:28 -0700767 all_static_libs = [lib for lib in self.static_libs if not lib in self.runner.args.lib_blocklist]
768 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 -0700769 self.dump_android_property_list('static_libs', '"lib%s"', static_libs)
Joel Galenson12467e52021-07-12 14:33:28 -0700770 whole_static_libs = [lib for lib in all_static_libs if lib in self.runner.args.whole_static_libs]
771 self.dump_android_property_list('whole_static_libs', '"lib%s"', whole_static_libs)
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700772 shared_libs = [lib for lib in self.shared_libs if not lib in self.runner.args.lib_blocklist]
773 self.dump_android_property_list('shared_libs', '"lib%s"', shared_libs)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800774
Joel Galensone4f53882021-07-19 11:14:55 -0700775 def dump_test_data(self):
776 data = [data for (name, data) in map(lambda kv: kv.split('=', 1), self.runner.args.test_data)
777 if self.srcs == [name]]
778 if data:
779 self.dump_android_property_list('data', '"%s"', data)
780
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700781 def main_src_basename_path(self):
782 return re.sub('/', '_', re.sub('.rs$', '', self.main_src))
783
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800784 def test_module_name(self):
785 """Return a unique name for a test module."""
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700786 # root_pkg+(_host|_device) + '_test_'+source_file_name
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700787 suffix = self.main_src_basename_path()
Joel Galenson54d65532021-08-31 14:08:05 -0700788 return self.root_pkg + '_test_' + suffix
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800789
790 def decide_module_type(self):
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700791 # Use the first crate type for the default/first module.
792 crate_type = self.crate_types[0] if self.crate_types else ''
793 self.decide_one_module_type(crate_type)
794
795 def decide_one_module_type(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800796 """Decide which Android module type to use."""
797 host = '' if self.device_supported else '_host'
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700798 rlib = '_rlib' if self.runner.args.force_rlib else ''
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700799 if crate_type == 'bin': # rust_binary[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800800 self.module_type = 'rust_binary' + host
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700801 # In rare cases like protobuf-codegen, the output binary name must
802 # be renamed to use as a plugin for protoc.
803 self.stem = altered_stem(self.crate_name)
804 self.module_name = altered_name(self.crate_name)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700805 elif crate_type == 'lib': # rust_library[_host]
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700806 # TODO(chh): should this be rust_library[_host]?
807 # Assuming that Cargo.toml do not use both 'lib' and 'rlib',
808 # because we map them both to rlib.
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700809 self.module_type = 'rust_library' + rlib + host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800810 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700811 self.module_name = altered_name(self.stem)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700812 elif crate_type == 'rlib': # rust_library[_host]
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700813 self.module_type = 'rust_library' + rlib + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700814 self.stem = 'lib' + self.crate_name
815 self.module_name = altered_name(self.stem)
816 elif crate_type == 'dylib': # rust_library[_host]_dylib
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800817 self.module_type = 'rust_library' + host + '_dylib'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700818 self.stem = 'lib' + self.crate_name
819 self.module_name = altered_name(self.stem) + '_dylib'
820 elif crate_type == 'cdylib': # rust_library[_host]_shared
Ivan Lozano0c057ad2020-12-15 10:41:26 -0500821 self.module_type = 'rust_ffi' + host + '_shared'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700822 self.stem = 'lib' + self.crate_name
823 self.module_name = altered_name(self.stem) + '_shared'
824 elif crate_type == 'staticlib': # rust_library[_host]_static
Ivan Lozano0c057ad2020-12-15 10:41:26 -0500825 self.module_type = 'rust_ffi' + host + '_static'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700826 self.stem = 'lib' + self.crate_name
827 self.module_name = altered_name(self.stem) + '_static'
828 elif crate_type == 'test': # rust_test[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800829 self.module_type = 'rust_test' + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700830 # Before do_merge, stem name is based on the --crate-name parameter.
831 # and test module name is based on stem.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800832 self.stem = self.test_module_name()
833 # self.stem will be changed after merging with other tests.
834 # self.stem is NOT used for final test binary name.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700835 # rust_test uses each source file base name as part of output file name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700836 # In do_merge, this function is called again, with a module_name.
837 # We make sure that the module name is unique in each package.
838 if self.module_name:
839 # Cargo uses "-C extra-filename=..." and "-C metadata=..." to add
840 # different suffixes and distinguish multiple tests of the same
841 # crate name. We ignore -C and use claim_module_name to get
842 # unique sequential suffix.
843 self.module_name = self.runner.claim_module_name(
844 self.module_name, self, 0)
845 # Now the module name is unique, stem should also match and unique.
846 self.stem = self.module_name
847 elif crate_type == 'proc-macro': # rust_proc_macro
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800848 self.module_type = 'rust_proc_macro'
849 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700850 self.module_name = altered_name(self.stem)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800851 else: # unknown module type, rust_prebuilt_dylib? rust_library[_host]?
852 self.module_type = ''
853 self.stem = ''
854
855 def dump_android_property_list_items(self, fmt, values):
856 for v in values:
857 # fmt has quotes, so we need escape_quotes(v)
858 self.write(' ' + (fmt % escape_quotes(v)) + ',')
859
860 def dump_android_property_list(self, name, fmt, values):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700861 if not values:
862 return
863 if len(values) > 1:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800864 self.write(' ' + name + ': [')
865 self.dump_android_property_list_items(fmt, values)
866 self.write(' ],')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700867 else:
868 self.write(' ' + name + ': [' +
869 (fmt % escape_quotes(values[0])) + '],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800870
871 def dump_android_core_properties(self):
872 """Dump the module header, name, stem, etc."""
873 self.write(' name: "' + self.module_name + '",')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700874 # see properties shared by dump_defaults_module
875 if self.defaults:
876 self.write(' defaults: ["' + self.defaults + '"],')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700877 elif self.runner.args.global_defaults:
878 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800879 if self.stem != self.module_name:
880 self.write(' stem: "' + self.stem + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700881 if self.has_warning and not self.cap_lints and not self.default_srcs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700882 self.write(' // has rustc warnings')
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700883 if self.host_supported and self.device_supported and self.module_type != 'rust_proc_macro':
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800884 self.write(' host_supported: true,')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700885 if not self.defaults:
886 self.write(' crate_name: "' + self.crate_name + '",')
Ivan Lozanocc660f12021-08-11 16:49:46 -0400887 if not self.defaults and self.cargo_env_compat:
888 self.write(' cargo_env_compat: true,')
Joel Galenson94b8a8d2021-09-28 11:53:51 -0700889 if not self.runner.args.no_pkg_vers:
890 self.write(' cargo_pkg_version: "' + self.cargo_pkg_version + '",')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700891 if not self.default_srcs:
892 self.dump_srcs_list()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700893 if 'test' in self.crate_types and not self.defaults:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800894 # self.root_pkg can have multiple test modules, with different *_tests[n]
895 # names, but their executables can all be installed under the same _tests
896 # directory. When built from Cargo.toml, all tests should have different
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700897 # file or crate names. So we used (root_pkg + '_tests') name as the
898 # relative_install_path.
899 # However, some package like 'slab' can have non-mergeable tests that
900 # must be separated by different module names. So, here we no longer
901 # emit relative_install_path.
902 # self.write(' relative_install_path: "' + self.root_pkg + '_tests",')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800903 self.write(' test_suites: ["general-tests"],')
904 self.write(' auto_gen_config: true,')
Joel Galensone261a152021-01-12 11:31:53 -0800905 if 'test' in self.crate_types and self.host_supported:
906 self.write(' test_options: {')
Andrew Walbranca9f9f22022-01-10 16:02:20 +0000907 if self.runner.args.no_presubmit:
908 self.write(' unit_test: false,')
909 else:
910 self.write(' unit_test: true,')
Joel Galensone261a152021-01-12 11:31:53 -0800911 self.write(' },')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800912
913 def dump_android_externs(self):
914 """Dump the dependent rlibs and dylibs property."""
915 so_libs = list()
916 rust_libs = ''
917 deps_libname = re.compile('^.* = lib(.*)-[0-9a-f]*.(rlib|so|rmeta)$')
918 for lib in self.externs:
919 # normal value of lib: "libc = liblibc-*.rlib"
920 # strange case in rand crate: "getrandom_package = libgetrandom-*.rlib"
921 # we should use "libgetrandom", not "lib" + "getrandom_package"
922 groups = deps_libname.match(lib)
923 if groups is not None:
924 lib_name = groups.group(1)
925 else:
926 lib_name = re.sub(' .*$', '', lib)
Joel Galenson97e414a2021-05-27 09:42:32 -0700927 if lib_name in self.runner.args.dependency_blocklist:
928 continue
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800929 if lib.endswith('.rlib') or lib.endswith('.rmeta'):
930 # On MacOS .rmeta is used when Linux uses .rlib or .rmeta.
931 rust_libs += ' "' + altered_name('lib' + lib_name) + '",\n'
932 elif lib.endswith('.so'):
933 so_libs.append(lib_name)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700934 elif lib != 'proc_macro': # --extern proc_macro is special and ignored
935 rust_libs += ' // ERROR: unknown type of lib ' + lib + '\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800936 if rust_libs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700937 self.write(' rustlibs: [\n' + rust_libs + ' ],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800938 # Are all dependent .so files proc_macros?
939 # TODO(chh): Separate proc_macros and dylib.
940 self.dump_android_property_list('proc_macros', '"lib%s"', so_libs)
941
942
943class ARObject(object):
944 """Information of an "ar" link command."""
945
946 def __init__(self, runner, outf_name):
947 # Remembered global runner and its members.
948 self.runner = runner
949 self.pkg = ''
950 self.outf_name = outf_name # path to Android.bp
951 # "ar" arguments
952 self.line_num = 1
953 self.line = ''
954 self.flags = '' # e.g. "crs"
955 self.lib = '' # e.g. "/.../out/lib*.a"
956 self.objs = list() # e.g. "/.../out/.../*.o"
957
958 def parse(self, pkg, line_num, args_line):
959 """Collect ar obj/lib file names."""
960 self.pkg = pkg
961 self.line_num = line_num
962 self.line = args_line
963 args = args_line.split()
964 num_args = len(args)
965 if num_args < 3:
966 print('ERROR: "ar" command has too few arguments', args_line)
967 else:
968 self.flags = unquote(args[0])
969 self.lib = unquote(args[1])
970 self.objs = sorted(set(map(unquote, args[2:])))
971 return self
972
973 def write(self, s):
974 self.outf.write(s + '\n')
975
976 def dump_debug_info(self):
977 self.write('\n// Line ' + str(self.line_num) + ' "ar" ' + self.line)
978 self.write('// ar_object for %12s' % self.pkg)
979 self.write('// flags = %s' % self.flags)
980 self.write('// lib = %s' % short_out_name(self.pkg, self.lib))
981 for o in self.objs:
982 self.write('// obj = %s' % short_out_name(self.pkg, o))
983
984 def dump_android_lib(self):
985 """Write cc_library_static into Android.bp."""
986 self.write('\ncc_library_static {')
987 self.write(' name: "' + file_base_name(self.lib) + '",')
988 self.write(' host_supported: true,')
989 if self.flags != 'crs':
990 self.write(' // ar flags = %s' % self.flags)
991 if self.pkg not in self.runner.pkg_obj2cc:
992 self.write(' ERROR: cannot find source files.\n}')
993 return
994 self.write(' srcs: [')
995 obj2cc = self.runner.pkg_obj2cc[self.pkg]
996 # Note: wflags are ignored.
997 dflags = list()
998 fflags = list()
999 for obj in self.objs:
1000 self.write(' "' + short_out_name(self.pkg, obj2cc[obj].src) + '",')
1001 # TODO(chh): union of dflags and flags of all obj
1002 # Now, just a temporary hack that uses the last obj's flags
1003 dflags = obj2cc[obj].dflags
1004 fflags = obj2cc[obj].fflags
1005 self.write(' ],')
1006 self.write(' cflags: [')
1007 self.write(' "-O3",') # TODO(chh): is this default correct?
1008 self.write(' "-Wno-error",')
1009 for x in fflags:
1010 self.write(' "-f' + x + '",')
1011 for x in dflags:
1012 self.write(' "-D' + x + '",')
1013 self.write(' ],')
1014 self.write('}')
1015
1016 def dump(self):
1017 """Dump error/debug/module info to the output .bp file."""
1018 self.runner.init_bp_file(self.outf_name)
1019 with open(self.outf_name, 'a') as outf:
1020 self.outf = outf
1021 if self.runner.args.debug:
1022 self.dump_debug_info()
1023 self.dump_android_lib()
1024
1025
1026class CCObject(object):
1027 """Information of a "cc" compilation command."""
1028
1029 def __init__(self, runner, outf_name):
1030 # Remembered global runner and its members.
1031 self.runner = runner
1032 self.pkg = ''
1033 self.outf_name = outf_name # path to Android.bp
1034 # "cc" arguments
1035 self.line_num = 1
1036 self.line = ''
1037 self.src = ''
1038 self.obj = ''
1039 self.dflags = list() # -D flags
1040 self.fflags = list() # -f flags
1041 self.iflags = list() # -I flags
1042 self.wflags = list() # -W flags
1043 self.other_args = list()
1044
1045 def parse(self, pkg, line_num, args_line):
1046 """Collect cc compilation flags and src/out file names."""
1047 self.pkg = pkg
1048 self.line_num = line_num
1049 self.line = args_line
1050 args = args_line.split()
1051 i = 0
1052 while i < len(args):
1053 arg = args[i]
1054 if arg == '"-c"':
1055 i += 1
1056 if args[i].startswith('"-o'):
1057 # ring-0.13.5 dumps: ... "-c" "-o/.../*.o" ".../*.c"
1058 self.obj = unquote(args[i])[2:]
1059 i += 1
1060 self.src = unquote(args[i])
1061 else:
1062 self.src = unquote(args[i])
1063 elif arg == '"-o"':
1064 i += 1
1065 self.obj = unquote(args[i])
1066 elif arg == '"-I"':
1067 i += 1
1068 self.iflags.append(unquote(args[i]))
1069 elif arg.startswith('"-D'):
1070 self.dflags.append(unquote(args[i])[2:])
1071 elif arg.startswith('"-f'):
1072 self.fflags.append(unquote(args[i])[2:])
1073 elif arg.startswith('"-W'):
1074 self.wflags.append(unquote(args[i])[2:])
1075 elif not (arg.startswith('"-O') or arg == '"-m64"' or arg == '"-g"' or
1076 arg == '"-g3"'):
1077 # ignore -O -m64 -g
1078 self.other_args.append(unquote(args[i]))
1079 i += 1
1080 self.dflags = sorted(set(self.dflags))
1081 self.fflags = sorted(set(self.fflags))
1082 # self.wflags is not sorted because some are order sensitive
1083 # and we ignore them anyway.
1084 if self.pkg not in self.runner.pkg_obj2cc:
1085 self.runner.pkg_obj2cc[self.pkg] = {}
1086 self.runner.pkg_obj2cc[self.pkg][self.obj] = self
1087 return self
1088
1089 def write(self, s):
1090 self.outf.write(s + '\n')
1091
1092 def dump_debug_flags(self, name, flags):
1093 self.write('// ' + name + ':')
1094 for f in flags:
1095 self.write('// %s' % f)
1096
1097 def dump(self):
1098 """Dump only error/debug info to the output .bp file."""
1099 if not self.runner.args.debug:
1100 return
1101 self.runner.init_bp_file(self.outf_name)
1102 with open(self.outf_name, 'a') as outf:
1103 self.outf = outf
1104 self.write('\n// Line ' + str(self.line_num) + ' "cc" ' + self.line)
1105 self.write('// cc_object for %12s' % self.pkg)
1106 self.write('// src = %s' % short_out_name(self.pkg, self.src))
1107 self.write('// obj = %s' % short_out_name(self.pkg, self.obj))
1108 self.dump_debug_flags('-I flags', self.iflags)
1109 self.dump_debug_flags('-D flags', self.dflags)
1110 self.dump_debug_flags('-f flags', self.fflags)
1111 self.dump_debug_flags('-W flags', self.wflags)
1112 if self.other_args:
1113 self.dump_debug_flags('other args', self.other_args)
1114
1115
1116class Runner(object):
1117 """Main class to parse cargo -v output and print Android module definitions."""
1118
1119 def __init__(self, args):
1120 self.bp_files = set() # Remember all output Android.bp files.
1121 self.root_pkg = '' # name of package in ./Cargo.toml
1122 # Saved flags, modes, and data.
1123 self.args = args
1124 self.dry_run = not args.run
1125 self.skip_cargo = args.skipcargo
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001126 self.cargo_path = './cargo' # path to cargo, will be set later
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001127 self.checked_out_files = False # to check only once
1128 self.build_out_files = [] # output files generated by build.rs
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001129 # All cc/ar objects, crates, dependencies, and warning files
1130 self.cc_objects = list()
1131 self.pkg_obj2cc = {}
1132 # pkg_obj2cc[cc_object[i].pkg][cc_objects[i].obj] = cc_objects[i]
1133 self.ar_objects = list()
1134 self.crates = list()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001135 self.warning_files = set()
1136 # Keep a unique mapping from (module name) to crate
1137 self.name_owners = {}
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001138 # Save and dump all errors from cargo to Android.bp.
1139 self.errors = ''
Joel Galenson56b5f4b2021-11-30 10:02:57 -08001140 self.test_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)
ThiƩbaud Weksteend5d03392022-02-09 10:48:15 +11001163 print('INFO: using cargo in ' + self.args.cargo_bin)
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001164 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
Matthew Maurer1231af22022-06-16 13:31:35 -07001204 version = (int(result.group(1)), int(result.group(2)), int(result.group(3)))
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001205 if version > rust_version:
1206 rust_version = version
Matthew Maurer1231af22022-06-16 13:31:35 -07001207 return '.'.join(map(str, rust_version))
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001208
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 Galenson769c3c32021-11-24 15:04:29 -08001281 print_args = sys.argv[1:].copy()
1282 if '--cargo_bin' in print_args:
1283 index = print_args.index('--cargo_bin')
1284 del print_args[index:index+2]
1285 outf.write(ANDROID_BP_HEADER.format(args=' '.join(print_args)))
Haibo Huang0f72c952021-03-19 11:34:15 -07001286 outf.write('\n')
1287 outf.write(license_section)
1288 outf.write('\n')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001289 # at most one copy_out module per .bp file
1290 self.dump_copy_out_module(outf)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001291
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001292 def try_claim_module_name(self, name, owner):
1293 """Reserve and return True if it has not been reserved yet."""
1294 if name not in self.name_owners or owner == self.name_owners[name]:
1295 self.name_owners[name] = owner
1296 return True
1297 return False
1298
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001299 def claim_module_name(self, prefix, owner, counter):
1300 """Return prefix if not owned yet, otherwise, prefix+str(counter)."""
1301 while True:
1302 name = prefix
1303 if counter > 0:
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001304 name += '_' + str(counter)
1305 if self.try_claim_module_name(name, owner):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001306 return name
1307 counter += 1
1308
1309 def find_root_pkg(self):
1310 """Read name of [package] in ./Cargo.toml."""
1311 if not os.path.exists('./Cargo.toml'):
1312 return
1313 with open('./Cargo.toml', 'r') as inf:
1314 pkg_section = re.compile(r'^ *\[package\]')
1315 name = re.compile('^ *name *= * "([^"]*)"')
1316 in_pkg = False
1317 for line in inf:
1318 if in_pkg:
1319 if name.match(line):
1320 self.root_pkg = name.match(line).group(1)
1321 break
1322 else:
1323 in_pkg = pkg_section.match(line) is not None
1324
1325 def run_cargo(self):
1326 """Calls cargo -v and save its output to ./cargo.out."""
1327 if self.skip_cargo:
1328 return self
Jeffrey Vander Stoep6529bca2022-07-06 12:34:37 +00001329 cargo_toml = './Cargo.toml'
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001330 cargo_out = './cargo.out'
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001331 # Do not use Cargo.lock, because .bp rules are designed to
1332 # run with "latest" crates avaialable on Android.
1333 cargo_lock = './Cargo.lock'
1334 cargo_lock_saved = './cargo.lock.saved'
1335 had_cargo_lock = os.path.exists(cargo_lock)
Jeffrey Vander Stoep6529bca2022-07-06 12:34:37 +00001336 if not os.access(cargo_toml, os.R_OK):
1337 print('ERROR: Cannot find or read', cargo_toml)
1338 return self
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001339 if not self.dry_run:
1340 if os.path.exists(cargo_out):
1341 os.remove(cargo_out)
1342 if not self.args.use_cargo_lock and had_cargo_lock: # save it
1343 os.rename(cargo_lock, cargo_lock_saved)
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001344 cmd_tail_target = ' --target-dir ' + TARGET_TMP
1345 cmd_tail_redir = ' >> ' + cargo_out + ' 2>&1'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001346 # set up search PATH for cargo to find the correct rustc
1347 saved_path = os.environ['PATH']
1348 os.environ['PATH'] = os.path.dirname(self.cargo_path) + ':' + saved_path
Jeffrey Vander Stoep6529bca2022-07-06 12:34:37 +00001349 # Add [workspace] to Cargo.toml if it is not there.
1350 added_workspace = False
1351 if self.args.add_workspace:
1352 with open(cargo_toml, 'r') as in_file:
1353 cargo_toml_lines = in_file.readlines()
1354 found_workspace = '[workspace]\n' in cargo_toml_lines
1355 if found_workspace:
1356 print('### WARNING: found [workspace] in Cargo.toml')
1357 else:
1358 with open(cargo_toml, 'a') as out_file:
Frederick Mayle71722b12022-07-27 00:13:52 +00001359 out_file.write('\n\n[workspace]\n')
Jeffrey Vander Stoep6529bca2022-07-06 12:34:37 +00001360 added_workspace = True
1361 if self.args.verbose:
1362 print('### INFO: added [workspace] to Cargo.toml')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001363 for c in self.cargo:
1364 features = ''
Chih-Hung Hsieh6c8d52f2020-03-30 18:28:52 -07001365 if c != 'clean':
1366 if self.args.features is not None:
1367 features = ' --no-default-features'
1368 if self.args.features:
1369 features += ' --features ' + self.args.features
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001370 cmd_v_flag = ' -vv ' if self.args.vv else ' -v '
1371 cmd = self.cargo_path + cmd_v_flag
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001372 cmd += c + features + cmd_tail_target + cmd_tail_redir
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001373 if self.args.rustflags and c != 'clean':
Joel Galenson903a0f82021-11-24 11:09:36 -08001374 cmd = 'RUSTFLAGS="' + self.args.rustflags + '" ' + cmd
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001375 self.run_cmd(cmd, cargo_out)
1376 if self.args.tests:
1377 cmd = self.cargo_path + ' test' + features + cmd_tail_target + ' -- --list' + cmd_tail_redir
1378 self.run_cmd(cmd, cargo_out)
Jeffrey Vander Stoep6529bca2022-07-06 12:34:37 +00001379 if added_workspace: # restore original Cargo.toml
1380 with open(cargo_toml, 'w') as out_file:
1381 out_file.writelines(cargo_toml_lines)
1382 if self.args.verbose:
1383 print('### INFO: restored original Cargo.toml')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001384 os.environ['PATH'] = saved_path
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001385 if not self.dry_run:
1386 if not had_cargo_lock: # restore to no Cargo.lock state
Andrew Walbrana32a0482022-01-06 14:03:50 +00001387 if os.path.exists(cargo_lock):
1388 os.remove(cargo_lock)
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001389 elif not self.args.use_cargo_lock: # restore saved Cargo.lock
1390 os.rename(cargo_lock_saved, cargo_lock)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001391 return self
1392
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001393 def run_cmd(self, cmd, cargo_out):
1394 if self.dry_run:
1395 print('Dry-run skip:', cmd)
1396 else:
1397 if self.args.verbose:
1398 print('Running:', cmd)
1399 with open(cargo_out, 'a') as out_file:
1400 out_file.write('### Running: ' + cmd + '\n')
1401 ret = os.system(cmd)
1402 if ret != 0:
1403 print('*** There was an error while running cargo. ' +
1404 'See the cargo.out file for details.')
1405
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001406 def dump_pkg_obj2cc(self):
1407 """Dump debug info of the pkg_obj2cc map."""
1408 if not self.args.debug:
1409 return
1410 self.init_bp_file('Android.bp')
1411 with open('Android.bp', 'a') as outf:
1412 sorted_pkgs = sorted(self.pkg_obj2cc.keys())
1413 for pkg in sorted_pkgs:
1414 if not self.pkg_obj2cc[pkg]:
1415 continue
1416 outf.write('\n// obj => src for %s\n' % pkg)
1417 obj2cc = self.pkg_obj2cc[pkg]
1418 for obj in sorted(obj2cc.keys()):
1419 outf.write('// ' + short_out_name(pkg, obj) + ' => ' +
1420 short_out_name(pkg, obj2cc[obj].src) + '\n')
1421
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001422 def apply_patch(self):
1423 """Apply local patch file if it is given."""
1424 if self.args.patch:
1425 if self.dry_run:
1426 print('Dry-run skip patch file:', self.args.patch)
1427 else:
1428 if not os.path.exists(self.args.patch):
1429 self.append_to_bp('ERROR cannot find patch file: ' + self.args.patch)
1430 return self
1431 if self.args.verbose:
1432 print('### INFO: applying local patch file:', self.args.patch)
Joel Galenson7e8247e2021-05-20 18:51:42 -07001433 subprocess.run(['patch', '-s', '--no-backup-if-mismatch', './Android.bp',
1434 self.args.patch], check=True)
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001435 return self
1436
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001437 def gen_bp(self):
1438 """Parse cargo.out and generate Android.bp files."""
1439 if self.dry_run:
1440 print('Dry-run skip: read', CARGO_OUT, 'write Android.bp')
1441 elif os.path.exists(CARGO_OUT):
1442 self.find_root_pkg()
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001443 if self.args.copy_out:
1444 self.copy_out_files()
1445 elif self.find_out_files() and self.has_used_out_dir():
1446 print('WARNING: ' + self.root_pkg + ' has cargo output files; ' +
1447 'please rerun with the --copy-out flag.')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001448 with open(CARGO_OUT, 'r') as cargo_out:
1449 self.parse(cargo_out, 'Android.bp')
1450 self.crates.sort(key=get_module_name)
1451 for obj in self.cc_objects:
1452 obj.dump()
1453 self.dump_pkg_obj2cc()
1454 for crate in self.crates:
1455 crate.dump()
1456 dumped_libs = set()
1457 for lib in self.ar_objects:
1458 if lib.pkg == self.root_pkg:
1459 lib_name = file_base_name(lib.lib)
1460 if lib_name not in dumped_libs:
1461 dumped_libs.add(lib_name)
1462 lib.dump()
Joel Galenson5664f2a2021-06-10 10:13:49 -07001463 if self.args.add_toplevel_block:
1464 with open(self.args.add_toplevel_block, 'r') as f:
1465 self.append_to_bp('\n' + f.read() + '\n')
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001466 if self.errors:
Joel Galenson3f42f802021-04-07 12:42:17 -07001467 self.append_to_bp('\n' + ERRORS_LINE + '\n' + self.errors)
Joel Galenson56b5f4b2021-11-30 10:02:57 -08001468 if self.test_errors:
1469 self.append_to_bp('\n// Errors when listing tests:\n' + self.test_errors)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001470 return self
1471
1472 def add_ar_object(self, obj):
1473 self.ar_objects.append(obj)
1474
1475 def add_cc_object(self, obj):
1476 self.cc_objects.append(obj)
1477
1478 def add_crate(self, crate):
1479 """Merge crate with someone in crates, or append to it. Return crates."""
1480 if crate.skip_crate():
1481 if self.args.debug: # include debug info of all crates
1482 self.crates.append(crate)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001483 else:
1484 for c in self.crates:
1485 if c.merge(crate, 'Android.bp'):
1486 return
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001487 # If not merged, decide module type and name now.
1488 crate.decide_module_type()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001489 self.crates.append(crate)
1490
1491 def find_warning_owners(self):
1492 """For each warning file, find its owner crate."""
1493 missing_owner = False
1494 for f in self.warning_files:
1495 cargo_dir = '' # find lowest crate, with longest path
1496 owner = None # owner crate of this warning
1497 for c in self.crates:
1498 if (f.startswith(c.cargo_dir + '/') and
1499 len(cargo_dir) < len(c.cargo_dir)):
1500 cargo_dir = c.cargo_dir
1501 owner = c
1502 if owner:
1503 owner.has_warning = True
1504 else:
1505 missing_owner = True
1506 if missing_owner and os.path.exists('Cargo.toml'):
1507 # owner is the root cargo, with empty cargo_dir
1508 for c in self.crates:
1509 if not c.cargo_dir:
1510 c.has_warning = True
1511
1512 def rustc_command(self, n, rustc_line, line, outf_name):
1513 """Process a rustc command line from cargo -vv output."""
1514 # cargo build -vv output can have multiple lines for a rustc command
1515 # due to '\n' in strings for environment variables.
1516 # strip removes leading spaces and '\n' at the end
1517 new_rustc = (rustc_line.strip() + line) if rustc_line else line
1518 # Use an heuristic to detect the completions of a multi-line command.
1519 # This might fail for some very rare case, but easy to fix manually.
1520 if not line.endswith('`\n') or (new_rustc.count('`') % 2) != 0:
1521 return new_rustc
1522 if RUSTC_VV_CMD_ARGS.match(new_rustc):
1523 args = RUSTC_VV_CMD_ARGS.match(new_rustc).group(1)
1524 self.add_crate(Crate(self, outf_name).parse(n, args))
1525 else:
1526 self.assert_empty_vv_line(new_rustc)
1527 return ''
1528
1529 def cc_ar_command(self, n, groups, outf_name):
1530 pkg = groups.group(1)
1531 line = groups.group(3)
1532 if groups.group(2) == 'cc':
1533 self.add_cc_object(CCObject(self, outf_name).parse(pkg, n, line))
1534 else:
1535 self.add_ar_object(ARObject(self, outf_name).parse(pkg, n, line))
1536
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001537 def append_to_bp(self, line):
1538 self.init_bp_file('Android.bp')
1539 with open('Android.bp', 'a') as outf:
1540 outf.write(line)
1541
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001542 def assert_empty_vv_line(self, line):
1543 if line: # report error if line is not empty
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001544 self.append_to_bp('ERROR -vv line: ' + line)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001545 return ''
1546
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001547 def add_empty_test(self, name):
Matthew Maurerae5da712022-09-12 12:55:47 -07001548 if name.startswith('unittests'):
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001549 self.empty_unittests = True
1550 else:
1551 self.empty_tests.add(name)
1552
1553 def should_ignore_test(self, src):
1554 # cargo test outputs the source file for integration tests but "unittests"
1555 # for unit tests. To figure out to which crate this corresponds, we check
1556 # if the current source file is the main source of a non-test crate, e.g.,
1557 # a library or a binary.
1558 return (src in self.args.test_blocklist or src in self.empty_tests
1559 or (self.empty_unittests
1560 and src in [c.main_src for c in self.crates if c.crate_types != ['test']]))
1561
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001562 def parse(self, inf, outf_name):
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001563 """Parse rustc, test, and warning messages in inf, return a list of Crates."""
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001564 n = 0 # line number
Joel Galenson7cfc6fe2021-10-14 13:09:32 -07001565 # We read the file in two passes, where the first simply checks for empty tests.
1566 # Otherwise we would add and merge tests before seeing they're empty.
1567 cur_test_name = None
1568 for line in inf:
1569 if CARGO_TEST_LIST_START_PAT.match(line):
1570 cur_test_name = CARGO_TEST_LIST_START_PAT.match(line).group(1)
1571 elif cur_test_name and CARGO_TEST_LIST_END_PAT.match(line):
1572 match = CARGO_TEST_LIST_END_PAT.match(line)
1573 if int(match.group(1)) + int(match.group(2)) == 0:
1574 self.add_empty_test(cur_test_name)
1575 cur_test_name = None
1576 inf.seek(0)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001577 prev_warning = False # true if the previous line was warning: ...
1578 rustc_line = '' # previous line(s) matching RUSTC_VV_PAT
Joel Galenson56b5f4b2021-11-30 10:02:57 -08001579 in_tests = False
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001580 for line in inf:
1581 n += 1
1582 if line.startswith('warning: '):
1583 prev_warning = True
1584 rustc_line = self.assert_empty_vv_line(rustc_line)
1585 continue
1586 new_rustc = ''
1587 if RUSTC_PAT.match(line):
1588 args_line = RUSTC_PAT.match(line).group(1)
1589 self.add_crate(Crate(self, outf_name).parse(n, args_line))
1590 self.assert_empty_vv_line(rustc_line)
1591 elif rustc_line or RUSTC_VV_PAT.match(line):
1592 new_rustc = self.rustc_command(n, rustc_line, line, outf_name)
1593 elif CC_AR_VV_PAT.match(line):
1594 self.cc_ar_command(n, CC_AR_VV_PAT.match(line), outf_name)
1595 elif prev_warning and WARNING_FILE_PAT.match(line):
1596 self.assert_empty_vv_line(rustc_line)
1597 fpath = WARNING_FILE_PAT.match(line).group(1)
1598 if fpath[0] != '/': # ignore absolute path
1599 self.warning_files.add(fpath)
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001600 elif line.startswith('error: ') or line.startswith('error[E'):
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001601 if not self.args.ignore_cargo_errors:
Joel Galenson56b5f4b2021-11-30 10:02:57 -08001602 if in_tests:
1603 self.test_errors += '// ' + line
1604 else:
1605 self.errors += line
1606 elif CARGO2ANDROID_RUNNING_PAT.match(line):
1607 in_tests = "cargo test" in line and "--list" in line
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001608 prev_warning = False
1609 rustc_line = new_rustc
1610 self.find_warning_owners()
1611
1612
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001613def get_parser():
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001614 """Parse main arguments."""
1615 parser = argparse.ArgumentParser('cargo2android')
1616 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001617 '--add_workspace',
1618 action='store_true',
1619 default=False,
1620 help=('append [workspace] to Cargo.toml before calling cargo,' +
1621 ' to treat current directory as root of package source;' +
1622 ' otherwise the relative source file path in generated' +
1623 ' .bp file will be from the parent directory.'))
1624 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001625 '--cargo',
1626 action='append',
1627 metavar='args_string',
1628 help=('extra cargo build -v args in a string, ' +
1629 'each --cargo flag calls cargo build -v once'))
1630 parser.add_argument(
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001631 '--cargo_bin',
1632 type=str,
1633 help='use cargo in the cargo_bin directory instead of the prebuilt one')
1634 parser.add_argument(
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001635 '--copy-out',
1636 action='store_true',
1637 default=False,
1638 help=('only for root directory, ' +
1639 'copy build.rs output to ./out/* and add a genrule to copy ' +
1640 './out/* to genrule output; for crates with code pattern: ' +
1641 'include!(concat!(env!("OUT_DIR"), "/<some_file>.rs"))'))
1642 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001643 '--debug',
1644 action='store_true',
1645 default=False,
1646 help='dump debug info into Android.bp')
1647 parser.add_argument(
1648 '--dependencies',
1649 action='store_true',
1650 default=False,
Joel Galenson833848c2021-08-17 10:50:42 -07001651 help='Deprecated. Has no effect.')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001652 parser.add_argument(
1653 '--device',
1654 action='store_true',
1655 default=False,
1656 help='run cargo also for a default device target')
1657 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001658 '--features',
1659 type=str,
1660 help=('pass features to cargo build, ' +
1661 'empty string means no default features'))
1662 parser.add_argument(
1663 '--global_defaults',
1664 type=str,
1665 help='add a defaults name to every module')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001666 parser.add_argument(
1667 '--host-first-multilib',
1668 action='store_true',
1669 default=False,
1670 help=('add a compile_multilib:"first" property ' +
1671 'to Android.bp host modules.'))
1672 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001673 '--ignore-cargo-errors',
1674 action='store_true',
1675 default=False,
1676 help='do not append cargo/rustc error messages to Android.bp')
1677 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001678 '--no-host',
1679 action='store_true',
1680 default=False,
1681 help='do not run cargo for the host; only for the device target')
1682 parser.add_argument(
Andrew Walbranca9f9f22022-01-10 16:02:20 +00001683 '--no-presubmit',
1684 action='store_true',
1685 default=False,
1686 help='set unit_test to false for test targets, to avoid host tests running in presubmit')
1687 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001688 '--no-subdir',
1689 action='store_true',
1690 default=False,
1691 help='do not output anything for sub-directories')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001692 parser.add_argument(
1693 '--onefile',
1694 action='store_true',
1695 default=False,
1696 help=('output all into one ./Android.bp, default will generate ' +
1697 'one Android.bp per Cargo.toml in subdirectories'))
1698 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001699 '--patch',
1700 type=str,
1701 help='apply the given patch file to generated ./Android.bp')
1702 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001703 '--run',
1704 action='store_true',
1705 default=False,
1706 help='run it, default is dry-run')
1707 parser.add_argument('--rustflags', type=str, help='passing flags to rustc')
1708 parser.add_argument(
1709 '--skipcargo',
1710 action='store_true',
1711 default=False,
1712 help='skip cargo command, parse cargo.out, and generate Android.bp')
1713 parser.add_argument(
1714 '--tests',
1715 action='store_true',
1716 default=False,
1717 help='run cargo build --tests after normal build')
1718 parser.add_argument(
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001719 '--use-cargo-lock',
1720 action='store_true',
1721 default=False,
1722 help=('run cargo build with existing Cargo.lock ' +
1723 '(used when some latest dependent crates failed)'))
1724 parser.add_argument(
Matthew Maurer062709c2021-08-17 11:27:36 -07001725 '--exported_c_header_dir',
1726 nargs='*',
1727 help='Directories with headers to export for C usage'
1728 )
1729 parser.add_argument(
Joel Galensond9c4de62021-04-23 10:26:40 -07001730 '--min-sdk-version',
1731 type=str,
1732 help='Minimum SDK version')
1733 parser.add_argument(
1734 '--apex-available',
1735 nargs='*',
1736 help='Mark the main library as apex_available with the given apexes.')
1737 parser.add_argument(
Matthew Maurerac677252021-08-13 15:52:52 -07001738 '--native-bridge-supported',
1739 action='store_true',
1740 default=False,
1741 help='Mark the main library as native_bridge_supported.')
1742 parser.add_argument(
1743 '--product-available',
1744 action='store_true',
1745 default=False,
1746 help='Mark the main library as product_available.')
1747 parser.add_argument(
1748 '--recovery-available',
1749 action='store_true',
1750 default=False,
1751 help='Mark the main library as recovery_available.')
1752 parser.add_argument(
Ivan Lozano91920862021-07-19 10:49:08 -04001753 '--vendor-available',
1754 action='store_true',
1755 default=False,
1756 help='Mark the main library as vendor_available.')
1757 parser.add_argument(
1758 '--vendor-ramdisk-available',
1759 action='store_true',
1760 default=False,
1761 help='Mark the main library as vendor_ramdisk_available.')
1762 parser.add_argument(
Matthew Maurerac677252021-08-13 15:52:52 -07001763 '--ramdisk-available',
1764 action='store_true',
1765 default=False,
1766 help='Mark the main library as ramdisk_available.')
1767 parser.add_argument(
Joel Galensoncb5f2f02021-06-08 14:47:55 -07001768 '--force-rlib',
1769 action='store_true',
1770 default=False,
1771 help='Make the main library an rlib.')
1772 parser.add_argument(
Joel Galenson12467e52021-07-12 14:33:28 -07001773 '--whole-static-libs',
1774 nargs='*',
1775 default=[],
1776 help='Make the given libraries (without lib prefixes) whole_static_libs.')
1777 parser.add_argument(
Ivan Lozano26aa1c32021-08-16 11:20:32 -04001778 '--no-pkg-vers',
1779 action='store_true',
1780 default=False,
1781 help='Do not attempt to determine the package version automatically.')
1782 parser.add_argument(
Joel Galensone4f53882021-07-19 11:14:55 -07001783 '--test-data',
1784 nargs='*',
1785 default=[],
1786 help=('Add the given file to the given test\'s data property. ' +
1787 'Usage: test-path=data-path'))
1788 parser.add_argument(
Joel Galenson97e414a2021-05-27 09:42:32 -07001789 '--dependency-blocklist',
1790 nargs='*',
1791 default=[],
Joel Galenson12467e52021-07-12 14:33:28 -07001792 help='Do not emit the given dependencies (without lib prefixes).')
Joel Galenson97e414a2021-05-27 09:42:32 -07001793 parser.add_argument(
Joel Galensoncb5f2f02021-06-08 14:47:55 -07001794 '--lib-blocklist',
1795 nargs='*',
1796 default=[],
Joel Galenson12467e52021-07-12 14:33:28 -07001797 help='Do not emit the given C libraries as dependencies (without lib prefixes).')
Joel Galensoncb5f2f02021-06-08 14:47:55 -07001798 parser.add_argument(
Joel Galensonf6b3c912021-06-03 16:00:54 -07001799 '--test-blocklist',
1800 nargs='*',
1801 default=[],
1802 help=('Do not emit the given tests. ' +
1803 'Pass the path to the test file to exclude.'))
1804 parser.add_argument(
Joel Galenson3d6d1e72021-06-07 15:00:24 -07001805 '--cfg-blocklist',
1806 nargs='*',
1807 default=[],
1808 help='Do not emit the given cfg.')
1809 parser.add_argument(
Joel Galenson5664f2a2021-06-10 10:13:49 -07001810 '--add-toplevel-block',
1811 type=str,
Joel Galenson493c26a2021-10-18 15:54:41 -07001812 help=('Add the contents of the given file to the top level of the Android.bp. ' +
1813 'The filename should start with cargo2android to work with the updater.'))
Joel Galenson5664f2a2021-06-10 10:13:49 -07001814 parser.add_argument(
1815 '--add-module-block',
1816 type=str,
Joel Galenson493c26a2021-10-18 15:54:41 -07001817 help=('Add the contents of the given file to the main module. '+
1818 'The filename should start with cargo2android to work with the updater.'))
Joel Galenson5664f2a2021-06-10 10:13:49 -07001819 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001820 '--verbose',
1821 action='store_true',
1822 default=False,
1823 help='echo executed commands')
1824 parser.add_argument(
1825 '--vv',
1826 action='store_true',
1827 default=False,
1828 help='run cargo with -vv instead of default -v')
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001829 parser.add_argument(
1830 '--dump-config-and-exit',
1831 type=str,
1832 help=('Dump command-line arguments (minus this flag) to a config file and exit. ' +
1833 'This is intended to help migrate from command line options to config files.'))
1834 parser.add_argument(
1835 '--config',
1836 type=str,
1837 help=('Load command-line options from the given config file. ' +
1838 'Options in this file will override those passed on the command line.'))
1839 return parser
1840
1841
1842def parse_args(parser):
1843 """Parses command-line options."""
1844 args = parser.parse_args()
1845 # Use the values specified in a config file if one was found.
1846 if args.config:
1847 with open(args.config, 'r') as f:
1848 config = json.load(f)
1849 args_dict = vars(args)
1850 for arg in config:
1851 args_dict[arg.replace('-', '_')] = config[arg]
1852 return args
1853
1854
1855def dump_config(parser, args):
1856 """Writes the non-default command-line options to the specified file."""
1857 args_dict = vars(args)
1858 # Filter out the arguments that have their default value.
Joel Galenson367360c2021-04-29 14:31:43 -07001859 # Also filter certain "temporary" arguments.
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001860 non_default_args = {}
1861 for arg in args_dict:
Joel Galenson9a82ad92021-08-17 17:52:04 -07001862 if (args_dict[arg] != parser.get_default(arg) and arg != 'dump_config_and_exit'
Joel Galenson769c3c32021-11-24 15:04:29 -08001863 and arg != 'config' and arg != 'cargo_bin'):
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001864 non_default_args[arg.replace('_', '-')] = args_dict[arg]
1865 # Write to the specified file.
1866 with open(args.dump_config_and_exit, 'w') as f:
1867 json.dump(non_default_args, f, indent=2, sort_keys=True)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001868
1869
1870def main():
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001871 parser = get_parser()
1872 args = parse_args(parser)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001873 if not args.run: # default is dry-run
1874 print(DRY_RUN_NOTE)
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001875 if args.dump_config_and_exit:
1876 dump_config(parser, args)
1877 else:
ThiƩbaud Weksteen198e93f2021-07-02 14:49:19 +02001878 Runner(args).run_cargo().gen_bp().apply_patch()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001879
1880
1881if __name__ == '__main__':
1882 main()