blob: 25e98800bc8aac2f745547a444436fe2bacd93aa [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',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080069 'libgcc': 'libgcc_rust',
70 'liblog': 'liblog_rust',
Chih-Hung Hsieh07119862020-07-24 15:34:06 -070071 'libminijail': 'libminijail_rust',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080072 'libsync': 'libsync_rust',
73 'libx86_64': 'libx86_64_rust',
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -070074 'protoc_gen_rust': 'protoc-gen-rust',
75}
76
77RENAME_STEM_MAP = {
78 # This map includes all changes to the default rust module stem names,
79 # which is used for output files when different from the module name.
80 'protoc_gen_rust': 'protoc-gen-rust',
81}
82
83RENAME_DEFAULTS_MAP = {
84 # This map includes all changes to the default prefix of rust_default
85 # module names, to avoid conflict with existing Android modules.
86 'libc': 'rust_libc',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080087}
88
89# Header added to all generated Android.bp files.
Andrew Walbran80e90be2020-06-09 14:33:18 +010090ANDROID_BP_HEADER = '// This file is generated by cargo2android.py {args}.\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080091
92CARGO_OUT = 'cargo.out' # Name of file to keep cargo build -v output.
93
94TARGET_TMP = 'target.tmp' # Name of temporary output directory.
95
96# Message to be displayed when this script is called without the --run flag.
97DRY_RUN_NOTE = (
98 'Dry-run: This script uses ./' + TARGET_TMP + ' for output directory,\n' +
99 'runs cargo clean, runs cargo build -v, saves output to ./cargo.out,\n' +
100 'and writes to Android.bp in the current and subdirectories.\n\n' +
101 'To do do all of the above, use the --run flag.\n' +
102 'See --help for other flags, and more usage notes in this script.\n')
103
104# Cargo -v output of a call to rustc.
105RUSTC_PAT = re.compile('^ +Running `rustc (.*)`$')
106
107# Cargo -vv output of a call to rustc could be split into multiple lines.
108# Assume that the first line will contain some CARGO_* env definition.
109RUSTC_VV_PAT = re.compile('^ +Running `.*CARGO_.*=.*$')
110# The combined -vv output rustc command line pattern.
111RUSTC_VV_CMD_ARGS = re.compile('^ *Running `.*CARGO_.*=.* rustc (.*)`$')
112
113# Cargo -vv output of a "cc" or "ar" command; all in one line.
114CC_AR_VV_PAT = re.compile(r'^\[([^ ]*)[^\]]*\] running:? "(cc|ar)" (.*)$')
115# Some package, such as ring-0.13.5, has pattern '... running "cc"'.
116
117# Rustc output of file location path pattern for a warning message.
118WARNING_FILE_PAT = re.compile('^ *--> ([^:]*):[0-9]+')
119
120# Rust package name with suffix -d1.d2.d3.
121VERSION_SUFFIX_PAT = re.compile(r'^(.*)-[0-9]+\.[0-9]+\.[0-9]+$')
122
123
124def altered_name(name):
125 return RENAME_MAP[name] if (name in RENAME_MAP) else name
126
127
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700128def altered_stem(name):
129 return RENAME_STEM_MAP[name] if (name in RENAME_STEM_MAP) else name
130
131
132def altered_defaults(name):
133 return RENAME_DEFAULTS_MAP[name] if (name in RENAME_DEFAULTS_MAP) else name
134
135
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800136def is_build_crate_name(name):
137 # We added special prefix to build script crate names.
138 return name.startswith('build_script_')
139
140
141def is_dependent_file_path(path):
142 # Absolute or dependent '.../' paths are not main files of this crate.
143 return path.startswith('/') or path.startswith('.../')
144
145
146def get_module_name(crate): # to sort crates in a list
147 return crate.module_name
148
149
150def pkg2crate_name(s):
151 return s.replace('-', '_').replace('.', '_')
152
153
154def file_base_name(path):
155 return os.path.splitext(os.path.basename(path))[0]
156
157
158def test_base_name(path):
159 return pkg2crate_name(file_base_name(path))
160
161
162def unquote(s): # remove quotes around str
163 if s and len(s) > 1 and s[0] == '"' and s[-1] == '"':
164 return s[1:-1]
165 return s
166
167
168def remove_version_suffix(s): # remove -d1.d2.d3 suffix
169 if VERSION_SUFFIX_PAT.match(s):
170 return VERSION_SUFFIX_PAT.match(s).group(1)
171 return s
172
173
174def short_out_name(pkg, s): # replace /.../pkg-*/out/* with .../out/*
175 return re.sub('^/.*/' + pkg + '-[0-9a-f]*/out/', '.../out/', s)
176
177
178def escape_quotes(s): # replace '"' with '\\"'
179 return s.replace('"', '\\"')
180
181
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700182class TestMapping(object):
183 """Entries for a TEST_MAPPING file."""
184
185 def __init__(self):
186 self.entries = []
187
188 def add_test(self, name, host):
189 self.entries.append((name, host))
190
191 def is_empty(self):
192 return not self.entries
193
194 def dump(self, outf_name):
195 """Append all entries into the output file."""
196 if self.is_empty():
197 return
198 with open(outf_name, 'w') as outf:
199 outf.write('// Generated by cargo2android.py for tests in Android.bp\n')
200 outf.write('{\n "presubmit": [\n')
201 is_first = True
202 for (name, host) in self.entries:
203 if not is_first: # add comma and '\n' after the previous entry
204 outf.write(',\n')
205 is_first = False
Jeff Vander Stoepd11be482020-11-05 16:13:36 +0100206 outf.write(' {\n')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700207 if host:
Jeff Vander Stoepd11be482020-11-05 16:13:36 +0100208 outf.write(' "host": true,\n')
209 outf.write(' "name": "' + name + '"' + '\n }')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700210 outf.write('\n ]\n}\n')
211
212
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800213class Crate(object):
214 """Information of a Rust crate to collect/emit for an Android.bp module."""
215
216 def __init__(self, runner, outf_name):
217 # Remembered global runner and its members.
218 self.runner = runner
219 self.debug = runner.args.debug
220 self.cargo_dir = '' # directory of my Cargo.toml
221 self.outf_name = outf_name # path to Android.bp
222 self.outf = None # open file handle of outf_name during dump*
223 # Variants/results that could be merged from multiple rustc lines.
224 self.host_supported = False
225 self.device_supported = False
226 self.has_warning = False
227 # Android module properties derived from rustc parameters.
228 self.module_name = '' # unique in Android build system
229 self.module_type = '' # rust_{binary,library,test}[_host] etc.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700230 self.defaults = '' # rust_defaults used by rust_test* modules
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700231 self.default_srcs = False # use 'srcs' defined in self.defaults
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800232 self.root_pkg = '' # parent package name of a sub/test packge, from -L
233 self.srcs = list() # main_src or merged multiple source files
234 self.stem = '' # real base name of output file
235 # Kept parsed status
236 self.errors = '' # all errors found during parsing
237 self.line_num = 1 # runner told input source line number
238 self.line = '' # original rustc command line parameters
239 # Parameters collected from rustc command line.
240 self.crate_name = '' # follows --crate-name
241 self.main_src = '' # follows crate_name parameter, shortened
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700242 self.crate_types = list() # follows --crate-type
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800243 self.cfgs = list() # follows --cfg, without feature= prefix
244 self.features = list() # follows --cfg, name in 'feature="..."'
245 self.codegens = list() # follows -C, some ignored
246 self.externs = list() # follows --extern
247 self.core_externs = list() # first part of self.externs elements
248 self.static_libs = list() # e.g. -l static=host_cpuid
249 self.shared_libs = list() # e.g. -l dylib=wayland-client, -l z
250 self.cap_lints = '' # follows --cap-lints
251 self.emit_list = '' # e.g., --emit=dep-info,metadata,link
252 self.edition = '2015' # rustc default, e.g., --edition=2018
253 self.target = '' # follows --target
254
255 def write(self, s):
256 # convenient way to output one line at a time with EOL.
257 self.outf.write(s + '\n')
258
259 def same_flags(self, other):
260 # host_supported, device_supported, has_warning are not compared but merged
261 # target is not compared, to merge different target/host modules
262 # externs is not compared; only core_externs is compared
263 return (not self.errors and not other.errors and
264 self.edition == other.edition and
265 self.cap_lints == other.cap_lints and
266 self.emit_list == other.emit_list and
267 self.core_externs == other.core_externs and
268 self.codegens == other.codegens and
269 self.features == other.features and
270 self.static_libs == other.static_libs and
271 self.shared_libs == other.shared_libs and self.cfgs == other.cfgs)
272
273 def merge_host_device(self, other):
274 """Returns true if attributes are the same except host/device support."""
275 return (self.crate_name == other.crate_name and
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700276 self.crate_types == other.crate_types and
277 self.main_src == other.main_src and
278 # before merge, each test module has an unique module name and stem
279 (self.stem == other.stem or self.crate_types == ['test']) and
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800280 self.root_pkg == other.root_pkg and not self.skip_crate() and
281 self.same_flags(other))
282
283 def merge_test(self, other):
284 """Returns true if self and other are tests of same root_pkg."""
285 # Before merger, each test has its own crate_name.
286 # A merged test uses its source file base name as output file name,
287 # so a test is mergeable only if its base name equals to its crate name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700288 return (self.crate_types == other.crate_types and
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700289 self.crate_types == ['test'] and self.root_pkg == other.root_pkg and
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800290 not self.skip_crate() and
291 other.crate_name == test_base_name(other.main_src) and
292 (len(self.srcs) > 1 or
293 (self.crate_name == test_base_name(self.main_src)) and
294 self.host_supported == other.host_supported and
295 self.device_supported == other.device_supported) and
296 self.same_flags(other))
297
298 def merge(self, other, outf_name):
299 """Try to merge crate into self."""
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700300 # Cargo build --tests could recompile a library for tests.
301 # We need to merge such duplicated calls to rustc, with
302 # the algorithm in merge_host_device.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800303 should_merge_host_device = self.merge_host_device(other)
304 should_merge_test = False
305 if not should_merge_host_device:
306 should_merge_test = self.merge_test(other)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800307 if should_merge_host_device or should_merge_test:
308 self.runner.init_bp_file(outf_name)
309 with open(outf_name, 'a') as outf: # to write debug info
310 self.outf = outf
311 other.outf = outf
312 self.do_merge(other, should_merge_test)
313 return True
314 return False
315
316 def do_merge(self, other, should_merge_test):
317 """Merge attributes of other to self."""
318 if self.debug:
319 self.write('\n// Before merge definition (1):')
320 self.dump_debug_info()
321 self.write('\n// Before merge definition (2):')
322 other.dump_debug_info()
323 # Merge properties of other to self.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800324 self.has_warning = self.has_warning or other.has_warning
325 if not self.target: # okay to keep only the first target triple
326 self.target = other.target
327 # decide_module_type sets up default self.stem,
328 # which can be changed if self is a merged test module.
329 self.decide_module_type()
330 if should_merge_test:
331 self.srcs.append(other.main_src)
332 # use a short unique name as the merged module name.
333 prefix = self.root_pkg + '_tests'
334 self.module_name = self.runner.claim_module_name(prefix, self, 0)
335 self.stem = self.module_name
336 # This normalized root_pkg name although might be the same
337 # as other module's crate_name, it is not actually used for
338 # output file name. A merged test module always have multiple
339 # source files and each source file base name is used as
340 # its output file name.
341 self.crate_name = pkg2crate_name(self.root_pkg)
342 if self.debug:
343 self.write('\n// After merge definition (1):')
344 self.dump_debug_info()
345
346 def find_cargo_dir(self):
347 """Deepest directory with Cargo.toml and contains the main_src."""
348 if not is_dependent_file_path(self.main_src):
349 dir_name = os.path.dirname(self.main_src)
350 while dir_name:
351 if os.path.exists(dir_name + '/Cargo.toml'):
352 self.cargo_dir = dir_name
353 return
354 dir_name = os.path.dirname(dir_name)
355
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700356 def add_codegens_flag(self, flag):
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700357 """Ignore options not used in Android."""
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700358 # 'prefer-dynamic' does not work with common flag -C lto
Chih-Hung Hsieh63459ed2020-08-26 11:51:15 -0700359 # 'embed-bitcode' is ignored; we might control LTO with other .bp flag
Chih-Hung Hsieh6c13b722020-09-11 21:24:03 -0700360 # 'codegen-units' is set in Android global config or by default
361 if not (flag.startswith('codegen-units=') or
362 flag.startswith('debuginfo=') or
Chih-Hung Hsieh63459ed2020-08-26 11:51:15 -0700363 flag.startswith('embed-bitcode=') or
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700364 flag.startswith('extra-filename=') or
365 flag.startswith('incremental=') or
366 flag.startswith('metadata=') or
367 flag == 'prefer-dynamic'):
368 self.codegens.append(flag)
369
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800370 def parse(self, line_num, line):
371 """Find important rustc arguments to convert to Android.bp properties."""
372 self.line_num = line_num
373 self.line = line
374 args = line.split() # Loop through every argument of rustc.
375 i = 0
376 while i < len(args):
377 arg = args[i]
378 if arg == '--crate-name':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700379 i += 1
380 self.crate_name = args[i]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800381 elif arg == '--crate-type':
382 i += 1
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700383 # cargo calls rustc with multiple --crate-type flags.
384 # rustc can accept:
385 # --crate-type [bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]
386 self.crate_types.append(args[i])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800387 elif arg == '--test':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700388 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800389 elif arg == '--target':
390 i += 1
391 self.target = args[i]
392 elif arg == '--cfg':
393 i += 1
394 if args[i].startswith('\'feature='):
395 self.features.append(unquote(args[i].replace('\'feature=', '')[:-1]))
396 else:
397 self.cfgs.append(args[i])
398 elif arg == '--extern':
399 i += 1
400 extern_names = re.sub('=/[^ ]*/deps/', ' = ', args[i])
401 self.externs.append(extern_names)
402 self.core_externs.append(re.sub(' = .*', '', extern_names))
403 elif arg == '-C': # codegen options
404 i += 1
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700405 self.add_codegens_flag(args[i])
406 elif arg.startswith('-C'):
407 # cargo has been passing "-C <xyz>" flag to rustc,
408 # but newer cargo could pass '-Cembed-bitcode=no' to rustc.
409 self.add_codegens_flag(arg[2:])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800410 elif arg == '--cap-lints':
411 i += 1
412 self.cap_lints = args[i]
413 elif arg == '-L':
414 i += 1
415 if args[i].startswith('dependency=') and args[i].endswith('/deps'):
416 if '/' + TARGET_TMP + '/' in args[i]:
417 self.root_pkg = re.sub(
418 '^.*/', '', re.sub('/' + TARGET_TMP + '/.*/deps$', '', args[i]))
419 else:
420 self.root_pkg = re.sub('^.*/', '',
421 re.sub('/[^/]+/[^/]+/deps$', '', args[i]))
422 self.root_pkg = remove_version_suffix(self.root_pkg)
423 elif arg == '-l':
424 i += 1
425 if args[i].startswith('static='):
426 self.static_libs.append(re.sub('static=', '', args[i]))
427 elif args[i].startswith('dylib='):
428 self.shared_libs.append(re.sub('dylib=', '', args[i]))
429 else:
430 self.shared_libs.append(args[i])
431 elif arg == '--out-dir' or arg == '--color': # ignored
432 i += 1
433 elif arg.startswith('--error-format=') or arg.startswith('--json='):
434 _ = arg # ignored
435 elif arg.startswith('--emit='):
436 self.emit_list = arg.replace('--emit=', '')
437 elif arg.startswith('--edition='):
438 self.edition = arg.replace('--edition=', '')
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700439 elif not arg.startswith('-'):
440 # shorten imported crate main source paths like $HOME/.cargo/
441 # registry/src/github.com-1ecc6299db9ec823/memchr-2.3.3/src/lib.rs
442 self.main_src = re.sub(r'^/[^ ]*/registry/src/', '.../', args[i])
443 self.main_src = re.sub(r'^\.\.\./github.com-[0-9a-f]*/', '.../',
444 self.main_src)
445 self.find_cargo_dir()
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700446 if self.cargo_dir: # for a subdirectory
447 if self.runner.args.no_subdir: # all .bp content to /dev/null
448 self.outf_name = '/dev/null'
449 elif not self.runner.args.onefile:
450 # Write to Android.bp in the subdirectory with Cargo.toml.
451 self.outf_name = self.cargo_dir + '/Android.bp'
452 self.main_src = self.main_src[len(self.cargo_dir) + 1:]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800453 else:
454 self.errors += 'ERROR: unknown ' + arg + '\n'
455 i += 1
456 if not self.crate_name:
457 self.errors += 'ERROR: missing --crate-name\n'
458 if not self.main_src:
459 self.errors += 'ERROR: missing main source file\n'
460 else:
461 self.srcs.append(self.main_src)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700462 if not self.crate_types:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800463 # Treat "--cfg test" as "--test"
464 if 'test' in self.cfgs:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700465 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800466 else:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700467 self.errors += 'ERROR: missing --crate-type or --test\n'
468 elif len(self.crate_types) > 1:
469 if 'test' in self.crate_types:
470 self.errors += 'ERROR: cannot handle both --crate-type and --test\n'
471 if 'lib' in self.crate_types and 'rlib' in self.crate_types:
472 self.errors += 'ERROR: cannot generate both lib and rlib crate types\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800473 if not self.root_pkg:
474 self.root_pkg = self.crate_name
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700475 self.device_supported = self.runner.args.device
476 self.host_supported = not self.runner.args.no_host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800477 self.cfgs = sorted(set(self.cfgs))
478 self.features = sorted(set(self.features))
479 self.codegens = sorted(set(self.codegens))
480 self.externs = sorted(set(self.externs))
481 self.core_externs = sorted(set(self.core_externs))
482 self.static_libs = sorted(set(self.static_libs))
483 self.shared_libs = sorted(set(self.shared_libs))
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700484 self.crate_types = sorted(set(self.crate_types))
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800485 self.decide_module_type()
486 self.module_name = altered_name(self.stem)
487 return self
488
489 def dump_line(self):
490 self.write('\n// Line ' + str(self.line_num) + ' ' + self.line)
491
492 def feature_list(self):
493 """Return a string of main_src + "feature_list"."""
494 pkg = self.main_src
495 if pkg.startswith('.../'): # keep only the main package name
496 pkg = re.sub('/.*', '', pkg[4:])
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700497 elif pkg.startswith('/'): # use relative path for a local package
498 pkg = os.path.relpath(pkg)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800499 if not self.features:
500 return pkg
501 return pkg + ' "' + ','.join(self.features) + '"'
502
503 def dump_skip_crate(self, kind):
504 if self.debug:
505 self.write('\n// IGNORED: ' + kind + ' ' + self.main_src)
506 return self
507
508 def skip_crate(self):
509 """Return crate_name or a message if this crate should be skipped."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700510 if (is_build_crate_name(self.crate_name) or
511 self.crate_name in EXCLUDED_CRATES):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800512 return self.crate_name
513 if is_dependent_file_path(self.main_src):
514 return 'dependent crate'
515 return ''
516
517 def dump(self):
518 """Dump all error/debug/module code to the output .bp file."""
519 self.runner.init_bp_file(self.outf_name)
520 with open(self.outf_name, 'a') as outf:
521 self.outf = outf
522 if self.errors:
523 self.dump_line()
524 self.write(self.errors)
525 elif self.skip_crate():
526 self.dump_skip_crate(self.skip_crate())
527 else:
528 if self.debug:
529 self.dump_debug_info()
530 self.dump_android_module()
531
532 def dump_debug_info(self):
533 """Dump parsed data, when cargo2android is called with --debug."""
534
535 def dump(name, value):
536 self.write('//%12s = %s' % (name, value))
537
538 def opt_dump(name, value):
539 if value:
540 dump(name, value)
541
542 def dump_list(fmt, values):
543 for v in values:
544 self.write(fmt % v)
545
546 self.dump_line()
547 dump('module_name', self.module_name)
548 dump('crate_name', self.crate_name)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700549 dump('crate_types', self.crate_types)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800550 dump('main_src', self.main_src)
551 dump('has_warning', self.has_warning)
552 dump('for_host', self.host_supported)
553 dump('for_device', self.device_supported)
554 dump('module_type', self.module_type)
555 opt_dump('target', self.target)
556 opt_dump('edition', self.edition)
557 opt_dump('emit_list', self.emit_list)
558 opt_dump('cap_lints', self.cap_lints)
559 dump_list('// cfg = %s', self.cfgs)
560 dump_list('// cfg = \'feature "%s"\'', self.features)
561 # TODO(chh): escape quotes in self.features, but not in other dump_list
562 dump_list('// codegen = %s', self.codegens)
563 dump_list('// externs = %s', self.externs)
564 dump_list('// -l static = %s', self.static_libs)
565 dump_list('// -l (dylib) = %s', self.shared_libs)
566
567 def dump_android_module(self):
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700568 """Dump one or more Android module definition, depending on crate_types."""
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700569 if len(self.crate_types) == 1:
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700570 self.dump_single_type_android_module()
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700571 return
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700572 if 'test' in self.crate_types:
573 self.write('\nERROR: multiple crate types cannot include test type')
574 return
575 # Dump one Android module per crate_type.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700576 for crate_type in self.crate_types:
577 self.decide_one_module_type(crate_type)
578 self.dump_one_android_module(crate_type)
579
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700580 def build_default_name(self):
581 """Return a short and readable name for the rust_defaults module."""
582 # Choices: (1) root_pkg + '_defaults',
583 # (2) root_pkg + '_defaults_' + crate_name
584 # (3) root_pkg + '_defaults_' + main_src_basename_path
585 # (4) root_pkg + '_defaults_' + a_positive_sequence_number
586 name1 = altered_defaults(self.root_pkg) + '_defaults'
587 if self.runner.try_claim_module_name(name1, self):
588 return name1
589 name2 = name1 + '_' + self.crate_name
590 if self.runner.try_claim_module_name(name2, self):
591 return name2
592 name3 = name1 + '_' + self.main_src_basename_path()
593 if self.runner.try_claim_module_name(name3, self):
594 return name3
595 return self.runner.claim_module_name(name1, self, 0)
596
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700597 def dump_srcs_list(self):
598 """Dump the srcs list, for defaults or regular modules."""
599 if len(self.srcs) > 1:
600 srcs = sorted(set(self.srcs)) # make a copy and dedup
601 else:
602 srcs = [self.main_src]
603 copy_out = self.runner.copy_out_module_name()
604 if copy_out:
605 srcs.append(':' + copy_out)
606 self.dump_android_property_list('srcs', '"%s"', srcs)
607
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700608 def dump_defaults_module(self):
609 """Dump a rust_defaults module to be shared by other modules."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700610 name = self.build_default_name()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700611 self.defaults = name
612 self.write('\nrust_defaults {')
613 self.write(' name: "' + name + '",')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700614 if self.runner.args.global_defaults:
615 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700616 self.write(' crate_name: "' + self.crate_name + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700617 if len(self.srcs) == 1: # only one source file; share it in defaults
618 self.default_srcs = True
619 if self.has_warning and not self.cap_lints:
620 self.write(' // has rustc warnings')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700621 self.dump_srcs_list()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700622 if 'test' in self.crate_types:
623 self.write(' test_suites: ["general-tests"],')
624 self.write(' auto_gen_config: true,')
625 self.dump_edition_flags_libs()
626 self.write('}')
627
628 def dump_single_type_android_module(self):
629 """Dump one simple Android module, which has only one crate_type."""
630 crate_type = self.crate_types[0]
631 if crate_type != 'test':
632 # do not change self.stem or self.module_name
633 self.dump_one_android_module(crate_type)
634 return
635 # Dump one test module per source file, and separate host and device tests.
636 # crate_type == 'test'
637 if (self.host_supported and self.device_supported) or len(self.srcs) > 1:
638 self.srcs = sorted(set(self.srcs))
639 self.dump_defaults_module()
640 saved_srcs = self.srcs
641 for src in saved_srcs:
642 self.srcs = [src]
643 saved_device_supported = self.device_supported
644 saved_host_supported = self.host_supported
645 saved_main_src = self.main_src
646 self.main_src = src
647 if saved_host_supported:
648 self.device_supported = False
649 self.host_supported = True
650 self.module_name = self.test_module_name()
651 self.decide_one_module_type(crate_type)
652 self.dump_one_android_module(crate_type)
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700653 self.runner.add_test(self.outf_name, self.module_name, True)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700654 if saved_device_supported:
655 self.device_supported = True
656 self.host_supported = False
657 self.module_name = self.test_module_name()
658 self.decide_one_module_type(crate_type)
659 self.dump_one_android_module(crate_type)
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700660 self.runner.add_test(self.outf_name, self.module_name, False)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700661 self.host_supported = saved_host_supported
662 self.device_supported = saved_device_supported
663 self.main_src = saved_main_src
664 self.srcs = saved_srcs
665
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700666 def dump_one_android_module(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800667 """Dump one Android module definition."""
668 if not self.module_type:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700669 self.write('\nERROR: unknown crate_type ' + crate_type)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800670 return
671 self.write('\n' + self.module_type + ' {')
672 self.dump_android_core_properties()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700673 if not self.defaults:
674 self.dump_edition_flags_libs()
675 if self.runner.args.host_first_multilib and self.host_supported and crate_type != 'test':
676 self.write(' compile_multilib: "first",')
677 self.write('}')
678
679 def dump_android_flags(self):
680 """Dump Android module flags property."""
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700681 if not self.cfgs and not self.codegens and not self.cap_lints:
682 return
683 self.write(' flags: [')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800684 if self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700685 self.write(' "--cap-lints ' + self.cap_lints + '",')
686 cfg_fmt = '"--cfg %s"'
687 codegens_fmt = '"-C %s"'
688 self.dump_android_property_list_items(cfg_fmt, self.cfgs)
689 self.dump_android_property_list_items(codegens_fmt, self.codegens)
690 self.write(' ],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700691
692 def dump_edition_flags_libs(self):
693 if self.edition:
694 self.write(' edition: "' + self.edition + '",')
695 self.dump_android_property_list('features', '"%s"', self.features)
696 self.dump_android_flags()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800697 if self.externs:
698 self.dump_android_externs()
699 self.dump_android_property_list('static_libs', '"lib%s"', self.static_libs)
700 self.dump_android_property_list('shared_libs', '"lib%s"', self.shared_libs)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800701
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700702 def main_src_basename_path(self):
703 return re.sub('/', '_', re.sub('.rs$', '', self.main_src))
704
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800705 def test_module_name(self):
706 """Return a unique name for a test module."""
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700707 # root_pkg+(_host|_device) + '_test_'+source_file_name
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700708 suffix = self.main_src_basename_path()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700709 host_device = '_host'
710 if self.device_supported:
711 host_device = '_device'
712 return self.root_pkg + host_device + '_test_' + suffix
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800713
714 def decide_module_type(self):
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700715 # Use the first crate type for the default/first module.
716 crate_type = self.crate_types[0] if self.crate_types else ''
717 self.decide_one_module_type(crate_type)
718
719 def decide_one_module_type(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800720 """Decide which Android module type to use."""
721 host = '' if self.device_supported else '_host'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700722 if crate_type == 'bin': # rust_binary[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800723 self.module_type = 'rust_binary' + host
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700724 # In rare cases like protobuf-codegen, the output binary name must
725 # be renamed to use as a plugin for protoc.
726 self.stem = altered_stem(self.crate_name)
727 self.module_name = altered_name(self.crate_name)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700728 elif crate_type == 'lib': # rust_library[_host]
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700729 # TODO(chh): should this be rust_library[_host]?
730 # Assuming that Cargo.toml do not use both 'lib' and 'rlib',
731 # because we map them both to rlib.
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700732 self.module_type = 'rust_library' + host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800733 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700734 self.module_name = altered_name(self.stem)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700735 elif crate_type == 'rlib': # rust_library[_host]
736 self.module_type = 'rust_library' + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700737 self.stem = 'lib' + self.crate_name
738 self.module_name = altered_name(self.stem)
739 elif crate_type == 'dylib': # rust_library[_host]_dylib
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800740 self.module_type = 'rust_library' + host + '_dylib'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700741 self.stem = 'lib' + self.crate_name
742 self.module_name = altered_name(self.stem) + '_dylib'
743 elif crate_type == 'cdylib': # rust_library[_host]_shared
744 self.module_type = 'rust_library' + host + '_shared'
745 self.stem = 'lib' + self.crate_name
746 self.module_name = altered_name(self.stem) + '_shared'
747 elif crate_type == 'staticlib': # rust_library[_host]_static
748 self.module_type = 'rust_library' + host + '_static'
749 self.stem = 'lib' + self.crate_name
750 self.module_name = altered_name(self.stem) + '_static'
751 elif crate_type == 'test': # rust_test[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800752 self.module_type = 'rust_test' + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700753 # Before do_merge, stem name is based on the --crate-name parameter.
754 # and test module name is based on stem.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800755 self.stem = self.test_module_name()
756 # self.stem will be changed after merging with other tests.
757 # self.stem is NOT used for final test binary name.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700758 # rust_test uses each source file base name as part of output file name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700759 # In do_merge, this function is called again, with a module_name.
760 # We make sure that the module name is unique in each package.
761 if self.module_name:
762 # Cargo uses "-C extra-filename=..." and "-C metadata=..." to add
763 # different suffixes and distinguish multiple tests of the same
764 # crate name. We ignore -C and use claim_module_name to get
765 # unique sequential suffix.
766 self.module_name = self.runner.claim_module_name(
767 self.module_name, self, 0)
768 # Now the module name is unique, stem should also match and unique.
769 self.stem = self.module_name
770 elif crate_type == 'proc-macro': # rust_proc_macro
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800771 self.module_type = 'rust_proc_macro'
772 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700773 self.module_name = altered_name(self.stem)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800774 else: # unknown module type, rust_prebuilt_dylib? rust_library[_host]?
775 self.module_type = ''
776 self.stem = ''
777
778 def dump_android_property_list_items(self, fmt, values):
779 for v in values:
780 # fmt has quotes, so we need escape_quotes(v)
781 self.write(' ' + (fmt % escape_quotes(v)) + ',')
782
783 def dump_android_property_list(self, name, fmt, values):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700784 if not values:
785 return
786 if len(values) > 1:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800787 self.write(' ' + name + ': [')
788 self.dump_android_property_list_items(fmt, values)
789 self.write(' ],')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700790 else:
791 self.write(' ' + name + ': [' +
792 (fmt % escape_quotes(values[0])) + '],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800793
794 def dump_android_core_properties(self):
795 """Dump the module header, name, stem, etc."""
796 self.write(' name: "' + self.module_name + '",')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700797 # see properties shared by dump_defaults_module
798 if self.defaults:
799 self.write(' defaults: ["' + self.defaults + '"],')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700800 elif self.runner.args.global_defaults:
801 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800802 if self.stem != self.module_name:
803 self.write(' stem: "' + self.stem + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700804 if self.has_warning and not self.cap_lints and not self.default_srcs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700805 self.write(' // has rustc warnings')
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700806 if self.host_supported and self.device_supported and self.module_type != 'rust_proc_macro':
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800807 self.write(' host_supported: true,')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700808 if not self.defaults:
809 self.write(' crate_name: "' + self.crate_name + '",')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700810 if not self.default_srcs:
811 self.dump_srcs_list()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700812 if 'test' in self.crate_types and not self.defaults:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800813 # self.root_pkg can have multiple test modules, with different *_tests[n]
814 # names, but their executables can all be installed under the same _tests
815 # directory. When built from Cargo.toml, all tests should have different
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700816 # file or crate names. So we used (root_pkg + '_tests') name as the
817 # relative_install_path.
818 # However, some package like 'slab' can have non-mergeable tests that
819 # must be separated by different module names. So, here we no longer
820 # emit relative_install_path.
821 # self.write(' relative_install_path: "' + self.root_pkg + '_tests",')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800822 self.write(' test_suites: ["general-tests"],')
823 self.write(' auto_gen_config: true,')
824
825 def dump_android_externs(self):
826 """Dump the dependent rlibs and dylibs property."""
827 so_libs = list()
828 rust_libs = ''
829 deps_libname = re.compile('^.* = lib(.*)-[0-9a-f]*.(rlib|so|rmeta)$')
830 for lib in self.externs:
831 # normal value of lib: "libc = liblibc-*.rlib"
832 # strange case in rand crate: "getrandom_package = libgetrandom-*.rlib"
833 # we should use "libgetrandom", not "lib" + "getrandom_package"
834 groups = deps_libname.match(lib)
835 if groups is not None:
836 lib_name = groups.group(1)
837 else:
838 lib_name = re.sub(' .*$', '', lib)
839 if lib.endswith('.rlib') or lib.endswith('.rmeta'):
840 # On MacOS .rmeta is used when Linux uses .rlib or .rmeta.
841 rust_libs += ' "' + altered_name('lib' + lib_name) + '",\n'
842 elif lib.endswith('.so'):
843 so_libs.append(lib_name)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700844 elif lib != 'proc_macro': # --extern proc_macro is special and ignored
845 rust_libs += ' // ERROR: unknown type of lib ' + lib + '\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800846 if rust_libs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700847 self.write(' rustlibs: [\n' + rust_libs + ' ],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800848 # Are all dependent .so files proc_macros?
849 # TODO(chh): Separate proc_macros and dylib.
850 self.dump_android_property_list('proc_macros', '"lib%s"', so_libs)
851
852
853class ARObject(object):
854 """Information of an "ar" link command."""
855
856 def __init__(self, runner, outf_name):
857 # Remembered global runner and its members.
858 self.runner = runner
859 self.pkg = ''
860 self.outf_name = outf_name # path to Android.bp
861 # "ar" arguments
862 self.line_num = 1
863 self.line = ''
864 self.flags = '' # e.g. "crs"
865 self.lib = '' # e.g. "/.../out/lib*.a"
866 self.objs = list() # e.g. "/.../out/.../*.o"
867
868 def parse(self, pkg, line_num, args_line):
869 """Collect ar obj/lib file names."""
870 self.pkg = pkg
871 self.line_num = line_num
872 self.line = args_line
873 args = args_line.split()
874 num_args = len(args)
875 if num_args < 3:
876 print('ERROR: "ar" command has too few arguments', args_line)
877 else:
878 self.flags = unquote(args[0])
879 self.lib = unquote(args[1])
880 self.objs = sorted(set(map(unquote, args[2:])))
881 return self
882
883 def write(self, s):
884 self.outf.write(s + '\n')
885
886 def dump_debug_info(self):
887 self.write('\n// Line ' + str(self.line_num) + ' "ar" ' + self.line)
888 self.write('// ar_object for %12s' % self.pkg)
889 self.write('// flags = %s' % self.flags)
890 self.write('// lib = %s' % short_out_name(self.pkg, self.lib))
891 for o in self.objs:
892 self.write('// obj = %s' % short_out_name(self.pkg, o))
893
894 def dump_android_lib(self):
895 """Write cc_library_static into Android.bp."""
896 self.write('\ncc_library_static {')
897 self.write(' name: "' + file_base_name(self.lib) + '",')
898 self.write(' host_supported: true,')
899 if self.flags != 'crs':
900 self.write(' // ar flags = %s' % self.flags)
901 if self.pkg not in self.runner.pkg_obj2cc:
902 self.write(' ERROR: cannot find source files.\n}')
903 return
904 self.write(' srcs: [')
905 obj2cc = self.runner.pkg_obj2cc[self.pkg]
906 # Note: wflags are ignored.
907 dflags = list()
908 fflags = list()
909 for obj in self.objs:
910 self.write(' "' + short_out_name(self.pkg, obj2cc[obj].src) + '",')
911 # TODO(chh): union of dflags and flags of all obj
912 # Now, just a temporary hack that uses the last obj's flags
913 dflags = obj2cc[obj].dflags
914 fflags = obj2cc[obj].fflags
915 self.write(' ],')
916 self.write(' cflags: [')
917 self.write(' "-O3",') # TODO(chh): is this default correct?
918 self.write(' "-Wno-error",')
919 for x in fflags:
920 self.write(' "-f' + x + '",')
921 for x in dflags:
922 self.write(' "-D' + x + '",')
923 self.write(' ],')
924 self.write('}')
925
926 def dump(self):
927 """Dump error/debug/module info to the output .bp file."""
928 self.runner.init_bp_file(self.outf_name)
929 with open(self.outf_name, 'a') as outf:
930 self.outf = outf
931 if self.runner.args.debug:
932 self.dump_debug_info()
933 self.dump_android_lib()
934
935
936class CCObject(object):
937 """Information of a "cc" compilation command."""
938
939 def __init__(self, runner, outf_name):
940 # Remembered global runner and its members.
941 self.runner = runner
942 self.pkg = ''
943 self.outf_name = outf_name # path to Android.bp
944 # "cc" arguments
945 self.line_num = 1
946 self.line = ''
947 self.src = ''
948 self.obj = ''
949 self.dflags = list() # -D flags
950 self.fflags = list() # -f flags
951 self.iflags = list() # -I flags
952 self.wflags = list() # -W flags
953 self.other_args = list()
954
955 def parse(self, pkg, line_num, args_line):
956 """Collect cc compilation flags and src/out file names."""
957 self.pkg = pkg
958 self.line_num = line_num
959 self.line = args_line
960 args = args_line.split()
961 i = 0
962 while i < len(args):
963 arg = args[i]
964 if arg == '"-c"':
965 i += 1
966 if args[i].startswith('"-o'):
967 # ring-0.13.5 dumps: ... "-c" "-o/.../*.o" ".../*.c"
968 self.obj = unquote(args[i])[2:]
969 i += 1
970 self.src = unquote(args[i])
971 else:
972 self.src = unquote(args[i])
973 elif arg == '"-o"':
974 i += 1
975 self.obj = unquote(args[i])
976 elif arg == '"-I"':
977 i += 1
978 self.iflags.append(unquote(args[i]))
979 elif arg.startswith('"-D'):
980 self.dflags.append(unquote(args[i])[2:])
981 elif arg.startswith('"-f'):
982 self.fflags.append(unquote(args[i])[2:])
983 elif arg.startswith('"-W'):
984 self.wflags.append(unquote(args[i])[2:])
985 elif not (arg.startswith('"-O') or arg == '"-m64"' or arg == '"-g"' or
986 arg == '"-g3"'):
987 # ignore -O -m64 -g
988 self.other_args.append(unquote(args[i]))
989 i += 1
990 self.dflags = sorted(set(self.dflags))
991 self.fflags = sorted(set(self.fflags))
992 # self.wflags is not sorted because some are order sensitive
993 # and we ignore them anyway.
994 if self.pkg not in self.runner.pkg_obj2cc:
995 self.runner.pkg_obj2cc[self.pkg] = {}
996 self.runner.pkg_obj2cc[self.pkg][self.obj] = self
997 return self
998
999 def write(self, s):
1000 self.outf.write(s + '\n')
1001
1002 def dump_debug_flags(self, name, flags):
1003 self.write('// ' + name + ':')
1004 for f in flags:
1005 self.write('// %s' % f)
1006
1007 def dump(self):
1008 """Dump only error/debug info to the output .bp file."""
1009 if not self.runner.args.debug:
1010 return
1011 self.runner.init_bp_file(self.outf_name)
1012 with open(self.outf_name, 'a') as outf:
1013 self.outf = outf
1014 self.write('\n// Line ' + str(self.line_num) + ' "cc" ' + self.line)
1015 self.write('// cc_object for %12s' % self.pkg)
1016 self.write('// src = %s' % short_out_name(self.pkg, self.src))
1017 self.write('// obj = %s' % short_out_name(self.pkg, self.obj))
1018 self.dump_debug_flags('-I flags', self.iflags)
1019 self.dump_debug_flags('-D flags', self.dflags)
1020 self.dump_debug_flags('-f flags', self.fflags)
1021 self.dump_debug_flags('-W flags', self.wflags)
1022 if self.other_args:
1023 self.dump_debug_flags('other args', self.other_args)
1024
1025
1026class Runner(object):
1027 """Main class to parse cargo -v output and print Android module definitions."""
1028
1029 def __init__(self, args):
1030 self.bp_files = set() # Remember all output Android.bp files.
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001031 self.test_mappings = {} # Map from Android.bp file path to TestMapping.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001032 self.root_pkg = '' # name of package in ./Cargo.toml
1033 # Saved flags, modes, and data.
1034 self.args = args
1035 self.dry_run = not args.run
1036 self.skip_cargo = args.skipcargo
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001037 self.cargo_path = './cargo' # path to cargo, will be set later
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001038 self.checked_out_files = False # to check only once
1039 self.build_out_files = [] # output files generated by build.rs
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001040 # All cc/ar objects, crates, dependencies, and warning files
1041 self.cc_objects = list()
1042 self.pkg_obj2cc = {}
1043 # pkg_obj2cc[cc_object[i].pkg][cc_objects[i].obj] = cc_objects[i]
1044 self.ar_objects = list()
1045 self.crates = list()
1046 self.dependencies = list() # dependent and build script crates
1047 self.warning_files = set()
1048 # Keep a unique mapping from (module name) to crate
1049 self.name_owners = {}
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001050 # Save and dump all errors from cargo to Android.bp.
1051 self.errors = ''
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001052 self.setup_cargo_path()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001053 # Default action is cargo clean, followed by build or user given actions.
1054 if args.cargo:
1055 self.cargo = ['clean'] + args.cargo
1056 else:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001057 default_target = '--target x86_64-unknown-linux-gnu'
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -07001058 # Use the same target for both host and default device builds.
1059 # Same target is used as default in host x86_64 Android compilation.
1060 # Note: b/169872957, prebuilt cargo failed to build vsock
1061 # on x86_64-unknown-linux-musl systems.
1062 self.cargo = ['clean', 'build ' + default_target]
1063 if args.tests:
1064 self.cargo.append('build --tests ' + default_target)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001065
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001066 def setup_cargo_path(self):
1067 """Find cargo in the --cargo_bin or prebuilt rust bin directory."""
1068 if self.args.cargo_bin:
1069 self.cargo_path = os.path.join(self.args.cargo_bin, 'cargo')
1070 if not os.path.isfile(self.cargo_path):
1071 sys.exit('ERROR: cannot find cargo in ' + self.args.cargo_bin)
1072 print('WARNING: using cargo in ' + self.args.cargo_bin)
1073 return
1074 # We have only tested this on Linux.
1075 if platform.system() != 'Linux':
1076 sys.exit('ERROR: this script has only been tested on Linux with cargo.')
1077 # Assuming that this script is in development/scripts.
1078 my_dir = os.path.dirname(os.path.abspath(__file__))
1079 linux_dir = os.path.join(my_dir, '..', '..',
1080 'prebuilts', 'rust', 'linux-x86')
1081 if not os.path.isdir(linux_dir):
1082 sys.exit('ERROR: cannot find directory ' + linux_dir)
1083 rust_version = self.find_rust_version(my_dir, linux_dir)
1084 cargo_bin = os.path.join(linux_dir, rust_version, 'bin')
1085 self.cargo_path = os.path.join(cargo_bin, 'cargo')
1086 if not os.path.isfile(self.cargo_path):
1087 sys.exit('ERROR: cannot find cargo in ' + cargo_bin
1088 + '; please try --cargo_bin= flag.')
1089 return
1090
1091 def find_rust_version(self, my_dir, linux_dir):
1092 """Use my script directory, find prebuilt rust version."""
1093 # First look up build/soong/rust/config/global.go.
1094 path2global = os.path.join(my_dir, '..', '..',
1095 'build', 'soong', 'rust', 'config', 'global.go')
1096 if os.path.isfile(path2global):
1097 # try to find: RustDefaultVersion = "1.44.0"
1098 version_pat = re.compile(
1099 r'\s*RustDefaultVersion\s*=\s*"([0-9]+\.[0-9]+\.[0-9]+)".*$')
1100 with open(path2global, 'r') as inf:
1101 for line in inf:
1102 result = version_pat.match(line)
1103 if result:
1104 return result.group(1)
1105 print('WARNING: cannot find RustDefaultVersion in ' + path2global)
1106 # Otherwise, find the newest (largest) version number in linux_dir.
1107 rust_version = (0, 0, 0) # the prebuilt version to use
1108 version_pat = re.compile(r'([0-9]+)\.([0-9]+)\.([0-9]+)$')
1109 for dir_name in os.listdir(linux_dir):
1110 result = version_pat.match(dir_name)
1111 if not result:
1112 continue
1113 version = (result.group(1), result.group(2), result.group(3))
1114 if version > rust_version:
1115 rust_version = version
1116 return '.'.join(rust_version)
1117
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001118 def copy_out_files(self):
1119 """Copy build.rs output files to ./out and set up build_out_files."""
1120 if not self.args.copy_out or self.checked_out_files:
1121 return
1122 self.checked_out_files = True
1123 # list1 has build.rs output for normal crates
1124 list1 = glob.glob(TARGET_TMP + '/*/*/build/' + self.root_pkg + '-*/out/*')
1125 # list2 has build.rs output for proc-macro crates
1126 list2 = glob.glob(TARGET_TMP + '/*/build/' + self.root_pkg + '-*/out/*')
1127 out_files = set()
1128 if list1 or list2:
1129 os.makedirs('out', exist_ok=True)
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001130 for path in list1 + list2:
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001131 file_name = path.split('/')[-1]
1132 out_files.add(file_name)
1133 shutil.copy(path, 'out/' + file_name)
1134 self.build_out_files = sorted(out_files)
1135
1136 def copy_out_module_name(self):
1137 if self.args.copy_out and self.build_out_files:
1138 return 'copy_' + self.root_pkg + '_build_out'
1139 else:
1140 return ''
1141
1142 def dump_copy_out_module(self, outf):
1143 """Output the genrule module to copy out/* to $(genDir)."""
1144 copy_out = self.copy_out_module_name()
1145 if not copy_out:
1146 return
1147 outf.write('\ngenrule {\n')
1148 outf.write(' name: "' + copy_out + '",\n')
1149 outf.write(' srcs: ["out/*"],\n')
1150 outf.write(' cmd: "cp $(in) $(genDir)",\n')
1151 if len(self.build_out_files) > 1:
1152 outf.write(' out: [\n')
1153 for f in self.build_out_files:
1154 outf.write(' "' + f + '",\n')
1155 outf.write(' ],\n')
1156 else:
1157 outf.write(' out: ["' + self.build_out_files[0] + '"],\n')
1158 outf.write('}\n')
1159
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001160 def init_bp_file(self, name):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001161 # name could be Android.bp or sub_dir_path/Android.bp
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001162 if name not in self.bp_files:
1163 self.bp_files.add(name)
1164 with open(name, 'w') as outf:
Andrew Walbran80e90be2020-06-09 14:33:18 +01001165 outf.write(ANDROID_BP_HEADER.format(args=' '.join(sys.argv[1:])))
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001166 # at most one copy_out module per .bp file
1167 self.dump_copy_out_module(outf)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001168
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001169 def dump_test_mapping_files(self):
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001170 """Dump all TEST_MAPPING files."""
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001171 if self.dry_run:
1172 print('Dry-run skip dump of TEST_MAPPING')
1173 else:
1174 for bp_file_name in self.test_mappings:
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001175 if bp_file_name != '/dev/null':
1176 name = os.path.join(os.path.dirname(bp_file_name), 'TEST_MAPPING')
1177 self.test_mappings[bp_file_name].dump(name)
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001178 return self
1179
1180 def add_test(self, bp_file_name, test_name, host):
1181 if bp_file_name not in self.test_mappings:
1182 self.test_mappings[bp_file_name] = TestMapping()
1183 mapping = self.test_mappings[bp_file_name]
1184 mapping.add_test(test_name, host)
1185
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001186 def try_claim_module_name(self, name, owner):
1187 """Reserve and return True if it has not been reserved yet."""
1188 if name not in self.name_owners or owner == self.name_owners[name]:
1189 self.name_owners[name] = owner
1190 return True
1191 return False
1192
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001193 def claim_module_name(self, prefix, owner, counter):
1194 """Return prefix if not owned yet, otherwise, prefix+str(counter)."""
1195 while True:
1196 name = prefix
1197 if counter > 0:
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001198 name += '_' + str(counter)
1199 if self.try_claim_module_name(name, owner):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001200 return name
1201 counter += 1
1202
1203 def find_root_pkg(self):
1204 """Read name of [package] in ./Cargo.toml."""
1205 if not os.path.exists('./Cargo.toml'):
1206 return
1207 with open('./Cargo.toml', 'r') as inf:
1208 pkg_section = re.compile(r'^ *\[package\]')
1209 name = re.compile('^ *name *= * "([^"]*)"')
1210 in_pkg = False
1211 for line in inf:
1212 if in_pkg:
1213 if name.match(line):
1214 self.root_pkg = name.match(line).group(1)
1215 break
1216 else:
1217 in_pkg = pkg_section.match(line) is not None
1218
1219 def run_cargo(self):
1220 """Calls cargo -v and save its output to ./cargo.out."""
1221 if self.skip_cargo:
1222 return self
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001223 cargo_toml = './Cargo.toml'
1224 cargo_out = './cargo.out'
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001225 # Do not use Cargo.lock, because .bp rules are designed to
1226 # run with "latest" crates avaialable on Android.
1227 cargo_lock = './Cargo.lock'
1228 cargo_lock_saved = './cargo.lock.saved'
1229 had_cargo_lock = os.path.exists(cargo_lock)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001230 if not os.access(cargo_toml, os.R_OK):
1231 print('ERROR: Cannot find or read', cargo_toml)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001232 return self
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001233 if not self.dry_run:
1234 if os.path.exists(cargo_out):
1235 os.remove(cargo_out)
1236 if not self.args.use_cargo_lock and had_cargo_lock: # save it
1237 os.rename(cargo_lock, cargo_lock_saved)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001238 cmd_tail = ' --target-dir ' + TARGET_TMP + ' >> ' + cargo_out + ' 2>&1'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001239 # set up search PATH for cargo to find the correct rustc
1240 saved_path = os.environ['PATH']
1241 os.environ['PATH'] = os.path.dirname(self.cargo_path) + ':' + saved_path
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001242 # Add [workspace] to Cargo.toml if it is not there.
1243 added_workspace = False
1244 if self.args.add_workspace:
1245 with open(cargo_toml, 'r') as in_file:
1246 cargo_toml_lines = in_file.readlines()
1247 found_workspace = '[workspace]\n' in cargo_toml_lines
1248 if found_workspace:
1249 print('### WARNING: found [workspace] in Cargo.toml')
1250 else:
1251 with open(cargo_toml, 'a') as out_file:
1252 out_file.write('[workspace]\n')
1253 added_workspace = True
1254 if self.args.verbose:
1255 print('### INFO: added [workspace] to Cargo.toml')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001256 for c in self.cargo:
1257 features = ''
Chih-Hung Hsieh6c8d52f2020-03-30 18:28:52 -07001258 if c != 'clean':
1259 if self.args.features is not None:
1260 features = ' --no-default-features'
1261 if self.args.features:
1262 features += ' --features ' + self.args.features
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001263 cmd_v_flag = ' -vv ' if self.args.vv else ' -v '
1264 cmd = self.cargo_path + cmd_v_flag
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001265 cmd += c + features + cmd_tail
1266 if self.args.rustflags and c != 'clean':
1267 cmd = 'RUSTFLAGS="' + self.args.rustflags + '" ' + cmd
1268 if self.dry_run:
1269 print('Dry-run skip:', cmd)
1270 else:
1271 if self.args.verbose:
1272 print('Running:', cmd)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001273 with open(cargo_out, 'a') as out_file:
1274 out_file.write('### Running: ' + cmd + '\n')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001275 os.system(cmd)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001276 if added_workspace: # restore original Cargo.toml
1277 with open(cargo_toml, 'w') as out_file:
1278 out_file.writelines(cargo_toml_lines)
1279 if self.args.verbose:
1280 print('### INFO: restored original Cargo.toml')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001281 os.environ['PATH'] = saved_path
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001282 if not self.dry_run:
1283 if not had_cargo_lock: # restore to no Cargo.lock state
1284 os.remove(cargo_lock)
1285 elif not self.args.use_cargo_lock: # restore saved Cargo.lock
1286 os.rename(cargo_lock_saved, cargo_lock)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001287 return self
1288
1289 def dump_dependencies(self):
1290 """Append dependencies and their features to Android.bp."""
1291 if not self.dependencies:
1292 return
1293 dependent_list = list()
1294 for c in self.dependencies:
1295 dependent_list.append(c.feature_list())
1296 sorted_dependencies = sorted(set(dependent_list))
1297 self.init_bp_file('Android.bp')
1298 with open('Android.bp', 'a') as outf:
1299 outf.write('\n// dependent_library ["feature_list"]\n')
1300 for s in sorted_dependencies:
1301 outf.write('// ' + s + '\n')
1302
1303 def dump_pkg_obj2cc(self):
1304 """Dump debug info of the pkg_obj2cc map."""
1305 if not self.args.debug:
1306 return
1307 self.init_bp_file('Android.bp')
1308 with open('Android.bp', 'a') as outf:
1309 sorted_pkgs = sorted(self.pkg_obj2cc.keys())
1310 for pkg in sorted_pkgs:
1311 if not self.pkg_obj2cc[pkg]:
1312 continue
1313 outf.write('\n// obj => src for %s\n' % pkg)
1314 obj2cc = self.pkg_obj2cc[pkg]
1315 for obj in sorted(obj2cc.keys()):
1316 outf.write('// ' + short_out_name(pkg, obj) + ' => ' +
1317 short_out_name(pkg, obj2cc[obj].src) + '\n')
1318
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001319 def apply_patch(self):
1320 """Apply local patch file if it is given."""
1321 if self.args.patch:
1322 if self.dry_run:
1323 print('Dry-run skip patch file:', self.args.patch)
1324 else:
1325 if not os.path.exists(self.args.patch):
1326 self.append_to_bp('ERROR cannot find patch file: ' + self.args.patch)
1327 return self
1328 if self.args.verbose:
1329 print('### INFO: applying local patch file:', self.args.patch)
1330 os.system('patch -s --no-backup-if-mismatch ./Android.bp ' + self.args.patch)
1331 return self
1332
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001333 def gen_bp(self):
1334 """Parse cargo.out and generate Android.bp files."""
1335 if self.dry_run:
1336 print('Dry-run skip: read', CARGO_OUT, 'write Android.bp')
1337 elif os.path.exists(CARGO_OUT):
1338 self.find_root_pkg()
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001339 self.copy_out_files()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001340 with open(CARGO_OUT, 'r') as cargo_out:
1341 self.parse(cargo_out, 'Android.bp')
1342 self.crates.sort(key=get_module_name)
1343 for obj in self.cc_objects:
1344 obj.dump()
1345 self.dump_pkg_obj2cc()
1346 for crate in self.crates:
1347 crate.dump()
1348 dumped_libs = set()
1349 for lib in self.ar_objects:
1350 if lib.pkg == self.root_pkg:
1351 lib_name = file_base_name(lib.lib)
1352 if lib_name not in dumped_libs:
1353 dumped_libs.add(lib_name)
1354 lib.dump()
1355 if self.args.dependencies and self.dependencies:
1356 self.dump_dependencies()
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001357 if self.errors:
1358 self.append_to_bp('\nErrors in ' + CARGO_OUT + ':\n' + self.errors)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001359 return self
1360
1361 def add_ar_object(self, obj):
1362 self.ar_objects.append(obj)
1363
1364 def add_cc_object(self, obj):
1365 self.cc_objects.append(obj)
1366
1367 def add_crate(self, crate):
1368 """Merge crate with someone in crates, or append to it. Return crates."""
1369 if crate.skip_crate():
1370 if self.args.debug: # include debug info of all crates
1371 self.crates.append(crate)
1372 if self.args.dependencies: # include only dependent crates
1373 if (is_dependent_file_path(crate.main_src) and
1374 not is_build_crate_name(crate.crate_name)):
1375 self.dependencies.append(crate)
1376 else:
1377 for c in self.crates:
1378 if c.merge(crate, 'Android.bp'):
1379 return
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001380 # If not merged, decide module type and name now.
1381 crate.decide_module_type()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001382 self.crates.append(crate)
1383
1384 def find_warning_owners(self):
1385 """For each warning file, find its owner crate."""
1386 missing_owner = False
1387 for f in self.warning_files:
1388 cargo_dir = '' # find lowest crate, with longest path
1389 owner = None # owner crate of this warning
1390 for c in self.crates:
1391 if (f.startswith(c.cargo_dir + '/') and
1392 len(cargo_dir) < len(c.cargo_dir)):
1393 cargo_dir = c.cargo_dir
1394 owner = c
1395 if owner:
1396 owner.has_warning = True
1397 else:
1398 missing_owner = True
1399 if missing_owner and os.path.exists('Cargo.toml'):
1400 # owner is the root cargo, with empty cargo_dir
1401 for c in self.crates:
1402 if not c.cargo_dir:
1403 c.has_warning = True
1404
1405 def rustc_command(self, n, rustc_line, line, outf_name):
1406 """Process a rustc command line from cargo -vv output."""
1407 # cargo build -vv output can have multiple lines for a rustc command
1408 # due to '\n' in strings for environment variables.
1409 # strip removes leading spaces and '\n' at the end
1410 new_rustc = (rustc_line.strip() + line) if rustc_line else line
1411 # Use an heuristic to detect the completions of a multi-line command.
1412 # This might fail for some very rare case, but easy to fix manually.
1413 if not line.endswith('`\n') or (new_rustc.count('`') % 2) != 0:
1414 return new_rustc
1415 if RUSTC_VV_CMD_ARGS.match(new_rustc):
1416 args = RUSTC_VV_CMD_ARGS.match(new_rustc).group(1)
1417 self.add_crate(Crate(self, outf_name).parse(n, args))
1418 else:
1419 self.assert_empty_vv_line(new_rustc)
1420 return ''
1421
1422 def cc_ar_command(self, n, groups, outf_name):
1423 pkg = groups.group(1)
1424 line = groups.group(3)
1425 if groups.group(2) == 'cc':
1426 self.add_cc_object(CCObject(self, outf_name).parse(pkg, n, line))
1427 else:
1428 self.add_ar_object(ARObject(self, outf_name).parse(pkg, n, line))
1429
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001430 def append_to_bp(self, line):
1431 self.init_bp_file('Android.bp')
1432 with open('Android.bp', 'a') as outf:
1433 outf.write(line)
1434
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001435 def assert_empty_vv_line(self, line):
1436 if line: # report error if line is not empty
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001437 self.append_to_bp('ERROR -vv line: ' + line)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001438 return ''
1439
1440 def parse(self, inf, outf_name):
1441 """Parse rustc and warning messages in inf, return a list of Crates."""
1442 n = 0 # line number
1443 prev_warning = False # true if the previous line was warning: ...
1444 rustc_line = '' # previous line(s) matching RUSTC_VV_PAT
1445 for line in inf:
1446 n += 1
1447 if line.startswith('warning: '):
1448 prev_warning = True
1449 rustc_line = self.assert_empty_vv_line(rustc_line)
1450 continue
1451 new_rustc = ''
1452 if RUSTC_PAT.match(line):
1453 args_line = RUSTC_PAT.match(line).group(1)
1454 self.add_crate(Crate(self, outf_name).parse(n, args_line))
1455 self.assert_empty_vv_line(rustc_line)
1456 elif rustc_line or RUSTC_VV_PAT.match(line):
1457 new_rustc = self.rustc_command(n, rustc_line, line, outf_name)
1458 elif CC_AR_VV_PAT.match(line):
1459 self.cc_ar_command(n, CC_AR_VV_PAT.match(line), outf_name)
1460 elif prev_warning and WARNING_FILE_PAT.match(line):
1461 self.assert_empty_vv_line(rustc_line)
1462 fpath = WARNING_FILE_PAT.match(line).group(1)
1463 if fpath[0] != '/': # ignore absolute path
1464 self.warning_files.add(fpath)
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001465 elif line.startswith('error: ') or line.startswith('error[E'):
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001466 if not self.args.ignore_cargo_errors:
1467 self.errors += line
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001468 prev_warning = False
1469 rustc_line = new_rustc
1470 self.find_warning_owners()
1471
1472
1473def parse_args():
1474 """Parse main arguments."""
1475 parser = argparse.ArgumentParser('cargo2android')
1476 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001477 '--add_workspace',
1478 action='store_true',
1479 default=False,
1480 help=('append [workspace] to Cargo.toml before calling cargo,' +
1481 ' to treat current directory as root of package source;' +
1482 ' otherwise the relative source file path in generated' +
1483 ' .bp file will be from the parent directory.'))
1484 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001485 '--cargo',
1486 action='append',
1487 metavar='args_string',
1488 help=('extra cargo build -v args in a string, ' +
1489 'each --cargo flag calls cargo build -v once'))
1490 parser.add_argument(
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001491 '--cargo_bin',
1492 type=str,
1493 help='use cargo in the cargo_bin directory instead of the prebuilt one')
1494 parser.add_argument(
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001495 '--copy-out',
1496 action='store_true',
1497 default=False,
1498 help=('only for root directory, ' +
1499 'copy build.rs output to ./out/* and add a genrule to copy ' +
1500 './out/* to genrule output; for crates with code pattern: ' +
1501 'include!(concat!(env!("OUT_DIR"), "/<some_file>.rs"))'))
1502 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001503 '--debug',
1504 action='store_true',
1505 default=False,
1506 help='dump debug info into Android.bp')
1507 parser.add_argument(
1508 '--dependencies',
1509 action='store_true',
1510 default=False,
1511 help='dump debug info of dependent crates')
1512 parser.add_argument(
1513 '--device',
1514 action='store_true',
1515 default=False,
1516 help='run cargo also for a default device target')
1517 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001518 '--features',
1519 type=str,
1520 help=('pass features to cargo build, ' +
1521 'empty string means no default features'))
1522 parser.add_argument(
1523 '--global_defaults',
1524 type=str,
1525 help='add a defaults name to every module')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001526 parser.add_argument(
1527 '--host-first-multilib',
1528 action='store_true',
1529 default=False,
1530 help=('add a compile_multilib:"first" property ' +
1531 'to Android.bp host modules.'))
1532 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001533 '--ignore-cargo-errors',
1534 action='store_true',
1535 default=False,
1536 help='do not append cargo/rustc error messages to Android.bp')
1537 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001538 '--no-host',
1539 action='store_true',
1540 default=False,
1541 help='do not run cargo for the host; only for the device target')
1542 parser.add_argument(
1543 '--no-subdir',
1544 action='store_true',
1545 default=False,
1546 help='do not output anything for sub-directories')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001547 parser.add_argument(
1548 '--onefile',
1549 action='store_true',
1550 default=False,
1551 help=('output all into one ./Android.bp, default will generate ' +
1552 'one Android.bp per Cargo.toml in subdirectories'))
1553 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001554 '--patch',
1555 type=str,
1556 help='apply the given patch file to generated ./Android.bp')
1557 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001558 '--run',
1559 action='store_true',
1560 default=False,
1561 help='run it, default is dry-run')
1562 parser.add_argument('--rustflags', type=str, help='passing flags to rustc')
1563 parser.add_argument(
1564 '--skipcargo',
1565 action='store_true',
1566 default=False,
1567 help='skip cargo command, parse cargo.out, and generate Android.bp')
1568 parser.add_argument(
1569 '--tests',
1570 action='store_true',
1571 default=False,
1572 help='run cargo build --tests after normal build')
1573 parser.add_argument(
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001574 '--use-cargo-lock',
1575 action='store_true',
1576 default=False,
1577 help=('run cargo build with existing Cargo.lock ' +
1578 '(used when some latest dependent crates failed)'))
1579 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001580 '--verbose',
1581 action='store_true',
1582 default=False,
1583 help='echo executed commands')
1584 parser.add_argument(
1585 '--vv',
1586 action='store_true',
1587 default=False,
1588 help='run cargo with -vv instead of default -v')
1589 return parser.parse_args()
1590
1591
1592def main():
1593 args = parse_args()
1594 if not args.run: # default is dry-run
1595 print(DRY_RUN_NOTE)
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001596 Runner(args).run_cargo().gen_bp().apply_patch().dump_test_mapping_files()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001597
1598
1599if __name__ == '__main__':
1600 main()