blob: 99f8976e4ef6906815b9a9626d84579b1de49a13 [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
206 outf.write(' {\n "name": "' + name + '"')
207 if host:
208 outf.write(',\n "host": true\n }')
209 else:
210 outf.write('\n }')
211 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
745 self.module_type = 'rust_library' + host + '_shared'
746 self.stem = 'lib' + self.crate_name
747 self.module_name = altered_name(self.stem) + '_shared'
748 elif crate_type == 'staticlib': # rust_library[_host]_static
749 self.module_type = 'rust_library' + host + '_static'
750 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 Hsiehe2342ba2020-10-25 03:51:24 -07001119 def copy_out_files(self):
1120 """Copy build.rs output files to ./out and set up build_out_files."""
1121 if not self.args.copy_out or self.checked_out_files:
1122 return
1123 self.checked_out_files = True
1124 # list1 has build.rs output for normal crates
1125 list1 = glob.glob(TARGET_TMP + '/*/*/build/' + self.root_pkg + '-*/out/*')
1126 # list2 has build.rs output for proc-macro crates
1127 list2 = glob.glob(TARGET_TMP + '/*/build/' + self.root_pkg + '-*/out/*')
1128 out_files = set()
1129 if list1 or list2:
1130 os.makedirs('out', exist_ok=True)
1131 for path in (list1 + list2):
1132 file_name = path.split('/')[-1]
1133 out_files.add(file_name)
1134 shutil.copy(path, 'out/' + file_name)
1135 self.build_out_files = sorted(out_files)
1136
1137 def copy_out_module_name(self):
1138 if self.args.copy_out and self.build_out_files:
1139 return 'copy_' + self.root_pkg + '_build_out'
1140 else:
1141 return ''
1142
1143 def dump_copy_out_module(self, outf):
1144 """Output the genrule module to copy out/* to $(genDir)."""
1145 copy_out = self.copy_out_module_name()
1146 if not copy_out:
1147 return
1148 outf.write('\ngenrule {\n')
1149 outf.write(' name: "' + copy_out + '",\n')
1150 outf.write(' srcs: ["out/*"],\n')
1151 outf.write(' cmd: "cp $(in) $(genDir)",\n')
1152 if len(self.build_out_files) > 1:
1153 outf.write(' out: [\n')
1154 for f in self.build_out_files:
1155 outf.write(' "' + f + '",\n')
1156 outf.write(' ],\n')
1157 else:
1158 outf.write(' out: ["' + self.build_out_files[0] + '"],\n')
1159 outf.write('}\n')
1160
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001161 def init_bp_file(self, name):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001162 # name could be Android.bp or sub_dir_path/Android.bp
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001163 if name not in self.bp_files:
1164 self.bp_files.add(name)
1165 with open(name, 'w') as outf:
Andrew Walbran80e90be2020-06-09 14:33:18 +01001166 outf.write(ANDROID_BP_HEADER.format(args=' '.join(sys.argv[1:])))
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001167 # at most one copy_out module per .bp file
1168 self.dump_copy_out_module(outf)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001169
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001170 def dump_test_mapping_files(self):
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001171 """Dump all TEST_MAPPING files."""
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001172 if self.dry_run:
1173 print('Dry-run skip dump of TEST_MAPPING')
1174 else:
1175 for bp_file_name in self.test_mappings:
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001176 if bp_file_name != '/dev/null':
1177 name = os.path.join(os.path.dirname(bp_file_name), 'TEST_MAPPING')
1178 self.test_mappings[bp_file_name].dump(name)
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001179 return self
1180
1181 def add_test(self, bp_file_name, test_name, host):
1182 if bp_file_name not in self.test_mappings:
1183 self.test_mappings[bp_file_name] = TestMapping()
1184 mapping = self.test_mappings[bp_file_name]
1185 mapping.add_test(test_name, host)
1186
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001187 def try_claim_module_name(self, name, owner):
1188 """Reserve and return True if it has not been reserved yet."""
1189 if name not in self.name_owners or owner == self.name_owners[name]:
1190 self.name_owners[name] = owner
1191 return True
1192 return False
1193
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001194 def claim_module_name(self, prefix, owner, counter):
1195 """Return prefix if not owned yet, otherwise, prefix+str(counter)."""
1196 while True:
1197 name = prefix
1198 if counter > 0:
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001199 name += '_' + str(counter)
1200 if self.try_claim_module_name(name, owner):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001201 return name
1202 counter += 1
1203
1204 def find_root_pkg(self):
1205 """Read name of [package] in ./Cargo.toml."""
1206 if not os.path.exists('./Cargo.toml'):
1207 return
1208 with open('./Cargo.toml', 'r') as inf:
1209 pkg_section = re.compile(r'^ *\[package\]')
1210 name = re.compile('^ *name *= * "([^"]*)"')
1211 in_pkg = False
1212 for line in inf:
1213 if in_pkg:
1214 if name.match(line):
1215 self.root_pkg = name.match(line).group(1)
1216 break
1217 else:
1218 in_pkg = pkg_section.match(line) is not None
1219
1220 def run_cargo(self):
1221 """Calls cargo -v and save its output to ./cargo.out."""
1222 if self.skip_cargo:
1223 return self
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001224 cargo_toml = './Cargo.toml'
1225 cargo_out = './cargo.out'
1226 if not os.access(cargo_toml, os.R_OK):
1227 print('ERROR: Cannot find or read', cargo_toml)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001228 return self
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001229 if not self.dry_run and os.path.exists(cargo_out):
1230 os.remove(cargo_out)
1231 cmd_tail = ' --target-dir ' + TARGET_TMP + ' >> ' + cargo_out + ' 2>&1'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001232 # set up search PATH for cargo to find the correct rustc
1233 saved_path = os.environ['PATH']
1234 os.environ['PATH'] = os.path.dirname(self.cargo_path) + ':' + saved_path
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001235 # Add [workspace] to Cargo.toml if it is not there.
1236 added_workspace = False
1237 if self.args.add_workspace:
1238 with open(cargo_toml, 'r') as in_file:
1239 cargo_toml_lines = in_file.readlines()
1240 found_workspace = '[workspace]\n' in cargo_toml_lines
1241 if found_workspace:
1242 print('### WARNING: found [workspace] in Cargo.toml')
1243 else:
1244 with open(cargo_toml, 'a') as out_file:
1245 out_file.write('[workspace]\n')
1246 added_workspace = True
1247 if self.args.verbose:
1248 print('### INFO: added [workspace] to Cargo.toml')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001249 for c in self.cargo:
1250 features = ''
Chih-Hung Hsieh6c8d52f2020-03-30 18:28:52 -07001251 if c != 'clean':
1252 if self.args.features is not None:
1253 features = ' --no-default-features'
1254 if self.args.features:
1255 features += ' --features ' + self.args.features
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001256 cmd_v_flag = ' -vv ' if self.args.vv else ' -v '
1257 cmd = self.cargo_path + cmd_v_flag
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001258 cmd += c + features + cmd_tail
1259 if self.args.rustflags and c != 'clean':
1260 cmd = 'RUSTFLAGS="' + self.args.rustflags + '" ' + cmd
1261 if self.dry_run:
1262 print('Dry-run skip:', cmd)
1263 else:
1264 if self.args.verbose:
1265 print('Running:', cmd)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001266 with open(cargo_out, 'a') as out_file:
1267 out_file.write('### Running: ' + cmd + '\n')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001268 os.system(cmd)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001269 if added_workspace: # restore original Cargo.toml
1270 with open(cargo_toml, 'w') as out_file:
1271 out_file.writelines(cargo_toml_lines)
1272 if self.args.verbose:
1273 print('### INFO: restored original Cargo.toml')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001274 os.environ['PATH'] = saved_path
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001275 return self
1276
1277 def dump_dependencies(self):
1278 """Append dependencies and their features to Android.bp."""
1279 if not self.dependencies:
1280 return
1281 dependent_list = list()
1282 for c in self.dependencies:
1283 dependent_list.append(c.feature_list())
1284 sorted_dependencies = sorted(set(dependent_list))
1285 self.init_bp_file('Android.bp')
1286 with open('Android.bp', 'a') as outf:
1287 outf.write('\n// dependent_library ["feature_list"]\n')
1288 for s in sorted_dependencies:
1289 outf.write('// ' + s + '\n')
1290
1291 def dump_pkg_obj2cc(self):
1292 """Dump debug info of the pkg_obj2cc map."""
1293 if not self.args.debug:
1294 return
1295 self.init_bp_file('Android.bp')
1296 with open('Android.bp', 'a') as outf:
1297 sorted_pkgs = sorted(self.pkg_obj2cc.keys())
1298 for pkg in sorted_pkgs:
1299 if not self.pkg_obj2cc[pkg]:
1300 continue
1301 outf.write('\n// obj => src for %s\n' % pkg)
1302 obj2cc = self.pkg_obj2cc[pkg]
1303 for obj in sorted(obj2cc.keys()):
1304 outf.write('// ' + short_out_name(pkg, obj) + ' => ' +
1305 short_out_name(pkg, obj2cc[obj].src) + '\n')
1306
1307 def gen_bp(self):
1308 """Parse cargo.out and generate Android.bp files."""
1309 if self.dry_run:
1310 print('Dry-run skip: read', CARGO_OUT, 'write Android.bp')
1311 elif os.path.exists(CARGO_OUT):
1312 self.find_root_pkg()
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001313 self.copy_out_files()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001314 with open(CARGO_OUT, 'r') as cargo_out:
1315 self.parse(cargo_out, 'Android.bp')
1316 self.crates.sort(key=get_module_name)
1317 for obj in self.cc_objects:
1318 obj.dump()
1319 self.dump_pkg_obj2cc()
1320 for crate in self.crates:
1321 crate.dump()
1322 dumped_libs = set()
1323 for lib in self.ar_objects:
1324 if lib.pkg == self.root_pkg:
1325 lib_name = file_base_name(lib.lib)
1326 if lib_name not in dumped_libs:
1327 dumped_libs.add(lib_name)
1328 lib.dump()
1329 if self.args.dependencies and self.dependencies:
1330 self.dump_dependencies()
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001331 if self.errors:
1332 self.append_to_bp('\nErrors in ' + CARGO_OUT + ':\n' + self.errors)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001333 return self
1334
1335 def add_ar_object(self, obj):
1336 self.ar_objects.append(obj)
1337
1338 def add_cc_object(self, obj):
1339 self.cc_objects.append(obj)
1340
1341 def add_crate(self, crate):
1342 """Merge crate with someone in crates, or append to it. Return crates."""
1343 if crate.skip_crate():
1344 if self.args.debug: # include debug info of all crates
1345 self.crates.append(crate)
1346 if self.args.dependencies: # include only dependent crates
1347 if (is_dependent_file_path(crate.main_src) and
1348 not is_build_crate_name(crate.crate_name)):
1349 self.dependencies.append(crate)
1350 else:
1351 for c in self.crates:
1352 if c.merge(crate, 'Android.bp'):
1353 return
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001354 # If not merged, decide module type and name now.
1355 crate.decide_module_type()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001356 self.crates.append(crate)
1357
1358 def find_warning_owners(self):
1359 """For each warning file, find its owner crate."""
1360 missing_owner = False
1361 for f in self.warning_files:
1362 cargo_dir = '' # find lowest crate, with longest path
1363 owner = None # owner crate of this warning
1364 for c in self.crates:
1365 if (f.startswith(c.cargo_dir + '/') and
1366 len(cargo_dir) < len(c.cargo_dir)):
1367 cargo_dir = c.cargo_dir
1368 owner = c
1369 if owner:
1370 owner.has_warning = True
1371 else:
1372 missing_owner = True
1373 if missing_owner and os.path.exists('Cargo.toml'):
1374 # owner is the root cargo, with empty cargo_dir
1375 for c in self.crates:
1376 if not c.cargo_dir:
1377 c.has_warning = True
1378
1379 def rustc_command(self, n, rustc_line, line, outf_name):
1380 """Process a rustc command line from cargo -vv output."""
1381 # cargo build -vv output can have multiple lines for a rustc command
1382 # due to '\n' in strings for environment variables.
1383 # strip removes leading spaces and '\n' at the end
1384 new_rustc = (rustc_line.strip() + line) if rustc_line else line
1385 # Use an heuristic to detect the completions of a multi-line command.
1386 # This might fail for some very rare case, but easy to fix manually.
1387 if not line.endswith('`\n') or (new_rustc.count('`') % 2) != 0:
1388 return new_rustc
1389 if RUSTC_VV_CMD_ARGS.match(new_rustc):
1390 args = RUSTC_VV_CMD_ARGS.match(new_rustc).group(1)
1391 self.add_crate(Crate(self, outf_name).parse(n, args))
1392 else:
1393 self.assert_empty_vv_line(new_rustc)
1394 return ''
1395
1396 def cc_ar_command(self, n, groups, outf_name):
1397 pkg = groups.group(1)
1398 line = groups.group(3)
1399 if groups.group(2) == 'cc':
1400 self.add_cc_object(CCObject(self, outf_name).parse(pkg, n, line))
1401 else:
1402 self.add_ar_object(ARObject(self, outf_name).parse(pkg, n, line))
1403
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001404 def append_to_bp(self, line):
1405 self.init_bp_file('Android.bp')
1406 with open('Android.bp', 'a') as outf:
1407 outf.write(line)
1408
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001409 def assert_empty_vv_line(self, line):
1410 if line: # report error if line is not empty
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001411 self.append_to_bp('ERROR -vv line: ' + line)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001412 return ''
1413
1414 def parse(self, inf, outf_name):
1415 """Parse rustc and warning messages in inf, return a list of Crates."""
1416 n = 0 # line number
1417 prev_warning = False # true if the previous line was warning: ...
1418 rustc_line = '' # previous line(s) matching RUSTC_VV_PAT
1419 for line in inf:
1420 n += 1
1421 if line.startswith('warning: '):
1422 prev_warning = True
1423 rustc_line = self.assert_empty_vv_line(rustc_line)
1424 continue
1425 new_rustc = ''
1426 if RUSTC_PAT.match(line):
1427 args_line = RUSTC_PAT.match(line).group(1)
1428 self.add_crate(Crate(self, outf_name).parse(n, args_line))
1429 self.assert_empty_vv_line(rustc_line)
1430 elif rustc_line or RUSTC_VV_PAT.match(line):
1431 new_rustc = self.rustc_command(n, rustc_line, line, outf_name)
1432 elif CC_AR_VV_PAT.match(line):
1433 self.cc_ar_command(n, CC_AR_VV_PAT.match(line), outf_name)
1434 elif prev_warning and WARNING_FILE_PAT.match(line):
1435 self.assert_empty_vv_line(rustc_line)
1436 fpath = WARNING_FILE_PAT.match(line).group(1)
1437 if fpath[0] != '/': # ignore absolute path
1438 self.warning_files.add(fpath)
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001439 elif line.startswith('error: ') or line.startswith('error[E'):
1440 self.errors += line
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001441 prev_warning = False
1442 rustc_line = new_rustc
1443 self.find_warning_owners()
1444
1445
1446def parse_args():
1447 """Parse main arguments."""
1448 parser = argparse.ArgumentParser('cargo2android')
1449 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001450 '--add_workspace',
1451 action='store_true',
1452 default=False,
1453 help=('append [workspace] to Cargo.toml before calling cargo,' +
1454 ' to treat current directory as root of package source;' +
1455 ' otherwise the relative source file path in generated' +
1456 ' .bp file will be from the parent directory.'))
1457 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001458 '--cargo',
1459 action='append',
1460 metavar='args_string',
1461 help=('extra cargo build -v args in a string, ' +
1462 'each --cargo flag calls cargo build -v once'))
1463 parser.add_argument(
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001464 '--cargo_bin',
1465 type=str,
1466 help='use cargo in the cargo_bin directory instead of the prebuilt one')
1467 parser.add_argument(
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001468 '--copy-out',
1469 action='store_true',
1470 default=False,
1471 help=('only for root directory, ' +
1472 'copy build.rs output to ./out/* and add a genrule to copy ' +
1473 './out/* to genrule output; for crates with code pattern: ' +
1474 'include!(concat!(env!("OUT_DIR"), "/<some_file>.rs"))'))
1475 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001476 '--debug',
1477 action='store_true',
1478 default=False,
1479 help='dump debug info into Android.bp')
1480 parser.add_argument(
1481 '--dependencies',
1482 action='store_true',
1483 default=False,
1484 help='dump debug info of dependent crates')
1485 parser.add_argument(
1486 '--device',
1487 action='store_true',
1488 default=False,
1489 help='run cargo also for a default device target')
1490 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001491 '--features',
1492 type=str,
1493 help=('pass features to cargo build, ' +
1494 'empty string means no default features'))
1495 parser.add_argument(
1496 '--global_defaults',
1497 type=str,
1498 help='add a defaults name to every module')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001499 parser.add_argument(
1500 '--host-first-multilib',
1501 action='store_true',
1502 default=False,
1503 help=('add a compile_multilib:"first" property ' +
1504 'to Android.bp host modules.'))
1505 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001506 '--no-host',
1507 action='store_true',
1508 default=False,
1509 help='do not run cargo for the host; only for the device target')
1510 parser.add_argument(
1511 '--no-subdir',
1512 action='store_true',
1513 default=False,
1514 help='do not output anything for sub-directories')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001515 parser.add_argument(
1516 '--onefile',
1517 action='store_true',
1518 default=False,
1519 help=('output all into one ./Android.bp, default will generate ' +
1520 'one Android.bp per Cargo.toml in subdirectories'))
1521 parser.add_argument(
1522 '--run',
1523 action='store_true',
1524 default=False,
1525 help='run it, default is dry-run')
1526 parser.add_argument('--rustflags', type=str, help='passing flags to rustc')
1527 parser.add_argument(
1528 '--skipcargo',
1529 action='store_true',
1530 default=False,
1531 help='skip cargo command, parse cargo.out, and generate Android.bp')
1532 parser.add_argument(
1533 '--tests',
1534 action='store_true',
1535 default=False,
1536 help='run cargo build --tests after normal build')
1537 parser.add_argument(
1538 '--verbose',
1539 action='store_true',
1540 default=False,
1541 help='echo executed commands')
1542 parser.add_argument(
1543 '--vv',
1544 action='store_true',
1545 default=False,
1546 help='run cargo with -vv instead of default -v')
1547 return parser.parse_args()
1548
1549
1550def main():
1551 args = parse_args()
1552 if not args.run: # default is dry-run
1553 print(DRY_RUN_NOTE)
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001554 Runner(args).run_cargo().gen_bp().dump_test_mapping_files()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001555
1556
1557if __name__ == '__main__':
1558 main()