blob: cab2492f2cf23c52ac4c5c3c09bbfb5649cb7c42 [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)
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700654 self.runner.add_test(self.outf_name, self.module_name, True)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700655 if saved_device_supported:
656 self.device_supported = True
657 self.host_supported = False
658 self.module_name = self.test_module_name()
659 self.decide_one_module_type(crate_type)
660 self.dump_one_android_module(crate_type)
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700661 self.runner.add_test(self.outf_name, self.module_name, False)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700662 self.host_supported = saved_host_supported
663 self.device_supported = saved_device_supported
664 self.main_src = saved_main_src
665 self.srcs = saved_srcs
666
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700667 def dump_one_android_module(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800668 """Dump one Android module definition."""
669 if not self.module_type:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700670 self.write('\nERROR: unknown crate_type ' + crate_type)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800671 return
672 self.write('\n' + self.module_type + ' {')
673 self.dump_android_core_properties()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700674 if not self.defaults:
675 self.dump_edition_flags_libs()
676 if self.runner.args.host_first_multilib and self.host_supported and crate_type != 'test':
677 self.write(' compile_multilib: "first",')
678 self.write('}')
679
680 def dump_android_flags(self):
681 """Dump Android module flags property."""
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700682 if not self.cfgs and not self.codegens and not self.cap_lints:
683 return
684 self.write(' flags: [')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800685 if self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700686 self.write(' "--cap-lints ' + self.cap_lints + '",')
687 cfg_fmt = '"--cfg %s"'
688 codegens_fmt = '"-C %s"'
689 self.dump_android_property_list_items(cfg_fmt, self.cfgs)
690 self.dump_android_property_list_items(codegens_fmt, self.codegens)
691 self.write(' ],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700692
693 def dump_edition_flags_libs(self):
694 if self.edition:
695 self.write(' edition: "' + self.edition + '",')
696 self.dump_android_property_list('features', '"%s"', self.features)
697 self.dump_android_flags()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800698 if self.externs:
699 self.dump_android_externs()
700 self.dump_android_property_list('static_libs', '"lib%s"', self.static_libs)
701 self.dump_android_property_list('shared_libs', '"lib%s"', self.shared_libs)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800702
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700703 def main_src_basename_path(self):
704 return re.sub('/', '_', re.sub('.rs$', '', self.main_src))
705
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800706 def test_module_name(self):
707 """Return a unique name for a test module."""
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700708 # root_pkg+(_host|_device) + '_test_'+source_file_name
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700709 suffix = self.main_src_basename_path()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700710 host_device = '_host'
711 if self.device_supported:
712 host_device = '_device'
713 return self.root_pkg + host_device + '_test_' + suffix
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800714
715 def decide_module_type(self):
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700716 # Use the first crate type for the default/first module.
717 crate_type = self.crate_types[0] if self.crate_types else ''
718 self.decide_one_module_type(crate_type)
719
720 def decide_one_module_type(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800721 """Decide which Android module type to use."""
722 host = '' if self.device_supported else '_host'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700723 if crate_type == 'bin': # rust_binary[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800724 self.module_type = 'rust_binary' + host
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700725 # In rare cases like protobuf-codegen, the output binary name must
726 # be renamed to use as a plugin for protoc.
727 self.stem = altered_stem(self.crate_name)
728 self.module_name = altered_name(self.crate_name)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700729 elif crate_type == 'lib': # rust_library[_host]
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700730 # TODO(chh): should this be rust_library[_host]?
731 # Assuming that Cargo.toml do not use both 'lib' and 'rlib',
732 # because we map them both to rlib.
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700733 self.module_type = 'rust_library' + host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800734 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700735 self.module_name = altered_name(self.stem)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700736 elif crate_type == 'rlib': # rust_library[_host]
737 self.module_type = 'rust_library' + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700738 self.stem = 'lib' + self.crate_name
739 self.module_name = altered_name(self.stem)
740 elif crate_type == 'dylib': # rust_library[_host]_dylib
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800741 self.module_type = 'rust_library' + host + '_dylib'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700742 self.stem = 'lib' + self.crate_name
743 self.module_name = altered_name(self.stem) + '_dylib'
744 elif crate_type == 'cdylib': # rust_library[_host]_shared
Ivan Lozano0c057ad2020-12-15 10:41:26 -0500745 self.module_type = 'rust_ffi' + host + '_shared'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700746 self.stem = 'lib' + self.crate_name
747 self.module_name = altered_name(self.stem) + '_shared'
748 elif crate_type == 'staticlib': # rust_library[_host]_static
Ivan Lozano0c057ad2020-12-15 10:41:26 -0500749 self.module_type = 'rust_ffi' + host + '_static'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700750 self.stem = 'lib' + self.crate_name
751 self.module_name = altered_name(self.stem) + '_static'
752 elif crate_type == 'test': # rust_test[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800753 self.module_type = 'rust_test' + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700754 # Before do_merge, stem name is based on the --crate-name parameter.
755 # and test module name is based on stem.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800756 self.stem = self.test_module_name()
757 # self.stem will be changed after merging with other tests.
758 # self.stem is NOT used for final test binary name.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700759 # rust_test uses each source file base name as part of output file name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700760 # In do_merge, this function is called again, with a module_name.
761 # We make sure that the module name is unique in each package.
762 if self.module_name:
763 # Cargo uses "-C extra-filename=..." and "-C metadata=..." to add
764 # different suffixes and distinguish multiple tests of the same
765 # crate name. We ignore -C and use claim_module_name to get
766 # unique sequential suffix.
767 self.module_name = self.runner.claim_module_name(
768 self.module_name, self, 0)
769 # Now the module name is unique, stem should also match and unique.
770 self.stem = self.module_name
771 elif crate_type == 'proc-macro': # rust_proc_macro
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800772 self.module_type = 'rust_proc_macro'
773 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700774 self.module_name = altered_name(self.stem)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800775 else: # unknown module type, rust_prebuilt_dylib? rust_library[_host]?
776 self.module_type = ''
777 self.stem = ''
778
779 def dump_android_property_list_items(self, fmt, values):
780 for v in values:
781 # fmt has quotes, so we need escape_quotes(v)
782 self.write(' ' + (fmt % escape_quotes(v)) + ',')
783
784 def dump_android_property_list(self, name, fmt, values):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700785 if not values:
786 return
787 if len(values) > 1:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800788 self.write(' ' + name + ': [')
789 self.dump_android_property_list_items(fmt, values)
790 self.write(' ],')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700791 else:
792 self.write(' ' + name + ': [' +
793 (fmt % escape_quotes(values[0])) + '],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800794
795 def dump_android_core_properties(self):
796 """Dump the module header, name, stem, etc."""
797 self.write(' name: "' + self.module_name + '",')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700798 # see properties shared by dump_defaults_module
799 if self.defaults:
800 self.write(' defaults: ["' + self.defaults + '"],')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700801 elif self.runner.args.global_defaults:
802 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800803 if self.stem != self.module_name:
804 self.write(' stem: "' + self.stem + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700805 if self.has_warning and not self.cap_lints and not self.default_srcs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700806 self.write(' // has rustc warnings')
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700807 if self.host_supported and self.device_supported and self.module_type != 'rust_proc_macro':
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800808 self.write(' host_supported: true,')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700809 if not self.defaults:
810 self.write(' crate_name: "' + self.crate_name + '",')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700811 if not self.default_srcs:
812 self.dump_srcs_list()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700813 if 'test' in self.crate_types and not self.defaults:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800814 # self.root_pkg can have multiple test modules, with different *_tests[n]
815 # names, but their executables can all be installed under the same _tests
816 # directory. When built from Cargo.toml, all tests should have different
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700817 # file or crate names. So we used (root_pkg + '_tests') name as the
818 # relative_install_path.
819 # However, some package like 'slab' can have non-mergeable tests that
820 # must be separated by different module names. So, here we no longer
821 # emit relative_install_path.
822 # self.write(' relative_install_path: "' + self.root_pkg + '_tests",')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800823 self.write(' test_suites: ["general-tests"],')
824 self.write(' auto_gen_config: true,')
825
826 def dump_android_externs(self):
827 """Dump the dependent rlibs and dylibs property."""
828 so_libs = list()
829 rust_libs = ''
830 deps_libname = re.compile('^.* = lib(.*)-[0-9a-f]*.(rlib|so|rmeta)$')
831 for lib in self.externs:
832 # normal value of lib: "libc = liblibc-*.rlib"
833 # strange case in rand crate: "getrandom_package = libgetrandom-*.rlib"
834 # we should use "libgetrandom", not "lib" + "getrandom_package"
835 groups = deps_libname.match(lib)
836 if groups is not None:
837 lib_name = groups.group(1)
838 else:
839 lib_name = re.sub(' .*$', '', lib)
840 if lib.endswith('.rlib') or lib.endswith('.rmeta'):
841 # On MacOS .rmeta is used when Linux uses .rlib or .rmeta.
842 rust_libs += ' "' + altered_name('lib' + lib_name) + '",\n'
843 elif lib.endswith('.so'):
844 so_libs.append(lib_name)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700845 elif lib != 'proc_macro': # --extern proc_macro is special and ignored
846 rust_libs += ' // ERROR: unknown type of lib ' + lib + '\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800847 if rust_libs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700848 self.write(' rustlibs: [\n' + rust_libs + ' ],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800849 # Are all dependent .so files proc_macros?
850 # TODO(chh): Separate proc_macros and dylib.
851 self.dump_android_property_list('proc_macros', '"lib%s"', so_libs)
852
853
854class ARObject(object):
855 """Information of an "ar" link command."""
856
857 def __init__(self, runner, outf_name):
858 # Remembered global runner and its members.
859 self.runner = runner
860 self.pkg = ''
861 self.outf_name = outf_name # path to Android.bp
862 # "ar" arguments
863 self.line_num = 1
864 self.line = ''
865 self.flags = '' # e.g. "crs"
866 self.lib = '' # e.g. "/.../out/lib*.a"
867 self.objs = list() # e.g. "/.../out/.../*.o"
868
869 def parse(self, pkg, line_num, args_line):
870 """Collect ar obj/lib file names."""
871 self.pkg = pkg
872 self.line_num = line_num
873 self.line = args_line
874 args = args_line.split()
875 num_args = len(args)
876 if num_args < 3:
877 print('ERROR: "ar" command has too few arguments', args_line)
878 else:
879 self.flags = unquote(args[0])
880 self.lib = unquote(args[1])
881 self.objs = sorted(set(map(unquote, args[2:])))
882 return self
883
884 def write(self, s):
885 self.outf.write(s + '\n')
886
887 def dump_debug_info(self):
888 self.write('\n// Line ' + str(self.line_num) + ' "ar" ' + self.line)
889 self.write('// ar_object for %12s' % self.pkg)
890 self.write('// flags = %s' % self.flags)
891 self.write('// lib = %s' % short_out_name(self.pkg, self.lib))
892 for o in self.objs:
893 self.write('// obj = %s' % short_out_name(self.pkg, o))
894
895 def dump_android_lib(self):
896 """Write cc_library_static into Android.bp."""
897 self.write('\ncc_library_static {')
898 self.write(' name: "' + file_base_name(self.lib) + '",')
899 self.write(' host_supported: true,')
900 if self.flags != 'crs':
901 self.write(' // ar flags = %s' % self.flags)
902 if self.pkg not in self.runner.pkg_obj2cc:
903 self.write(' ERROR: cannot find source files.\n}')
904 return
905 self.write(' srcs: [')
906 obj2cc = self.runner.pkg_obj2cc[self.pkg]
907 # Note: wflags are ignored.
908 dflags = list()
909 fflags = list()
910 for obj in self.objs:
911 self.write(' "' + short_out_name(self.pkg, obj2cc[obj].src) + '",')
912 # TODO(chh): union of dflags and flags of all obj
913 # Now, just a temporary hack that uses the last obj's flags
914 dflags = obj2cc[obj].dflags
915 fflags = obj2cc[obj].fflags
916 self.write(' ],')
917 self.write(' cflags: [')
918 self.write(' "-O3",') # TODO(chh): is this default correct?
919 self.write(' "-Wno-error",')
920 for x in fflags:
921 self.write(' "-f' + x + '",')
922 for x in dflags:
923 self.write(' "-D' + x + '",')
924 self.write(' ],')
925 self.write('}')
926
927 def dump(self):
928 """Dump error/debug/module info to the output .bp file."""
929 self.runner.init_bp_file(self.outf_name)
930 with open(self.outf_name, 'a') as outf:
931 self.outf = outf
932 if self.runner.args.debug:
933 self.dump_debug_info()
934 self.dump_android_lib()
935
936
937class CCObject(object):
938 """Information of a "cc" compilation command."""
939
940 def __init__(self, runner, outf_name):
941 # Remembered global runner and its members.
942 self.runner = runner
943 self.pkg = ''
944 self.outf_name = outf_name # path to Android.bp
945 # "cc" arguments
946 self.line_num = 1
947 self.line = ''
948 self.src = ''
949 self.obj = ''
950 self.dflags = list() # -D flags
951 self.fflags = list() # -f flags
952 self.iflags = list() # -I flags
953 self.wflags = list() # -W flags
954 self.other_args = list()
955
956 def parse(self, pkg, line_num, args_line):
957 """Collect cc compilation flags and src/out file names."""
958 self.pkg = pkg
959 self.line_num = line_num
960 self.line = args_line
961 args = args_line.split()
962 i = 0
963 while i < len(args):
964 arg = args[i]
965 if arg == '"-c"':
966 i += 1
967 if args[i].startswith('"-o'):
968 # ring-0.13.5 dumps: ... "-c" "-o/.../*.o" ".../*.c"
969 self.obj = unquote(args[i])[2:]
970 i += 1
971 self.src = unquote(args[i])
972 else:
973 self.src = unquote(args[i])
974 elif arg == '"-o"':
975 i += 1
976 self.obj = unquote(args[i])
977 elif arg == '"-I"':
978 i += 1
979 self.iflags.append(unquote(args[i]))
980 elif arg.startswith('"-D'):
981 self.dflags.append(unquote(args[i])[2:])
982 elif arg.startswith('"-f'):
983 self.fflags.append(unquote(args[i])[2:])
984 elif arg.startswith('"-W'):
985 self.wflags.append(unquote(args[i])[2:])
986 elif not (arg.startswith('"-O') or arg == '"-m64"' or arg == '"-g"' or
987 arg == '"-g3"'):
988 # ignore -O -m64 -g
989 self.other_args.append(unquote(args[i]))
990 i += 1
991 self.dflags = sorted(set(self.dflags))
992 self.fflags = sorted(set(self.fflags))
993 # self.wflags is not sorted because some are order sensitive
994 # and we ignore them anyway.
995 if self.pkg not in self.runner.pkg_obj2cc:
996 self.runner.pkg_obj2cc[self.pkg] = {}
997 self.runner.pkg_obj2cc[self.pkg][self.obj] = self
998 return self
999
1000 def write(self, s):
1001 self.outf.write(s + '\n')
1002
1003 def dump_debug_flags(self, name, flags):
1004 self.write('// ' + name + ':')
1005 for f in flags:
1006 self.write('// %s' % f)
1007
1008 def dump(self):
1009 """Dump only error/debug info to the output .bp file."""
1010 if not self.runner.args.debug:
1011 return
1012 self.runner.init_bp_file(self.outf_name)
1013 with open(self.outf_name, 'a') as outf:
1014 self.outf = outf
1015 self.write('\n// Line ' + str(self.line_num) + ' "cc" ' + self.line)
1016 self.write('// cc_object for %12s' % self.pkg)
1017 self.write('// src = %s' % short_out_name(self.pkg, self.src))
1018 self.write('// obj = %s' % short_out_name(self.pkg, self.obj))
1019 self.dump_debug_flags('-I flags', self.iflags)
1020 self.dump_debug_flags('-D flags', self.dflags)
1021 self.dump_debug_flags('-f flags', self.fflags)
1022 self.dump_debug_flags('-W flags', self.wflags)
1023 if self.other_args:
1024 self.dump_debug_flags('other args', self.other_args)
1025
1026
1027class Runner(object):
1028 """Main class to parse cargo -v output and print Android module definitions."""
1029
1030 def __init__(self, args):
1031 self.bp_files = set() # Remember all output Android.bp files.
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001032 self.test_mappings = {} # Map from Android.bp file path to TestMapping.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001033 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
1152 def dump_copy_out_module(self, outf):
1153 """Output the genrule module to copy out/* to $(genDir)."""
1154 copy_out = self.copy_out_module_name()
1155 if not copy_out:
1156 return
1157 outf.write('\ngenrule {\n')
1158 outf.write(' name: "' + copy_out + '",\n')
1159 outf.write(' srcs: ["out/*"],\n')
1160 outf.write(' cmd: "cp $(in) $(genDir)",\n')
1161 if len(self.build_out_files) > 1:
1162 outf.write(' out: [\n')
1163 for f in self.build_out_files:
1164 outf.write(' "' + f + '",\n')
1165 outf.write(' ],\n')
1166 else:
1167 outf.write(' out: ["' + self.build_out_files[0] + '"],\n')
1168 outf.write('}\n')
1169
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001170 def init_bp_file(self, name):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001171 # name could be Android.bp or sub_dir_path/Android.bp
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001172 if name not in self.bp_files:
1173 self.bp_files.add(name)
1174 with open(name, 'w') as outf:
Andrew Walbran80e90be2020-06-09 14:33:18 +01001175 outf.write(ANDROID_BP_HEADER.format(args=' '.join(sys.argv[1:])))
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001176 # at most one copy_out module per .bp file
1177 self.dump_copy_out_module(outf)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001178
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001179 def dump_test_mapping_files(self):
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001180 """Dump all TEST_MAPPING files."""
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001181 if self.dry_run:
1182 print('Dry-run skip dump of TEST_MAPPING')
1183 else:
1184 for bp_file_name in self.test_mappings:
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001185 if bp_file_name != '/dev/null':
1186 name = os.path.join(os.path.dirname(bp_file_name), 'TEST_MAPPING')
1187 self.test_mappings[bp_file_name].dump(name)
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001188 return self
1189
1190 def add_test(self, bp_file_name, test_name, host):
1191 if bp_file_name not in self.test_mappings:
1192 self.test_mappings[bp_file_name] = TestMapping()
1193 mapping = self.test_mappings[bp_file_name]
1194 mapping.add_test(test_name, host)
1195
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001196 def try_claim_module_name(self, name, owner):
1197 """Reserve and return True if it has not been reserved yet."""
1198 if name not in self.name_owners or owner == self.name_owners[name]:
1199 self.name_owners[name] = owner
1200 return True
1201 return False
1202
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001203 def claim_module_name(self, prefix, owner, counter):
1204 """Return prefix if not owned yet, otherwise, prefix+str(counter)."""
1205 while True:
1206 name = prefix
1207 if counter > 0:
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001208 name += '_' + str(counter)
1209 if self.try_claim_module_name(name, owner):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001210 return name
1211 counter += 1
1212
1213 def find_root_pkg(self):
1214 """Read name of [package] in ./Cargo.toml."""
1215 if not os.path.exists('./Cargo.toml'):
1216 return
1217 with open('./Cargo.toml', 'r') as inf:
1218 pkg_section = re.compile(r'^ *\[package\]')
1219 name = re.compile('^ *name *= * "([^"]*)"')
1220 in_pkg = False
1221 for line in inf:
1222 if in_pkg:
1223 if name.match(line):
1224 self.root_pkg = name.match(line).group(1)
1225 break
1226 else:
1227 in_pkg = pkg_section.match(line) is not None
1228
1229 def run_cargo(self):
1230 """Calls cargo -v and save its output to ./cargo.out."""
1231 if self.skip_cargo:
1232 return self
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001233 cargo_toml = './Cargo.toml'
1234 cargo_out = './cargo.out'
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001235 # Do not use Cargo.lock, because .bp rules are designed to
1236 # run with "latest" crates avaialable on Android.
1237 cargo_lock = './Cargo.lock'
1238 cargo_lock_saved = './cargo.lock.saved'
1239 had_cargo_lock = os.path.exists(cargo_lock)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001240 if not os.access(cargo_toml, os.R_OK):
1241 print('ERROR: Cannot find or read', cargo_toml)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001242 return self
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001243 if not self.dry_run:
1244 if os.path.exists(cargo_out):
1245 os.remove(cargo_out)
1246 if not self.args.use_cargo_lock and had_cargo_lock: # save it
1247 os.rename(cargo_lock, cargo_lock_saved)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001248 cmd_tail = ' --target-dir ' + TARGET_TMP + ' >> ' + cargo_out + ' 2>&1'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001249 # set up search PATH for cargo to find the correct rustc
1250 saved_path = os.environ['PATH']
1251 os.environ['PATH'] = os.path.dirname(self.cargo_path) + ':' + saved_path
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001252 # Add [workspace] to Cargo.toml if it is not there.
1253 added_workspace = False
1254 if self.args.add_workspace:
1255 with open(cargo_toml, 'r') as in_file:
1256 cargo_toml_lines = in_file.readlines()
1257 found_workspace = '[workspace]\n' in cargo_toml_lines
1258 if found_workspace:
1259 print('### WARNING: found [workspace] in Cargo.toml')
1260 else:
1261 with open(cargo_toml, 'a') as out_file:
1262 out_file.write('[workspace]\n')
1263 added_workspace = True
1264 if self.args.verbose:
1265 print('### INFO: added [workspace] to Cargo.toml')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001266 for c in self.cargo:
1267 features = ''
Chih-Hung Hsieh6c8d52f2020-03-30 18:28:52 -07001268 if c != 'clean':
1269 if self.args.features is not None:
1270 features = ' --no-default-features'
1271 if self.args.features:
1272 features += ' --features ' + self.args.features
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001273 cmd_v_flag = ' -vv ' if self.args.vv else ' -v '
1274 cmd = self.cargo_path + cmd_v_flag
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001275 cmd += c + features + cmd_tail
1276 if self.args.rustflags and c != 'clean':
1277 cmd = 'RUSTFLAGS="' + self.args.rustflags + '" ' + cmd
1278 if self.dry_run:
1279 print('Dry-run skip:', cmd)
1280 else:
1281 if self.args.verbose:
1282 print('Running:', cmd)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001283 with open(cargo_out, 'a') as out_file:
1284 out_file.write('### Running: ' + cmd + '\n')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001285 os.system(cmd)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001286 if added_workspace: # restore original Cargo.toml
1287 with open(cargo_toml, 'w') as out_file:
1288 out_file.writelines(cargo_toml_lines)
1289 if self.args.verbose:
1290 print('### INFO: restored original Cargo.toml')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001291 os.environ['PATH'] = saved_path
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001292 if not self.dry_run:
1293 if not had_cargo_lock: # restore to no Cargo.lock state
1294 os.remove(cargo_lock)
1295 elif not self.args.use_cargo_lock: # restore saved Cargo.lock
1296 os.rename(cargo_lock_saved, cargo_lock)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001297 return self
1298
1299 def dump_dependencies(self):
1300 """Append dependencies and their features to Android.bp."""
1301 if not self.dependencies:
1302 return
1303 dependent_list = list()
1304 for c in self.dependencies:
1305 dependent_list.append(c.feature_list())
1306 sorted_dependencies = sorted(set(dependent_list))
1307 self.init_bp_file('Android.bp')
1308 with open('Android.bp', 'a') as outf:
1309 outf.write('\n// dependent_library ["feature_list"]\n')
1310 for s in sorted_dependencies:
1311 outf.write('// ' + s + '\n')
1312
1313 def dump_pkg_obj2cc(self):
1314 """Dump debug info of the pkg_obj2cc map."""
1315 if not self.args.debug:
1316 return
1317 self.init_bp_file('Android.bp')
1318 with open('Android.bp', 'a') as outf:
1319 sorted_pkgs = sorted(self.pkg_obj2cc.keys())
1320 for pkg in sorted_pkgs:
1321 if not self.pkg_obj2cc[pkg]:
1322 continue
1323 outf.write('\n// obj => src for %s\n' % pkg)
1324 obj2cc = self.pkg_obj2cc[pkg]
1325 for obj in sorted(obj2cc.keys()):
1326 outf.write('// ' + short_out_name(pkg, obj) + ' => ' +
1327 short_out_name(pkg, obj2cc[obj].src) + '\n')
1328
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001329 def apply_patch(self):
1330 """Apply local patch file if it is given."""
1331 if self.args.patch:
1332 if self.dry_run:
1333 print('Dry-run skip patch file:', self.args.patch)
1334 else:
1335 if not os.path.exists(self.args.patch):
1336 self.append_to_bp('ERROR cannot find patch file: ' + self.args.patch)
1337 return self
1338 if self.args.verbose:
1339 print('### INFO: applying local patch file:', self.args.patch)
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001340 os.system('patch -s --no-backup-if-mismatch ./Android.bp ' +
1341 self.args.patch)
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001342 return self
1343
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001344 def gen_bp(self):
1345 """Parse cargo.out and generate Android.bp files."""
1346 if self.dry_run:
1347 print('Dry-run skip: read', CARGO_OUT, 'write Android.bp')
1348 elif os.path.exists(CARGO_OUT):
1349 self.find_root_pkg()
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001350 if self.args.copy_out:
1351 self.copy_out_files()
1352 elif self.find_out_files() and self.has_used_out_dir():
1353 print('WARNING: ' + self.root_pkg + ' has cargo output files; ' +
1354 'please rerun with the --copy-out flag.')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001355 with open(CARGO_OUT, 'r') as cargo_out:
1356 self.parse(cargo_out, 'Android.bp')
1357 self.crates.sort(key=get_module_name)
1358 for obj in self.cc_objects:
1359 obj.dump()
1360 self.dump_pkg_obj2cc()
1361 for crate in self.crates:
1362 crate.dump()
1363 dumped_libs = set()
1364 for lib in self.ar_objects:
1365 if lib.pkg == self.root_pkg:
1366 lib_name = file_base_name(lib.lib)
1367 if lib_name not in dumped_libs:
1368 dumped_libs.add(lib_name)
1369 lib.dump()
1370 if self.args.dependencies and self.dependencies:
1371 self.dump_dependencies()
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001372 if self.errors:
1373 self.append_to_bp('\nErrors in ' + CARGO_OUT + ':\n' + self.errors)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001374 return self
1375
1376 def add_ar_object(self, obj):
1377 self.ar_objects.append(obj)
1378
1379 def add_cc_object(self, obj):
1380 self.cc_objects.append(obj)
1381
1382 def add_crate(self, crate):
1383 """Merge crate with someone in crates, or append to it. Return crates."""
1384 if crate.skip_crate():
1385 if self.args.debug: # include debug info of all crates
1386 self.crates.append(crate)
1387 if self.args.dependencies: # include only dependent crates
1388 if (is_dependent_file_path(crate.main_src) and
1389 not is_build_crate_name(crate.crate_name)):
1390 self.dependencies.append(crate)
1391 else:
1392 for c in self.crates:
1393 if c.merge(crate, 'Android.bp'):
1394 return
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001395 # If not merged, decide module type and name now.
1396 crate.decide_module_type()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001397 self.crates.append(crate)
1398
1399 def find_warning_owners(self):
1400 """For each warning file, find its owner crate."""
1401 missing_owner = False
1402 for f in self.warning_files:
1403 cargo_dir = '' # find lowest crate, with longest path
1404 owner = None # owner crate of this warning
1405 for c in self.crates:
1406 if (f.startswith(c.cargo_dir + '/') and
1407 len(cargo_dir) < len(c.cargo_dir)):
1408 cargo_dir = c.cargo_dir
1409 owner = c
1410 if owner:
1411 owner.has_warning = True
1412 else:
1413 missing_owner = True
1414 if missing_owner and os.path.exists('Cargo.toml'):
1415 # owner is the root cargo, with empty cargo_dir
1416 for c in self.crates:
1417 if not c.cargo_dir:
1418 c.has_warning = True
1419
1420 def rustc_command(self, n, rustc_line, line, outf_name):
1421 """Process a rustc command line from cargo -vv output."""
1422 # cargo build -vv output can have multiple lines for a rustc command
1423 # due to '\n' in strings for environment variables.
1424 # strip removes leading spaces and '\n' at the end
1425 new_rustc = (rustc_line.strip() + line) if rustc_line else line
1426 # Use an heuristic to detect the completions of a multi-line command.
1427 # This might fail for some very rare case, but easy to fix manually.
1428 if not line.endswith('`\n') or (new_rustc.count('`') % 2) != 0:
1429 return new_rustc
1430 if RUSTC_VV_CMD_ARGS.match(new_rustc):
1431 args = RUSTC_VV_CMD_ARGS.match(new_rustc).group(1)
1432 self.add_crate(Crate(self, outf_name).parse(n, args))
1433 else:
1434 self.assert_empty_vv_line(new_rustc)
1435 return ''
1436
1437 def cc_ar_command(self, n, groups, outf_name):
1438 pkg = groups.group(1)
1439 line = groups.group(3)
1440 if groups.group(2) == 'cc':
1441 self.add_cc_object(CCObject(self, outf_name).parse(pkg, n, line))
1442 else:
1443 self.add_ar_object(ARObject(self, outf_name).parse(pkg, n, line))
1444
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001445 def append_to_bp(self, line):
1446 self.init_bp_file('Android.bp')
1447 with open('Android.bp', 'a') as outf:
1448 outf.write(line)
1449
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001450 def assert_empty_vv_line(self, line):
1451 if line: # report error if line is not empty
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001452 self.append_to_bp('ERROR -vv line: ' + line)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001453 return ''
1454
1455 def parse(self, inf, outf_name):
1456 """Parse rustc and warning messages in inf, return a list of Crates."""
1457 n = 0 # line number
1458 prev_warning = False # true if the previous line was warning: ...
1459 rustc_line = '' # previous line(s) matching RUSTC_VV_PAT
1460 for line in inf:
1461 n += 1
1462 if line.startswith('warning: '):
1463 prev_warning = True
1464 rustc_line = self.assert_empty_vv_line(rustc_line)
1465 continue
1466 new_rustc = ''
1467 if RUSTC_PAT.match(line):
1468 args_line = RUSTC_PAT.match(line).group(1)
1469 self.add_crate(Crate(self, outf_name).parse(n, args_line))
1470 self.assert_empty_vv_line(rustc_line)
1471 elif rustc_line or RUSTC_VV_PAT.match(line):
1472 new_rustc = self.rustc_command(n, rustc_line, line, outf_name)
1473 elif CC_AR_VV_PAT.match(line):
1474 self.cc_ar_command(n, CC_AR_VV_PAT.match(line), outf_name)
1475 elif prev_warning and WARNING_FILE_PAT.match(line):
1476 self.assert_empty_vv_line(rustc_line)
1477 fpath = WARNING_FILE_PAT.match(line).group(1)
1478 if fpath[0] != '/': # ignore absolute path
1479 self.warning_files.add(fpath)
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001480 elif line.startswith('error: ') or line.startswith('error[E'):
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001481 if not self.args.ignore_cargo_errors:
1482 self.errors += line
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001483 prev_warning = False
1484 rustc_line = new_rustc
1485 self.find_warning_owners()
1486
1487
1488def parse_args():
1489 """Parse main arguments."""
1490 parser = argparse.ArgumentParser('cargo2android')
1491 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001492 '--add_workspace',
1493 action='store_true',
1494 default=False,
1495 help=('append [workspace] to Cargo.toml before calling cargo,' +
1496 ' to treat current directory as root of package source;' +
1497 ' otherwise the relative source file path in generated' +
1498 ' .bp file will be from the parent directory.'))
1499 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001500 '--cargo',
1501 action='append',
1502 metavar='args_string',
1503 help=('extra cargo build -v args in a string, ' +
1504 'each --cargo flag calls cargo build -v once'))
1505 parser.add_argument(
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001506 '--cargo_bin',
1507 type=str,
1508 help='use cargo in the cargo_bin directory instead of the prebuilt one')
1509 parser.add_argument(
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001510 '--copy-out',
1511 action='store_true',
1512 default=False,
1513 help=('only for root directory, ' +
1514 'copy build.rs output to ./out/* and add a genrule to copy ' +
1515 './out/* to genrule output; for crates with code pattern: ' +
1516 'include!(concat!(env!("OUT_DIR"), "/<some_file>.rs"))'))
1517 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001518 '--debug',
1519 action='store_true',
1520 default=False,
1521 help='dump debug info into Android.bp')
1522 parser.add_argument(
1523 '--dependencies',
1524 action='store_true',
1525 default=False,
1526 help='dump debug info of dependent crates')
1527 parser.add_argument(
1528 '--device',
1529 action='store_true',
1530 default=False,
1531 help='run cargo also for a default device target')
1532 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001533 '--features',
1534 type=str,
1535 help=('pass features to cargo build, ' +
1536 'empty string means no default features'))
1537 parser.add_argument(
1538 '--global_defaults',
1539 type=str,
1540 help='add a defaults name to every module')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001541 parser.add_argument(
1542 '--host-first-multilib',
1543 action='store_true',
1544 default=False,
1545 help=('add a compile_multilib:"first" property ' +
1546 'to Android.bp host modules.'))
1547 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001548 '--ignore-cargo-errors',
1549 action='store_true',
1550 default=False,
1551 help='do not append cargo/rustc error messages to Android.bp')
1552 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001553 '--no-host',
1554 action='store_true',
1555 default=False,
1556 help='do not run cargo for the host; only for the device target')
1557 parser.add_argument(
1558 '--no-subdir',
1559 action='store_true',
1560 default=False,
1561 help='do not output anything for sub-directories')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001562 parser.add_argument(
1563 '--onefile',
1564 action='store_true',
1565 default=False,
1566 help=('output all into one ./Android.bp, default will generate ' +
1567 'one Android.bp per Cargo.toml in subdirectories'))
1568 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001569 '--patch',
1570 type=str,
1571 help='apply the given patch file to generated ./Android.bp')
1572 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001573 '--run',
1574 action='store_true',
1575 default=False,
1576 help='run it, default is dry-run')
1577 parser.add_argument('--rustflags', type=str, help='passing flags to rustc')
1578 parser.add_argument(
1579 '--skipcargo',
1580 action='store_true',
1581 default=False,
1582 help='skip cargo command, parse cargo.out, and generate Android.bp')
1583 parser.add_argument(
1584 '--tests',
1585 action='store_true',
1586 default=False,
1587 help='run cargo build --tests after normal build')
1588 parser.add_argument(
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001589 '--use-cargo-lock',
1590 action='store_true',
1591 default=False,
1592 help=('run cargo build with existing Cargo.lock ' +
1593 '(used when some latest dependent crates failed)'))
1594 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001595 '--verbose',
1596 action='store_true',
1597 default=False,
1598 help='echo executed commands')
1599 parser.add_argument(
1600 '--vv',
1601 action='store_true',
1602 default=False,
1603 help='run cargo with -vv instead of default -v')
1604 return parser.parse_args()
1605
1606
1607def main():
1608 args = parse_args()
1609 if not args.run: # default is dry-run
1610 print(DRY_RUN_NOTE)
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001611 Runner(args).run_cargo().gen_bp().apply_patch().dump_test_mapping_files()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001612
1613
1614if __name__ == '__main__':
1615 main()