blob: 5aeada193e54fa5a6cf06ac297a91fd91e8822ac [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
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +010042 Note that when there are tests for this module or for its reverse
43 dependencies, these tests will be added to the TEST_MAPPING file.
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -070044
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -070045If there are rustc warning messages, this script will add
46a warning comment to the owner crate module in Android.bp.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080047"""
48
49from __future__ import print_function
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +010050from update_crate_tests import TestMapping
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080051
52import argparse
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -070053import glob
Joel Galenson0fbdafe2021-04-21 16:33:33 -070054import json
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080055import os
56import os.path
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -070057import platform
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080058import re
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -070059import shutil
Joel Galenson7e8247e2021-05-20 18:51:42 -070060import subprocess
Andrew Walbran80e90be2020-06-09 14:33:18 +010061import sys
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080062
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -070063# Some Rust packages include extra unwanted crates.
64# This set contains all such excluded crate names.
65EXCLUDED_CRATES = set(['protobuf_bin_gen_rust_do_not_use'])
66
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080067RENAME_MAP = {
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -070068 # This map includes all changes to the default rust module names
69 # to resolve name conflicts, avoid confusion, or work as plugin.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080070 'libbacktrace': 'libbacktrace_rust',
Andrew Walbrane51f1042020-08-11 16:42:48 +010071 'libbase': 'libbase_rust',
Luke Huanga1371af2021-06-29 18:04:40 +080072 'libbase64': 'libbase64_rust',
Victor Hsieh21bea792020-12-04 10:59:16 -080073 'libfuse': 'libfuse_rust',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080074 'libgcc': 'libgcc_rust',
75 'liblog': 'liblog_rust',
Chih-Hung Hsieh07119862020-07-24 15:34:06 -070076 'libminijail': 'libminijail_rust',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080077 'libsync': 'libsync_rust',
78 'libx86_64': 'libx86_64_rust',
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -070079 'protoc_gen_rust': 'protoc-gen-rust',
80}
81
82RENAME_STEM_MAP = {
83 # This map includes all changes to the default rust module stem names,
84 # which is used for output files when different from the module name.
85 'protoc_gen_rust': 'protoc-gen-rust',
86}
87
88RENAME_DEFAULTS_MAP = {
89 # This map includes all changes to the default prefix of rust_default
90 # module names, to avoid conflict with existing Android modules.
91 'libc': 'rust_libc',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080092}
93
94# Header added to all generated Android.bp files.
Joel Galenson56446742021-02-18 08:27:48 -080095ANDROID_BP_HEADER = (
96 '// This file is generated by cargo2android.py {args}.\n' +
97 '// Do not modify this file as changes will be overridden on upgrade.\n')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080098
99CARGO_OUT = 'cargo.out' # Name of file to keep cargo build -v output.
100
Joel Galenson3f42f802021-04-07 12:42:17 -0700101# This should be kept in sync with tools/external_updater/crates_updater.py.
102ERRORS_LINE = 'Errors in ' + CARGO_OUT + ':'
103
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800104TARGET_TMP = 'target.tmp' # Name of temporary output directory.
105
106# Message to be displayed when this script is called without the --run flag.
107DRY_RUN_NOTE = (
108 'Dry-run: This script uses ./' + TARGET_TMP + ' for output directory,\n' +
109 'runs cargo clean, runs cargo build -v, saves output to ./cargo.out,\n' +
110 'and writes to Android.bp in the current and subdirectories.\n\n' +
111 'To do do all of the above, use the --run flag.\n' +
112 'See --help for other flags, and more usage notes in this script.\n')
113
114# Cargo -v output of a call to rustc.
115RUSTC_PAT = re.compile('^ +Running `rustc (.*)`$')
116
117# Cargo -vv output of a call to rustc could be split into multiple lines.
118# Assume that the first line will contain some CARGO_* env definition.
119RUSTC_VV_PAT = re.compile('^ +Running `.*CARGO_.*=.*$')
120# The combined -vv output rustc command line pattern.
121RUSTC_VV_CMD_ARGS = re.compile('^ *Running `.*CARGO_.*=.* rustc (.*)`$')
122
123# Cargo -vv output of a "cc" or "ar" command; all in one line.
124CC_AR_VV_PAT = re.compile(r'^\[([^ ]*)[^\]]*\] running:? "(cc|ar)" (.*)$')
125# Some package, such as ring-0.13.5, has pattern '... running "cc"'.
126
127# Rustc output of file location path pattern for a warning message.
128WARNING_FILE_PAT = re.compile('^ *--> ([^:]*):[0-9]+')
129
130# Rust package name with suffix -d1.d2.d3.
131VERSION_SUFFIX_PAT = re.compile(r'^(.*)-[0-9]+\.[0-9]+\.[0-9]+$')
132
133
134def altered_name(name):
135 return RENAME_MAP[name] if (name in RENAME_MAP) else name
136
137
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700138def altered_stem(name):
139 return RENAME_STEM_MAP[name] if (name in RENAME_STEM_MAP) else name
140
141
142def altered_defaults(name):
143 return RENAME_DEFAULTS_MAP[name] if (name in RENAME_DEFAULTS_MAP) else name
144
145
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800146def is_build_crate_name(name):
147 # We added special prefix to build script crate names.
148 return name.startswith('build_script_')
149
150
151def is_dependent_file_path(path):
152 # Absolute or dependent '.../' paths are not main files of this crate.
153 return path.startswith('/') or path.startswith('.../')
154
155
156def get_module_name(crate): # to sort crates in a list
157 return crate.module_name
158
159
160def pkg2crate_name(s):
161 return s.replace('-', '_').replace('.', '_')
162
163
164def file_base_name(path):
165 return os.path.splitext(os.path.basename(path))[0]
166
167
168def test_base_name(path):
169 return pkg2crate_name(file_base_name(path))
170
171
172def unquote(s): # remove quotes around str
173 if s and len(s) > 1 and s[0] == '"' and s[-1] == '"':
174 return s[1:-1]
175 return s
176
177
178def remove_version_suffix(s): # remove -d1.d2.d3 suffix
179 if VERSION_SUFFIX_PAT.match(s):
180 return VERSION_SUFFIX_PAT.match(s).group(1)
181 return s
182
183
184def short_out_name(pkg, s): # replace /.../pkg-*/out/* with .../out/*
185 return re.sub('^/.*/' + pkg + '-[0-9a-f]*/out/', '.../out/', s)
186
187
188def escape_quotes(s): # replace '"' with '\\"'
189 return s.replace('"', '\\"')
190
191
192class Crate(object):
193 """Information of a Rust crate to collect/emit for an Android.bp module."""
194
195 def __init__(self, runner, outf_name):
196 # Remembered global runner and its members.
197 self.runner = runner
198 self.debug = runner.args.debug
199 self.cargo_dir = '' # directory of my Cargo.toml
200 self.outf_name = outf_name # path to Android.bp
201 self.outf = None # open file handle of outf_name during dump*
202 # Variants/results that could be merged from multiple rustc lines.
203 self.host_supported = False
204 self.device_supported = False
205 self.has_warning = False
206 # Android module properties derived from rustc parameters.
207 self.module_name = '' # unique in Android build system
208 self.module_type = '' # rust_{binary,library,test}[_host] etc.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700209 self.defaults = '' # rust_defaults used by rust_test* modules
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700210 self.default_srcs = False # use 'srcs' defined in self.defaults
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800211 self.root_pkg = '' # parent package name of a sub/test packge, from -L
212 self.srcs = list() # main_src or merged multiple source files
213 self.stem = '' # real base name of output file
214 # Kept parsed status
215 self.errors = '' # all errors found during parsing
216 self.line_num = 1 # runner told input source line number
217 self.line = '' # original rustc command line parameters
218 # Parameters collected from rustc command line.
219 self.crate_name = '' # follows --crate-name
220 self.main_src = '' # follows crate_name parameter, shortened
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700221 self.crate_types = list() # follows --crate-type
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800222 self.cfgs = list() # follows --cfg, without feature= prefix
223 self.features = list() # follows --cfg, name in 'feature="..."'
224 self.codegens = list() # follows -C, some ignored
225 self.externs = list() # follows --extern
226 self.core_externs = list() # first part of self.externs elements
227 self.static_libs = list() # e.g. -l static=host_cpuid
228 self.shared_libs = list() # e.g. -l dylib=wayland-client, -l z
229 self.cap_lints = '' # follows --cap-lints
230 self.emit_list = '' # e.g., --emit=dep-info,metadata,link
231 self.edition = '2015' # rustc default, e.g., --edition=2018
232 self.target = '' # follows --target
233
234 def write(self, s):
235 # convenient way to output one line at a time with EOL.
236 self.outf.write(s + '\n')
237
238 def same_flags(self, other):
239 # host_supported, device_supported, has_warning are not compared but merged
240 # target is not compared, to merge different target/host modules
241 # externs is not compared; only core_externs is compared
242 return (not self.errors and not other.errors and
243 self.edition == other.edition and
244 self.cap_lints == other.cap_lints and
245 self.emit_list == other.emit_list and
246 self.core_externs == other.core_externs and
247 self.codegens == other.codegens and
248 self.features == other.features and
249 self.static_libs == other.static_libs and
250 self.shared_libs == other.shared_libs and self.cfgs == other.cfgs)
251
252 def merge_host_device(self, other):
253 """Returns true if attributes are the same except host/device support."""
254 return (self.crate_name == other.crate_name and
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700255 self.crate_types == other.crate_types and
256 self.main_src == other.main_src and
257 # before merge, each test module has an unique module name and stem
258 (self.stem == other.stem or self.crate_types == ['test']) and
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800259 self.root_pkg == other.root_pkg and not self.skip_crate() and
260 self.same_flags(other))
261
262 def merge_test(self, other):
263 """Returns true if self and other are tests of same root_pkg."""
264 # Before merger, each test has its own crate_name.
265 # A merged test uses its source file base name as output file name,
266 # so a test is mergeable only if its base name equals to its crate name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700267 return (self.crate_types == other.crate_types and
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700268 self.crate_types == ['test'] and self.root_pkg == other.root_pkg and
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800269 not self.skip_crate() and
270 other.crate_name == test_base_name(other.main_src) and
271 (len(self.srcs) > 1 or
272 (self.crate_name == test_base_name(self.main_src)) and
273 self.host_supported == other.host_supported and
274 self.device_supported == other.device_supported) and
275 self.same_flags(other))
276
277 def merge(self, other, outf_name):
278 """Try to merge crate into self."""
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700279 # Cargo build --tests could recompile a library for tests.
280 # We need to merge such duplicated calls to rustc, with
281 # the algorithm in merge_host_device.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800282 should_merge_host_device = self.merge_host_device(other)
283 should_merge_test = False
284 if not should_merge_host_device:
285 should_merge_test = self.merge_test(other)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800286 if should_merge_host_device or should_merge_test:
287 self.runner.init_bp_file(outf_name)
288 with open(outf_name, 'a') as outf: # to write debug info
289 self.outf = outf
290 other.outf = outf
291 self.do_merge(other, should_merge_test)
292 return True
293 return False
294
295 def do_merge(self, other, should_merge_test):
296 """Merge attributes of other to self."""
297 if self.debug:
298 self.write('\n// Before merge definition (1):')
299 self.dump_debug_info()
300 self.write('\n// Before merge definition (2):')
301 other.dump_debug_info()
302 # Merge properties of other to self.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800303 self.has_warning = self.has_warning or other.has_warning
304 if not self.target: # okay to keep only the first target triple
305 self.target = other.target
306 # decide_module_type sets up default self.stem,
307 # which can be changed if self is a merged test module.
308 self.decide_module_type()
309 if should_merge_test:
310 self.srcs.append(other.main_src)
311 # use a short unique name as the merged module name.
312 prefix = self.root_pkg + '_tests'
313 self.module_name = self.runner.claim_module_name(prefix, self, 0)
314 self.stem = self.module_name
315 # This normalized root_pkg name although might be the same
316 # as other module's crate_name, it is not actually used for
317 # output file name. A merged test module always have multiple
318 # source files and each source file base name is used as
319 # its output file name.
320 self.crate_name = pkg2crate_name(self.root_pkg)
321 if self.debug:
322 self.write('\n// After merge definition (1):')
323 self.dump_debug_info()
324
325 def find_cargo_dir(self):
326 """Deepest directory with Cargo.toml and contains the main_src."""
327 if not is_dependent_file_path(self.main_src):
328 dir_name = os.path.dirname(self.main_src)
329 while dir_name:
330 if os.path.exists(dir_name + '/Cargo.toml'):
331 self.cargo_dir = dir_name
332 return
333 dir_name = os.path.dirname(dir_name)
334
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700335 def add_codegens_flag(self, flag):
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700336 """Ignore options not used in Android."""
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700337 # 'prefer-dynamic' does not work with common flag -C lto
Chih-Hung Hsieh63459ed2020-08-26 11:51:15 -0700338 # 'embed-bitcode' is ignored; we might control LTO with other .bp flag
Chih-Hung Hsieh6c13b722020-09-11 21:24:03 -0700339 # 'codegen-units' is set in Android global config or by default
340 if not (flag.startswith('codegen-units=') or
341 flag.startswith('debuginfo=') or
Chih-Hung Hsieh63459ed2020-08-26 11:51:15 -0700342 flag.startswith('embed-bitcode=') or
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700343 flag.startswith('extra-filename=') or
344 flag.startswith('incremental=') or
345 flag.startswith('metadata=') or
346 flag == 'prefer-dynamic'):
347 self.codegens.append(flag)
348
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800349 def parse(self, line_num, line):
350 """Find important rustc arguments to convert to Android.bp properties."""
351 self.line_num = line_num
352 self.line = line
353 args = line.split() # Loop through every argument of rustc.
354 i = 0
355 while i < len(args):
356 arg = args[i]
357 if arg == '--crate-name':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700358 i += 1
359 self.crate_name = args[i]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800360 elif arg == '--crate-type':
361 i += 1
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700362 # cargo calls rustc with multiple --crate-type flags.
363 # rustc can accept:
364 # --crate-type [bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]
365 self.crate_types.append(args[i])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800366 elif arg == '--test':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700367 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800368 elif arg == '--target':
369 i += 1
370 self.target = args[i]
371 elif arg == '--cfg':
372 i += 1
373 if args[i].startswith('\'feature='):
374 self.features.append(unquote(args[i].replace('\'feature=', '')[:-1]))
375 else:
376 self.cfgs.append(args[i])
377 elif arg == '--extern':
378 i += 1
379 extern_names = re.sub('=/[^ ]*/deps/', ' = ', args[i])
380 self.externs.append(extern_names)
381 self.core_externs.append(re.sub(' = .*', '', extern_names))
382 elif arg == '-C': # codegen options
383 i += 1
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700384 self.add_codegens_flag(args[i])
385 elif arg.startswith('-C'):
386 # cargo has been passing "-C <xyz>" flag to rustc,
387 # but newer cargo could pass '-Cembed-bitcode=no' to rustc.
388 self.add_codegens_flag(arg[2:])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800389 elif arg == '--cap-lints':
390 i += 1
391 self.cap_lints = args[i]
392 elif arg == '-L':
393 i += 1
394 if args[i].startswith('dependency=') and args[i].endswith('/deps'):
395 if '/' + TARGET_TMP + '/' in args[i]:
396 self.root_pkg = re.sub(
397 '^.*/', '', re.sub('/' + TARGET_TMP + '/.*/deps$', '', args[i]))
398 else:
399 self.root_pkg = re.sub('^.*/', '',
400 re.sub('/[^/]+/[^/]+/deps$', '', args[i]))
401 self.root_pkg = remove_version_suffix(self.root_pkg)
402 elif arg == '-l':
403 i += 1
404 if args[i].startswith('static='):
405 self.static_libs.append(re.sub('static=', '', args[i]))
406 elif args[i].startswith('dylib='):
407 self.shared_libs.append(re.sub('dylib=', '', args[i]))
408 else:
409 self.shared_libs.append(args[i])
410 elif arg == '--out-dir' or arg == '--color': # ignored
411 i += 1
412 elif arg.startswith('--error-format=') or arg.startswith('--json='):
413 _ = arg # ignored
414 elif arg.startswith('--emit='):
415 self.emit_list = arg.replace('--emit=', '')
416 elif arg.startswith('--edition='):
417 self.edition = arg.replace('--edition=', '')
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700418 elif not arg.startswith('-'):
419 # shorten imported crate main source paths like $HOME/.cargo/
420 # registry/src/github.com-1ecc6299db9ec823/memchr-2.3.3/src/lib.rs
421 self.main_src = re.sub(r'^/[^ ]*/registry/src/', '.../', args[i])
422 self.main_src = re.sub(r'^\.\.\./github.com-[0-9a-f]*/', '.../',
423 self.main_src)
424 self.find_cargo_dir()
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700425 if self.cargo_dir: # for a subdirectory
426 if self.runner.args.no_subdir: # all .bp content to /dev/null
427 self.outf_name = '/dev/null'
428 elif not self.runner.args.onefile:
429 # Write to Android.bp in the subdirectory with Cargo.toml.
430 self.outf_name = self.cargo_dir + '/Android.bp'
431 self.main_src = self.main_src[len(self.cargo_dir) + 1:]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800432 else:
433 self.errors += 'ERROR: unknown ' + arg + '\n'
434 i += 1
435 if not self.crate_name:
436 self.errors += 'ERROR: missing --crate-name\n'
437 if not self.main_src:
438 self.errors += 'ERROR: missing main source file\n'
439 else:
440 self.srcs.append(self.main_src)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700441 if not self.crate_types:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800442 # Treat "--cfg test" as "--test"
443 if 'test' in self.cfgs:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700444 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800445 else:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700446 self.errors += 'ERROR: missing --crate-type or --test\n'
447 elif len(self.crate_types) > 1:
448 if 'test' in self.crate_types:
449 self.errors += 'ERROR: cannot handle both --crate-type and --test\n'
450 if 'lib' in self.crate_types and 'rlib' in self.crate_types:
451 self.errors += 'ERROR: cannot generate both lib and rlib crate types\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800452 if not self.root_pkg:
453 self.root_pkg = self.crate_name
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700454 self.device_supported = self.runner.args.device
455 self.host_supported = not self.runner.args.no_host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800456 self.cfgs = sorted(set(self.cfgs))
457 self.features = sorted(set(self.features))
458 self.codegens = sorted(set(self.codegens))
459 self.externs = sorted(set(self.externs))
460 self.core_externs = sorted(set(self.core_externs))
461 self.static_libs = sorted(set(self.static_libs))
462 self.shared_libs = sorted(set(self.shared_libs))
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700463 self.crate_types = sorted(set(self.crate_types))
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800464 self.decide_module_type()
465 self.module_name = altered_name(self.stem)
466 return self
467
468 def dump_line(self):
469 self.write('\n// Line ' + str(self.line_num) + ' ' + self.line)
470
471 def feature_list(self):
472 """Return a string of main_src + "feature_list"."""
473 pkg = self.main_src
474 if pkg.startswith('.../'): # keep only the main package name
475 pkg = re.sub('/.*', '', pkg[4:])
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700476 elif pkg.startswith('/'): # use relative path for a local package
477 pkg = os.path.relpath(pkg)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800478 if not self.features:
479 return pkg
480 return pkg + ' "' + ','.join(self.features) + '"'
481
482 def dump_skip_crate(self, kind):
483 if self.debug:
484 self.write('\n// IGNORED: ' + kind + ' ' + self.main_src)
485 return self
486
487 def skip_crate(self):
488 """Return crate_name or a message if this crate should be skipped."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700489 if (is_build_crate_name(self.crate_name) or
490 self.crate_name in EXCLUDED_CRATES):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800491 return self.crate_name
492 if is_dependent_file_path(self.main_src):
493 return 'dependent crate'
494 return ''
495
496 def dump(self):
497 """Dump all error/debug/module code to the output .bp file."""
498 self.runner.init_bp_file(self.outf_name)
499 with open(self.outf_name, 'a') as outf:
500 self.outf = outf
501 if self.errors:
502 self.dump_line()
503 self.write(self.errors)
504 elif self.skip_crate():
505 self.dump_skip_crate(self.skip_crate())
506 else:
507 if self.debug:
508 self.dump_debug_info()
509 self.dump_android_module()
510
511 def dump_debug_info(self):
512 """Dump parsed data, when cargo2android is called with --debug."""
513
514 def dump(name, value):
515 self.write('//%12s = %s' % (name, value))
516
517 def opt_dump(name, value):
518 if value:
519 dump(name, value)
520
521 def dump_list(fmt, values):
522 for v in values:
523 self.write(fmt % v)
524
525 self.dump_line()
526 dump('module_name', self.module_name)
527 dump('crate_name', self.crate_name)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700528 dump('crate_types', self.crate_types)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800529 dump('main_src', self.main_src)
530 dump('has_warning', self.has_warning)
531 dump('for_host', self.host_supported)
532 dump('for_device', self.device_supported)
533 dump('module_type', self.module_type)
534 opt_dump('target', self.target)
535 opt_dump('edition', self.edition)
536 opt_dump('emit_list', self.emit_list)
537 opt_dump('cap_lints', self.cap_lints)
538 dump_list('// cfg = %s', self.cfgs)
539 dump_list('// cfg = \'feature "%s"\'', self.features)
540 # TODO(chh): escape quotes in self.features, but not in other dump_list
541 dump_list('// codegen = %s', self.codegens)
542 dump_list('// externs = %s', self.externs)
543 dump_list('// -l static = %s', self.static_libs)
544 dump_list('// -l (dylib) = %s', self.shared_libs)
545
546 def dump_android_module(self):
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700547 """Dump one or more Android module definition, depending on crate_types."""
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700548 if len(self.crate_types) == 1:
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700549 self.dump_single_type_android_module()
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700550 return
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700551 if 'test' in self.crate_types:
552 self.write('\nERROR: multiple crate types cannot include test type')
553 return
554 # Dump one Android module per crate_type.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700555 for crate_type in self.crate_types:
556 self.decide_one_module_type(crate_type)
557 self.dump_one_android_module(crate_type)
558
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700559 def build_default_name(self):
560 """Return a short and readable name for the rust_defaults module."""
561 # Choices: (1) root_pkg + '_defaults',
562 # (2) root_pkg + '_defaults_' + crate_name
563 # (3) root_pkg + '_defaults_' + main_src_basename_path
564 # (4) root_pkg + '_defaults_' + a_positive_sequence_number
565 name1 = altered_defaults(self.root_pkg) + '_defaults'
566 if self.runner.try_claim_module_name(name1, self):
567 return name1
568 name2 = name1 + '_' + self.crate_name
569 if self.runner.try_claim_module_name(name2, self):
570 return name2
571 name3 = name1 + '_' + self.main_src_basename_path()
572 if self.runner.try_claim_module_name(name3, self):
573 return name3
574 return self.runner.claim_module_name(name1, self, 0)
575
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700576 def dump_srcs_list(self):
577 """Dump the srcs list, for defaults or regular modules."""
578 if len(self.srcs) > 1:
579 srcs = sorted(set(self.srcs)) # make a copy and dedup
580 else:
581 srcs = [self.main_src]
582 copy_out = self.runner.copy_out_module_name()
583 if copy_out:
584 srcs.append(':' + copy_out)
585 self.dump_android_property_list('srcs', '"%s"', srcs)
586
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700587 def dump_defaults_module(self):
588 """Dump a rust_defaults module to be shared by other modules."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700589 name = self.build_default_name()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700590 self.defaults = name
591 self.write('\nrust_defaults {')
592 self.write(' name: "' + name + '",')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700593 if self.runner.args.global_defaults:
594 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700595 self.write(' crate_name: "' + self.crate_name + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700596 if len(self.srcs) == 1: # only one source file; share it in defaults
597 self.default_srcs = True
598 if self.has_warning and not self.cap_lints:
599 self.write(' // has rustc warnings')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700600 self.dump_srcs_list()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700601 if 'test' in self.crate_types:
602 self.write(' test_suites: ["general-tests"],')
603 self.write(' auto_gen_config: true,')
604 self.dump_edition_flags_libs()
605 self.write('}')
606
607 def dump_single_type_android_module(self):
608 """Dump one simple Android module, which has only one crate_type."""
609 crate_type = self.crate_types[0]
610 if crate_type != 'test':
611 # do not change self.stem or self.module_name
612 self.dump_one_android_module(crate_type)
613 return
614 # Dump one test module per source file, and separate host and device tests.
615 # crate_type == 'test'
Joel Galensonf6b3c912021-06-03 16:00:54 -0700616 self.srcs = [src for src in self.srcs if not src in self.runner.args.test_blocklist]
617 if ((self.host_supported and self.device_supported and len(self.srcs) > 0) or
618 len(self.srcs) > 1):
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700619 self.srcs = sorted(set(self.srcs))
620 self.dump_defaults_module()
621 saved_srcs = self.srcs
622 for src in saved_srcs:
623 self.srcs = [src]
624 saved_device_supported = self.device_supported
625 saved_host_supported = self.host_supported
626 saved_main_src = self.main_src
627 self.main_src = src
628 if saved_host_supported:
629 self.device_supported = False
630 self.host_supported = True
631 self.module_name = self.test_module_name()
632 self.decide_one_module_type(crate_type)
633 self.dump_one_android_module(crate_type)
634 if saved_device_supported:
635 self.device_supported = True
636 self.host_supported = False
637 self.module_name = self.test_module_name()
638 self.decide_one_module_type(crate_type)
639 self.dump_one_android_module(crate_type)
640 self.host_supported = saved_host_supported
641 self.device_supported = saved_device_supported
642 self.main_src = saved_main_src
643 self.srcs = saved_srcs
644
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700645 def dump_one_android_module(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800646 """Dump one Android module definition."""
647 if not self.module_type:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700648 self.write('\nERROR: unknown crate_type ' + crate_type)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800649 return
650 self.write('\n' + self.module_type + ' {')
651 self.dump_android_core_properties()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700652 if not self.defaults:
653 self.dump_edition_flags_libs()
654 if self.runner.args.host_first_multilib and self.host_supported and crate_type != 'test':
655 self.write(' compile_multilib: "first",')
Joel Galensond9c4de62021-04-23 10:26:40 -0700656 if self.runner.args.apex_available and crate_type == 'lib':
657 self.write(' apex_available: [')
658 for apex in self.runner.args.apex_available:
659 self.write(' "%s",' % apex)
660 self.write(' ],')
661 if self.runner.args.min_sdk_version and crate_type == 'lib':
662 self.write(' min_sdk_version: "%s",' % self.runner.args.min_sdk_version)
Joel Galenson5664f2a2021-06-10 10:13:49 -0700663 if self.runner.args.add_module_block:
664 with open(self.runner.args.add_module_block, 'r') as f:
665 self.write(' %s,' % f.read().replace('\n', '\n '))
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700666 self.write('}')
667
668 def dump_android_flags(self):
669 """Dump Android module flags property."""
Thiébaud Weksteena5a728b2021-04-08 14:23:49 +0200670 if not self.codegens and not self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700671 return
672 self.write(' flags: [')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800673 if self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700674 self.write(' "--cap-lints ' + self.cap_lints + '",')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700675 codegens_fmt = '"-C %s"'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700676 self.dump_android_property_list_items(codegens_fmt, self.codegens)
677 self.write(' ],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700678
679 def dump_edition_flags_libs(self):
680 if self.edition:
681 self.write(' edition: "' + self.edition + '",')
682 self.dump_android_property_list('features', '"%s"', self.features)
Joel Galenson3d6d1e72021-06-07 15:00:24 -0700683 cfgs = [cfg for cfg in self.cfgs if not cfg in self.runner.args.cfg_blocklist]
684 self.dump_android_property_list('cfgs', '"%s"', cfgs)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700685 self.dump_android_flags()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800686 if self.externs:
687 self.dump_android_externs()
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700688 static_libs = [lib for lib in self.static_libs if not lib in self.runner.args.lib_blocklist]
689 self.dump_android_property_list('static_libs', '"lib%s"', static_libs)
690 shared_libs = [lib for lib in self.shared_libs if not lib in self.runner.args.lib_blocklist]
691 self.dump_android_property_list('shared_libs', '"lib%s"', shared_libs)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800692
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700693 def main_src_basename_path(self):
694 return re.sub('/', '_', re.sub('.rs$', '', self.main_src))
695
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800696 def test_module_name(self):
697 """Return a unique name for a test module."""
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700698 # root_pkg+(_host|_device) + '_test_'+source_file_name
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700699 suffix = self.main_src_basename_path()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700700 host_device = '_host'
701 if self.device_supported:
702 host_device = '_device'
703 return self.root_pkg + host_device + '_test_' + suffix
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800704
705 def decide_module_type(self):
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700706 # Use the first crate type for the default/first module.
707 crate_type = self.crate_types[0] if self.crate_types else ''
708 self.decide_one_module_type(crate_type)
709
710 def decide_one_module_type(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800711 """Decide which Android module type to use."""
712 host = '' if self.device_supported else '_host'
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700713 rlib = '_rlib' if self.runner.args.force_rlib else ''
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700714 if crate_type == 'bin': # rust_binary[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800715 self.module_type = 'rust_binary' + host
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700716 # In rare cases like protobuf-codegen, the output binary name must
717 # be renamed to use as a plugin for protoc.
718 self.stem = altered_stem(self.crate_name)
719 self.module_name = altered_name(self.crate_name)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700720 elif crate_type == 'lib': # rust_library[_host]
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700721 # TODO(chh): should this be rust_library[_host]?
722 # Assuming that Cargo.toml do not use both 'lib' and 'rlib',
723 # because we map them both to rlib.
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700724 self.module_type = 'rust_library' + rlib + host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800725 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700726 self.module_name = altered_name(self.stem)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700727 elif crate_type == 'rlib': # rust_library[_host]
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700728 self.module_type = 'rust_library' + rlib + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700729 self.stem = 'lib' + self.crate_name
730 self.module_name = altered_name(self.stem)
731 elif crate_type == 'dylib': # rust_library[_host]_dylib
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800732 self.module_type = 'rust_library' + host + '_dylib'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700733 self.stem = 'lib' + self.crate_name
734 self.module_name = altered_name(self.stem) + '_dylib'
735 elif crate_type == 'cdylib': # rust_library[_host]_shared
Ivan Lozano0c057ad2020-12-15 10:41:26 -0500736 self.module_type = 'rust_ffi' + host + '_shared'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700737 self.stem = 'lib' + self.crate_name
738 self.module_name = altered_name(self.stem) + '_shared'
739 elif crate_type == 'staticlib': # rust_library[_host]_static
Ivan Lozano0c057ad2020-12-15 10:41:26 -0500740 self.module_type = 'rust_ffi' + host + '_static'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700741 self.stem = 'lib' + self.crate_name
742 self.module_name = altered_name(self.stem) + '_static'
743 elif crate_type == 'test': # rust_test[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800744 self.module_type = 'rust_test' + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700745 # Before do_merge, stem name is based on the --crate-name parameter.
746 # and test module name is based on stem.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800747 self.stem = self.test_module_name()
748 # self.stem will be changed after merging with other tests.
749 # self.stem is NOT used for final test binary name.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700750 # rust_test uses each source file base name as part of output file name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700751 # In do_merge, this function is called again, with a module_name.
752 # We make sure that the module name is unique in each package.
753 if self.module_name:
754 # Cargo uses "-C extra-filename=..." and "-C metadata=..." to add
755 # different suffixes and distinguish multiple tests of the same
756 # crate name. We ignore -C and use claim_module_name to get
757 # unique sequential suffix.
758 self.module_name = self.runner.claim_module_name(
759 self.module_name, self, 0)
760 # Now the module name is unique, stem should also match and unique.
761 self.stem = self.module_name
762 elif crate_type == 'proc-macro': # rust_proc_macro
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800763 self.module_type = 'rust_proc_macro'
764 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700765 self.module_name = altered_name(self.stem)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800766 else: # unknown module type, rust_prebuilt_dylib? rust_library[_host]?
767 self.module_type = ''
768 self.stem = ''
769
770 def dump_android_property_list_items(self, fmt, values):
771 for v in values:
772 # fmt has quotes, so we need escape_quotes(v)
773 self.write(' ' + (fmt % escape_quotes(v)) + ',')
774
775 def dump_android_property_list(self, name, fmt, values):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700776 if not values:
777 return
778 if len(values) > 1:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800779 self.write(' ' + name + ': [')
780 self.dump_android_property_list_items(fmt, values)
781 self.write(' ],')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700782 else:
783 self.write(' ' + name + ': [' +
784 (fmt % escape_quotes(values[0])) + '],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800785
786 def dump_android_core_properties(self):
787 """Dump the module header, name, stem, etc."""
788 self.write(' name: "' + self.module_name + '",')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700789 # see properties shared by dump_defaults_module
790 if self.defaults:
791 self.write(' defaults: ["' + self.defaults + '"],')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700792 elif self.runner.args.global_defaults:
793 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800794 if self.stem != self.module_name:
795 self.write(' stem: "' + self.stem + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700796 if self.has_warning and not self.cap_lints and not self.default_srcs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700797 self.write(' // has rustc warnings')
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700798 if self.host_supported and self.device_supported and self.module_type != 'rust_proc_macro':
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800799 self.write(' host_supported: true,')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700800 if not self.defaults:
801 self.write(' crate_name: "' + self.crate_name + '",')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700802 if not self.default_srcs:
803 self.dump_srcs_list()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700804 if 'test' in self.crate_types and not self.defaults:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800805 # self.root_pkg can have multiple test modules, with different *_tests[n]
806 # names, but their executables can all be installed under the same _tests
807 # directory. When built from Cargo.toml, all tests should have different
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700808 # file or crate names. So we used (root_pkg + '_tests') name as the
809 # relative_install_path.
810 # However, some package like 'slab' can have non-mergeable tests that
811 # must be separated by different module names. So, here we no longer
812 # emit relative_install_path.
813 # self.write(' relative_install_path: "' + self.root_pkg + '_tests",')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800814 self.write(' test_suites: ["general-tests"],')
815 self.write(' auto_gen_config: true,')
Joel Galensone261a152021-01-12 11:31:53 -0800816 if 'test' in self.crate_types and self.host_supported:
817 self.write(' test_options: {')
818 self.write(' unit_test: true,')
819 self.write(' },')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800820
821 def dump_android_externs(self):
822 """Dump the dependent rlibs and dylibs property."""
823 so_libs = list()
824 rust_libs = ''
825 deps_libname = re.compile('^.* = lib(.*)-[0-9a-f]*.(rlib|so|rmeta)$')
826 for lib in self.externs:
827 # normal value of lib: "libc = liblibc-*.rlib"
828 # strange case in rand crate: "getrandom_package = libgetrandom-*.rlib"
829 # we should use "libgetrandom", not "lib" + "getrandom_package"
830 groups = deps_libname.match(lib)
831 if groups is not None:
832 lib_name = groups.group(1)
833 else:
834 lib_name = re.sub(' .*$', '', lib)
Joel Galenson97e414a2021-05-27 09:42:32 -0700835 if lib_name in self.runner.args.dependency_blocklist:
836 continue
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800837 if lib.endswith('.rlib') or lib.endswith('.rmeta'):
838 # On MacOS .rmeta is used when Linux uses .rlib or .rmeta.
839 rust_libs += ' "' + altered_name('lib' + lib_name) + '",\n'
840 elif lib.endswith('.so'):
841 so_libs.append(lib_name)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700842 elif lib != 'proc_macro': # --extern proc_macro is special and ignored
843 rust_libs += ' // ERROR: unknown type of lib ' + lib + '\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800844 if rust_libs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700845 self.write(' rustlibs: [\n' + rust_libs + ' ],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800846 # Are all dependent .so files proc_macros?
847 # TODO(chh): Separate proc_macros and dylib.
848 self.dump_android_property_list('proc_macros', '"lib%s"', so_libs)
849
850
851class ARObject(object):
852 """Information of an "ar" link command."""
853
854 def __init__(self, runner, outf_name):
855 # Remembered global runner and its members.
856 self.runner = runner
857 self.pkg = ''
858 self.outf_name = outf_name # path to Android.bp
859 # "ar" arguments
860 self.line_num = 1
861 self.line = ''
862 self.flags = '' # e.g. "crs"
863 self.lib = '' # e.g. "/.../out/lib*.a"
864 self.objs = list() # e.g. "/.../out/.../*.o"
865
866 def parse(self, pkg, line_num, args_line):
867 """Collect ar obj/lib file names."""
868 self.pkg = pkg
869 self.line_num = line_num
870 self.line = args_line
871 args = args_line.split()
872 num_args = len(args)
873 if num_args < 3:
874 print('ERROR: "ar" command has too few arguments', args_line)
875 else:
876 self.flags = unquote(args[0])
877 self.lib = unquote(args[1])
878 self.objs = sorted(set(map(unquote, args[2:])))
879 return self
880
881 def write(self, s):
882 self.outf.write(s + '\n')
883
884 def dump_debug_info(self):
885 self.write('\n// Line ' + str(self.line_num) + ' "ar" ' + self.line)
886 self.write('// ar_object for %12s' % self.pkg)
887 self.write('// flags = %s' % self.flags)
888 self.write('// lib = %s' % short_out_name(self.pkg, self.lib))
889 for o in self.objs:
890 self.write('// obj = %s' % short_out_name(self.pkg, o))
891
892 def dump_android_lib(self):
893 """Write cc_library_static into Android.bp."""
894 self.write('\ncc_library_static {')
895 self.write(' name: "' + file_base_name(self.lib) + '",')
896 self.write(' host_supported: true,')
897 if self.flags != 'crs':
898 self.write(' // ar flags = %s' % self.flags)
899 if self.pkg not in self.runner.pkg_obj2cc:
900 self.write(' ERROR: cannot find source files.\n}')
901 return
902 self.write(' srcs: [')
903 obj2cc = self.runner.pkg_obj2cc[self.pkg]
904 # Note: wflags are ignored.
905 dflags = list()
906 fflags = list()
907 for obj in self.objs:
908 self.write(' "' + short_out_name(self.pkg, obj2cc[obj].src) + '",')
909 # TODO(chh): union of dflags and flags of all obj
910 # Now, just a temporary hack that uses the last obj's flags
911 dflags = obj2cc[obj].dflags
912 fflags = obj2cc[obj].fflags
913 self.write(' ],')
914 self.write(' cflags: [')
915 self.write(' "-O3",') # TODO(chh): is this default correct?
916 self.write(' "-Wno-error",')
917 for x in fflags:
918 self.write(' "-f' + x + '",')
919 for x in dflags:
920 self.write(' "-D' + x + '",')
921 self.write(' ],')
922 self.write('}')
923
924 def dump(self):
925 """Dump error/debug/module info to the output .bp file."""
926 self.runner.init_bp_file(self.outf_name)
927 with open(self.outf_name, 'a') as outf:
928 self.outf = outf
929 if self.runner.args.debug:
930 self.dump_debug_info()
931 self.dump_android_lib()
932
933
934class CCObject(object):
935 """Information of a "cc" compilation command."""
936
937 def __init__(self, runner, outf_name):
938 # Remembered global runner and its members.
939 self.runner = runner
940 self.pkg = ''
941 self.outf_name = outf_name # path to Android.bp
942 # "cc" arguments
943 self.line_num = 1
944 self.line = ''
945 self.src = ''
946 self.obj = ''
947 self.dflags = list() # -D flags
948 self.fflags = list() # -f flags
949 self.iflags = list() # -I flags
950 self.wflags = list() # -W flags
951 self.other_args = list()
952
953 def parse(self, pkg, line_num, args_line):
954 """Collect cc compilation flags and src/out file names."""
955 self.pkg = pkg
956 self.line_num = line_num
957 self.line = args_line
958 args = args_line.split()
959 i = 0
960 while i < len(args):
961 arg = args[i]
962 if arg == '"-c"':
963 i += 1
964 if args[i].startswith('"-o'):
965 # ring-0.13.5 dumps: ... "-c" "-o/.../*.o" ".../*.c"
966 self.obj = unquote(args[i])[2:]
967 i += 1
968 self.src = unquote(args[i])
969 else:
970 self.src = unquote(args[i])
971 elif arg == '"-o"':
972 i += 1
973 self.obj = unquote(args[i])
974 elif arg == '"-I"':
975 i += 1
976 self.iflags.append(unquote(args[i]))
977 elif arg.startswith('"-D'):
978 self.dflags.append(unquote(args[i])[2:])
979 elif arg.startswith('"-f'):
980 self.fflags.append(unquote(args[i])[2:])
981 elif arg.startswith('"-W'):
982 self.wflags.append(unquote(args[i])[2:])
983 elif not (arg.startswith('"-O') or arg == '"-m64"' or arg == '"-g"' or
984 arg == '"-g3"'):
985 # ignore -O -m64 -g
986 self.other_args.append(unquote(args[i]))
987 i += 1
988 self.dflags = sorted(set(self.dflags))
989 self.fflags = sorted(set(self.fflags))
990 # self.wflags is not sorted because some are order sensitive
991 # and we ignore them anyway.
992 if self.pkg not in self.runner.pkg_obj2cc:
993 self.runner.pkg_obj2cc[self.pkg] = {}
994 self.runner.pkg_obj2cc[self.pkg][self.obj] = self
995 return self
996
997 def write(self, s):
998 self.outf.write(s + '\n')
999
1000 def dump_debug_flags(self, name, flags):
1001 self.write('// ' + name + ':')
1002 for f in flags:
1003 self.write('// %s' % f)
1004
1005 def dump(self):
1006 """Dump only error/debug info to the output .bp file."""
1007 if not self.runner.args.debug:
1008 return
1009 self.runner.init_bp_file(self.outf_name)
1010 with open(self.outf_name, 'a') as outf:
1011 self.outf = outf
1012 self.write('\n// Line ' + str(self.line_num) + ' "cc" ' + self.line)
1013 self.write('// cc_object for %12s' % self.pkg)
1014 self.write('// src = %s' % short_out_name(self.pkg, self.src))
1015 self.write('// obj = %s' % short_out_name(self.pkg, self.obj))
1016 self.dump_debug_flags('-I flags', self.iflags)
1017 self.dump_debug_flags('-D flags', self.dflags)
1018 self.dump_debug_flags('-f flags', self.fflags)
1019 self.dump_debug_flags('-W flags', self.wflags)
1020 if self.other_args:
1021 self.dump_debug_flags('other args', self.other_args)
1022
1023
1024class Runner(object):
1025 """Main class to parse cargo -v output and print Android module definitions."""
1026
1027 def __init__(self, args):
1028 self.bp_files = set() # Remember all output Android.bp files.
1029 self.root_pkg = '' # name of package in ./Cargo.toml
1030 # Saved flags, modes, and data.
1031 self.args = args
1032 self.dry_run = not args.run
1033 self.skip_cargo = args.skipcargo
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001034 self.cargo_path = './cargo' # path to cargo, will be set later
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001035 self.checked_out_files = False # to check only once
1036 self.build_out_files = [] # output files generated by build.rs
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001037 # All cc/ar objects, crates, dependencies, and warning files
1038 self.cc_objects = list()
1039 self.pkg_obj2cc = {}
1040 # pkg_obj2cc[cc_object[i].pkg][cc_objects[i].obj] = cc_objects[i]
1041 self.ar_objects = list()
1042 self.crates = list()
1043 self.dependencies = list() # dependent and build script crates
1044 self.warning_files = set()
1045 # Keep a unique mapping from (module name) to crate
1046 self.name_owners = {}
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001047 # Save and dump all errors from cargo to Android.bp.
1048 self.errors = ''
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001049 self.setup_cargo_path()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001050 # Default action is cargo clean, followed by build or user given actions.
1051 if args.cargo:
1052 self.cargo = ['clean'] + args.cargo
1053 else:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001054 default_target = '--target x86_64-unknown-linux-gnu'
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -07001055 # Use the same target for both host and default device builds.
1056 # Same target is used as default in host x86_64 Android compilation.
1057 # Note: b/169872957, prebuilt cargo failed to build vsock
1058 # on x86_64-unknown-linux-musl systems.
1059 self.cargo = ['clean', 'build ' + default_target]
1060 if args.tests:
1061 self.cargo.append('build --tests ' + default_target)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001062
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001063 def setup_cargo_path(self):
1064 """Find cargo in the --cargo_bin or prebuilt rust bin directory."""
1065 if self.args.cargo_bin:
1066 self.cargo_path = os.path.join(self.args.cargo_bin, 'cargo')
1067 if not os.path.isfile(self.cargo_path):
1068 sys.exit('ERROR: cannot find cargo in ' + self.args.cargo_bin)
1069 print('WARNING: using cargo in ' + self.args.cargo_bin)
1070 return
1071 # We have only tested this on Linux.
1072 if platform.system() != 'Linux':
1073 sys.exit('ERROR: this script has only been tested on Linux with cargo.')
1074 # Assuming that this script is in development/scripts.
1075 my_dir = os.path.dirname(os.path.abspath(__file__))
1076 linux_dir = os.path.join(my_dir, '..', '..',
1077 'prebuilts', 'rust', 'linux-x86')
1078 if not os.path.isdir(linux_dir):
1079 sys.exit('ERROR: cannot find directory ' + linux_dir)
1080 rust_version = self.find_rust_version(my_dir, linux_dir)
1081 cargo_bin = os.path.join(linux_dir, rust_version, 'bin')
1082 self.cargo_path = os.path.join(cargo_bin, 'cargo')
1083 if not os.path.isfile(self.cargo_path):
1084 sys.exit('ERROR: cannot find cargo in ' + cargo_bin
1085 + '; please try --cargo_bin= flag.')
1086 return
1087
1088 def find_rust_version(self, my_dir, linux_dir):
1089 """Use my script directory, find prebuilt rust version."""
1090 # First look up build/soong/rust/config/global.go.
1091 path2global = os.path.join(my_dir, '..', '..',
1092 'build', 'soong', 'rust', 'config', 'global.go')
1093 if os.path.isfile(path2global):
1094 # try to find: RustDefaultVersion = "1.44.0"
1095 version_pat = re.compile(
1096 r'\s*RustDefaultVersion\s*=\s*"([0-9]+\.[0-9]+\.[0-9]+)".*$')
1097 with open(path2global, 'r') as inf:
1098 for line in inf:
1099 result = version_pat.match(line)
1100 if result:
1101 return result.group(1)
1102 print('WARNING: cannot find RustDefaultVersion in ' + path2global)
1103 # Otherwise, find the newest (largest) version number in linux_dir.
1104 rust_version = (0, 0, 0) # the prebuilt version to use
1105 version_pat = re.compile(r'([0-9]+)\.([0-9]+)\.([0-9]+)$')
1106 for dir_name in os.listdir(linux_dir):
1107 result = version_pat.match(dir_name)
1108 if not result:
1109 continue
1110 version = (result.group(1), result.group(2), result.group(3))
1111 if version > rust_version:
1112 rust_version = version
1113 return '.'.join(rust_version)
1114
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001115 def find_out_files(self):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001116 # list1 has build.rs output for normal crates
1117 list1 = glob.glob(TARGET_TMP + '/*/*/build/' + self.root_pkg + '-*/out/*')
1118 # list2 has build.rs output for proc-macro crates
1119 list2 = glob.glob(TARGET_TMP + '/*/build/' + self.root_pkg + '-*/out/*')
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001120 return list1 + list2
1121
1122 def copy_out_files(self):
1123 """Copy build.rs output files to ./out and set up build_out_files."""
1124 if self.checked_out_files:
1125 return
1126 self.checked_out_files = True
1127 cargo_out_files = self.find_out_files()
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001128 out_files = set()
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001129 if cargo_out_files:
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001130 os.makedirs('out', exist_ok=True)
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001131 for path in cargo_out_files:
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001132 file_name = path.split('/')[-1]
1133 out_files.add(file_name)
1134 shutil.copy(path, 'out/' + file_name)
1135 self.build_out_files = sorted(out_files)
1136
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001137 def has_used_out_dir(self):
1138 """Returns true if env!("OUT_DIR") is found."""
1139 return 0 == os.system('grep -rl --exclude build.rs --include \\*.rs' +
1140 ' \'env!("OUT_DIR")\' * > /dev/null')
1141
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001142 def copy_out_module_name(self):
1143 if self.args.copy_out and self.build_out_files:
1144 return 'copy_' + self.root_pkg + '_build_out'
1145 else:
1146 return ''
1147
Haibo Huang0f72c952021-03-19 11:34:15 -07001148 def read_license(self, name):
1149 if not os.path.isfile(name):
1150 return ''
1151 license = ''
1152 with open(name, 'r') as intf:
1153 line = intf.readline()
1154 # Firstly skip ANDROID_BP_HEADER
1155 while line.startswith('//'):
1156 line = intf.readline()
Joel Galensond9d13b82021-04-05 11:27:55 -07001157 # Read all lines until we see a rust_* or genrule rule.
1158 while line != '' and not (line.startswith('rust_') or line.startswith('genrule {')):
Haibo Huang0f72c952021-03-19 11:34:15 -07001159 license += line
1160 line = intf.readline()
1161 return license.strip()
1162
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001163 def dump_copy_out_module(self, outf):
1164 """Output the genrule module to copy out/* to $(genDir)."""
1165 copy_out = self.copy_out_module_name()
1166 if not copy_out:
1167 return
1168 outf.write('\ngenrule {\n')
1169 outf.write(' name: "' + copy_out + '",\n')
1170 outf.write(' srcs: ["out/*"],\n')
1171 outf.write(' cmd: "cp $(in) $(genDir)",\n')
1172 if len(self.build_out_files) > 1:
1173 outf.write(' out: [\n')
1174 for f in self.build_out_files:
1175 outf.write(' "' + f + '",\n')
1176 outf.write(' ],\n')
1177 else:
1178 outf.write(' out: ["' + self.build_out_files[0] + '"],\n')
1179 outf.write('}\n')
1180
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001181 def init_bp_file(self, name):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001182 # name could be Android.bp or sub_dir_path/Android.bp
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001183 if name not in self.bp_files:
1184 self.bp_files.add(name)
Haibo Huang0f72c952021-03-19 11:34:15 -07001185 license_section = self.read_license(name)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001186 with open(name, 'w') as outf:
Joel Galenson367360c2021-04-29 14:31:43 -07001187 print_args = filter(lambda x: x != "--no-test-mapping", sys.argv[1:])
1188 outf.write(ANDROID_BP_HEADER.format(args=' '.join(print_args)))
Haibo Huang0f72c952021-03-19 11:34:15 -07001189 outf.write('\n')
1190 outf.write(license_section)
1191 outf.write('\n')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001192 # at most one copy_out module per .bp file
1193 self.dump_copy_out_module(outf)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001194
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001195 def dump_test_mapping_files(self):
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001196 """Dump all TEST_MAPPING files."""
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001197 if self.dry_run:
1198 print('Dry-run skip dump of TEST_MAPPING')
Joel Galensoncf4ebb02021-04-07 08:39:51 -07001199 elif self.args.no_test_mapping:
1200 print('Skipping generation of TEST_MAPPING')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001201 else:
Jeff Vander Stoep1b24dc32021-02-03 18:52:42 +01001202 test_mapping = TestMapping(None)
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +01001203 for bp_file_name in self.bp_files:
1204 test_mapping.create_test_mapping(os.path.dirname(bp_file_name))
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001205 return self
1206
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001207 def try_claim_module_name(self, name, owner):
1208 """Reserve and return True if it has not been reserved yet."""
1209 if name not in self.name_owners or owner == self.name_owners[name]:
1210 self.name_owners[name] = owner
1211 return True
1212 return False
1213
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001214 def claim_module_name(self, prefix, owner, counter):
1215 """Return prefix if not owned yet, otherwise, prefix+str(counter)."""
1216 while True:
1217 name = prefix
1218 if counter > 0:
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001219 name += '_' + str(counter)
1220 if self.try_claim_module_name(name, owner):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001221 return name
1222 counter += 1
1223
1224 def find_root_pkg(self):
1225 """Read name of [package] in ./Cargo.toml."""
1226 if not os.path.exists('./Cargo.toml'):
1227 return
1228 with open('./Cargo.toml', 'r') as inf:
1229 pkg_section = re.compile(r'^ *\[package\]')
1230 name = re.compile('^ *name *= * "([^"]*)"')
1231 in_pkg = False
1232 for line in inf:
1233 if in_pkg:
1234 if name.match(line):
1235 self.root_pkg = name.match(line).group(1)
1236 break
1237 else:
1238 in_pkg = pkg_section.match(line) is not None
1239
1240 def run_cargo(self):
1241 """Calls cargo -v and save its output to ./cargo.out."""
1242 if self.skip_cargo:
1243 return self
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001244 cargo_toml = './Cargo.toml'
1245 cargo_out = './cargo.out'
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001246 # Do not use Cargo.lock, because .bp rules are designed to
1247 # run with "latest" crates avaialable on Android.
1248 cargo_lock = './Cargo.lock'
1249 cargo_lock_saved = './cargo.lock.saved'
1250 had_cargo_lock = os.path.exists(cargo_lock)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001251 if not os.access(cargo_toml, os.R_OK):
1252 print('ERROR: Cannot find or read', cargo_toml)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001253 return self
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001254 if not self.dry_run:
1255 if os.path.exists(cargo_out):
1256 os.remove(cargo_out)
1257 if not self.args.use_cargo_lock and had_cargo_lock: # save it
1258 os.rename(cargo_lock, cargo_lock_saved)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001259 cmd_tail = ' --target-dir ' + TARGET_TMP + ' >> ' + cargo_out + ' 2>&1'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001260 # set up search PATH for cargo to find the correct rustc
1261 saved_path = os.environ['PATH']
1262 os.environ['PATH'] = os.path.dirname(self.cargo_path) + ':' + saved_path
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001263 # Add [workspace] to Cargo.toml if it is not there.
1264 added_workspace = False
1265 if self.args.add_workspace:
1266 with open(cargo_toml, 'r') as in_file:
1267 cargo_toml_lines = in_file.readlines()
1268 found_workspace = '[workspace]\n' in cargo_toml_lines
1269 if found_workspace:
1270 print('### WARNING: found [workspace] in Cargo.toml')
1271 else:
1272 with open(cargo_toml, 'a') as out_file:
1273 out_file.write('[workspace]\n')
1274 added_workspace = True
1275 if self.args.verbose:
1276 print('### INFO: added [workspace] to Cargo.toml')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001277 for c in self.cargo:
1278 features = ''
Chih-Hung Hsieh6c8d52f2020-03-30 18:28:52 -07001279 if c != 'clean':
1280 if self.args.features is not None:
1281 features = ' --no-default-features'
1282 if self.args.features:
1283 features += ' --features ' + self.args.features
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001284 cmd_v_flag = ' -vv ' if self.args.vv else ' -v '
1285 cmd = self.cargo_path + cmd_v_flag
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001286 cmd += c + features + cmd_tail
1287 if self.args.rustflags and c != 'clean':
1288 cmd = 'RUSTFLAGS="' + self.args.rustflags + '" ' + cmd
1289 if self.dry_run:
1290 print('Dry-run skip:', cmd)
1291 else:
1292 if self.args.verbose:
1293 print('Running:', cmd)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001294 with open(cargo_out, 'a') as out_file:
1295 out_file.write('### Running: ' + cmd + '\n')
Joel Galenson6bf54e32021-05-17 10:54:50 -07001296 ret = os.system(cmd)
1297 if ret != 0:
1298 print('*** There was an error while running cargo. ' +
1299 'See the cargo.out file for details.')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001300 if added_workspace: # restore original Cargo.toml
1301 with open(cargo_toml, 'w') as out_file:
1302 out_file.writelines(cargo_toml_lines)
1303 if self.args.verbose:
1304 print('### INFO: restored original Cargo.toml')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001305 os.environ['PATH'] = saved_path
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001306 if not self.dry_run:
1307 if not had_cargo_lock: # restore to no Cargo.lock state
1308 os.remove(cargo_lock)
1309 elif not self.args.use_cargo_lock: # restore saved Cargo.lock
1310 os.rename(cargo_lock_saved, cargo_lock)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001311 return self
1312
1313 def dump_dependencies(self):
1314 """Append dependencies and their features to Android.bp."""
1315 if not self.dependencies:
1316 return
1317 dependent_list = list()
1318 for c in self.dependencies:
1319 dependent_list.append(c.feature_list())
1320 sorted_dependencies = sorted(set(dependent_list))
1321 self.init_bp_file('Android.bp')
1322 with open('Android.bp', 'a') as outf:
1323 outf.write('\n// dependent_library ["feature_list"]\n')
1324 for s in sorted_dependencies:
1325 outf.write('// ' + s + '\n')
1326
1327 def dump_pkg_obj2cc(self):
1328 """Dump debug info of the pkg_obj2cc map."""
1329 if not self.args.debug:
1330 return
1331 self.init_bp_file('Android.bp')
1332 with open('Android.bp', 'a') as outf:
1333 sorted_pkgs = sorted(self.pkg_obj2cc.keys())
1334 for pkg in sorted_pkgs:
1335 if not self.pkg_obj2cc[pkg]:
1336 continue
1337 outf.write('\n// obj => src for %s\n' % pkg)
1338 obj2cc = self.pkg_obj2cc[pkg]
1339 for obj in sorted(obj2cc.keys()):
1340 outf.write('// ' + short_out_name(pkg, obj) + ' => ' +
1341 short_out_name(pkg, obj2cc[obj].src) + '\n')
1342
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001343 def apply_patch(self):
1344 """Apply local patch file if it is given."""
1345 if self.args.patch:
1346 if self.dry_run:
1347 print('Dry-run skip patch file:', self.args.patch)
1348 else:
1349 if not os.path.exists(self.args.patch):
1350 self.append_to_bp('ERROR cannot find patch file: ' + self.args.patch)
1351 return self
1352 if self.args.verbose:
1353 print('### INFO: applying local patch file:', self.args.patch)
Joel Galenson7e8247e2021-05-20 18:51:42 -07001354 subprocess.run(['patch', '-s', '--no-backup-if-mismatch', './Android.bp',
1355 self.args.patch], check=True)
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001356 return self
1357
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001358 def gen_bp(self):
1359 """Parse cargo.out and generate Android.bp files."""
1360 if self.dry_run:
1361 print('Dry-run skip: read', CARGO_OUT, 'write Android.bp')
1362 elif os.path.exists(CARGO_OUT):
1363 self.find_root_pkg()
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001364 if self.args.copy_out:
1365 self.copy_out_files()
1366 elif self.find_out_files() and self.has_used_out_dir():
1367 print('WARNING: ' + self.root_pkg + ' has cargo output files; ' +
1368 'please rerun with the --copy-out flag.')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001369 with open(CARGO_OUT, 'r') as cargo_out:
1370 self.parse(cargo_out, 'Android.bp')
1371 self.crates.sort(key=get_module_name)
1372 for obj in self.cc_objects:
1373 obj.dump()
1374 self.dump_pkg_obj2cc()
1375 for crate in self.crates:
1376 crate.dump()
1377 dumped_libs = set()
1378 for lib in self.ar_objects:
1379 if lib.pkg == self.root_pkg:
1380 lib_name = file_base_name(lib.lib)
1381 if lib_name not in dumped_libs:
1382 dumped_libs.add(lib_name)
1383 lib.dump()
Joel Galenson5664f2a2021-06-10 10:13:49 -07001384 if self.args.add_toplevel_block:
1385 with open(self.args.add_toplevel_block, 'r') as f:
1386 self.append_to_bp('\n' + f.read() + '\n')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001387 if self.args.dependencies and self.dependencies:
1388 self.dump_dependencies()
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001389 if self.errors:
Joel Galenson3f42f802021-04-07 12:42:17 -07001390 self.append_to_bp('\n' + ERRORS_LINE + '\n' + self.errors)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001391 return self
1392
1393 def add_ar_object(self, obj):
1394 self.ar_objects.append(obj)
1395
1396 def add_cc_object(self, obj):
1397 self.cc_objects.append(obj)
1398
1399 def add_crate(self, crate):
1400 """Merge crate with someone in crates, or append to it. Return crates."""
1401 if crate.skip_crate():
1402 if self.args.debug: # include debug info of all crates
1403 self.crates.append(crate)
1404 if self.args.dependencies: # include only dependent crates
1405 if (is_dependent_file_path(crate.main_src) and
1406 not is_build_crate_name(crate.crate_name)):
1407 self.dependencies.append(crate)
1408 else:
1409 for c in self.crates:
1410 if c.merge(crate, 'Android.bp'):
1411 return
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001412 # If not merged, decide module type and name now.
1413 crate.decide_module_type()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001414 self.crates.append(crate)
1415
1416 def find_warning_owners(self):
1417 """For each warning file, find its owner crate."""
1418 missing_owner = False
1419 for f in self.warning_files:
1420 cargo_dir = '' # find lowest crate, with longest path
1421 owner = None # owner crate of this warning
1422 for c in self.crates:
1423 if (f.startswith(c.cargo_dir + '/') and
1424 len(cargo_dir) < len(c.cargo_dir)):
1425 cargo_dir = c.cargo_dir
1426 owner = c
1427 if owner:
1428 owner.has_warning = True
1429 else:
1430 missing_owner = True
1431 if missing_owner and os.path.exists('Cargo.toml'):
1432 # owner is the root cargo, with empty cargo_dir
1433 for c in self.crates:
1434 if not c.cargo_dir:
1435 c.has_warning = True
1436
1437 def rustc_command(self, n, rustc_line, line, outf_name):
1438 """Process a rustc command line from cargo -vv output."""
1439 # cargo build -vv output can have multiple lines for a rustc command
1440 # due to '\n' in strings for environment variables.
1441 # strip removes leading spaces and '\n' at the end
1442 new_rustc = (rustc_line.strip() + line) if rustc_line else line
1443 # Use an heuristic to detect the completions of a multi-line command.
1444 # This might fail for some very rare case, but easy to fix manually.
1445 if not line.endswith('`\n') or (new_rustc.count('`') % 2) != 0:
1446 return new_rustc
1447 if RUSTC_VV_CMD_ARGS.match(new_rustc):
1448 args = RUSTC_VV_CMD_ARGS.match(new_rustc).group(1)
1449 self.add_crate(Crate(self, outf_name).parse(n, args))
1450 else:
1451 self.assert_empty_vv_line(new_rustc)
1452 return ''
1453
1454 def cc_ar_command(self, n, groups, outf_name):
1455 pkg = groups.group(1)
1456 line = groups.group(3)
1457 if groups.group(2) == 'cc':
1458 self.add_cc_object(CCObject(self, outf_name).parse(pkg, n, line))
1459 else:
1460 self.add_ar_object(ARObject(self, outf_name).parse(pkg, n, line))
1461
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001462 def append_to_bp(self, line):
1463 self.init_bp_file('Android.bp')
1464 with open('Android.bp', 'a') as outf:
1465 outf.write(line)
1466
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001467 def assert_empty_vv_line(self, line):
1468 if line: # report error if line is not empty
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001469 self.append_to_bp('ERROR -vv line: ' + line)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001470 return ''
1471
1472 def parse(self, inf, outf_name):
1473 """Parse rustc and warning messages in inf, return a list of Crates."""
1474 n = 0 # line number
1475 prev_warning = False # true if the previous line was warning: ...
1476 rustc_line = '' # previous line(s) matching RUSTC_VV_PAT
1477 for line in inf:
1478 n += 1
1479 if line.startswith('warning: '):
1480 prev_warning = True
1481 rustc_line = self.assert_empty_vv_line(rustc_line)
1482 continue
1483 new_rustc = ''
1484 if RUSTC_PAT.match(line):
1485 args_line = RUSTC_PAT.match(line).group(1)
1486 self.add_crate(Crate(self, outf_name).parse(n, args_line))
1487 self.assert_empty_vv_line(rustc_line)
1488 elif rustc_line or RUSTC_VV_PAT.match(line):
1489 new_rustc = self.rustc_command(n, rustc_line, line, outf_name)
1490 elif CC_AR_VV_PAT.match(line):
1491 self.cc_ar_command(n, CC_AR_VV_PAT.match(line), outf_name)
1492 elif prev_warning and WARNING_FILE_PAT.match(line):
1493 self.assert_empty_vv_line(rustc_line)
1494 fpath = WARNING_FILE_PAT.match(line).group(1)
1495 if fpath[0] != '/': # ignore absolute path
1496 self.warning_files.add(fpath)
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001497 elif line.startswith('error: ') or line.startswith('error[E'):
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001498 if not self.args.ignore_cargo_errors:
1499 self.errors += line
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001500 prev_warning = False
1501 rustc_line = new_rustc
1502 self.find_warning_owners()
1503
1504
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001505def get_parser():
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001506 """Parse main arguments."""
1507 parser = argparse.ArgumentParser('cargo2android')
1508 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001509 '--add_workspace',
1510 action='store_true',
1511 default=False,
1512 help=('append [workspace] to Cargo.toml before calling cargo,' +
1513 ' to treat current directory as root of package source;' +
1514 ' otherwise the relative source file path in generated' +
1515 ' .bp file will be from the parent directory.'))
1516 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001517 '--cargo',
1518 action='append',
1519 metavar='args_string',
1520 help=('extra cargo build -v args in a string, ' +
1521 'each --cargo flag calls cargo build -v once'))
1522 parser.add_argument(
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001523 '--cargo_bin',
1524 type=str,
1525 help='use cargo in the cargo_bin directory instead of the prebuilt one')
1526 parser.add_argument(
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001527 '--copy-out',
1528 action='store_true',
1529 default=False,
1530 help=('only for root directory, ' +
1531 'copy build.rs output to ./out/* and add a genrule to copy ' +
1532 './out/* to genrule output; for crates with code pattern: ' +
1533 'include!(concat!(env!("OUT_DIR"), "/<some_file>.rs"))'))
1534 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001535 '--debug',
1536 action='store_true',
1537 default=False,
1538 help='dump debug info into Android.bp')
1539 parser.add_argument(
1540 '--dependencies',
1541 action='store_true',
1542 default=False,
1543 help='dump debug info of dependent crates')
1544 parser.add_argument(
1545 '--device',
1546 action='store_true',
1547 default=False,
1548 help='run cargo also for a default device target')
1549 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001550 '--features',
1551 type=str,
1552 help=('pass features to cargo build, ' +
1553 'empty string means no default features'))
1554 parser.add_argument(
1555 '--global_defaults',
1556 type=str,
1557 help='add a defaults name to every module')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001558 parser.add_argument(
1559 '--host-first-multilib',
1560 action='store_true',
1561 default=False,
1562 help=('add a compile_multilib:"first" property ' +
1563 'to Android.bp host modules.'))
1564 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001565 '--ignore-cargo-errors',
1566 action='store_true',
1567 default=False,
1568 help='do not append cargo/rustc error messages to Android.bp')
1569 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001570 '--no-host',
1571 action='store_true',
1572 default=False,
1573 help='do not run cargo for the host; only for the device target')
1574 parser.add_argument(
1575 '--no-subdir',
1576 action='store_true',
1577 default=False,
1578 help='do not output anything for sub-directories')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001579 parser.add_argument(
1580 '--onefile',
1581 action='store_true',
1582 default=False,
1583 help=('output all into one ./Android.bp, default will generate ' +
1584 'one Android.bp per Cargo.toml in subdirectories'))
1585 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001586 '--patch',
1587 type=str,
1588 help='apply the given patch file to generated ./Android.bp')
1589 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001590 '--run',
1591 action='store_true',
1592 default=False,
1593 help='run it, default is dry-run')
1594 parser.add_argument('--rustflags', type=str, help='passing flags to rustc')
1595 parser.add_argument(
1596 '--skipcargo',
1597 action='store_true',
1598 default=False,
1599 help='skip cargo command, parse cargo.out, and generate Android.bp')
1600 parser.add_argument(
1601 '--tests',
1602 action='store_true',
1603 default=False,
1604 help='run cargo build --tests after normal build')
1605 parser.add_argument(
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001606 '--use-cargo-lock',
1607 action='store_true',
1608 default=False,
1609 help=('run cargo build with existing Cargo.lock ' +
1610 '(used when some latest dependent crates failed)'))
1611 parser.add_argument(
Joel Galensond9c4de62021-04-23 10:26:40 -07001612 '--min-sdk-version',
1613 type=str,
1614 help='Minimum SDK version')
1615 parser.add_argument(
1616 '--apex-available',
1617 nargs='*',
1618 help='Mark the main library as apex_available with the given apexes.')
1619 parser.add_argument(
Joel Galensoncb5f2f02021-06-08 14:47:55 -07001620 '--force-rlib',
1621 action='store_true',
1622 default=False,
1623 help='Make the main library an rlib.')
1624 parser.add_argument(
Joel Galenson97e414a2021-05-27 09:42:32 -07001625 '--dependency-blocklist',
1626 nargs='*',
1627 default=[],
1628 help='Do not emit the given dependencies.')
1629 parser.add_argument(
Joel Galensoncb5f2f02021-06-08 14:47:55 -07001630 '--lib-blocklist',
1631 nargs='*',
1632 default=[],
1633 help='Do not emit the given C libraries as dependencies.')
1634 parser.add_argument(
Joel Galensonf6b3c912021-06-03 16:00:54 -07001635 '--test-blocklist',
1636 nargs='*',
1637 default=[],
1638 help=('Do not emit the given tests. ' +
1639 'Pass the path to the test file to exclude.'))
1640 parser.add_argument(
Joel Galenson3d6d1e72021-06-07 15:00:24 -07001641 '--cfg-blocklist',
1642 nargs='*',
1643 default=[],
1644 help='Do not emit the given cfg.')
1645 parser.add_argument(
Joel Galenson5664f2a2021-06-10 10:13:49 -07001646 '--add-toplevel-block',
1647 type=str,
1648 help='Add the contents of the given file to the top level of the Android.bp.')
1649 parser.add_argument(
1650 '--add-module-block',
1651 type=str,
1652 help='Add the contents of the given file to the main module.')
1653 parser.add_argument(
Joel Galensoncf4ebb02021-04-07 08:39:51 -07001654 '--no-test-mapping',
1655 action='store_true',
1656 default=False,
1657 help='Do not generate a TEST_MAPPING file. Use only to speed up debugging.')
1658 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001659 '--verbose',
1660 action='store_true',
1661 default=False,
1662 help='echo executed commands')
1663 parser.add_argument(
1664 '--vv',
1665 action='store_true',
1666 default=False,
1667 help='run cargo with -vv instead of default -v')
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001668 parser.add_argument(
1669 '--dump-config-and-exit',
1670 type=str,
1671 help=('Dump command-line arguments (minus this flag) to a config file and exit. ' +
1672 'This is intended to help migrate from command line options to config files.'))
1673 parser.add_argument(
1674 '--config',
1675 type=str,
1676 help=('Load command-line options from the given config file. ' +
1677 'Options in this file will override those passed on the command line.'))
1678 return parser
1679
1680
1681def parse_args(parser):
1682 """Parses command-line options."""
1683 args = parser.parse_args()
1684 # Use the values specified in a config file if one was found.
1685 if args.config:
1686 with open(args.config, 'r') as f:
1687 config = json.load(f)
1688 args_dict = vars(args)
1689 for arg in config:
1690 args_dict[arg.replace('-', '_')] = config[arg]
1691 return args
1692
1693
1694def dump_config(parser, args):
1695 """Writes the non-default command-line options to the specified file."""
1696 args_dict = vars(args)
1697 # Filter out the arguments that have their default value.
Joel Galenson367360c2021-04-29 14:31:43 -07001698 # Also filter certain "temporary" arguments.
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001699 non_default_args = {}
1700 for arg in args_dict:
Joel Galenson367360c2021-04-29 14:31:43 -07001701 if args_dict[arg] != parser.get_default(
1702 arg) and arg != 'dump_config_and_exit' and arg != 'no_test_mapping':
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001703 non_default_args[arg.replace('_', '-')] = args_dict[arg]
1704 # Write to the specified file.
1705 with open(args.dump_config_and_exit, 'w') as f:
1706 json.dump(non_default_args, f, indent=2, sort_keys=True)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001707
1708
1709def main():
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001710 parser = get_parser()
1711 args = parse_args(parser)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001712 if not args.run: # default is dry-run
1713 print(DRY_RUN_NOTE)
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001714 if args.dump_config_and_exit:
1715 dump_config(parser, args)
1716 else:
1717 Runner(args).run_cargo().gen_bp().apply_patch().dump_test_mapping_files()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001718
1719
1720if __name__ == '__main__':
1721 main()