blob: e68b862c4c84d1b9db58d55578aa9689bf5f2cda [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 Hsiehf7eff152020-07-16 15:36:22 -070042 Note that when there are test modules generated into Android.bp,
43 corresponding test entries will also be added into the TEST_MAPPING file.
44
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
50
51import argparse
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -070052import glob
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080053import os
54import os.path
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -070055import platform
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080056import re
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -070057import shutil
Andrew Walbran80e90be2020-06-09 14:33:18 +010058import sys
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080059
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -070060# Some Rust packages include extra unwanted crates.
61# This set contains all such excluded crate names.
62EXCLUDED_CRATES = set(['protobuf_bin_gen_rust_do_not_use'])
63
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080064RENAME_MAP = {
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -070065 # This map includes all changes to the default rust module names
66 # to resolve name conflicts, avoid confusion, or work as plugin.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080067 'libbacktrace': 'libbacktrace_rust',
Andrew Walbrane51f1042020-08-11 16:42:48 +010068 'libbase': 'libbase_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',
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -070075 'protoc_gen_rust': 'protoc-gen-rust',
76}
77
78RENAME_STEM_MAP = {
79 # This map includes all changes to the default rust module stem names,
80 # which is used for output files when different from the module name.
81 'protoc_gen_rust': 'protoc-gen-rust',
82}
83
84RENAME_DEFAULTS_MAP = {
85 # This map includes all changes to the default prefix of rust_default
86 # module names, to avoid conflict with existing Android modules.
87 'libc': 'rust_libc',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080088}
89
90# Header added to all generated Android.bp files.
Andrew Walbran80e90be2020-06-09 14:33:18 +010091ANDROID_BP_HEADER = '// This file is generated by cargo2android.py {args}.\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080092
93CARGO_OUT = 'cargo.out' # Name of file to keep cargo build -v output.
94
95TARGET_TMP = 'target.tmp' # Name of temporary output directory.
96
97# Message to be displayed when this script is called without the --run flag.
98DRY_RUN_NOTE = (
99 'Dry-run: This script uses ./' + TARGET_TMP + ' for output directory,\n' +
100 'runs cargo clean, runs cargo build -v, saves output to ./cargo.out,\n' +
101 'and writes to Android.bp in the current and subdirectories.\n\n' +
102 'To do do all of the above, use the --run flag.\n' +
103 'See --help for other flags, and more usage notes in this script.\n')
104
105# Cargo -v output of a call to rustc.
106RUSTC_PAT = re.compile('^ +Running `rustc (.*)`$')
107
108# Cargo -vv output of a call to rustc could be split into multiple lines.
109# Assume that the first line will contain some CARGO_* env definition.
110RUSTC_VV_PAT = re.compile('^ +Running `.*CARGO_.*=.*$')
111# The combined -vv output rustc command line pattern.
112RUSTC_VV_CMD_ARGS = re.compile('^ *Running `.*CARGO_.*=.* rustc (.*)`$')
113
114# Cargo -vv output of a "cc" or "ar" command; all in one line.
115CC_AR_VV_PAT = re.compile(r'^\[([^ ]*)[^\]]*\] running:? "(cc|ar)" (.*)$')
116# Some package, such as ring-0.13.5, has pattern '... running "cc"'.
117
118# Rustc output of file location path pattern for a warning message.
119WARNING_FILE_PAT = re.compile('^ *--> ([^:]*):[0-9]+')
120
121# Rust package name with suffix -d1.d2.d3.
122VERSION_SUFFIX_PAT = re.compile(r'^(.*)-[0-9]+\.[0-9]+\.[0-9]+$')
123
124
125def altered_name(name):
126 return RENAME_MAP[name] if (name in RENAME_MAP) else name
127
128
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700129def altered_stem(name):
130 return RENAME_STEM_MAP[name] if (name in RENAME_STEM_MAP) else name
131
132
133def altered_defaults(name):
134 return RENAME_DEFAULTS_MAP[name] if (name in RENAME_DEFAULTS_MAP) else name
135
136
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800137def is_build_crate_name(name):
138 # We added special prefix to build script crate names.
139 return name.startswith('build_script_')
140
141
142def is_dependent_file_path(path):
143 # Absolute or dependent '.../' paths are not main files of this crate.
144 return path.startswith('/') or path.startswith('.../')
145
146
147def get_module_name(crate): # to sort crates in a list
148 return crate.module_name
149
150
151def pkg2crate_name(s):
152 return s.replace('-', '_').replace('.', '_')
153
154
155def file_base_name(path):
156 return os.path.splitext(os.path.basename(path))[0]
157
158
159def test_base_name(path):
160 return pkg2crate_name(file_base_name(path))
161
162
163def unquote(s): # remove quotes around str
164 if s and len(s) > 1 and s[0] == '"' and s[-1] == '"':
165 return s[1:-1]
166 return s
167
168
169def remove_version_suffix(s): # remove -d1.d2.d3 suffix
170 if VERSION_SUFFIX_PAT.match(s):
171 return VERSION_SUFFIX_PAT.match(s).group(1)
172 return s
173
174
175def short_out_name(pkg, s): # replace /.../pkg-*/out/* with .../out/*
176 return re.sub('^/.*/' + pkg + '-[0-9a-f]*/out/', '.../out/', s)
177
178
179def escape_quotes(s): # replace '"' with '\\"'
180 return s.replace('"', '\\"')
181
182
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700183class TestMapping(object):
184 """Entries for a TEST_MAPPING file."""
185
186 def __init__(self):
187 self.entries = []
188
189 def add_test(self, name, host):
190 self.entries.append((name, host))
191
192 def is_empty(self):
193 return not self.entries
194
195 def dump(self, outf_name):
196 """Append all entries into the output file."""
197 if self.is_empty():
198 return
199 with open(outf_name, 'w') as outf:
200 outf.write('// Generated by cargo2android.py for tests in Android.bp\n')
201 outf.write('{\n "presubmit": [\n')
202 is_first = True
203 for (name, host) in self.entries:
204 if not is_first: # add comma and '\n' after the previous entry
205 outf.write(',\n')
206 is_first = False
Jeff Vander Stoepd11be482020-11-05 16:13:36 +0100207 outf.write(' {\n')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700208 if host:
Jeff Vander Stoepd11be482020-11-05 16:13:36 +0100209 outf.write(' "host": true,\n')
210 outf.write(' "name": "' + name + '"' + '\n }')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700211 outf.write('\n ]\n}\n')
212
213
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800214class Crate(object):
215 """Information of a Rust crate to collect/emit for an Android.bp module."""
216
217 def __init__(self, runner, outf_name):
218 # Remembered global runner and its members.
219 self.runner = runner
220 self.debug = runner.args.debug
221 self.cargo_dir = '' # directory of my Cargo.toml
222 self.outf_name = outf_name # path to Android.bp
223 self.outf = None # open file handle of outf_name during dump*
224 # Variants/results that could be merged from multiple rustc lines.
225 self.host_supported = False
226 self.device_supported = False
227 self.has_warning = False
228 # Android module properties derived from rustc parameters.
229 self.module_name = '' # unique in Android build system
230 self.module_type = '' # rust_{binary,library,test}[_host] etc.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700231 self.defaults = '' # rust_defaults used by rust_test* modules
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700232 self.default_srcs = False # use 'srcs' defined in self.defaults
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800233 self.root_pkg = '' # parent package name of a sub/test packge, from -L
234 self.srcs = list() # main_src or merged multiple source files
235 self.stem = '' # real base name of output file
236 # Kept parsed status
237 self.errors = '' # all errors found during parsing
238 self.line_num = 1 # runner told input source line number
239 self.line = '' # original rustc command line parameters
240 # Parameters collected from rustc command line.
241 self.crate_name = '' # follows --crate-name
242 self.main_src = '' # follows crate_name parameter, shortened
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700243 self.crate_types = list() # follows --crate-type
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800244 self.cfgs = list() # follows --cfg, without feature= prefix
245 self.features = list() # follows --cfg, name in 'feature="..."'
246 self.codegens = list() # follows -C, some ignored
247 self.externs = list() # follows --extern
248 self.core_externs = list() # first part of self.externs elements
249 self.static_libs = list() # e.g. -l static=host_cpuid
250 self.shared_libs = list() # e.g. -l dylib=wayland-client, -l z
251 self.cap_lints = '' # follows --cap-lints
252 self.emit_list = '' # e.g., --emit=dep-info,metadata,link
253 self.edition = '2015' # rustc default, e.g., --edition=2018
254 self.target = '' # follows --target
255
256 def write(self, s):
257 # convenient way to output one line at a time with EOL.
258 self.outf.write(s + '\n')
259
260 def same_flags(self, other):
261 # host_supported, device_supported, has_warning are not compared but merged
262 # target is not compared, to merge different target/host modules
263 # externs is not compared; only core_externs is compared
264 return (not self.errors and not other.errors and
265 self.edition == other.edition and
266 self.cap_lints == other.cap_lints and
267 self.emit_list == other.emit_list and
268 self.core_externs == other.core_externs and
269 self.codegens == other.codegens and
270 self.features == other.features and
271 self.static_libs == other.static_libs and
272 self.shared_libs == other.shared_libs and self.cfgs == other.cfgs)
273
274 def merge_host_device(self, other):
275 """Returns true if attributes are the same except host/device support."""
276 return (self.crate_name == other.crate_name and
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700277 self.crate_types == other.crate_types and
278 self.main_src == other.main_src and
279 # before merge, each test module has an unique module name and stem
280 (self.stem == other.stem or self.crate_types == ['test']) and
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800281 self.root_pkg == other.root_pkg and not self.skip_crate() and
282 self.same_flags(other))
283
284 def merge_test(self, other):
285 """Returns true if self and other are tests of same root_pkg."""
286 # Before merger, each test has its own crate_name.
287 # A merged test uses its source file base name as output file name,
288 # so a test is mergeable only if its base name equals to its crate name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700289 return (self.crate_types == other.crate_types and
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700290 self.crate_types == ['test'] and self.root_pkg == other.root_pkg and
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800291 not self.skip_crate() and
292 other.crate_name == test_base_name(other.main_src) and
293 (len(self.srcs) > 1 or
294 (self.crate_name == test_base_name(self.main_src)) and
295 self.host_supported == other.host_supported and
296 self.device_supported == other.device_supported) and
297 self.same_flags(other))
298
299 def merge(self, other, outf_name):
300 """Try to merge crate into self."""
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700301 # Cargo build --tests could recompile a library for tests.
302 # We need to merge such duplicated calls to rustc, with
303 # the algorithm in merge_host_device.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800304 should_merge_host_device = self.merge_host_device(other)
305 should_merge_test = False
306 if not should_merge_host_device:
307 should_merge_test = self.merge_test(other)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800308 if should_merge_host_device or should_merge_test:
309 self.runner.init_bp_file(outf_name)
310 with open(outf_name, 'a') as outf: # to write debug info
311 self.outf = outf
312 other.outf = outf
313 self.do_merge(other, should_merge_test)
314 return True
315 return False
316
317 def do_merge(self, other, should_merge_test):
318 """Merge attributes of other to self."""
319 if self.debug:
320 self.write('\n// Before merge definition (1):')
321 self.dump_debug_info()
322 self.write('\n// Before merge definition (2):')
323 other.dump_debug_info()
324 # Merge properties of other to self.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800325 self.has_warning = self.has_warning or other.has_warning
326 if not self.target: # okay to keep only the first target triple
327 self.target = other.target
328 # decide_module_type sets up default self.stem,
329 # which can be changed if self is a merged test module.
330 self.decide_module_type()
331 if should_merge_test:
332 self.srcs.append(other.main_src)
333 # use a short unique name as the merged module name.
334 prefix = self.root_pkg + '_tests'
335 self.module_name = self.runner.claim_module_name(prefix, self, 0)
336 self.stem = self.module_name
337 # This normalized root_pkg name although might be the same
338 # as other module's crate_name, it is not actually used for
339 # output file name. A merged test module always have multiple
340 # source files and each source file base name is used as
341 # its output file name.
342 self.crate_name = pkg2crate_name(self.root_pkg)
343 if self.debug:
344 self.write('\n// After merge definition (1):')
345 self.dump_debug_info()
346
347 def find_cargo_dir(self):
348 """Deepest directory with Cargo.toml and contains the main_src."""
349 if not is_dependent_file_path(self.main_src):
350 dir_name = os.path.dirname(self.main_src)
351 while dir_name:
352 if os.path.exists(dir_name + '/Cargo.toml'):
353 self.cargo_dir = dir_name
354 return
355 dir_name = os.path.dirname(dir_name)
356
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700357 def add_codegens_flag(self, flag):
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700358 """Ignore options not used in Android."""
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700359 # 'prefer-dynamic' does not work with common flag -C lto
Chih-Hung Hsieh63459ed2020-08-26 11:51:15 -0700360 # 'embed-bitcode' is ignored; we might control LTO with other .bp flag
Chih-Hung Hsieh6c13b722020-09-11 21:24:03 -0700361 # 'codegen-units' is set in Android global config or by default
362 if not (flag.startswith('codegen-units=') or
363 flag.startswith('debuginfo=') or
Chih-Hung Hsieh63459ed2020-08-26 11:51:15 -0700364 flag.startswith('embed-bitcode=') or
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700365 flag.startswith('extra-filename=') or
366 flag.startswith('incremental=') or
367 flag.startswith('metadata=') or
368 flag == 'prefer-dynamic'):
369 self.codegens.append(flag)
370
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800371 def parse(self, line_num, line):
372 """Find important rustc arguments to convert to Android.bp properties."""
373 self.line_num = line_num
374 self.line = line
375 args = line.split() # Loop through every argument of rustc.
376 i = 0
377 while i < len(args):
378 arg = args[i]
379 if arg == '--crate-name':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700380 i += 1
381 self.crate_name = args[i]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800382 elif arg == '--crate-type':
383 i += 1
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700384 # cargo calls rustc with multiple --crate-type flags.
385 # rustc can accept:
386 # --crate-type [bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]
387 self.crate_types.append(args[i])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800388 elif arg == '--test':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700389 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800390 elif arg == '--target':
391 i += 1
392 self.target = args[i]
393 elif arg == '--cfg':
394 i += 1
395 if args[i].startswith('\'feature='):
396 self.features.append(unquote(args[i].replace('\'feature=', '')[:-1]))
397 else:
398 self.cfgs.append(args[i])
399 elif arg == '--extern':
400 i += 1
401 extern_names = re.sub('=/[^ ]*/deps/', ' = ', args[i])
402 self.externs.append(extern_names)
403 self.core_externs.append(re.sub(' = .*', '', extern_names))
404 elif arg == '-C': # codegen options
405 i += 1
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700406 self.add_codegens_flag(args[i])
407 elif arg.startswith('-C'):
408 # cargo has been passing "-C <xyz>" flag to rustc,
409 # but newer cargo could pass '-Cembed-bitcode=no' to rustc.
410 self.add_codegens_flag(arg[2:])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800411 elif arg == '--cap-lints':
412 i += 1
413 self.cap_lints = args[i]
414 elif arg == '-L':
415 i += 1
416 if args[i].startswith('dependency=') and args[i].endswith('/deps'):
417 if '/' + TARGET_TMP + '/' in args[i]:
418 self.root_pkg = re.sub(
419 '^.*/', '', re.sub('/' + TARGET_TMP + '/.*/deps$', '', args[i]))
420 else:
421 self.root_pkg = re.sub('^.*/', '',
422 re.sub('/[^/]+/[^/]+/deps$', '', args[i]))
423 self.root_pkg = remove_version_suffix(self.root_pkg)
424 elif arg == '-l':
425 i += 1
426 if args[i].startswith('static='):
427 self.static_libs.append(re.sub('static=', '', args[i]))
428 elif args[i].startswith('dylib='):
429 self.shared_libs.append(re.sub('dylib=', '', args[i]))
430 else:
431 self.shared_libs.append(args[i])
432 elif arg == '--out-dir' or arg == '--color': # ignored
433 i += 1
434 elif arg.startswith('--error-format=') or arg.startswith('--json='):
435 _ = arg # ignored
436 elif arg.startswith('--emit='):
437 self.emit_list = arg.replace('--emit=', '')
438 elif arg.startswith('--edition='):
439 self.edition = arg.replace('--edition=', '')
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700440 elif not arg.startswith('-'):
441 # shorten imported crate main source paths like $HOME/.cargo/
442 # registry/src/github.com-1ecc6299db9ec823/memchr-2.3.3/src/lib.rs
443 self.main_src = re.sub(r'^/[^ ]*/registry/src/', '.../', args[i])
444 self.main_src = re.sub(r'^\.\.\./github.com-[0-9a-f]*/', '.../',
445 self.main_src)
446 self.find_cargo_dir()
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700447 if self.cargo_dir: # for a subdirectory
448 if self.runner.args.no_subdir: # all .bp content to /dev/null
449 self.outf_name = '/dev/null'
450 elif not self.runner.args.onefile:
451 # Write to Android.bp in the subdirectory with Cargo.toml.
452 self.outf_name = self.cargo_dir + '/Android.bp'
453 self.main_src = self.main_src[len(self.cargo_dir) + 1:]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800454 else:
455 self.errors += 'ERROR: unknown ' + arg + '\n'
456 i += 1
457 if not self.crate_name:
458 self.errors += 'ERROR: missing --crate-name\n'
459 if not self.main_src:
460 self.errors += 'ERROR: missing main source file\n'
461 else:
462 self.srcs.append(self.main_src)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700463 if not self.crate_types:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800464 # Treat "--cfg test" as "--test"
465 if 'test' in self.cfgs:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700466 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800467 else:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700468 self.errors += 'ERROR: missing --crate-type or --test\n'
469 elif len(self.crate_types) > 1:
470 if 'test' in self.crate_types:
471 self.errors += 'ERROR: cannot handle both --crate-type and --test\n'
472 if 'lib' in self.crate_types and 'rlib' in self.crate_types:
473 self.errors += 'ERROR: cannot generate both lib and rlib crate types\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800474 if not self.root_pkg:
475 self.root_pkg = self.crate_name
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700476 self.device_supported = self.runner.args.device
477 self.host_supported = not self.runner.args.no_host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800478 self.cfgs = sorted(set(self.cfgs))
479 self.features = sorted(set(self.features))
480 self.codegens = sorted(set(self.codegens))
481 self.externs = sorted(set(self.externs))
482 self.core_externs = sorted(set(self.core_externs))
483 self.static_libs = sorted(set(self.static_libs))
484 self.shared_libs = sorted(set(self.shared_libs))
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700485 self.crate_types = sorted(set(self.crate_types))
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800486 self.decide_module_type()
487 self.module_name = altered_name(self.stem)
488 return self
489
490 def dump_line(self):
491 self.write('\n// Line ' + str(self.line_num) + ' ' + self.line)
492
493 def feature_list(self):
494 """Return a string of main_src + "feature_list"."""
495 pkg = self.main_src
496 if pkg.startswith('.../'): # keep only the main package name
497 pkg = re.sub('/.*', '', pkg[4:])
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700498 elif pkg.startswith('/'): # use relative path for a local package
499 pkg = os.path.relpath(pkg)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800500 if not self.features:
501 return pkg
502 return pkg + ' "' + ','.join(self.features) + '"'
503
504 def dump_skip_crate(self, kind):
505 if self.debug:
506 self.write('\n// IGNORED: ' + kind + ' ' + self.main_src)
507 return self
508
509 def skip_crate(self):
510 """Return crate_name or a message if this crate should be skipped."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700511 if (is_build_crate_name(self.crate_name) or
512 self.crate_name in EXCLUDED_CRATES):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800513 return self.crate_name
514 if is_dependent_file_path(self.main_src):
515 return 'dependent crate'
516 return ''
517
518 def dump(self):
519 """Dump all error/debug/module code to the output .bp file."""
520 self.runner.init_bp_file(self.outf_name)
521 with open(self.outf_name, 'a') as outf:
522 self.outf = outf
523 if self.errors:
524 self.dump_line()
525 self.write(self.errors)
526 elif self.skip_crate():
527 self.dump_skip_crate(self.skip_crate())
528 else:
529 if self.debug:
530 self.dump_debug_info()
531 self.dump_android_module()
532
533 def dump_debug_info(self):
534 """Dump parsed data, when cargo2android is called with --debug."""
535
536 def dump(name, value):
537 self.write('//%12s = %s' % (name, value))
538
539 def opt_dump(name, value):
540 if value:
541 dump(name, value)
542
543 def dump_list(fmt, values):
544 for v in values:
545 self.write(fmt % v)
546
547 self.dump_line()
548 dump('module_name', self.module_name)
549 dump('crate_name', self.crate_name)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700550 dump('crate_types', self.crate_types)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800551 dump('main_src', self.main_src)
552 dump('has_warning', self.has_warning)
553 dump('for_host', self.host_supported)
554 dump('for_device', self.device_supported)
555 dump('module_type', self.module_type)
556 opt_dump('target', self.target)
557 opt_dump('edition', self.edition)
558 opt_dump('emit_list', self.emit_list)
559 opt_dump('cap_lints', self.cap_lints)
560 dump_list('// cfg = %s', self.cfgs)
561 dump_list('// cfg = \'feature "%s"\'', self.features)
562 # TODO(chh): escape quotes in self.features, but not in other dump_list
563 dump_list('// codegen = %s', self.codegens)
564 dump_list('// externs = %s', self.externs)
565 dump_list('// -l static = %s', self.static_libs)
566 dump_list('// -l (dylib) = %s', self.shared_libs)
567
568 def dump_android_module(self):
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700569 """Dump one or more Android module definition, depending on crate_types."""
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700570 if len(self.crate_types) == 1:
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700571 self.dump_single_type_android_module()
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700572 return
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700573 if 'test' in self.crate_types:
574 self.write('\nERROR: multiple crate types cannot include test type')
575 return
576 # Dump one Android module per crate_type.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700577 for crate_type in self.crate_types:
578 self.decide_one_module_type(crate_type)
579 self.dump_one_android_module(crate_type)
580
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700581 def build_default_name(self):
582 """Return a short and readable name for the rust_defaults module."""
583 # Choices: (1) root_pkg + '_defaults',
584 # (2) root_pkg + '_defaults_' + crate_name
585 # (3) root_pkg + '_defaults_' + main_src_basename_path
586 # (4) root_pkg + '_defaults_' + a_positive_sequence_number
587 name1 = altered_defaults(self.root_pkg) + '_defaults'
588 if self.runner.try_claim_module_name(name1, self):
589 return name1
590 name2 = name1 + '_' + self.crate_name
591 if self.runner.try_claim_module_name(name2, self):
592 return name2
593 name3 = name1 + '_' + self.main_src_basename_path()
594 if self.runner.try_claim_module_name(name3, self):
595 return name3
596 return self.runner.claim_module_name(name1, self, 0)
597
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700598 def dump_srcs_list(self):
599 """Dump the srcs list, for defaults or regular modules."""
600 if len(self.srcs) > 1:
601 srcs = sorted(set(self.srcs)) # make a copy and dedup
602 else:
603 srcs = [self.main_src]
604 copy_out = self.runner.copy_out_module_name()
605 if copy_out:
606 srcs.append(':' + copy_out)
607 self.dump_android_property_list('srcs', '"%s"', srcs)
608
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700609 def dump_defaults_module(self):
610 """Dump a rust_defaults module to be shared by other modules."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700611 name = self.build_default_name()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700612 self.defaults = name
613 self.write('\nrust_defaults {')
614 self.write(' name: "' + name + '",')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700615 if self.runner.args.global_defaults:
616 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700617 self.write(' crate_name: "' + self.crate_name + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700618 if len(self.srcs) == 1: # only one source file; share it in defaults
619 self.default_srcs = True
620 if self.has_warning and not self.cap_lints:
621 self.write(' // has rustc warnings')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700622 self.dump_srcs_list()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700623 if 'test' in self.crate_types:
624 self.write(' test_suites: ["general-tests"],')
625 self.write(' auto_gen_config: true,')
626 self.dump_edition_flags_libs()
627 self.write('}')
628
629 def dump_single_type_android_module(self):
630 """Dump one simple Android module, which has only one crate_type."""
631 crate_type = self.crate_types[0]
632 if crate_type != 'test':
633 # do not change self.stem or self.module_name
634 self.dump_one_android_module(crate_type)
635 return
636 # Dump one test module per source file, and separate host and device tests.
637 # crate_type == 'test'
638 if (self.host_supported and self.device_supported) or len(self.srcs) > 1:
639 self.srcs = sorted(set(self.srcs))
640 self.dump_defaults_module()
641 saved_srcs = self.srcs
642 for src in saved_srcs:
643 self.srcs = [src]
644 saved_device_supported = self.device_supported
645 saved_host_supported = self.host_supported
646 saved_main_src = self.main_src
647 self.main_src = src
648 if saved_host_supported:
649 self.device_supported = False
650 self.host_supported = True
651 self.module_name = self.test_module_name()
652 self.decide_one_module_type(crate_type)
653 self.dump_one_android_module(crate_type)
Joel Galensone261a152021-01-12 11:31:53 -0800654 # We do not add host tests, as these are handled in the Android.bp file.
655 # self.runner.add_test(self.outf_name, self.module_name, True)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700656 if saved_device_supported:
657 self.device_supported = True
658 self.host_supported = False
659 self.module_name = self.test_module_name()
660 self.decide_one_module_type(crate_type)
661 self.dump_one_android_module(crate_type)
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700662 self.runner.add_test(self.outf_name, self.module_name, False)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700663 self.host_supported = saved_host_supported
664 self.device_supported = saved_device_supported
665 self.main_src = saved_main_src
666 self.srcs = saved_srcs
667
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700668 def dump_one_android_module(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800669 """Dump one Android module definition."""
670 if not self.module_type:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700671 self.write('\nERROR: unknown crate_type ' + crate_type)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800672 return
673 self.write('\n' + self.module_type + ' {')
674 self.dump_android_core_properties()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700675 if not self.defaults:
676 self.dump_edition_flags_libs()
677 if self.runner.args.host_first_multilib and self.host_supported and crate_type != 'test':
678 self.write(' compile_multilib: "first",')
679 self.write('}')
680
681 def dump_android_flags(self):
682 """Dump Android module flags property."""
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700683 if not self.cfgs and not self.codegens and not self.cap_lints:
684 return
685 self.write(' flags: [')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800686 if self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700687 self.write(' "--cap-lints ' + self.cap_lints + '",')
688 cfg_fmt = '"--cfg %s"'
689 codegens_fmt = '"-C %s"'
690 self.dump_android_property_list_items(cfg_fmt, self.cfgs)
691 self.dump_android_property_list_items(codegens_fmt, self.codegens)
692 self.write(' ],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700693
694 def dump_edition_flags_libs(self):
695 if self.edition:
696 self.write(' edition: "' + self.edition + '",')
697 self.dump_android_property_list('features', '"%s"', self.features)
698 self.dump_android_flags()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800699 if self.externs:
700 self.dump_android_externs()
701 self.dump_android_property_list('static_libs', '"lib%s"', self.static_libs)
702 self.dump_android_property_list('shared_libs', '"lib%s"', self.shared_libs)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800703
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700704 def main_src_basename_path(self):
705 return re.sub('/', '_', re.sub('.rs$', '', self.main_src))
706
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800707 def test_module_name(self):
708 """Return a unique name for a test module."""
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700709 # root_pkg+(_host|_device) + '_test_'+source_file_name
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700710 suffix = self.main_src_basename_path()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700711 host_device = '_host'
712 if self.device_supported:
713 host_device = '_device'
714 return self.root_pkg + host_device + '_test_' + suffix
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800715
716 def decide_module_type(self):
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700717 # Use the first crate type for the default/first module.
718 crate_type = self.crate_types[0] if self.crate_types else ''
719 self.decide_one_module_type(crate_type)
720
721 def decide_one_module_type(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800722 """Decide which Android module type to use."""
723 host = '' if self.device_supported else '_host'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700724 if crate_type == 'bin': # rust_binary[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800725 self.module_type = 'rust_binary' + host
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700726 # In rare cases like protobuf-codegen, the output binary name must
727 # be renamed to use as a plugin for protoc.
728 self.stem = altered_stem(self.crate_name)
729 self.module_name = altered_name(self.crate_name)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700730 elif crate_type == 'lib': # rust_library[_host]
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700731 # TODO(chh): should this be rust_library[_host]?
732 # Assuming that Cargo.toml do not use both 'lib' and 'rlib',
733 # because we map them both to rlib.
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700734 self.module_type = 'rust_library' + host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800735 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700736 self.module_name = altered_name(self.stem)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700737 elif crate_type == 'rlib': # rust_library[_host]
738 self.module_type = 'rust_library' + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700739 self.stem = 'lib' + self.crate_name
740 self.module_name = altered_name(self.stem)
741 elif crate_type == 'dylib': # rust_library[_host]_dylib
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800742 self.module_type = 'rust_library' + host + '_dylib'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700743 self.stem = 'lib' + self.crate_name
744 self.module_name = altered_name(self.stem) + '_dylib'
745 elif crate_type == 'cdylib': # rust_library[_host]_shared
746 self.module_type = 'rust_library' + host + '_shared'
747 self.stem = 'lib' + self.crate_name
748 self.module_name = altered_name(self.stem) + '_shared'
749 elif crate_type == 'staticlib': # rust_library[_host]_static
750 self.module_type = 'rust_library' + host + '_static'
751 self.stem = 'lib' + self.crate_name
752 self.module_name = altered_name(self.stem) + '_static'
753 elif crate_type == 'test': # rust_test[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800754 self.module_type = 'rust_test' + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700755 # Before do_merge, stem name is based on the --crate-name parameter.
756 # and test module name is based on stem.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800757 self.stem = self.test_module_name()
758 # self.stem will be changed after merging with other tests.
759 # self.stem is NOT used for final test binary name.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700760 # rust_test uses each source file base name as part of output file name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700761 # In do_merge, this function is called again, with a module_name.
762 # We make sure that the module name is unique in each package.
763 if self.module_name:
764 # Cargo uses "-C extra-filename=..." and "-C metadata=..." to add
765 # different suffixes and distinguish multiple tests of the same
766 # crate name. We ignore -C and use claim_module_name to get
767 # unique sequential suffix.
768 self.module_name = self.runner.claim_module_name(
769 self.module_name, self, 0)
770 # Now the module name is unique, stem should also match and unique.
771 self.stem = self.module_name
772 elif crate_type == 'proc-macro': # rust_proc_macro
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800773 self.module_type = 'rust_proc_macro'
774 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700775 self.module_name = altered_name(self.stem)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800776 else: # unknown module type, rust_prebuilt_dylib? rust_library[_host]?
777 self.module_type = ''
778 self.stem = ''
779
780 def dump_android_property_list_items(self, fmt, values):
781 for v in values:
782 # fmt has quotes, so we need escape_quotes(v)
783 self.write(' ' + (fmt % escape_quotes(v)) + ',')
784
785 def dump_android_property_list(self, name, fmt, values):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700786 if not values:
787 return
788 if len(values) > 1:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800789 self.write(' ' + name + ': [')
790 self.dump_android_property_list_items(fmt, values)
791 self.write(' ],')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700792 else:
793 self.write(' ' + name + ': [' +
794 (fmt % escape_quotes(values[0])) + '],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800795
796 def dump_android_core_properties(self):
797 """Dump the module header, name, stem, etc."""
798 self.write(' name: "' + self.module_name + '",')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700799 # see properties shared by dump_defaults_module
800 if self.defaults:
801 self.write(' defaults: ["' + self.defaults + '"],')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700802 elif self.runner.args.global_defaults:
803 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800804 if self.stem != self.module_name:
805 self.write(' stem: "' + self.stem + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700806 if self.has_warning and not self.cap_lints and not self.default_srcs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700807 self.write(' // has rustc warnings')
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700808 if self.host_supported and self.device_supported and self.module_type != 'rust_proc_macro':
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800809 self.write(' host_supported: true,')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700810 if not self.defaults:
811 self.write(' crate_name: "' + self.crate_name + '",')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700812 if not self.default_srcs:
813 self.dump_srcs_list()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700814 if 'test' in self.crate_types and not self.defaults:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800815 # self.root_pkg can have multiple test modules, with different *_tests[n]
816 # names, but their executables can all be installed under the same _tests
817 # directory. When built from Cargo.toml, all tests should have different
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700818 # file or crate names. So we used (root_pkg + '_tests') name as the
819 # relative_install_path.
820 # However, some package like 'slab' can have non-mergeable tests that
821 # must be separated by different module names. So, here we no longer
822 # emit relative_install_path.
823 # self.write(' relative_install_path: "' + self.root_pkg + '_tests",')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800824 self.write(' test_suites: ["general-tests"],')
825 self.write(' auto_gen_config: true,')
Joel Galensone261a152021-01-12 11:31:53 -0800826 if 'test' in self.crate_types and self.host_supported:
827 self.write(' test_options: {')
828 self.write(' unit_test: true,')
829 self.write(' },')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800830
831 def dump_android_externs(self):
832 """Dump the dependent rlibs and dylibs property."""
833 so_libs = list()
834 rust_libs = ''
835 deps_libname = re.compile('^.* = lib(.*)-[0-9a-f]*.(rlib|so|rmeta)$')
836 for lib in self.externs:
837 # normal value of lib: "libc = liblibc-*.rlib"
838 # strange case in rand crate: "getrandom_package = libgetrandom-*.rlib"
839 # we should use "libgetrandom", not "lib" + "getrandom_package"
840 groups = deps_libname.match(lib)
841 if groups is not None:
842 lib_name = groups.group(1)
843 else:
844 lib_name = re.sub(' .*$', '', lib)
845 if lib.endswith('.rlib') or lib.endswith('.rmeta'):
846 # On MacOS .rmeta is used when Linux uses .rlib or .rmeta.
847 rust_libs += ' "' + altered_name('lib' + lib_name) + '",\n'
848 elif lib.endswith('.so'):
849 so_libs.append(lib_name)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700850 elif lib != 'proc_macro': # --extern proc_macro is special and ignored
851 rust_libs += ' // ERROR: unknown type of lib ' + lib + '\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800852 if rust_libs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700853 self.write(' rustlibs: [\n' + rust_libs + ' ],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800854 # Are all dependent .so files proc_macros?
855 # TODO(chh): Separate proc_macros and dylib.
856 self.dump_android_property_list('proc_macros', '"lib%s"', so_libs)
857
858
859class ARObject(object):
860 """Information of an "ar" link command."""
861
862 def __init__(self, runner, outf_name):
863 # Remembered global runner and its members.
864 self.runner = runner
865 self.pkg = ''
866 self.outf_name = outf_name # path to Android.bp
867 # "ar" arguments
868 self.line_num = 1
869 self.line = ''
870 self.flags = '' # e.g. "crs"
871 self.lib = '' # e.g. "/.../out/lib*.a"
872 self.objs = list() # e.g. "/.../out/.../*.o"
873
874 def parse(self, pkg, line_num, args_line):
875 """Collect ar obj/lib file names."""
876 self.pkg = pkg
877 self.line_num = line_num
878 self.line = args_line
879 args = args_line.split()
880 num_args = len(args)
881 if num_args < 3:
882 print('ERROR: "ar" command has too few arguments', args_line)
883 else:
884 self.flags = unquote(args[0])
885 self.lib = unquote(args[1])
886 self.objs = sorted(set(map(unquote, args[2:])))
887 return self
888
889 def write(self, s):
890 self.outf.write(s + '\n')
891
892 def dump_debug_info(self):
893 self.write('\n// Line ' + str(self.line_num) + ' "ar" ' + self.line)
894 self.write('// ar_object for %12s' % self.pkg)
895 self.write('// flags = %s' % self.flags)
896 self.write('// lib = %s' % short_out_name(self.pkg, self.lib))
897 for o in self.objs:
898 self.write('// obj = %s' % short_out_name(self.pkg, o))
899
900 def dump_android_lib(self):
901 """Write cc_library_static into Android.bp."""
902 self.write('\ncc_library_static {')
903 self.write(' name: "' + file_base_name(self.lib) + '",')
904 self.write(' host_supported: true,')
905 if self.flags != 'crs':
906 self.write(' // ar flags = %s' % self.flags)
907 if self.pkg not in self.runner.pkg_obj2cc:
908 self.write(' ERROR: cannot find source files.\n}')
909 return
910 self.write(' srcs: [')
911 obj2cc = self.runner.pkg_obj2cc[self.pkg]
912 # Note: wflags are ignored.
913 dflags = list()
914 fflags = list()
915 for obj in self.objs:
916 self.write(' "' + short_out_name(self.pkg, obj2cc[obj].src) + '",')
917 # TODO(chh): union of dflags and flags of all obj
918 # Now, just a temporary hack that uses the last obj's flags
919 dflags = obj2cc[obj].dflags
920 fflags = obj2cc[obj].fflags
921 self.write(' ],')
922 self.write(' cflags: [')
923 self.write(' "-O3",') # TODO(chh): is this default correct?
924 self.write(' "-Wno-error",')
925 for x in fflags:
926 self.write(' "-f' + x + '",')
927 for x in dflags:
928 self.write(' "-D' + x + '",')
929 self.write(' ],')
930 self.write('}')
931
932 def dump(self):
933 """Dump error/debug/module info to the output .bp file."""
934 self.runner.init_bp_file(self.outf_name)
935 with open(self.outf_name, 'a') as outf:
936 self.outf = outf
937 if self.runner.args.debug:
938 self.dump_debug_info()
939 self.dump_android_lib()
940
941
942class CCObject(object):
943 """Information of a "cc" compilation command."""
944
945 def __init__(self, runner, outf_name):
946 # Remembered global runner and its members.
947 self.runner = runner
948 self.pkg = ''
949 self.outf_name = outf_name # path to Android.bp
950 # "cc" arguments
951 self.line_num = 1
952 self.line = ''
953 self.src = ''
954 self.obj = ''
955 self.dflags = list() # -D flags
956 self.fflags = list() # -f flags
957 self.iflags = list() # -I flags
958 self.wflags = list() # -W flags
959 self.other_args = list()
960
961 def parse(self, pkg, line_num, args_line):
962 """Collect cc compilation flags and src/out file names."""
963 self.pkg = pkg
964 self.line_num = line_num
965 self.line = args_line
966 args = args_line.split()
967 i = 0
968 while i < len(args):
969 arg = args[i]
970 if arg == '"-c"':
971 i += 1
972 if args[i].startswith('"-o'):
973 # ring-0.13.5 dumps: ... "-c" "-o/.../*.o" ".../*.c"
974 self.obj = unquote(args[i])[2:]
975 i += 1
976 self.src = unquote(args[i])
977 else:
978 self.src = unquote(args[i])
979 elif arg == '"-o"':
980 i += 1
981 self.obj = unquote(args[i])
982 elif arg == '"-I"':
983 i += 1
984 self.iflags.append(unquote(args[i]))
985 elif arg.startswith('"-D'):
986 self.dflags.append(unquote(args[i])[2:])
987 elif arg.startswith('"-f'):
988 self.fflags.append(unquote(args[i])[2:])
989 elif arg.startswith('"-W'):
990 self.wflags.append(unquote(args[i])[2:])
991 elif not (arg.startswith('"-O') or arg == '"-m64"' or arg == '"-g"' or
992 arg == '"-g3"'):
993 # ignore -O -m64 -g
994 self.other_args.append(unquote(args[i]))
995 i += 1
996 self.dflags = sorted(set(self.dflags))
997 self.fflags = sorted(set(self.fflags))
998 # self.wflags is not sorted because some are order sensitive
999 # and we ignore them anyway.
1000 if self.pkg not in self.runner.pkg_obj2cc:
1001 self.runner.pkg_obj2cc[self.pkg] = {}
1002 self.runner.pkg_obj2cc[self.pkg][self.obj] = self
1003 return self
1004
1005 def write(self, s):
1006 self.outf.write(s + '\n')
1007
1008 def dump_debug_flags(self, name, flags):
1009 self.write('// ' + name + ':')
1010 for f in flags:
1011 self.write('// %s' % f)
1012
1013 def dump(self):
1014 """Dump only error/debug info to the output .bp file."""
1015 if not self.runner.args.debug:
1016 return
1017 self.runner.init_bp_file(self.outf_name)
1018 with open(self.outf_name, 'a') as outf:
1019 self.outf = outf
1020 self.write('\n// Line ' + str(self.line_num) + ' "cc" ' + self.line)
1021 self.write('// cc_object for %12s' % self.pkg)
1022 self.write('// src = %s' % short_out_name(self.pkg, self.src))
1023 self.write('// obj = %s' % short_out_name(self.pkg, self.obj))
1024 self.dump_debug_flags('-I flags', self.iflags)
1025 self.dump_debug_flags('-D flags', self.dflags)
1026 self.dump_debug_flags('-f flags', self.fflags)
1027 self.dump_debug_flags('-W flags', self.wflags)
1028 if self.other_args:
1029 self.dump_debug_flags('other args', self.other_args)
1030
1031
1032class Runner(object):
1033 """Main class to parse cargo -v output and print Android module definitions."""
1034
1035 def __init__(self, args):
1036 self.bp_files = set() # Remember all output Android.bp files.
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001037 self.test_mappings = {} # Map from Android.bp file path to TestMapping.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001038 self.root_pkg = '' # name of package in ./Cargo.toml
1039 # Saved flags, modes, and data.
1040 self.args = args
1041 self.dry_run = not args.run
1042 self.skip_cargo = args.skipcargo
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001043 self.cargo_path = './cargo' # path to cargo, will be set later
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001044 self.checked_out_files = False # to check only once
1045 self.build_out_files = [] # output files generated by build.rs
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001046 # All cc/ar objects, crates, dependencies, and warning files
1047 self.cc_objects = list()
1048 self.pkg_obj2cc = {}
1049 # pkg_obj2cc[cc_object[i].pkg][cc_objects[i].obj] = cc_objects[i]
1050 self.ar_objects = list()
1051 self.crates = list()
1052 self.dependencies = list() # dependent and build script crates
1053 self.warning_files = set()
1054 # Keep a unique mapping from (module name) to crate
1055 self.name_owners = {}
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001056 # Save and dump all errors from cargo to Android.bp.
1057 self.errors = ''
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001058 self.setup_cargo_path()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001059 # Default action is cargo clean, followed by build or user given actions.
1060 if args.cargo:
1061 self.cargo = ['clean'] + args.cargo
1062 else:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001063 default_target = '--target x86_64-unknown-linux-gnu'
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -07001064 # Use the same target for both host and default device builds.
1065 # Same target is used as default in host x86_64 Android compilation.
1066 # Note: b/169872957, prebuilt cargo failed to build vsock
1067 # on x86_64-unknown-linux-musl systems.
1068 self.cargo = ['clean', 'build ' + default_target]
1069 if args.tests:
1070 self.cargo.append('build --tests ' + default_target)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001071
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001072 def setup_cargo_path(self):
1073 """Find cargo in the --cargo_bin or prebuilt rust bin directory."""
1074 if self.args.cargo_bin:
1075 self.cargo_path = os.path.join(self.args.cargo_bin, 'cargo')
1076 if not os.path.isfile(self.cargo_path):
1077 sys.exit('ERROR: cannot find cargo in ' + self.args.cargo_bin)
1078 print('WARNING: using cargo in ' + self.args.cargo_bin)
1079 return
1080 # We have only tested this on Linux.
1081 if platform.system() != 'Linux':
1082 sys.exit('ERROR: this script has only been tested on Linux with cargo.')
1083 # Assuming that this script is in development/scripts.
1084 my_dir = os.path.dirname(os.path.abspath(__file__))
1085 linux_dir = os.path.join(my_dir, '..', '..',
1086 'prebuilts', 'rust', 'linux-x86')
1087 if not os.path.isdir(linux_dir):
1088 sys.exit('ERROR: cannot find directory ' + linux_dir)
1089 rust_version = self.find_rust_version(my_dir, linux_dir)
1090 cargo_bin = os.path.join(linux_dir, rust_version, 'bin')
1091 self.cargo_path = os.path.join(cargo_bin, 'cargo')
1092 if not os.path.isfile(self.cargo_path):
1093 sys.exit('ERROR: cannot find cargo in ' + cargo_bin
1094 + '; please try --cargo_bin= flag.')
1095 return
1096
1097 def find_rust_version(self, my_dir, linux_dir):
1098 """Use my script directory, find prebuilt rust version."""
1099 # First look up build/soong/rust/config/global.go.
1100 path2global = os.path.join(my_dir, '..', '..',
1101 'build', 'soong', 'rust', 'config', 'global.go')
1102 if os.path.isfile(path2global):
1103 # try to find: RustDefaultVersion = "1.44.0"
1104 version_pat = re.compile(
1105 r'\s*RustDefaultVersion\s*=\s*"([0-9]+\.[0-9]+\.[0-9]+)".*$')
1106 with open(path2global, 'r') as inf:
1107 for line in inf:
1108 result = version_pat.match(line)
1109 if result:
1110 return result.group(1)
1111 print('WARNING: cannot find RustDefaultVersion in ' + path2global)
1112 # Otherwise, find the newest (largest) version number in linux_dir.
1113 rust_version = (0, 0, 0) # the prebuilt version to use
1114 version_pat = re.compile(r'([0-9]+)\.([0-9]+)\.([0-9]+)$')
1115 for dir_name in os.listdir(linux_dir):
1116 result = version_pat.match(dir_name)
1117 if not result:
1118 continue
1119 version = (result.group(1), result.group(2), result.group(3))
1120 if version > rust_version:
1121 rust_version = version
1122 return '.'.join(rust_version)
1123
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001124 def find_out_files(self):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001125 # list1 has build.rs output for normal crates
1126 list1 = glob.glob(TARGET_TMP + '/*/*/build/' + self.root_pkg + '-*/out/*')
1127 # list2 has build.rs output for proc-macro crates
1128 list2 = glob.glob(TARGET_TMP + '/*/build/' + self.root_pkg + '-*/out/*')
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001129 return list1 + list2
1130
1131 def copy_out_files(self):
1132 """Copy build.rs output files to ./out and set up build_out_files."""
1133 if self.checked_out_files:
1134 return
1135 self.checked_out_files = True
1136 cargo_out_files = self.find_out_files()
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001137 out_files = set()
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001138 if cargo_out_files:
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001139 os.makedirs('out', exist_ok=True)
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001140 for path in cargo_out_files:
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001141 file_name = path.split('/')[-1]
1142 out_files.add(file_name)
1143 shutil.copy(path, 'out/' + file_name)
1144 self.build_out_files = sorted(out_files)
1145
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001146 def has_used_out_dir(self):
1147 """Returns true if env!("OUT_DIR") is found."""
1148 return 0 == os.system('grep -rl --exclude build.rs --include \\*.rs' +
1149 ' \'env!("OUT_DIR")\' * > /dev/null')
1150
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001151 def copy_out_module_name(self):
1152 if self.args.copy_out and self.build_out_files:
1153 return 'copy_' + self.root_pkg + '_build_out'
1154 else:
1155 return ''
1156
1157 def dump_copy_out_module(self, outf):
1158 """Output the genrule module to copy out/* to $(genDir)."""
1159 copy_out = self.copy_out_module_name()
1160 if not copy_out:
1161 return
1162 outf.write('\ngenrule {\n')
1163 outf.write(' name: "' + copy_out + '",\n')
1164 outf.write(' srcs: ["out/*"],\n')
1165 outf.write(' cmd: "cp $(in) $(genDir)",\n')
1166 if len(self.build_out_files) > 1:
1167 outf.write(' out: [\n')
1168 for f in self.build_out_files:
1169 outf.write(' "' + f + '",\n')
1170 outf.write(' ],\n')
1171 else:
1172 outf.write(' out: ["' + self.build_out_files[0] + '"],\n')
1173 outf.write('}\n')
1174
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001175 def init_bp_file(self, name):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001176 # name could be Android.bp or sub_dir_path/Android.bp
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001177 if name not in self.bp_files:
1178 self.bp_files.add(name)
1179 with open(name, 'w') as outf:
Andrew Walbran80e90be2020-06-09 14:33:18 +01001180 outf.write(ANDROID_BP_HEADER.format(args=' '.join(sys.argv[1:])))
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001181 # at most one copy_out module per .bp file
1182 self.dump_copy_out_module(outf)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001183
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001184 def dump_test_mapping_files(self):
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001185 """Dump all TEST_MAPPING files."""
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001186 if self.dry_run:
1187 print('Dry-run skip dump of TEST_MAPPING')
1188 else:
1189 for bp_file_name in self.test_mappings:
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001190 if bp_file_name != '/dev/null':
1191 name = os.path.join(os.path.dirname(bp_file_name), 'TEST_MAPPING')
1192 self.test_mappings[bp_file_name].dump(name)
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001193 return self
1194
1195 def add_test(self, bp_file_name, test_name, host):
1196 if bp_file_name not in self.test_mappings:
1197 self.test_mappings[bp_file_name] = TestMapping()
1198 mapping = self.test_mappings[bp_file_name]
1199 mapping.add_test(test_name, host)
1200
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001201 def try_claim_module_name(self, name, owner):
1202 """Reserve and return True if it has not been reserved yet."""
1203 if name not in self.name_owners or owner == self.name_owners[name]:
1204 self.name_owners[name] = owner
1205 return True
1206 return False
1207
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001208 def claim_module_name(self, prefix, owner, counter):
1209 """Return prefix if not owned yet, otherwise, prefix+str(counter)."""
1210 while True:
1211 name = prefix
1212 if counter > 0:
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001213 name += '_' + str(counter)
1214 if self.try_claim_module_name(name, owner):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001215 return name
1216 counter += 1
1217
1218 def find_root_pkg(self):
1219 """Read name of [package] in ./Cargo.toml."""
1220 if not os.path.exists('./Cargo.toml'):
1221 return
1222 with open('./Cargo.toml', 'r') as inf:
1223 pkg_section = re.compile(r'^ *\[package\]')
1224 name = re.compile('^ *name *= * "([^"]*)"')
1225 in_pkg = False
1226 for line in inf:
1227 if in_pkg:
1228 if name.match(line):
1229 self.root_pkg = name.match(line).group(1)
1230 break
1231 else:
1232 in_pkg = pkg_section.match(line) is not None
1233
1234 def run_cargo(self):
1235 """Calls cargo -v and save its output to ./cargo.out."""
1236 if self.skip_cargo:
1237 return self
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001238 cargo_toml = './Cargo.toml'
1239 cargo_out = './cargo.out'
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001240 # Do not use Cargo.lock, because .bp rules are designed to
1241 # run with "latest" crates avaialable on Android.
1242 cargo_lock = './Cargo.lock'
1243 cargo_lock_saved = './cargo.lock.saved'
1244 had_cargo_lock = os.path.exists(cargo_lock)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001245 if not os.access(cargo_toml, os.R_OK):
1246 print('ERROR: Cannot find or read', cargo_toml)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001247 return self
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001248 if not self.dry_run:
1249 if os.path.exists(cargo_out):
1250 os.remove(cargo_out)
1251 if not self.args.use_cargo_lock and had_cargo_lock: # save it
1252 os.rename(cargo_lock, cargo_lock_saved)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001253 cmd_tail = ' --target-dir ' + TARGET_TMP + ' >> ' + cargo_out + ' 2>&1'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001254 # set up search PATH for cargo to find the correct rustc
1255 saved_path = os.environ['PATH']
1256 os.environ['PATH'] = os.path.dirname(self.cargo_path) + ':' + saved_path
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001257 # Add [workspace] to Cargo.toml if it is not there.
1258 added_workspace = False
1259 if self.args.add_workspace:
1260 with open(cargo_toml, 'r') as in_file:
1261 cargo_toml_lines = in_file.readlines()
1262 found_workspace = '[workspace]\n' in cargo_toml_lines
1263 if found_workspace:
1264 print('### WARNING: found [workspace] in Cargo.toml')
1265 else:
1266 with open(cargo_toml, 'a') as out_file:
1267 out_file.write('[workspace]\n')
1268 added_workspace = True
1269 if self.args.verbose:
1270 print('### INFO: added [workspace] to Cargo.toml')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001271 for c in self.cargo:
1272 features = ''
Chih-Hung Hsieh6c8d52f2020-03-30 18:28:52 -07001273 if c != 'clean':
1274 if self.args.features is not None:
1275 features = ' --no-default-features'
1276 if self.args.features:
1277 features += ' --features ' + self.args.features
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001278 cmd_v_flag = ' -vv ' if self.args.vv else ' -v '
1279 cmd = self.cargo_path + cmd_v_flag
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001280 cmd += c + features + cmd_tail
1281 if self.args.rustflags and c != 'clean':
1282 cmd = 'RUSTFLAGS="' + self.args.rustflags + '" ' + cmd
1283 if self.dry_run:
1284 print('Dry-run skip:', cmd)
1285 else:
1286 if self.args.verbose:
1287 print('Running:', cmd)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001288 with open(cargo_out, 'a') as out_file:
1289 out_file.write('### Running: ' + cmd + '\n')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001290 os.system(cmd)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001291 if added_workspace: # restore original Cargo.toml
1292 with open(cargo_toml, 'w') as out_file:
1293 out_file.writelines(cargo_toml_lines)
1294 if self.args.verbose:
1295 print('### INFO: restored original Cargo.toml')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001296 os.environ['PATH'] = saved_path
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001297 if not self.dry_run:
1298 if not had_cargo_lock: # restore to no Cargo.lock state
1299 os.remove(cargo_lock)
1300 elif not self.args.use_cargo_lock: # restore saved Cargo.lock
1301 os.rename(cargo_lock_saved, cargo_lock)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001302 return self
1303
1304 def dump_dependencies(self):
1305 """Append dependencies and their features to Android.bp."""
1306 if not self.dependencies:
1307 return
1308 dependent_list = list()
1309 for c in self.dependencies:
1310 dependent_list.append(c.feature_list())
1311 sorted_dependencies = sorted(set(dependent_list))
1312 self.init_bp_file('Android.bp')
1313 with open('Android.bp', 'a') as outf:
1314 outf.write('\n// dependent_library ["feature_list"]\n')
1315 for s in sorted_dependencies:
1316 outf.write('// ' + s + '\n')
1317
1318 def dump_pkg_obj2cc(self):
1319 """Dump debug info of the pkg_obj2cc map."""
1320 if not self.args.debug:
1321 return
1322 self.init_bp_file('Android.bp')
1323 with open('Android.bp', 'a') as outf:
1324 sorted_pkgs = sorted(self.pkg_obj2cc.keys())
1325 for pkg in sorted_pkgs:
1326 if not self.pkg_obj2cc[pkg]:
1327 continue
1328 outf.write('\n// obj => src for %s\n' % pkg)
1329 obj2cc = self.pkg_obj2cc[pkg]
1330 for obj in sorted(obj2cc.keys()):
1331 outf.write('// ' + short_out_name(pkg, obj) + ' => ' +
1332 short_out_name(pkg, obj2cc[obj].src) + '\n')
1333
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001334 def apply_patch(self):
1335 """Apply local patch file if it is given."""
1336 if self.args.patch:
1337 if self.dry_run:
1338 print('Dry-run skip patch file:', self.args.patch)
1339 else:
1340 if not os.path.exists(self.args.patch):
1341 self.append_to_bp('ERROR cannot find patch file: ' + self.args.patch)
1342 return self
1343 if self.args.verbose:
1344 print('### INFO: applying local patch file:', self.args.patch)
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001345 os.system('patch -s --no-backup-if-mismatch ./Android.bp ' +
1346 self.args.patch)
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001347 return self
1348
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001349 def gen_bp(self):
1350 """Parse cargo.out and generate Android.bp files."""
1351 if self.dry_run:
1352 print('Dry-run skip: read', CARGO_OUT, 'write Android.bp')
1353 elif os.path.exists(CARGO_OUT):
1354 self.find_root_pkg()
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001355 if self.args.copy_out:
1356 self.copy_out_files()
1357 elif self.find_out_files() and self.has_used_out_dir():
1358 print('WARNING: ' + self.root_pkg + ' has cargo output files; ' +
1359 'please rerun with the --copy-out flag.')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001360 with open(CARGO_OUT, 'r') as cargo_out:
1361 self.parse(cargo_out, 'Android.bp')
1362 self.crates.sort(key=get_module_name)
1363 for obj in self.cc_objects:
1364 obj.dump()
1365 self.dump_pkg_obj2cc()
1366 for crate in self.crates:
1367 crate.dump()
1368 dumped_libs = set()
1369 for lib in self.ar_objects:
1370 if lib.pkg == self.root_pkg:
1371 lib_name = file_base_name(lib.lib)
1372 if lib_name not in dumped_libs:
1373 dumped_libs.add(lib_name)
1374 lib.dump()
1375 if self.args.dependencies and self.dependencies:
1376 self.dump_dependencies()
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001377 if self.errors:
1378 self.append_to_bp('\nErrors in ' + CARGO_OUT + ':\n' + self.errors)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001379 return self
1380
1381 def add_ar_object(self, obj):
1382 self.ar_objects.append(obj)
1383
1384 def add_cc_object(self, obj):
1385 self.cc_objects.append(obj)
1386
1387 def add_crate(self, crate):
1388 """Merge crate with someone in crates, or append to it. Return crates."""
1389 if crate.skip_crate():
1390 if self.args.debug: # include debug info of all crates
1391 self.crates.append(crate)
1392 if self.args.dependencies: # include only dependent crates
1393 if (is_dependent_file_path(crate.main_src) and
1394 not is_build_crate_name(crate.crate_name)):
1395 self.dependencies.append(crate)
1396 else:
1397 for c in self.crates:
1398 if c.merge(crate, 'Android.bp'):
1399 return
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001400 # If not merged, decide module type and name now.
1401 crate.decide_module_type()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001402 self.crates.append(crate)
1403
1404 def find_warning_owners(self):
1405 """For each warning file, find its owner crate."""
1406 missing_owner = False
1407 for f in self.warning_files:
1408 cargo_dir = '' # find lowest crate, with longest path
1409 owner = None # owner crate of this warning
1410 for c in self.crates:
1411 if (f.startswith(c.cargo_dir + '/') and
1412 len(cargo_dir) < len(c.cargo_dir)):
1413 cargo_dir = c.cargo_dir
1414 owner = c
1415 if owner:
1416 owner.has_warning = True
1417 else:
1418 missing_owner = True
1419 if missing_owner and os.path.exists('Cargo.toml'):
1420 # owner is the root cargo, with empty cargo_dir
1421 for c in self.crates:
1422 if not c.cargo_dir:
1423 c.has_warning = True
1424
1425 def rustc_command(self, n, rustc_line, line, outf_name):
1426 """Process a rustc command line from cargo -vv output."""
1427 # cargo build -vv output can have multiple lines for a rustc command
1428 # due to '\n' in strings for environment variables.
1429 # strip removes leading spaces and '\n' at the end
1430 new_rustc = (rustc_line.strip() + line) if rustc_line else line
1431 # Use an heuristic to detect the completions of a multi-line command.
1432 # This might fail for some very rare case, but easy to fix manually.
1433 if not line.endswith('`\n') or (new_rustc.count('`') % 2) != 0:
1434 return new_rustc
1435 if RUSTC_VV_CMD_ARGS.match(new_rustc):
1436 args = RUSTC_VV_CMD_ARGS.match(new_rustc).group(1)
1437 self.add_crate(Crate(self, outf_name).parse(n, args))
1438 else:
1439 self.assert_empty_vv_line(new_rustc)
1440 return ''
1441
1442 def cc_ar_command(self, n, groups, outf_name):
1443 pkg = groups.group(1)
1444 line = groups.group(3)
1445 if groups.group(2) == 'cc':
1446 self.add_cc_object(CCObject(self, outf_name).parse(pkg, n, line))
1447 else:
1448 self.add_ar_object(ARObject(self, outf_name).parse(pkg, n, line))
1449
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001450 def append_to_bp(self, line):
1451 self.init_bp_file('Android.bp')
1452 with open('Android.bp', 'a') as outf:
1453 outf.write(line)
1454
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001455 def assert_empty_vv_line(self, line):
1456 if line: # report error if line is not empty
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001457 self.append_to_bp('ERROR -vv line: ' + line)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001458 return ''
1459
1460 def parse(self, inf, outf_name):
1461 """Parse rustc and warning messages in inf, return a list of Crates."""
1462 n = 0 # line number
1463 prev_warning = False # true if the previous line was warning: ...
1464 rustc_line = '' # previous line(s) matching RUSTC_VV_PAT
1465 for line in inf:
1466 n += 1
1467 if line.startswith('warning: '):
1468 prev_warning = True
1469 rustc_line = self.assert_empty_vv_line(rustc_line)
1470 continue
1471 new_rustc = ''
1472 if RUSTC_PAT.match(line):
1473 args_line = RUSTC_PAT.match(line).group(1)
1474 self.add_crate(Crate(self, outf_name).parse(n, args_line))
1475 self.assert_empty_vv_line(rustc_line)
1476 elif rustc_line or RUSTC_VV_PAT.match(line):
1477 new_rustc = self.rustc_command(n, rustc_line, line, outf_name)
1478 elif CC_AR_VV_PAT.match(line):
1479 self.cc_ar_command(n, CC_AR_VV_PAT.match(line), outf_name)
1480 elif prev_warning and WARNING_FILE_PAT.match(line):
1481 self.assert_empty_vv_line(rustc_line)
1482 fpath = WARNING_FILE_PAT.match(line).group(1)
1483 if fpath[0] != '/': # ignore absolute path
1484 self.warning_files.add(fpath)
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001485 elif line.startswith('error: ') or line.startswith('error[E'):
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001486 if not self.args.ignore_cargo_errors:
1487 self.errors += line
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001488 prev_warning = False
1489 rustc_line = new_rustc
1490 self.find_warning_owners()
1491
1492
1493def parse_args():
1494 """Parse main arguments."""
1495 parser = argparse.ArgumentParser('cargo2android')
1496 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001497 '--add_workspace',
1498 action='store_true',
1499 default=False,
1500 help=('append [workspace] to Cargo.toml before calling cargo,' +
1501 ' to treat current directory as root of package source;' +
1502 ' otherwise the relative source file path in generated' +
1503 ' .bp file will be from the parent directory.'))
1504 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001505 '--cargo',
1506 action='append',
1507 metavar='args_string',
1508 help=('extra cargo build -v args in a string, ' +
1509 'each --cargo flag calls cargo build -v once'))
1510 parser.add_argument(
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001511 '--cargo_bin',
1512 type=str,
1513 help='use cargo in the cargo_bin directory instead of the prebuilt one')
1514 parser.add_argument(
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001515 '--copy-out',
1516 action='store_true',
1517 default=False,
1518 help=('only for root directory, ' +
1519 'copy build.rs output to ./out/* and add a genrule to copy ' +
1520 './out/* to genrule output; for crates with code pattern: ' +
1521 'include!(concat!(env!("OUT_DIR"), "/<some_file>.rs"))'))
1522 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001523 '--debug',
1524 action='store_true',
1525 default=False,
1526 help='dump debug info into Android.bp')
1527 parser.add_argument(
1528 '--dependencies',
1529 action='store_true',
1530 default=False,
1531 help='dump debug info of dependent crates')
1532 parser.add_argument(
1533 '--device',
1534 action='store_true',
1535 default=False,
1536 help='run cargo also for a default device target')
1537 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001538 '--features',
1539 type=str,
1540 help=('pass features to cargo build, ' +
1541 'empty string means no default features'))
1542 parser.add_argument(
1543 '--global_defaults',
1544 type=str,
1545 help='add a defaults name to every module')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001546 parser.add_argument(
1547 '--host-first-multilib',
1548 action='store_true',
1549 default=False,
1550 help=('add a compile_multilib:"first" property ' +
1551 'to Android.bp host modules.'))
1552 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001553 '--ignore-cargo-errors',
1554 action='store_true',
1555 default=False,
1556 help='do not append cargo/rustc error messages to Android.bp')
1557 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001558 '--no-host',
1559 action='store_true',
1560 default=False,
1561 help='do not run cargo for the host; only for the device target')
1562 parser.add_argument(
1563 '--no-subdir',
1564 action='store_true',
1565 default=False,
1566 help='do not output anything for sub-directories')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001567 parser.add_argument(
1568 '--onefile',
1569 action='store_true',
1570 default=False,
1571 help=('output all into one ./Android.bp, default will generate ' +
1572 'one Android.bp per Cargo.toml in subdirectories'))
1573 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001574 '--patch',
1575 type=str,
1576 help='apply the given patch file to generated ./Android.bp')
1577 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001578 '--run',
1579 action='store_true',
1580 default=False,
1581 help='run it, default is dry-run')
1582 parser.add_argument('--rustflags', type=str, help='passing flags to rustc')
1583 parser.add_argument(
1584 '--skipcargo',
1585 action='store_true',
1586 default=False,
1587 help='skip cargo command, parse cargo.out, and generate Android.bp')
1588 parser.add_argument(
1589 '--tests',
1590 action='store_true',
1591 default=False,
1592 help='run cargo build --tests after normal build')
1593 parser.add_argument(
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001594 '--use-cargo-lock',
1595 action='store_true',
1596 default=False,
1597 help=('run cargo build with existing Cargo.lock ' +
1598 '(used when some latest dependent crates failed)'))
1599 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001600 '--verbose',
1601 action='store_true',
1602 default=False,
1603 help='echo executed commands')
1604 parser.add_argument(
1605 '--vv',
1606 action='store_true',
1607 default=False,
1608 help='run cargo with -vv instead of default -v')
1609 return parser.parse_args()
1610
1611
1612def main():
1613 args = parse_args()
1614 if not args.run: # default is dry-run
1615 print(DRY_RUN_NOTE)
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001616 Runner(args).run_cargo().gen_bp().apply_patch().dump_test_mapping_files()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001617
1618
1619if __name__ == '__main__':
1620 main()