blob: 730df5a43143811d902dfd65970682d9e1412c4a [file] [log] [blame]
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001#!/usr/bin/env python3
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08002#
3# Copyright (C) 2019 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16"""Call cargo -v, parse its output, and generate Android.bp.
17
18Usage: Run this script in a crate workspace root directory.
19The Cargo.toml file should work at least for the host platform.
20
21(1) Without other flags, "cargo2android.py --run"
22 calls cargo clean, calls cargo build -v, and generates Android.bp.
23 The cargo build only generates crates for the host,
24 without test crates.
25
26(2) To build crates for both host and device in Android.bp, use the
27 --device flag, for example:
28 cargo2android.py --run --device
29
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -070030 Note that cargo build is only called once with the default target
31 x86_64-unknown-linux-gnu.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080032
33(3) To build default and test crates, for host and device, use both
34 --device and --tests flags:
35 cargo2android.py --run --device --tests
36
37 This is equivalent to using the --cargo flag to add extra builds:
38 cargo2android.py --run
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080039 --cargo "build --target x86_64-unknown-linux-gnu"
40 --cargo "build --tests --target x86_64-unknown-linux-gnu"
41
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -070042If there are rustc warning messages, this script will add
43a warning comment to the owner crate module in Android.bp.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080044"""
45
46from __future__ import print_function
47
48import argparse
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -070049import glob
Joel Galenson0fbdafe2021-04-21 16:33:33 -070050import json
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080051import os
52import os.path
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -070053import platform
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080054import re
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -070055import shutil
Joel Galenson7e8247e2021-05-20 18:51:42 -070056import subprocess
Andrew Walbran80e90be2020-06-09 14:33:18 +010057import sys
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080058
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -070059# Some Rust packages include extra unwanted crates.
60# This set contains all such excluded crate names.
61EXCLUDED_CRATES = set(['protobuf_bin_gen_rust_do_not_use'])
62
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080063RENAME_MAP = {
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -070064 # This map includes all changes to the default rust module names
65 # to resolve name conflicts, avoid confusion, or work as plugin.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080066 'libbacktrace': 'libbacktrace_rust',
Andrew Walbrane51f1042020-08-11 16:42:48 +010067 'libbase': 'libbase_rust',
Luke Huanga1371af2021-06-29 18:04:40 +080068 'libbase64': 'libbase64_rust',
Victor Hsieh21bea792020-12-04 10:59:16 -080069 'libfuse': 'libfuse_rust',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080070 'libgcc': 'libgcc_rust',
71 'liblog': 'liblog_rust',
Chih-Hung Hsieh07119862020-07-24 15:34:06 -070072 'libminijail': 'libminijail_rust',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080073 'libsync': 'libsync_rust',
74 'libx86_64': 'libx86_64_rust',
Jooyung Hana427c9b2021-07-16 08:53:14 +090075 'libxml': 'libxml_rust',
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -070076 'protoc_gen_rust': 'protoc-gen-rust',
77}
78
79RENAME_STEM_MAP = {
80 # This map includes all changes to the default rust module stem names,
81 # which is used for output files when different from the module name.
82 'protoc_gen_rust': 'protoc-gen-rust',
83}
84
85RENAME_DEFAULTS_MAP = {
86 # This map includes all changes to the default prefix of rust_default
87 # module names, to avoid conflict with existing Android modules.
88 'libc': 'rust_libc',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080089}
90
91# Header added to all generated Android.bp files.
Joel Galenson56446742021-02-18 08:27:48 -080092ANDROID_BP_HEADER = (
93 '// This file is generated by cargo2android.py {args}.\n' +
94 '// Do not modify this file as changes will be overridden on upgrade.\n')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080095
96CARGO_OUT = 'cargo.out' # Name of file to keep cargo build -v output.
97
Joel Galenson3f42f802021-04-07 12:42:17 -070098# This should be kept in sync with tools/external_updater/crates_updater.py.
99ERRORS_LINE = 'Errors in ' + CARGO_OUT + ':'
100
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800101TARGET_TMP = 'target.tmp' # Name of temporary output directory.
102
103# Message to be displayed when this script is called without the --run flag.
104DRY_RUN_NOTE = (
105 'Dry-run: This script uses ./' + TARGET_TMP + ' for output directory,\n' +
106 'runs cargo clean, runs cargo build -v, saves output to ./cargo.out,\n' +
107 'and writes to Android.bp in the current and subdirectories.\n\n' +
108 'To do do all of the above, use the --run flag.\n' +
109 'See --help for other flags, and more usage notes in this script.\n')
110
111# Cargo -v output of a call to rustc.
112RUSTC_PAT = re.compile('^ +Running `rustc (.*)`$')
113
114# Cargo -vv output of a call to rustc could be split into multiple lines.
115# Assume that the first line will contain some CARGO_* env definition.
116RUSTC_VV_PAT = re.compile('^ +Running `.*CARGO_.*=.*$')
117# The combined -vv output rustc command line pattern.
118RUSTC_VV_CMD_ARGS = re.compile('^ *Running `.*CARGO_.*=.* rustc (.*)`$')
119
120# Cargo -vv output of a "cc" or "ar" command; all in one line.
121CC_AR_VV_PAT = re.compile(r'^\[([^ ]*)[^\]]*\] running:? "(cc|ar)" (.*)$')
122# Some package, such as ring-0.13.5, has pattern '... running "cc"'.
123
124# Rustc output of file location path pattern for a warning message.
125WARNING_FILE_PAT = re.compile('^ *--> ([^:]*):[0-9]+')
126
127# Rust package name with suffix -d1.d2.d3.
128VERSION_SUFFIX_PAT = re.compile(r'^(.*)-[0-9]+\.[0-9]+\.[0-9]+$')
129
130
131def altered_name(name):
132 return RENAME_MAP[name] if (name in RENAME_MAP) else name
133
134
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700135def altered_stem(name):
136 return RENAME_STEM_MAP[name] if (name in RENAME_STEM_MAP) else name
137
138
139def altered_defaults(name):
140 return RENAME_DEFAULTS_MAP[name] if (name in RENAME_DEFAULTS_MAP) else name
141
142
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800143def is_build_crate_name(name):
144 # We added special prefix to build script crate names.
145 return name.startswith('build_script_')
146
147
148def is_dependent_file_path(path):
149 # Absolute or dependent '.../' paths are not main files of this crate.
150 return path.startswith('/') or path.startswith('.../')
151
152
153def get_module_name(crate): # to sort crates in a list
154 return crate.module_name
155
156
157def pkg2crate_name(s):
158 return s.replace('-', '_').replace('.', '_')
159
160
161def file_base_name(path):
162 return os.path.splitext(os.path.basename(path))[0]
163
164
165def test_base_name(path):
166 return pkg2crate_name(file_base_name(path))
167
168
169def unquote(s): # remove quotes around str
170 if s and len(s) > 1 and s[0] == '"' and s[-1] == '"':
171 return s[1:-1]
172 return s
173
174
175def remove_version_suffix(s): # remove -d1.d2.d3 suffix
176 if VERSION_SUFFIX_PAT.match(s):
177 return VERSION_SUFFIX_PAT.match(s).group(1)
178 return s
179
180
181def short_out_name(pkg, s): # replace /.../pkg-*/out/* with .../out/*
182 return re.sub('^/.*/' + pkg + '-[0-9a-f]*/out/', '.../out/', s)
183
184
185def escape_quotes(s): # replace '"' with '\\"'
186 return s.replace('"', '\\"')
187
188
189class Crate(object):
190 """Information of a Rust crate to collect/emit for an Android.bp module."""
191
192 def __init__(self, runner, outf_name):
193 # Remembered global runner and its members.
194 self.runner = runner
195 self.debug = runner.args.debug
196 self.cargo_dir = '' # directory of my Cargo.toml
197 self.outf_name = outf_name # path to Android.bp
198 self.outf = None # open file handle of outf_name during dump*
199 # Variants/results that could be merged from multiple rustc lines.
200 self.host_supported = False
201 self.device_supported = False
202 self.has_warning = False
203 # Android module properties derived from rustc parameters.
204 self.module_name = '' # unique in Android build system
205 self.module_type = '' # rust_{binary,library,test}[_host] etc.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700206 self.defaults = '' # rust_defaults used by rust_test* modules
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700207 self.default_srcs = False # use 'srcs' defined in self.defaults
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800208 self.root_pkg = '' # parent package name of a sub/test packge, from -L
209 self.srcs = list() # main_src or merged multiple source files
210 self.stem = '' # real base name of output file
211 # Kept parsed status
212 self.errors = '' # all errors found during parsing
213 self.line_num = 1 # runner told input source line number
214 self.line = '' # original rustc command line parameters
215 # Parameters collected from rustc command line.
216 self.crate_name = '' # follows --crate-name
217 self.main_src = '' # follows crate_name parameter, shortened
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700218 self.crate_types = list() # follows --crate-type
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800219 self.cfgs = list() # follows --cfg, without feature= prefix
220 self.features = list() # follows --cfg, name in 'feature="..."'
221 self.codegens = list() # follows -C, some ignored
222 self.externs = list() # follows --extern
223 self.core_externs = list() # first part of self.externs elements
224 self.static_libs = list() # e.g. -l static=host_cpuid
225 self.shared_libs = list() # e.g. -l dylib=wayland-client, -l z
226 self.cap_lints = '' # follows --cap-lints
227 self.emit_list = '' # e.g., --emit=dep-info,metadata,link
228 self.edition = '2015' # rustc default, e.g., --edition=2018
229 self.target = '' # follows --target
230
231 def write(self, s):
232 # convenient way to output one line at a time with EOL.
233 self.outf.write(s + '\n')
234
235 def same_flags(self, other):
236 # host_supported, device_supported, has_warning are not compared but merged
237 # target is not compared, to merge different target/host modules
238 # externs is not compared; only core_externs is compared
239 return (not self.errors and not other.errors and
240 self.edition == other.edition and
241 self.cap_lints == other.cap_lints and
242 self.emit_list == other.emit_list and
243 self.core_externs == other.core_externs and
244 self.codegens == other.codegens and
245 self.features == other.features and
246 self.static_libs == other.static_libs and
247 self.shared_libs == other.shared_libs and self.cfgs == other.cfgs)
248
249 def merge_host_device(self, other):
250 """Returns true if attributes are the same except host/device support."""
251 return (self.crate_name == other.crate_name and
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700252 self.crate_types == other.crate_types and
253 self.main_src == other.main_src and
254 # before merge, each test module has an unique module name and stem
255 (self.stem == other.stem or self.crate_types == ['test']) and
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800256 self.root_pkg == other.root_pkg and not self.skip_crate() and
257 self.same_flags(other))
258
259 def merge_test(self, other):
260 """Returns true if self and other are tests of same root_pkg."""
261 # Before merger, each test has its own crate_name.
262 # A merged test uses its source file base name as output file name,
263 # so a test is mergeable only if its base name equals to its crate name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700264 return (self.crate_types == other.crate_types and
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700265 self.crate_types == ['test'] and self.root_pkg == other.root_pkg and
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800266 not self.skip_crate() and
267 other.crate_name == test_base_name(other.main_src) and
268 (len(self.srcs) > 1 or
269 (self.crate_name == test_base_name(self.main_src)) and
270 self.host_supported == other.host_supported and
271 self.device_supported == other.device_supported) and
272 self.same_flags(other))
273
274 def merge(self, other, outf_name):
275 """Try to merge crate into self."""
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700276 # Cargo build --tests could recompile a library for tests.
277 # We need to merge such duplicated calls to rustc, with
278 # the algorithm in merge_host_device.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800279 should_merge_host_device = self.merge_host_device(other)
280 should_merge_test = False
281 if not should_merge_host_device:
282 should_merge_test = self.merge_test(other)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800283 if should_merge_host_device or should_merge_test:
284 self.runner.init_bp_file(outf_name)
285 with open(outf_name, 'a') as outf: # to write debug info
286 self.outf = outf
287 other.outf = outf
288 self.do_merge(other, should_merge_test)
289 return True
290 return False
291
292 def do_merge(self, other, should_merge_test):
293 """Merge attributes of other to self."""
294 if self.debug:
295 self.write('\n// Before merge definition (1):')
296 self.dump_debug_info()
297 self.write('\n// Before merge definition (2):')
298 other.dump_debug_info()
299 # Merge properties of other to self.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800300 self.has_warning = self.has_warning or other.has_warning
301 if not self.target: # okay to keep only the first target triple
302 self.target = other.target
303 # decide_module_type sets up default self.stem,
304 # which can be changed if self is a merged test module.
305 self.decide_module_type()
306 if should_merge_test:
Joel Galenson57fa23a2021-07-15 10:47:35 -0700307 if (self.main_src in self.runner.args.test_blocklist and
308 not other.main_src in self.runner.args.test_blocklist):
309 self.main_src = other.main_src
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800310 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."""
Joel Galensond37d7e62021-07-13 09:03:01 -0700561 # Choices: (1) root_pkg + '_test'? + '_defaults',
562 # (2) root_pkg + '_test'? + '_defaults_' + crate_name
563 # (3) root_pkg + '_test'? + '_defaults_' + main_src_basename_path
564 # (4) root_pkg + '_test'? + '_defaults_' + a_positive_sequence_number
565 test = "_test" if self.crate_types == ['test'] else ""
566 name1 = altered_defaults(self.root_pkg) + test + '_defaults'
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700567 if self.runner.try_claim_module_name(name1, self):
568 return name1
569 name2 = name1 + '_' + self.crate_name
570 if self.runner.try_claim_module_name(name2, self):
571 return name2
572 name3 = name1 + '_' + self.main_src_basename_path()
573 if self.runner.try_claim_module_name(name3, self):
574 return name3
575 return self.runner.claim_module_name(name1, self, 0)
576
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700577 def dump_srcs_list(self):
578 """Dump the srcs list, for defaults or regular modules."""
579 if len(self.srcs) > 1:
580 srcs = sorted(set(self.srcs)) # make a copy and dedup
581 else:
582 srcs = [self.main_src]
583 copy_out = self.runner.copy_out_module_name()
584 if copy_out:
585 srcs.append(':' + copy_out)
586 self.dump_android_property_list('srcs', '"%s"', srcs)
587
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700588 def dump_defaults_module(self):
589 """Dump a rust_defaults module to be shared by other modules."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700590 name = self.build_default_name()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700591 self.defaults = name
592 self.write('\nrust_defaults {')
593 self.write(' name: "' + name + '",')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700594 if self.runner.args.global_defaults:
595 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700596 self.write(' crate_name: "' + self.crate_name + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700597 if len(self.srcs) == 1: # only one source file; share it in defaults
598 self.default_srcs = True
599 if self.has_warning and not self.cap_lints:
600 self.write(' // has rustc warnings')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700601 self.dump_srcs_list()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700602 if 'test' in self.crate_types:
603 self.write(' test_suites: ["general-tests"],')
604 self.write(' auto_gen_config: true,')
605 self.dump_edition_flags_libs()
606 self.write('}')
607
608 def dump_single_type_android_module(self):
609 """Dump one simple Android module, which has only one crate_type."""
610 crate_type = self.crate_types[0]
611 if crate_type != 'test':
612 # do not change self.stem or self.module_name
613 self.dump_one_android_module(crate_type)
614 return
615 # Dump one test module per source file, and separate host and device tests.
616 # crate_type == 'test'
Joel Galensonf6b3c912021-06-03 16:00:54 -0700617 self.srcs = [src for src in self.srcs if not src in self.runner.args.test_blocklist]
618 if ((self.host_supported and self.device_supported and len(self.srcs) > 0) or
619 len(self.srcs) > 1):
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700620 self.srcs = sorted(set(self.srcs))
621 self.dump_defaults_module()
622 saved_srcs = self.srcs
623 for src in saved_srcs:
624 self.srcs = [src]
625 saved_device_supported = self.device_supported
626 saved_host_supported = self.host_supported
627 saved_main_src = self.main_src
628 self.main_src = src
629 if saved_host_supported:
630 self.device_supported = False
631 self.host_supported = True
632 self.module_name = self.test_module_name()
633 self.decide_one_module_type(crate_type)
634 self.dump_one_android_module(crate_type)
635 if saved_device_supported:
636 self.device_supported = True
637 self.host_supported = False
638 self.module_name = self.test_module_name()
639 self.decide_one_module_type(crate_type)
640 self.dump_one_android_module(crate_type)
641 self.host_supported = saved_host_supported
642 self.device_supported = saved_device_supported
643 self.main_src = saved_main_src
644 self.srcs = saved_srcs
645
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700646 def dump_one_android_module(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800647 """Dump one Android module definition."""
648 if not self.module_type:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700649 self.write('\nERROR: unknown crate_type ' + crate_type)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800650 return
651 self.write('\n' + self.module_type + ' {')
652 self.dump_android_core_properties()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700653 if not self.defaults:
654 self.dump_edition_flags_libs()
655 if self.runner.args.host_first_multilib and self.host_supported and crate_type != 'test':
656 self.write(' compile_multilib: "first",')
Joel Galensond9c4de62021-04-23 10:26:40 -0700657 if self.runner.args.apex_available and crate_type == 'lib':
658 self.write(' apex_available: [')
659 for apex in self.runner.args.apex_available:
660 self.write(' "%s",' % apex)
661 self.write(' ],')
662 if self.runner.args.min_sdk_version and crate_type == 'lib':
663 self.write(' min_sdk_version: "%s",' % self.runner.args.min_sdk_version)
Joel Galenson5664f2a2021-06-10 10:13:49 -0700664 if self.runner.args.add_module_block:
665 with open(self.runner.args.add_module_block, 'r') as f:
666 self.write(' %s,' % f.read().replace('\n', '\n '))
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700667 self.write('}')
668
669 def dump_android_flags(self):
670 """Dump Android module flags property."""
ThiƩbaud Weksteena5a728b2021-04-08 14:23:49 +0200671 if not self.codegens and not self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700672 return
673 self.write(' flags: [')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800674 if self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700675 self.write(' "--cap-lints ' + self.cap_lints + '",')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700676 codegens_fmt = '"-C %s"'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700677 self.dump_android_property_list_items(codegens_fmt, self.codegens)
678 self.write(' ],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700679
680 def dump_edition_flags_libs(self):
681 if self.edition:
682 self.write(' edition: "' + self.edition + '",')
683 self.dump_android_property_list('features', '"%s"', self.features)
Joel Galenson3d6d1e72021-06-07 15:00:24 -0700684 cfgs = [cfg for cfg in self.cfgs if not cfg in self.runner.args.cfg_blocklist]
685 self.dump_android_property_list('cfgs', '"%s"', cfgs)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700686 self.dump_android_flags()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800687 if self.externs:
688 self.dump_android_externs()
Joel Galenson12467e52021-07-12 14:33:28 -0700689 all_static_libs = [lib for lib in self.static_libs if not lib in self.runner.args.lib_blocklist]
690 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 -0700691 self.dump_android_property_list('static_libs', '"lib%s"', static_libs)
Joel Galenson12467e52021-07-12 14:33:28 -0700692 whole_static_libs = [lib for lib in all_static_libs if lib in self.runner.args.whole_static_libs]
693 self.dump_android_property_list('whole_static_libs', '"lib%s"', whole_static_libs)
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700694 shared_libs = [lib for lib in self.shared_libs if not lib in self.runner.args.lib_blocklist]
695 self.dump_android_property_list('shared_libs', '"lib%s"', shared_libs)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800696
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700697 def main_src_basename_path(self):
698 return re.sub('/', '_', re.sub('.rs$', '', self.main_src))
699
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800700 def test_module_name(self):
701 """Return a unique name for a test module."""
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700702 # root_pkg+(_host|_device) + '_test_'+source_file_name
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700703 suffix = self.main_src_basename_path()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700704 host_device = '_host'
705 if self.device_supported:
706 host_device = '_device'
707 return self.root_pkg + host_device + '_test_' + suffix
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800708
709 def decide_module_type(self):
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700710 # Use the first crate type for the default/first module.
711 crate_type = self.crate_types[0] if self.crate_types else ''
712 self.decide_one_module_type(crate_type)
713
714 def decide_one_module_type(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800715 """Decide which Android module type to use."""
716 host = '' if self.device_supported else '_host'
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700717 rlib = '_rlib' if self.runner.args.force_rlib else ''
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700718 if crate_type == 'bin': # rust_binary[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800719 self.module_type = 'rust_binary' + host
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700720 # In rare cases like protobuf-codegen, the output binary name must
721 # be renamed to use as a plugin for protoc.
722 self.stem = altered_stem(self.crate_name)
723 self.module_name = altered_name(self.crate_name)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700724 elif crate_type == 'lib': # rust_library[_host]
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700725 # TODO(chh): should this be rust_library[_host]?
726 # Assuming that Cargo.toml do not use both 'lib' and 'rlib',
727 # because we map them both to rlib.
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700728 self.module_type = 'rust_library' + rlib + host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800729 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700730 self.module_name = altered_name(self.stem)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700731 elif crate_type == 'rlib': # rust_library[_host]
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700732 self.module_type = 'rust_library' + rlib + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700733 self.stem = 'lib' + self.crate_name
734 self.module_name = altered_name(self.stem)
735 elif crate_type == 'dylib': # rust_library[_host]_dylib
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800736 self.module_type = 'rust_library' + host + '_dylib'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700737 self.stem = 'lib' + self.crate_name
738 self.module_name = altered_name(self.stem) + '_dylib'
739 elif crate_type == 'cdylib': # rust_library[_host]_shared
Ivan Lozano0c057ad2020-12-15 10:41:26 -0500740 self.module_type = 'rust_ffi' + host + '_shared'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700741 self.stem = 'lib' + self.crate_name
742 self.module_name = altered_name(self.stem) + '_shared'
743 elif crate_type == 'staticlib': # rust_library[_host]_static
Ivan Lozano0c057ad2020-12-15 10:41:26 -0500744 self.module_type = 'rust_ffi' + host + '_static'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700745 self.stem = 'lib' + self.crate_name
746 self.module_name = altered_name(self.stem) + '_static'
747 elif crate_type == 'test': # rust_test[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800748 self.module_type = 'rust_test' + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700749 # Before do_merge, stem name is based on the --crate-name parameter.
750 # and test module name is based on stem.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800751 self.stem = self.test_module_name()
752 # self.stem will be changed after merging with other tests.
753 # self.stem is NOT used for final test binary name.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700754 # rust_test uses each source file base name as part of output file name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700755 # In do_merge, this function is called again, with a module_name.
756 # We make sure that the module name is unique in each package.
757 if self.module_name:
758 # Cargo uses "-C extra-filename=..." and "-C metadata=..." to add
759 # different suffixes and distinguish multiple tests of the same
760 # crate name. We ignore -C and use claim_module_name to get
761 # unique sequential suffix.
762 self.module_name = self.runner.claim_module_name(
763 self.module_name, self, 0)
764 # Now the module name is unique, stem should also match and unique.
765 self.stem = self.module_name
766 elif crate_type == 'proc-macro': # rust_proc_macro
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800767 self.module_type = 'rust_proc_macro'
768 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700769 self.module_name = altered_name(self.stem)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800770 else: # unknown module type, rust_prebuilt_dylib? rust_library[_host]?
771 self.module_type = ''
772 self.stem = ''
773
774 def dump_android_property_list_items(self, fmt, values):
775 for v in values:
776 # fmt has quotes, so we need escape_quotes(v)
777 self.write(' ' + (fmt % escape_quotes(v)) + ',')
778
779 def dump_android_property_list(self, name, fmt, values):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700780 if not values:
781 return
782 if len(values) > 1:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800783 self.write(' ' + name + ': [')
784 self.dump_android_property_list_items(fmt, values)
785 self.write(' ],')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700786 else:
787 self.write(' ' + name + ': [' +
788 (fmt % escape_quotes(values[0])) + '],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800789
790 def dump_android_core_properties(self):
791 """Dump the module header, name, stem, etc."""
792 self.write(' name: "' + self.module_name + '",')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700793 # see properties shared by dump_defaults_module
794 if self.defaults:
795 self.write(' defaults: ["' + self.defaults + '"],')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700796 elif self.runner.args.global_defaults:
797 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800798 if self.stem != self.module_name:
799 self.write(' stem: "' + self.stem + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700800 if self.has_warning and not self.cap_lints and not self.default_srcs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700801 self.write(' // has rustc warnings')
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700802 if self.host_supported and self.device_supported and self.module_type != 'rust_proc_macro':
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800803 self.write(' host_supported: true,')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700804 if not self.defaults:
805 self.write(' crate_name: "' + self.crate_name + '",')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700806 if not self.default_srcs:
807 self.dump_srcs_list()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700808 if 'test' in self.crate_types and not self.defaults:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800809 # self.root_pkg can have multiple test modules, with different *_tests[n]
810 # names, but their executables can all be installed under the same _tests
811 # directory. When built from Cargo.toml, all tests should have different
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700812 # file or crate names. So we used (root_pkg + '_tests') name as the
813 # relative_install_path.
814 # However, some package like 'slab' can have non-mergeable tests that
815 # must be separated by different module names. So, here we no longer
816 # emit relative_install_path.
817 # self.write(' relative_install_path: "' + self.root_pkg + '_tests",')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800818 self.write(' test_suites: ["general-tests"],')
819 self.write(' auto_gen_config: true,')
Joel Galensone261a152021-01-12 11:31:53 -0800820 if 'test' in self.crate_types and self.host_supported:
821 self.write(' test_options: {')
822 self.write(' unit_test: true,')
823 self.write(' },')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800824
825 def dump_android_externs(self):
826 """Dump the dependent rlibs and dylibs property."""
827 so_libs = list()
828 rust_libs = ''
829 deps_libname = re.compile('^.* = lib(.*)-[0-9a-f]*.(rlib|so|rmeta)$')
830 for lib in self.externs:
831 # normal value of lib: "libc = liblibc-*.rlib"
832 # strange case in rand crate: "getrandom_package = libgetrandom-*.rlib"
833 # we should use "libgetrandom", not "lib" + "getrandom_package"
834 groups = deps_libname.match(lib)
835 if groups is not None:
836 lib_name = groups.group(1)
837 else:
838 lib_name = re.sub(' .*$', '', lib)
Joel Galenson97e414a2021-05-27 09:42:32 -0700839 if lib_name in self.runner.args.dependency_blocklist:
840 continue
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800841 if lib.endswith('.rlib') or lib.endswith('.rmeta'):
842 # On MacOS .rmeta is used when Linux uses .rlib or .rmeta.
843 rust_libs += ' "' + altered_name('lib' + lib_name) + '",\n'
844 elif lib.endswith('.so'):
845 so_libs.append(lib_name)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700846 elif lib != 'proc_macro': # --extern proc_macro is special and ignored
847 rust_libs += ' // ERROR: unknown type of lib ' + lib + '\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800848 if rust_libs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700849 self.write(' rustlibs: [\n' + rust_libs + ' ],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800850 # Are all dependent .so files proc_macros?
851 # TODO(chh): Separate proc_macros and dylib.
852 self.dump_android_property_list('proc_macros', '"lib%s"', so_libs)
853
854
855class ARObject(object):
856 """Information of an "ar" link command."""
857
858 def __init__(self, runner, outf_name):
859 # Remembered global runner and its members.
860 self.runner = runner
861 self.pkg = ''
862 self.outf_name = outf_name # path to Android.bp
863 # "ar" arguments
864 self.line_num = 1
865 self.line = ''
866 self.flags = '' # e.g. "crs"
867 self.lib = '' # e.g. "/.../out/lib*.a"
868 self.objs = list() # e.g. "/.../out/.../*.o"
869
870 def parse(self, pkg, line_num, args_line):
871 """Collect ar obj/lib file names."""
872 self.pkg = pkg
873 self.line_num = line_num
874 self.line = args_line
875 args = args_line.split()
876 num_args = len(args)
877 if num_args < 3:
878 print('ERROR: "ar" command has too few arguments', args_line)
879 else:
880 self.flags = unquote(args[0])
881 self.lib = unquote(args[1])
882 self.objs = sorted(set(map(unquote, args[2:])))
883 return self
884
885 def write(self, s):
886 self.outf.write(s + '\n')
887
888 def dump_debug_info(self):
889 self.write('\n// Line ' + str(self.line_num) + ' "ar" ' + self.line)
890 self.write('// ar_object for %12s' % self.pkg)
891 self.write('// flags = %s' % self.flags)
892 self.write('// lib = %s' % short_out_name(self.pkg, self.lib))
893 for o in self.objs:
894 self.write('// obj = %s' % short_out_name(self.pkg, o))
895
896 def dump_android_lib(self):
897 """Write cc_library_static into Android.bp."""
898 self.write('\ncc_library_static {')
899 self.write(' name: "' + file_base_name(self.lib) + '",')
900 self.write(' host_supported: true,')
901 if self.flags != 'crs':
902 self.write(' // ar flags = %s' % self.flags)
903 if self.pkg not in self.runner.pkg_obj2cc:
904 self.write(' ERROR: cannot find source files.\n}')
905 return
906 self.write(' srcs: [')
907 obj2cc = self.runner.pkg_obj2cc[self.pkg]
908 # Note: wflags are ignored.
909 dflags = list()
910 fflags = list()
911 for obj in self.objs:
912 self.write(' "' + short_out_name(self.pkg, obj2cc[obj].src) + '",')
913 # TODO(chh): union of dflags and flags of all obj
914 # Now, just a temporary hack that uses the last obj's flags
915 dflags = obj2cc[obj].dflags
916 fflags = obj2cc[obj].fflags
917 self.write(' ],')
918 self.write(' cflags: [')
919 self.write(' "-O3",') # TODO(chh): is this default correct?
920 self.write(' "-Wno-error",')
921 for x in fflags:
922 self.write(' "-f' + x + '",')
923 for x in dflags:
924 self.write(' "-D' + x + '",')
925 self.write(' ],')
926 self.write('}')
927
928 def dump(self):
929 """Dump error/debug/module info to the output .bp file."""
930 self.runner.init_bp_file(self.outf_name)
931 with open(self.outf_name, 'a') as outf:
932 self.outf = outf
933 if self.runner.args.debug:
934 self.dump_debug_info()
935 self.dump_android_lib()
936
937
938class CCObject(object):
939 """Information of a "cc" compilation command."""
940
941 def __init__(self, runner, outf_name):
942 # Remembered global runner and its members.
943 self.runner = runner
944 self.pkg = ''
945 self.outf_name = outf_name # path to Android.bp
946 # "cc" arguments
947 self.line_num = 1
948 self.line = ''
949 self.src = ''
950 self.obj = ''
951 self.dflags = list() # -D flags
952 self.fflags = list() # -f flags
953 self.iflags = list() # -I flags
954 self.wflags = list() # -W flags
955 self.other_args = list()
956
957 def parse(self, pkg, line_num, args_line):
958 """Collect cc compilation flags and src/out file names."""
959 self.pkg = pkg
960 self.line_num = line_num
961 self.line = args_line
962 args = args_line.split()
963 i = 0
964 while i < len(args):
965 arg = args[i]
966 if arg == '"-c"':
967 i += 1
968 if args[i].startswith('"-o'):
969 # ring-0.13.5 dumps: ... "-c" "-o/.../*.o" ".../*.c"
970 self.obj = unquote(args[i])[2:]
971 i += 1
972 self.src = unquote(args[i])
973 else:
974 self.src = unquote(args[i])
975 elif arg == '"-o"':
976 i += 1
977 self.obj = unquote(args[i])
978 elif arg == '"-I"':
979 i += 1
980 self.iflags.append(unquote(args[i]))
981 elif arg.startswith('"-D'):
982 self.dflags.append(unquote(args[i])[2:])
983 elif arg.startswith('"-f'):
984 self.fflags.append(unquote(args[i])[2:])
985 elif arg.startswith('"-W'):
986 self.wflags.append(unquote(args[i])[2:])
987 elif not (arg.startswith('"-O') or arg == '"-m64"' or arg == '"-g"' or
988 arg == '"-g3"'):
989 # ignore -O -m64 -g
990 self.other_args.append(unquote(args[i]))
991 i += 1
992 self.dflags = sorted(set(self.dflags))
993 self.fflags = sorted(set(self.fflags))
994 # self.wflags is not sorted because some are order sensitive
995 # and we ignore them anyway.
996 if self.pkg not in self.runner.pkg_obj2cc:
997 self.runner.pkg_obj2cc[self.pkg] = {}
998 self.runner.pkg_obj2cc[self.pkg][self.obj] = self
999 return self
1000
1001 def write(self, s):
1002 self.outf.write(s + '\n')
1003
1004 def dump_debug_flags(self, name, flags):
1005 self.write('// ' + name + ':')
1006 for f in flags:
1007 self.write('// %s' % f)
1008
1009 def dump(self):
1010 """Dump only error/debug info to the output .bp file."""
1011 if not self.runner.args.debug:
1012 return
1013 self.runner.init_bp_file(self.outf_name)
1014 with open(self.outf_name, 'a') as outf:
1015 self.outf = outf
1016 self.write('\n// Line ' + str(self.line_num) + ' "cc" ' + self.line)
1017 self.write('// cc_object for %12s' % self.pkg)
1018 self.write('// src = %s' % short_out_name(self.pkg, self.src))
1019 self.write('// obj = %s' % short_out_name(self.pkg, self.obj))
1020 self.dump_debug_flags('-I flags', self.iflags)
1021 self.dump_debug_flags('-D flags', self.dflags)
1022 self.dump_debug_flags('-f flags', self.fflags)
1023 self.dump_debug_flags('-W flags', self.wflags)
1024 if self.other_args:
1025 self.dump_debug_flags('other args', self.other_args)
1026
1027
1028class Runner(object):
1029 """Main class to parse cargo -v output and print Android module definitions."""
1030
1031 def __init__(self, args):
1032 self.bp_files = set() # Remember all output Android.bp files.
1033 self.root_pkg = '' # name of package in ./Cargo.toml
1034 # Saved flags, modes, and data.
1035 self.args = args
1036 self.dry_run = not args.run
1037 self.skip_cargo = args.skipcargo
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001038 self.cargo_path = './cargo' # path to cargo, will be set later
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001039 self.checked_out_files = False # to check only once
1040 self.build_out_files = [] # output files generated by build.rs
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001041 # All cc/ar objects, crates, dependencies, and warning files
1042 self.cc_objects = list()
1043 self.pkg_obj2cc = {}
1044 # pkg_obj2cc[cc_object[i].pkg][cc_objects[i].obj] = cc_objects[i]
1045 self.ar_objects = list()
1046 self.crates = list()
1047 self.dependencies = list() # dependent and build script crates
1048 self.warning_files = set()
1049 # Keep a unique mapping from (module name) to crate
1050 self.name_owners = {}
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001051 # Save and dump all errors from cargo to Android.bp.
1052 self.errors = ''
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001053 self.setup_cargo_path()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001054 # Default action is cargo clean, followed by build or user given actions.
1055 if args.cargo:
1056 self.cargo = ['clean'] + args.cargo
1057 else:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001058 default_target = '--target x86_64-unknown-linux-gnu'
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -07001059 # Use the same target for both host and default device builds.
1060 # Same target is used as default in host x86_64 Android compilation.
1061 # Note: b/169872957, prebuilt cargo failed to build vsock
1062 # on x86_64-unknown-linux-musl systems.
1063 self.cargo = ['clean', 'build ' + default_target]
1064 if args.tests:
1065 self.cargo.append('build --tests ' + default_target)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001066
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001067 def setup_cargo_path(self):
1068 """Find cargo in the --cargo_bin or prebuilt rust bin directory."""
1069 if self.args.cargo_bin:
1070 self.cargo_path = os.path.join(self.args.cargo_bin, 'cargo')
1071 if not os.path.isfile(self.cargo_path):
1072 sys.exit('ERROR: cannot find cargo in ' + self.args.cargo_bin)
1073 print('WARNING: using cargo in ' + self.args.cargo_bin)
1074 return
1075 # We have only tested this on Linux.
1076 if platform.system() != 'Linux':
1077 sys.exit('ERROR: this script has only been tested on Linux with cargo.')
1078 # Assuming that this script is in development/scripts.
1079 my_dir = os.path.dirname(os.path.abspath(__file__))
1080 linux_dir = os.path.join(my_dir, '..', '..',
1081 'prebuilts', 'rust', 'linux-x86')
1082 if not os.path.isdir(linux_dir):
1083 sys.exit('ERROR: cannot find directory ' + linux_dir)
1084 rust_version = self.find_rust_version(my_dir, linux_dir)
1085 cargo_bin = os.path.join(linux_dir, rust_version, 'bin')
1086 self.cargo_path = os.path.join(cargo_bin, 'cargo')
1087 if not os.path.isfile(self.cargo_path):
1088 sys.exit('ERROR: cannot find cargo in ' + cargo_bin
1089 + '; please try --cargo_bin= flag.')
1090 return
1091
1092 def find_rust_version(self, my_dir, linux_dir):
1093 """Use my script directory, find prebuilt rust version."""
1094 # First look up build/soong/rust/config/global.go.
1095 path2global = os.path.join(my_dir, '..', '..',
1096 'build', 'soong', 'rust', 'config', 'global.go')
1097 if os.path.isfile(path2global):
1098 # try to find: RustDefaultVersion = "1.44.0"
1099 version_pat = re.compile(
1100 r'\s*RustDefaultVersion\s*=\s*"([0-9]+\.[0-9]+\.[0-9]+)".*$')
1101 with open(path2global, 'r') as inf:
1102 for line in inf:
1103 result = version_pat.match(line)
1104 if result:
1105 return result.group(1)
1106 print('WARNING: cannot find RustDefaultVersion in ' + path2global)
1107 # Otherwise, find the newest (largest) version number in linux_dir.
1108 rust_version = (0, 0, 0) # the prebuilt version to use
1109 version_pat = re.compile(r'([0-9]+)\.([0-9]+)\.([0-9]+)$')
1110 for dir_name in os.listdir(linux_dir):
1111 result = version_pat.match(dir_name)
1112 if not result:
1113 continue
1114 version = (result.group(1), result.group(2), result.group(3))
1115 if version > rust_version:
1116 rust_version = version
1117 return '.'.join(rust_version)
1118
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001119 def find_out_files(self):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001120 # list1 has build.rs output for normal crates
1121 list1 = glob.glob(TARGET_TMP + '/*/*/build/' + self.root_pkg + '-*/out/*')
1122 # list2 has build.rs output for proc-macro crates
1123 list2 = glob.glob(TARGET_TMP + '/*/build/' + self.root_pkg + '-*/out/*')
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001124 return list1 + list2
1125
1126 def copy_out_files(self):
1127 """Copy build.rs output files to ./out and set up build_out_files."""
1128 if self.checked_out_files:
1129 return
1130 self.checked_out_files = True
1131 cargo_out_files = self.find_out_files()
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001132 out_files = set()
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001133 if cargo_out_files:
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001134 os.makedirs('out', exist_ok=True)
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001135 for path in cargo_out_files:
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001136 file_name = path.split('/')[-1]
1137 out_files.add(file_name)
1138 shutil.copy(path, 'out/' + file_name)
1139 self.build_out_files = sorted(out_files)
1140
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001141 def has_used_out_dir(self):
1142 """Returns true if env!("OUT_DIR") is found."""
1143 return 0 == os.system('grep -rl --exclude build.rs --include \\*.rs' +
1144 ' \'env!("OUT_DIR")\' * > /dev/null')
1145
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001146 def copy_out_module_name(self):
1147 if self.args.copy_out and self.build_out_files:
1148 return 'copy_' + self.root_pkg + '_build_out'
1149 else:
1150 return ''
1151
Haibo Huang0f72c952021-03-19 11:34:15 -07001152 def read_license(self, name):
1153 if not os.path.isfile(name):
1154 return ''
1155 license = ''
1156 with open(name, 'r') as intf:
1157 line = intf.readline()
1158 # Firstly skip ANDROID_BP_HEADER
1159 while line.startswith('//'):
1160 line = intf.readline()
Joel Galensond9d13b82021-04-05 11:27:55 -07001161 # Read all lines until we see a rust_* or genrule rule.
1162 while line != '' and not (line.startswith('rust_') or line.startswith('genrule {')):
Haibo Huang0f72c952021-03-19 11:34:15 -07001163 license += line
1164 line = intf.readline()
1165 return license.strip()
1166
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001167 def dump_copy_out_module(self, outf):
1168 """Output the genrule module to copy out/* to $(genDir)."""
1169 copy_out = self.copy_out_module_name()
1170 if not copy_out:
1171 return
1172 outf.write('\ngenrule {\n')
1173 outf.write(' name: "' + copy_out + '",\n')
1174 outf.write(' srcs: ["out/*"],\n')
1175 outf.write(' cmd: "cp $(in) $(genDir)",\n')
1176 if len(self.build_out_files) > 1:
1177 outf.write(' out: [\n')
1178 for f in self.build_out_files:
1179 outf.write(' "' + f + '",\n')
1180 outf.write(' ],\n')
1181 else:
1182 outf.write(' out: ["' + self.build_out_files[0] + '"],\n')
1183 outf.write('}\n')
1184
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001185 def init_bp_file(self, name):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001186 # name could be Android.bp or sub_dir_path/Android.bp
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001187 if name not in self.bp_files:
1188 self.bp_files.add(name)
Haibo Huang0f72c952021-03-19 11:34:15 -07001189 license_section = self.read_license(name)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001190 with open(name, 'w') as outf:
Joel Galenson367360c2021-04-29 14:31:43 -07001191 print_args = filter(lambda x: x != "--no-test-mapping", sys.argv[1:])
1192 outf.write(ANDROID_BP_HEADER.format(args=' '.join(print_args)))
Haibo Huang0f72c952021-03-19 11:34:15 -07001193 outf.write('\n')
1194 outf.write(license_section)
1195 outf.write('\n')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001196 # at most one copy_out module per .bp file
1197 self.dump_copy_out_module(outf)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001198
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001199 def try_claim_module_name(self, name, owner):
1200 """Reserve and return True if it has not been reserved yet."""
1201 if name not in self.name_owners or owner == self.name_owners[name]:
1202 self.name_owners[name] = owner
1203 return True
1204 return False
1205
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001206 def claim_module_name(self, prefix, owner, counter):
1207 """Return prefix if not owned yet, otherwise, prefix+str(counter)."""
1208 while True:
1209 name = prefix
1210 if counter > 0:
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001211 name += '_' + str(counter)
1212 if self.try_claim_module_name(name, owner):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001213 return name
1214 counter += 1
1215
1216 def find_root_pkg(self):
1217 """Read name of [package] in ./Cargo.toml."""
1218 if not os.path.exists('./Cargo.toml'):
1219 return
1220 with open('./Cargo.toml', 'r') as inf:
1221 pkg_section = re.compile(r'^ *\[package\]')
1222 name = re.compile('^ *name *= * "([^"]*)"')
1223 in_pkg = False
1224 for line in inf:
1225 if in_pkg:
1226 if name.match(line):
1227 self.root_pkg = name.match(line).group(1)
1228 break
1229 else:
1230 in_pkg = pkg_section.match(line) is not None
1231
1232 def run_cargo(self):
1233 """Calls cargo -v and save its output to ./cargo.out."""
1234 if self.skip_cargo:
1235 return self
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001236 cargo_toml = './Cargo.toml'
1237 cargo_out = './cargo.out'
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001238 # Do not use Cargo.lock, because .bp rules are designed to
1239 # run with "latest" crates avaialable on Android.
1240 cargo_lock = './Cargo.lock'
1241 cargo_lock_saved = './cargo.lock.saved'
1242 had_cargo_lock = os.path.exists(cargo_lock)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001243 if not os.access(cargo_toml, os.R_OK):
1244 print('ERROR: Cannot find or read', cargo_toml)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001245 return self
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001246 if not self.dry_run:
1247 if os.path.exists(cargo_out):
1248 os.remove(cargo_out)
1249 if not self.args.use_cargo_lock and had_cargo_lock: # save it
1250 os.rename(cargo_lock, cargo_lock_saved)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001251 cmd_tail = ' --target-dir ' + TARGET_TMP + ' >> ' + cargo_out + ' 2>&1'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001252 # set up search PATH for cargo to find the correct rustc
1253 saved_path = os.environ['PATH']
1254 os.environ['PATH'] = os.path.dirname(self.cargo_path) + ':' + saved_path
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001255 # Add [workspace] to Cargo.toml if it is not there.
1256 added_workspace = False
1257 if self.args.add_workspace:
1258 with open(cargo_toml, 'r') as in_file:
1259 cargo_toml_lines = in_file.readlines()
1260 found_workspace = '[workspace]\n' in cargo_toml_lines
1261 if found_workspace:
1262 print('### WARNING: found [workspace] in Cargo.toml')
1263 else:
1264 with open(cargo_toml, 'a') as out_file:
1265 out_file.write('[workspace]\n')
1266 added_workspace = True
1267 if self.args.verbose:
1268 print('### INFO: added [workspace] to Cargo.toml')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001269 for c in self.cargo:
1270 features = ''
Chih-Hung Hsieh6c8d52f2020-03-30 18:28:52 -07001271 if c != 'clean':
1272 if self.args.features is not None:
1273 features = ' --no-default-features'
1274 if self.args.features:
1275 features += ' --features ' + self.args.features
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001276 cmd_v_flag = ' -vv ' if self.args.vv else ' -v '
1277 cmd = self.cargo_path + cmd_v_flag
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001278 cmd += c + features + cmd_tail
1279 if self.args.rustflags and c != 'clean':
1280 cmd = 'RUSTFLAGS="' + self.args.rustflags + '" ' + cmd
1281 if self.dry_run:
1282 print('Dry-run skip:', cmd)
1283 else:
1284 if self.args.verbose:
1285 print('Running:', cmd)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001286 with open(cargo_out, 'a') as out_file:
1287 out_file.write('### Running: ' + cmd + '\n')
Joel Galenson6bf54e32021-05-17 10:54:50 -07001288 ret = os.system(cmd)
1289 if ret != 0:
1290 print('*** There was an error while running cargo. ' +
1291 'See the cargo.out file for details.')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001292 if added_workspace: # restore original Cargo.toml
1293 with open(cargo_toml, 'w') as out_file:
1294 out_file.writelines(cargo_toml_lines)
1295 if self.args.verbose:
1296 print('### INFO: restored original Cargo.toml')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001297 os.environ['PATH'] = saved_path
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001298 if not self.dry_run:
1299 if not had_cargo_lock: # restore to no Cargo.lock state
1300 os.remove(cargo_lock)
1301 elif not self.args.use_cargo_lock: # restore saved Cargo.lock
1302 os.rename(cargo_lock_saved, cargo_lock)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001303 return self
1304
1305 def dump_dependencies(self):
1306 """Append dependencies and their features to Android.bp."""
1307 if not self.dependencies:
1308 return
1309 dependent_list = list()
1310 for c in self.dependencies:
1311 dependent_list.append(c.feature_list())
1312 sorted_dependencies = sorted(set(dependent_list))
1313 self.init_bp_file('Android.bp')
1314 with open('Android.bp', 'a') as outf:
1315 outf.write('\n// dependent_library ["feature_list"]\n')
1316 for s in sorted_dependencies:
1317 outf.write('// ' + s + '\n')
1318
1319 def dump_pkg_obj2cc(self):
1320 """Dump debug info of the pkg_obj2cc map."""
1321 if not self.args.debug:
1322 return
1323 self.init_bp_file('Android.bp')
1324 with open('Android.bp', 'a') as outf:
1325 sorted_pkgs = sorted(self.pkg_obj2cc.keys())
1326 for pkg in sorted_pkgs:
1327 if not self.pkg_obj2cc[pkg]:
1328 continue
1329 outf.write('\n// obj => src for %s\n' % pkg)
1330 obj2cc = self.pkg_obj2cc[pkg]
1331 for obj in sorted(obj2cc.keys()):
1332 outf.write('// ' + short_out_name(pkg, obj) + ' => ' +
1333 short_out_name(pkg, obj2cc[obj].src) + '\n')
1334
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001335 def apply_patch(self):
1336 """Apply local patch file if it is given."""
1337 if self.args.patch:
1338 if self.dry_run:
1339 print('Dry-run skip patch file:', self.args.patch)
1340 else:
1341 if not os.path.exists(self.args.patch):
1342 self.append_to_bp('ERROR cannot find patch file: ' + self.args.patch)
1343 return self
1344 if self.args.verbose:
1345 print('### INFO: applying local patch file:', self.args.patch)
Joel Galenson7e8247e2021-05-20 18:51:42 -07001346 subprocess.run(['patch', '-s', '--no-backup-if-mismatch', './Android.bp',
1347 self.args.patch], check=True)
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001348 return self
1349
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001350 def gen_bp(self):
1351 """Parse cargo.out and generate Android.bp files."""
1352 if self.dry_run:
1353 print('Dry-run skip: read', CARGO_OUT, 'write Android.bp')
1354 elif os.path.exists(CARGO_OUT):
1355 self.find_root_pkg()
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001356 if self.args.copy_out:
1357 self.copy_out_files()
1358 elif self.find_out_files() and self.has_used_out_dir():
1359 print('WARNING: ' + self.root_pkg + ' has cargo output files; ' +
1360 'please rerun with the --copy-out flag.')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001361 with open(CARGO_OUT, 'r') as cargo_out:
1362 self.parse(cargo_out, 'Android.bp')
1363 self.crates.sort(key=get_module_name)
1364 for obj in self.cc_objects:
1365 obj.dump()
1366 self.dump_pkg_obj2cc()
1367 for crate in self.crates:
1368 crate.dump()
1369 dumped_libs = set()
1370 for lib in self.ar_objects:
1371 if lib.pkg == self.root_pkg:
1372 lib_name = file_base_name(lib.lib)
1373 if lib_name not in dumped_libs:
1374 dumped_libs.add(lib_name)
1375 lib.dump()
Joel Galenson5664f2a2021-06-10 10:13:49 -07001376 if self.args.add_toplevel_block:
1377 with open(self.args.add_toplevel_block, 'r') as f:
1378 self.append_to_bp('\n' + f.read() + '\n')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001379 if self.args.dependencies and self.dependencies:
1380 self.dump_dependencies()
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001381 if self.errors:
Joel Galenson3f42f802021-04-07 12:42:17 -07001382 self.append_to_bp('\n' + ERRORS_LINE + '\n' + self.errors)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001383 return self
1384
1385 def add_ar_object(self, obj):
1386 self.ar_objects.append(obj)
1387
1388 def add_cc_object(self, obj):
1389 self.cc_objects.append(obj)
1390
1391 def add_crate(self, crate):
1392 """Merge crate with someone in crates, or append to it. Return crates."""
1393 if crate.skip_crate():
1394 if self.args.debug: # include debug info of all crates
1395 self.crates.append(crate)
1396 if self.args.dependencies: # include only dependent crates
1397 if (is_dependent_file_path(crate.main_src) and
1398 not is_build_crate_name(crate.crate_name)):
1399 self.dependencies.append(crate)
1400 else:
1401 for c in self.crates:
1402 if c.merge(crate, 'Android.bp'):
1403 return
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001404 # If not merged, decide module type and name now.
1405 crate.decide_module_type()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001406 self.crates.append(crate)
1407
1408 def find_warning_owners(self):
1409 """For each warning file, find its owner crate."""
1410 missing_owner = False
1411 for f in self.warning_files:
1412 cargo_dir = '' # find lowest crate, with longest path
1413 owner = None # owner crate of this warning
1414 for c in self.crates:
1415 if (f.startswith(c.cargo_dir + '/') and
1416 len(cargo_dir) < len(c.cargo_dir)):
1417 cargo_dir = c.cargo_dir
1418 owner = c
1419 if owner:
1420 owner.has_warning = True
1421 else:
1422 missing_owner = True
1423 if missing_owner and os.path.exists('Cargo.toml'):
1424 # owner is the root cargo, with empty cargo_dir
1425 for c in self.crates:
1426 if not c.cargo_dir:
1427 c.has_warning = True
1428
1429 def rustc_command(self, n, rustc_line, line, outf_name):
1430 """Process a rustc command line from cargo -vv output."""
1431 # cargo build -vv output can have multiple lines for a rustc command
1432 # due to '\n' in strings for environment variables.
1433 # strip removes leading spaces and '\n' at the end
1434 new_rustc = (rustc_line.strip() + line) if rustc_line else line
1435 # Use an heuristic to detect the completions of a multi-line command.
1436 # This might fail for some very rare case, but easy to fix manually.
1437 if not line.endswith('`\n') or (new_rustc.count('`') % 2) != 0:
1438 return new_rustc
1439 if RUSTC_VV_CMD_ARGS.match(new_rustc):
1440 args = RUSTC_VV_CMD_ARGS.match(new_rustc).group(1)
1441 self.add_crate(Crate(self, outf_name).parse(n, args))
1442 else:
1443 self.assert_empty_vv_line(new_rustc)
1444 return ''
1445
1446 def cc_ar_command(self, n, groups, outf_name):
1447 pkg = groups.group(1)
1448 line = groups.group(3)
1449 if groups.group(2) == 'cc':
1450 self.add_cc_object(CCObject(self, outf_name).parse(pkg, n, line))
1451 else:
1452 self.add_ar_object(ARObject(self, outf_name).parse(pkg, n, line))
1453
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001454 def append_to_bp(self, line):
1455 self.init_bp_file('Android.bp')
1456 with open('Android.bp', 'a') as outf:
1457 outf.write(line)
1458
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001459 def assert_empty_vv_line(self, line):
1460 if line: # report error if line is not empty
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001461 self.append_to_bp('ERROR -vv line: ' + line)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001462 return ''
1463
1464 def parse(self, inf, outf_name):
1465 """Parse rustc and warning messages in inf, return a list of Crates."""
1466 n = 0 # line number
1467 prev_warning = False # true if the previous line was warning: ...
1468 rustc_line = '' # previous line(s) matching RUSTC_VV_PAT
1469 for line in inf:
1470 n += 1
1471 if line.startswith('warning: '):
1472 prev_warning = True
1473 rustc_line = self.assert_empty_vv_line(rustc_line)
1474 continue
1475 new_rustc = ''
1476 if RUSTC_PAT.match(line):
1477 args_line = RUSTC_PAT.match(line).group(1)
1478 self.add_crate(Crate(self, outf_name).parse(n, args_line))
1479 self.assert_empty_vv_line(rustc_line)
1480 elif rustc_line or RUSTC_VV_PAT.match(line):
1481 new_rustc = self.rustc_command(n, rustc_line, line, outf_name)
1482 elif CC_AR_VV_PAT.match(line):
1483 self.cc_ar_command(n, CC_AR_VV_PAT.match(line), outf_name)
1484 elif prev_warning and WARNING_FILE_PAT.match(line):
1485 self.assert_empty_vv_line(rustc_line)
1486 fpath = WARNING_FILE_PAT.match(line).group(1)
1487 if fpath[0] != '/': # ignore absolute path
1488 self.warning_files.add(fpath)
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001489 elif line.startswith('error: ') or line.startswith('error[E'):
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001490 if not self.args.ignore_cargo_errors:
1491 self.errors += line
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001492 prev_warning = False
1493 rustc_line = new_rustc
1494 self.find_warning_owners()
1495
1496
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001497def get_parser():
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001498 """Parse main arguments."""
1499 parser = argparse.ArgumentParser('cargo2android')
1500 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001501 '--add_workspace',
1502 action='store_true',
1503 default=False,
1504 help=('append [workspace] to Cargo.toml before calling cargo,' +
1505 ' to treat current directory as root of package source;' +
1506 ' otherwise the relative source file path in generated' +
1507 ' .bp file will be from the parent directory.'))
1508 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001509 '--cargo',
1510 action='append',
1511 metavar='args_string',
1512 help=('extra cargo build -v args in a string, ' +
1513 'each --cargo flag calls cargo build -v once'))
1514 parser.add_argument(
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001515 '--cargo_bin',
1516 type=str,
1517 help='use cargo in the cargo_bin directory instead of the prebuilt one')
1518 parser.add_argument(
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001519 '--copy-out',
1520 action='store_true',
1521 default=False,
1522 help=('only for root directory, ' +
1523 'copy build.rs output to ./out/* and add a genrule to copy ' +
1524 './out/* to genrule output; for crates with code pattern: ' +
1525 'include!(concat!(env!("OUT_DIR"), "/<some_file>.rs"))'))
1526 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001527 '--debug',
1528 action='store_true',
1529 default=False,
1530 help='dump debug info into Android.bp')
1531 parser.add_argument(
1532 '--dependencies',
1533 action='store_true',
1534 default=False,
1535 help='dump debug info of dependent crates')
1536 parser.add_argument(
1537 '--device',
1538 action='store_true',
1539 default=False,
1540 help='run cargo also for a default device target')
1541 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001542 '--features',
1543 type=str,
1544 help=('pass features to cargo build, ' +
1545 'empty string means no default features'))
1546 parser.add_argument(
1547 '--global_defaults',
1548 type=str,
1549 help='add a defaults name to every module')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001550 parser.add_argument(
1551 '--host-first-multilib',
1552 action='store_true',
1553 default=False,
1554 help=('add a compile_multilib:"first" property ' +
1555 'to Android.bp host modules.'))
1556 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001557 '--ignore-cargo-errors',
1558 action='store_true',
1559 default=False,
1560 help='do not append cargo/rustc error messages to Android.bp')
1561 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001562 '--no-host',
1563 action='store_true',
1564 default=False,
1565 help='do not run cargo for the host; only for the device target')
1566 parser.add_argument(
1567 '--no-subdir',
1568 action='store_true',
1569 default=False,
1570 help='do not output anything for sub-directories')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001571 parser.add_argument(
1572 '--onefile',
1573 action='store_true',
1574 default=False,
1575 help=('output all into one ./Android.bp, default will generate ' +
1576 'one Android.bp per Cargo.toml in subdirectories'))
1577 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001578 '--patch',
1579 type=str,
1580 help='apply the given patch file to generated ./Android.bp')
1581 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001582 '--run',
1583 action='store_true',
1584 default=False,
1585 help='run it, default is dry-run')
1586 parser.add_argument('--rustflags', type=str, help='passing flags to rustc')
1587 parser.add_argument(
1588 '--skipcargo',
1589 action='store_true',
1590 default=False,
1591 help='skip cargo command, parse cargo.out, and generate Android.bp')
1592 parser.add_argument(
1593 '--tests',
1594 action='store_true',
1595 default=False,
1596 help='run cargo build --tests after normal build')
1597 parser.add_argument(
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001598 '--use-cargo-lock',
1599 action='store_true',
1600 default=False,
1601 help=('run cargo build with existing Cargo.lock ' +
1602 '(used when some latest dependent crates failed)'))
1603 parser.add_argument(
Joel Galensond9c4de62021-04-23 10:26:40 -07001604 '--min-sdk-version',
1605 type=str,
1606 help='Minimum SDK version')
1607 parser.add_argument(
1608 '--apex-available',
1609 nargs='*',
1610 help='Mark the main library as apex_available with the given apexes.')
1611 parser.add_argument(
Joel Galensoncb5f2f02021-06-08 14:47:55 -07001612 '--force-rlib',
1613 action='store_true',
1614 default=False,
1615 help='Make the main library an rlib.')
1616 parser.add_argument(
Joel Galenson12467e52021-07-12 14:33:28 -07001617 '--whole-static-libs',
1618 nargs='*',
1619 default=[],
1620 help='Make the given libraries (without lib prefixes) whole_static_libs.')
1621 parser.add_argument(
Joel Galenson97e414a2021-05-27 09:42:32 -07001622 '--dependency-blocklist',
1623 nargs='*',
1624 default=[],
Joel Galenson12467e52021-07-12 14:33:28 -07001625 help='Do not emit the given dependencies (without lib prefixes).')
Joel Galenson97e414a2021-05-27 09:42:32 -07001626 parser.add_argument(
Joel Galensoncb5f2f02021-06-08 14:47:55 -07001627 '--lib-blocklist',
1628 nargs='*',
1629 default=[],
Joel Galenson12467e52021-07-12 14:33:28 -07001630 help='Do not emit the given C libraries as dependencies (without lib prefixes).')
Joel Galensoncb5f2f02021-06-08 14:47:55 -07001631 parser.add_argument(
Joel Galensonf6b3c912021-06-03 16:00:54 -07001632 '--test-blocklist',
1633 nargs='*',
1634 default=[],
1635 help=('Do not emit the given tests. ' +
1636 'Pass the path to the test file to exclude.'))
1637 parser.add_argument(
Joel Galenson3d6d1e72021-06-07 15:00:24 -07001638 '--cfg-blocklist',
1639 nargs='*',
1640 default=[],
1641 help='Do not emit the given cfg.')
1642 parser.add_argument(
Joel Galenson5664f2a2021-06-10 10:13:49 -07001643 '--add-toplevel-block',
1644 type=str,
1645 help='Add the contents of the given file to the top level of the Android.bp.')
1646 parser.add_argument(
1647 '--add-module-block',
1648 type=str,
1649 help='Add the contents of the given file to the main module.')
1650 parser.add_argument(
Joel Galensoncf4ebb02021-04-07 08:39:51 -07001651 '--no-test-mapping',
1652 action='store_true',
1653 default=False,
ThiƩbaud Weksteen198e93f2021-07-02 14:49:19 +02001654 help='Deprecated. Has no effect.')
Joel Galensoncf4ebb02021-04-07 08:39:51 -07001655 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001656 '--verbose',
1657 action='store_true',
1658 default=False,
1659 help='echo executed commands')
1660 parser.add_argument(
1661 '--vv',
1662 action='store_true',
1663 default=False,
1664 help='run cargo with -vv instead of default -v')
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001665 parser.add_argument(
1666 '--dump-config-and-exit',
1667 type=str,
1668 help=('Dump command-line arguments (minus this flag) to a config file and exit. ' +
1669 'This is intended to help migrate from command line options to config files.'))
1670 parser.add_argument(
1671 '--config',
1672 type=str,
1673 help=('Load command-line options from the given config file. ' +
1674 'Options in this file will override those passed on the command line.'))
1675 return parser
1676
1677
1678def parse_args(parser):
1679 """Parses command-line options."""
1680 args = parser.parse_args()
1681 # Use the values specified in a config file if one was found.
1682 if args.config:
1683 with open(args.config, 'r') as f:
1684 config = json.load(f)
1685 args_dict = vars(args)
1686 for arg in config:
1687 args_dict[arg.replace('-', '_')] = config[arg]
1688 return args
1689
1690
1691def dump_config(parser, args):
1692 """Writes the non-default command-line options to the specified file."""
1693 args_dict = vars(args)
1694 # Filter out the arguments that have their default value.
Joel Galenson367360c2021-04-29 14:31:43 -07001695 # Also filter certain "temporary" arguments.
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001696 non_default_args = {}
1697 for arg in args_dict:
Joel Galenson367360c2021-04-29 14:31:43 -07001698 if args_dict[arg] != parser.get_default(
1699 arg) and arg != 'dump_config_and_exit' and arg != 'no_test_mapping':
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001700 non_default_args[arg.replace('_', '-')] = args_dict[arg]
1701 # Write to the specified file.
1702 with open(args.dump_config_and_exit, 'w') as f:
1703 json.dump(non_default_args, f, indent=2, sort_keys=True)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001704
1705
1706def main():
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001707 parser = get_parser()
1708 args = parse_args(parser)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001709 if not args.run: # default is dry-run
1710 print(DRY_RUN_NOTE)
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001711 if args.dump_config_and_exit:
1712 dump_config(parser, args)
1713 else:
ThiƩbaud Weksteen198e93f2021-07-02 14:49:19 +02001714 Runner(args).run_cargo().gen_bp().apply_patch()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001715
1716
1717if __name__ == '__main__':
1718 main()