blob: b5ddfe39796ba51e38928b59de588112411625f7 [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
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +010042 Note that when there are tests for this module or for its reverse
43 dependencies, these tests will be added to the TEST_MAPPING file.
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -070044
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
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +010050from update_crate_tests import TestMapping
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080051
52import argparse
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -070053import glob
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080054import os
55import os.path
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -070056import platform
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080057import re
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -070058import shutil
Andrew Walbran80e90be2020-06-09 14:33:18 +010059import sys
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080060
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -070061# Some Rust packages include extra unwanted crates.
62# This set contains all such excluded crate names.
63EXCLUDED_CRATES = set(['protobuf_bin_gen_rust_do_not_use'])
64
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080065RENAME_MAP = {
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -070066 # This map includes all changes to the default rust module names
67 # to resolve name conflicts, avoid confusion, or work as plugin.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080068 'libbacktrace': 'libbacktrace_rust',
Andrew Walbrane51f1042020-08-11 16:42:48 +010069 'libbase': 'libbase_rust',
Victor Hsieh21bea792020-12-04 10:59:16 -080070 'libfuse': 'libfuse_rust',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080071 'libgcc': 'libgcc_rust',
72 'liblog': 'liblog_rust',
Chih-Hung Hsieh07119862020-07-24 15:34:06 -070073 'libminijail': 'libminijail_rust',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080074 'libsync': 'libsync_rust',
75 'libx86_64': 'libx86_64_rust',
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -070076 'protoc_gen_rust': 'protoc-gen-rust',
77}
78
79RENAME_STEM_MAP = {
80 # This map includes all changes to the default rust module stem names,
81 # which is used for output files when different from the module name.
82 'protoc_gen_rust': 'protoc-gen-rust',
83}
84
85RENAME_DEFAULTS_MAP = {
86 # This map includes all changes to the default prefix of rust_default
87 # module names, to avoid conflict with existing Android modules.
88 'libc': 'rust_libc',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080089}
90
91# Header added to all generated Android.bp files.
Joel Galenson56446742021-02-18 08:27:48 -080092ANDROID_BP_HEADER = (
93 '// This file is generated by cargo2android.py {args}.\n' +
94 '// Do not modify this file as changes will be overridden on upgrade.\n')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080095
96CARGO_OUT = 'cargo.out' # Name of file to keep cargo build -v output.
97
98TARGET_TMP = 'target.tmp' # Name of temporary output directory.
99
100# Message to be displayed when this script is called without the --run flag.
101DRY_RUN_NOTE = (
102 'Dry-run: This script uses ./' + TARGET_TMP + ' for output directory,\n' +
103 'runs cargo clean, runs cargo build -v, saves output to ./cargo.out,\n' +
104 'and writes to Android.bp in the current and subdirectories.\n\n' +
105 'To do do all of the above, use the --run flag.\n' +
106 'See --help for other flags, and more usage notes in this script.\n')
107
108# Cargo -v output of a call to rustc.
109RUSTC_PAT = re.compile('^ +Running `rustc (.*)`$')
110
111# Cargo -vv output of a call to rustc could be split into multiple lines.
112# Assume that the first line will contain some CARGO_* env definition.
113RUSTC_VV_PAT = re.compile('^ +Running `.*CARGO_.*=.*$')
114# The combined -vv output rustc command line pattern.
115RUSTC_VV_CMD_ARGS = re.compile('^ *Running `.*CARGO_.*=.* rustc (.*)`$')
116
117# Cargo -vv output of a "cc" or "ar" command; all in one line.
118CC_AR_VV_PAT = re.compile(r'^\[([^ ]*)[^\]]*\] running:? "(cc|ar)" (.*)$')
119# Some package, such as ring-0.13.5, has pattern '... running "cc"'.
120
121# Rustc output of file location path pattern for a warning message.
122WARNING_FILE_PAT = re.compile('^ *--> ([^:]*):[0-9]+')
123
124# Rust package name with suffix -d1.d2.d3.
125VERSION_SUFFIX_PAT = re.compile(r'^(.*)-[0-9]+\.[0-9]+\.[0-9]+$')
126
127
128def altered_name(name):
129 return RENAME_MAP[name] if (name in RENAME_MAP) else name
130
131
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700132def altered_stem(name):
133 return RENAME_STEM_MAP[name] if (name in RENAME_STEM_MAP) else name
134
135
136def altered_defaults(name):
137 return RENAME_DEFAULTS_MAP[name] if (name in RENAME_DEFAULTS_MAP) else name
138
139
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800140def is_build_crate_name(name):
141 # We added special prefix to build script crate names.
142 return name.startswith('build_script_')
143
144
145def is_dependent_file_path(path):
146 # Absolute or dependent '.../' paths are not main files of this crate.
147 return path.startswith('/') or path.startswith('.../')
148
149
150def get_module_name(crate): # to sort crates in a list
151 return crate.module_name
152
153
154def pkg2crate_name(s):
155 return s.replace('-', '_').replace('.', '_')
156
157
158def file_base_name(path):
159 return os.path.splitext(os.path.basename(path))[0]
160
161
162def test_base_name(path):
163 return pkg2crate_name(file_base_name(path))
164
165
166def unquote(s): # remove quotes around str
167 if s and len(s) > 1 and s[0] == '"' and s[-1] == '"':
168 return s[1:-1]
169 return s
170
171
172def remove_version_suffix(s): # remove -d1.d2.d3 suffix
173 if VERSION_SUFFIX_PAT.match(s):
174 return VERSION_SUFFIX_PAT.match(s).group(1)
175 return s
176
177
178def short_out_name(pkg, s): # replace /.../pkg-*/out/* with .../out/*
179 return re.sub('^/.*/' + pkg + '-[0-9a-f]*/out/', '.../out/', s)
180
181
182def escape_quotes(s): # replace '"' with '\\"'
183 return s.replace('"', '\\"')
184
185
186class Crate(object):
187 """Information of a Rust crate to collect/emit for an Android.bp module."""
188
189 def __init__(self, runner, outf_name):
190 # Remembered global runner and its members.
191 self.runner = runner
192 self.debug = runner.args.debug
193 self.cargo_dir = '' # directory of my Cargo.toml
194 self.outf_name = outf_name # path to Android.bp
195 self.outf = None # open file handle of outf_name during dump*
196 # Variants/results that could be merged from multiple rustc lines.
197 self.host_supported = False
198 self.device_supported = False
199 self.has_warning = False
200 # Android module properties derived from rustc parameters.
201 self.module_name = '' # unique in Android build system
202 self.module_type = '' # rust_{binary,library,test}[_host] etc.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700203 self.defaults = '' # rust_defaults used by rust_test* modules
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700204 self.default_srcs = False # use 'srcs' defined in self.defaults
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800205 self.root_pkg = '' # parent package name of a sub/test packge, from -L
206 self.srcs = list() # main_src or merged multiple source files
207 self.stem = '' # real base name of output file
208 # Kept parsed status
209 self.errors = '' # all errors found during parsing
210 self.line_num = 1 # runner told input source line number
211 self.line = '' # original rustc command line parameters
212 # Parameters collected from rustc command line.
213 self.crate_name = '' # follows --crate-name
214 self.main_src = '' # follows crate_name parameter, shortened
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700215 self.crate_types = list() # follows --crate-type
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800216 self.cfgs = list() # follows --cfg, without feature= prefix
217 self.features = list() # follows --cfg, name in 'feature="..."'
218 self.codegens = list() # follows -C, some ignored
219 self.externs = list() # follows --extern
220 self.core_externs = list() # first part of self.externs elements
221 self.static_libs = list() # e.g. -l static=host_cpuid
222 self.shared_libs = list() # e.g. -l dylib=wayland-client, -l z
223 self.cap_lints = '' # follows --cap-lints
224 self.emit_list = '' # e.g., --emit=dep-info,metadata,link
225 self.edition = '2015' # rustc default, e.g., --edition=2018
226 self.target = '' # follows --target
227
228 def write(self, s):
229 # convenient way to output one line at a time with EOL.
230 self.outf.write(s + '\n')
231
232 def same_flags(self, other):
233 # host_supported, device_supported, has_warning are not compared but merged
234 # target is not compared, to merge different target/host modules
235 # externs is not compared; only core_externs is compared
236 return (not self.errors and not other.errors and
237 self.edition == other.edition and
238 self.cap_lints == other.cap_lints and
239 self.emit_list == other.emit_list and
240 self.core_externs == other.core_externs and
241 self.codegens == other.codegens and
242 self.features == other.features and
243 self.static_libs == other.static_libs and
244 self.shared_libs == other.shared_libs and self.cfgs == other.cfgs)
245
246 def merge_host_device(self, other):
247 """Returns true if attributes are the same except host/device support."""
248 return (self.crate_name == other.crate_name and
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700249 self.crate_types == other.crate_types and
250 self.main_src == other.main_src and
251 # before merge, each test module has an unique module name and stem
252 (self.stem == other.stem or self.crate_types == ['test']) and
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800253 self.root_pkg == other.root_pkg and not self.skip_crate() and
254 self.same_flags(other))
255
256 def merge_test(self, other):
257 """Returns true if self and other are tests of same root_pkg."""
258 # Before merger, each test has its own crate_name.
259 # A merged test uses its source file base name as output file name,
260 # so a test is mergeable only if its base name equals to its crate name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700261 return (self.crate_types == other.crate_types and
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700262 self.crate_types == ['test'] and self.root_pkg == other.root_pkg and
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800263 not self.skip_crate() and
264 other.crate_name == test_base_name(other.main_src) and
265 (len(self.srcs) > 1 or
266 (self.crate_name == test_base_name(self.main_src)) and
267 self.host_supported == other.host_supported and
268 self.device_supported == other.device_supported) and
269 self.same_flags(other))
270
271 def merge(self, other, outf_name):
272 """Try to merge crate into self."""
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700273 # Cargo build --tests could recompile a library for tests.
274 # We need to merge such duplicated calls to rustc, with
275 # the algorithm in merge_host_device.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800276 should_merge_host_device = self.merge_host_device(other)
277 should_merge_test = False
278 if not should_merge_host_device:
279 should_merge_test = self.merge_test(other)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800280 if should_merge_host_device or should_merge_test:
281 self.runner.init_bp_file(outf_name)
282 with open(outf_name, 'a') as outf: # to write debug info
283 self.outf = outf
284 other.outf = outf
285 self.do_merge(other, should_merge_test)
286 return True
287 return False
288
289 def do_merge(self, other, should_merge_test):
290 """Merge attributes of other to self."""
291 if self.debug:
292 self.write('\n// Before merge definition (1):')
293 self.dump_debug_info()
294 self.write('\n// Before merge definition (2):')
295 other.dump_debug_info()
296 # Merge properties of other to self.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800297 self.has_warning = self.has_warning or other.has_warning
298 if not self.target: # okay to keep only the first target triple
299 self.target = other.target
300 # decide_module_type sets up default self.stem,
301 # which can be changed if self is a merged test module.
302 self.decide_module_type()
303 if should_merge_test:
304 self.srcs.append(other.main_src)
305 # use a short unique name as the merged module name.
306 prefix = self.root_pkg + '_tests'
307 self.module_name = self.runner.claim_module_name(prefix, self, 0)
308 self.stem = self.module_name
309 # This normalized root_pkg name although might be the same
310 # as other module's crate_name, it is not actually used for
311 # output file name. A merged test module always have multiple
312 # source files and each source file base name is used as
313 # its output file name.
314 self.crate_name = pkg2crate_name(self.root_pkg)
315 if self.debug:
316 self.write('\n// After merge definition (1):')
317 self.dump_debug_info()
318
319 def find_cargo_dir(self):
320 """Deepest directory with Cargo.toml and contains the main_src."""
321 if not is_dependent_file_path(self.main_src):
322 dir_name = os.path.dirname(self.main_src)
323 while dir_name:
324 if os.path.exists(dir_name + '/Cargo.toml'):
325 self.cargo_dir = dir_name
326 return
327 dir_name = os.path.dirname(dir_name)
328
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700329 def add_codegens_flag(self, flag):
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700330 """Ignore options not used in Android."""
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700331 # 'prefer-dynamic' does not work with common flag -C lto
Chih-Hung Hsieh63459ed2020-08-26 11:51:15 -0700332 # 'embed-bitcode' is ignored; we might control LTO with other .bp flag
Chih-Hung Hsieh6c13b722020-09-11 21:24:03 -0700333 # 'codegen-units' is set in Android global config or by default
334 if not (flag.startswith('codegen-units=') or
335 flag.startswith('debuginfo=') or
Chih-Hung Hsieh63459ed2020-08-26 11:51:15 -0700336 flag.startswith('embed-bitcode=') or
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700337 flag.startswith('extra-filename=') or
338 flag.startswith('incremental=') or
339 flag.startswith('metadata=') or
340 flag == 'prefer-dynamic'):
341 self.codegens.append(flag)
342
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800343 def parse(self, line_num, line):
344 """Find important rustc arguments to convert to Android.bp properties."""
345 self.line_num = line_num
346 self.line = line
347 args = line.split() # Loop through every argument of rustc.
348 i = 0
349 while i < len(args):
350 arg = args[i]
351 if arg == '--crate-name':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700352 i += 1
353 self.crate_name = args[i]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800354 elif arg == '--crate-type':
355 i += 1
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700356 # cargo calls rustc with multiple --crate-type flags.
357 # rustc can accept:
358 # --crate-type [bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]
359 self.crate_types.append(args[i])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800360 elif arg == '--test':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700361 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800362 elif arg == '--target':
363 i += 1
364 self.target = args[i]
365 elif arg == '--cfg':
366 i += 1
367 if args[i].startswith('\'feature='):
368 self.features.append(unquote(args[i].replace('\'feature=', '')[:-1]))
369 else:
370 self.cfgs.append(args[i])
371 elif arg == '--extern':
372 i += 1
373 extern_names = re.sub('=/[^ ]*/deps/', ' = ', args[i])
374 self.externs.append(extern_names)
375 self.core_externs.append(re.sub(' = .*', '', extern_names))
376 elif arg == '-C': # codegen options
377 i += 1
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700378 self.add_codegens_flag(args[i])
379 elif arg.startswith('-C'):
380 # cargo has been passing "-C <xyz>" flag to rustc,
381 # but newer cargo could pass '-Cembed-bitcode=no' to rustc.
382 self.add_codegens_flag(arg[2:])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800383 elif arg == '--cap-lints':
384 i += 1
385 self.cap_lints = args[i]
386 elif arg == '-L':
387 i += 1
388 if args[i].startswith('dependency=') and args[i].endswith('/deps'):
389 if '/' + TARGET_TMP + '/' in args[i]:
390 self.root_pkg = re.sub(
391 '^.*/', '', re.sub('/' + TARGET_TMP + '/.*/deps$', '', args[i]))
392 else:
393 self.root_pkg = re.sub('^.*/', '',
394 re.sub('/[^/]+/[^/]+/deps$', '', args[i]))
395 self.root_pkg = remove_version_suffix(self.root_pkg)
396 elif arg == '-l':
397 i += 1
398 if args[i].startswith('static='):
399 self.static_libs.append(re.sub('static=', '', args[i]))
400 elif args[i].startswith('dylib='):
401 self.shared_libs.append(re.sub('dylib=', '', args[i]))
402 else:
403 self.shared_libs.append(args[i])
404 elif arg == '--out-dir' or arg == '--color': # ignored
405 i += 1
406 elif arg.startswith('--error-format=') or arg.startswith('--json='):
407 _ = arg # ignored
408 elif arg.startswith('--emit='):
409 self.emit_list = arg.replace('--emit=', '')
410 elif arg.startswith('--edition='):
411 self.edition = arg.replace('--edition=', '')
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700412 elif not arg.startswith('-'):
413 # shorten imported crate main source paths like $HOME/.cargo/
414 # registry/src/github.com-1ecc6299db9ec823/memchr-2.3.3/src/lib.rs
415 self.main_src = re.sub(r'^/[^ ]*/registry/src/', '.../', args[i])
416 self.main_src = re.sub(r'^\.\.\./github.com-[0-9a-f]*/', '.../',
417 self.main_src)
418 self.find_cargo_dir()
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700419 if self.cargo_dir: # for a subdirectory
420 if self.runner.args.no_subdir: # all .bp content to /dev/null
421 self.outf_name = '/dev/null'
422 elif not self.runner.args.onefile:
423 # Write to Android.bp in the subdirectory with Cargo.toml.
424 self.outf_name = self.cargo_dir + '/Android.bp'
425 self.main_src = self.main_src[len(self.cargo_dir) + 1:]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800426 else:
427 self.errors += 'ERROR: unknown ' + arg + '\n'
428 i += 1
429 if not self.crate_name:
430 self.errors += 'ERROR: missing --crate-name\n'
431 if not self.main_src:
432 self.errors += 'ERROR: missing main source file\n'
433 else:
434 self.srcs.append(self.main_src)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700435 if not self.crate_types:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800436 # Treat "--cfg test" as "--test"
437 if 'test' in self.cfgs:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700438 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800439 else:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700440 self.errors += 'ERROR: missing --crate-type or --test\n'
441 elif len(self.crate_types) > 1:
442 if 'test' in self.crate_types:
443 self.errors += 'ERROR: cannot handle both --crate-type and --test\n'
444 if 'lib' in self.crate_types and 'rlib' in self.crate_types:
445 self.errors += 'ERROR: cannot generate both lib and rlib crate types\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800446 if not self.root_pkg:
447 self.root_pkg = self.crate_name
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700448 self.device_supported = self.runner.args.device
449 self.host_supported = not self.runner.args.no_host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800450 self.cfgs = sorted(set(self.cfgs))
451 self.features = sorted(set(self.features))
452 self.codegens = sorted(set(self.codegens))
453 self.externs = sorted(set(self.externs))
454 self.core_externs = sorted(set(self.core_externs))
455 self.static_libs = sorted(set(self.static_libs))
456 self.shared_libs = sorted(set(self.shared_libs))
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700457 self.crate_types = sorted(set(self.crate_types))
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800458 self.decide_module_type()
459 self.module_name = altered_name(self.stem)
460 return self
461
462 def dump_line(self):
463 self.write('\n// Line ' + str(self.line_num) + ' ' + self.line)
464
465 def feature_list(self):
466 """Return a string of main_src + "feature_list"."""
467 pkg = self.main_src
468 if pkg.startswith('.../'): # keep only the main package name
469 pkg = re.sub('/.*', '', pkg[4:])
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700470 elif pkg.startswith('/'): # use relative path for a local package
471 pkg = os.path.relpath(pkg)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800472 if not self.features:
473 return pkg
474 return pkg + ' "' + ','.join(self.features) + '"'
475
476 def dump_skip_crate(self, kind):
477 if self.debug:
478 self.write('\n// IGNORED: ' + kind + ' ' + self.main_src)
479 return self
480
481 def skip_crate(self):
482 """Return crate_name or a message if this crate should be skipped."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700483 if (is_build_crate_name(self.crate_name) or
484 self.crate_name in EXCLUDED_CRATES):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800485 return self.crate_name
486 if is_dependent_file_path(self.main_src):
487 return 'dependent crate'
488 return ''
489
490 def dump(self):
491 """Dump all error/debug/module code to the output .bp file."""
492 self.runner.init_bp_file(self.outf_name)
493 with open(self.outf_name, 'a') as outf:
494 self.outf = outf
495 if self.errors:
496 self.dump_line()
497 self.write(self.errors)
498 elif self.skip_crate():
499 self.dump_skip_crate(self.skip_crate())
500 else:
501 if self.debug:
502 self.dump_debug_info()
503 self.dump_android_module()
504
505 def dump_debug_info(self):
506 """Dump parsed data, when cargo2android is called with --debug."""
507
508 def dump(name, value):
509 self.write('//%12s = %s' % (name, value))
510
511 def opt_dump(name, value):
512 if value:
513 dump(name, value)
514
515 def dump_list(fmt, values):
516 for v in values:
517 self.write(fmt % v)
518
519 self.dump_line()
520 dump('module_name', self.module_name)
521 dump('crate_name', self.crate_name)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700522 dump('crate_types', self.crate_types)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800523 dump('main_src', self.main_src)
524 dump('has_warning', self.has_warning)
525 dump('for_host', self.host_supported)
526 dump('for_device', self.device_supported)
527 dump('module_type', self.module_type)
528 opt_dump('target', self.target)
529 opt_dump('edition', self.edition)
530 opt_dump('emit_list', self.emit_list)
531 opt_dump('cap_lints', self.cap_lints)
532 dump_list('// cfg = %s', self.cfgs)
533 dump_list('// cfg = \'feature "%s"\'', self.features)
534 # TODO(chh): escape quotes in self.features, but not in other dump_list
535 dump_list('// codegen = %s', self.codegens)
536 dump_list('// externs = %s', self.externs)
537 dump_list('// -l static = %s', self.static_libs)
538 dump_list('// -l (dylib) = %s', self.shared_libs)
539
540 def dump_android_module(self):
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700541 """Dump one or more Android module definition, depending on crate_types."""
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700542 if len(self.crate_types) == 1:
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700543 self.dump_single_type_android_module()
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700544 return
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700545 if 'test' in self.crate_types:
546 self.write('\nERROR: multiple crate types cannot include test type')
547 return
548 # Dump one Android module per crate_type.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700549 for crate_type in self.crate_types:
550 self.decide_one_module_type(crate_type)
551 self.dump_one_android_module(crate_type)
552
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700553 def build_default_name(self):
554 """Return a short and readable name for the rust_defaults module."""
555 # Choices: (1) root_pkg + '_defaults',
556 # (2) root_pkg + '_defaults_' + crate_name
557 # (3) root_pkg + '_defaults_' + main_src_basename_path
558 # (4) root_pkg + '_defaults_' + a_positive_sequence_number
559 name1 = altered_defaults(self.root_pkg) + '_defaults'
560 if self.runner.try_claim_module_name(name1, self):
561 return name1
562 name2 = name1 + '_' + self.crate_name
563 if self.runner.try_claim_module_name(name2, self):
564 return name2
565 name3 = name1 + '_' + self.main_src_basename_path()
566 if self.runner.try_claim_module_name(name3, self):
567 return name3
568 return self.runner.claim_module_name(name1, self, 0)
569
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700570 def dump_srcs_list(self):
571 """Dump the srcs list, for defaults or regular modules."""
572 if len(self.srcs) > 1:
573 srcs = sorted(set(self.srcs)) # make a copy and dedup
574 else:
575 srcs = [self.main_src]
576 copy_out = self.runner.copy_out_module_name()
577 if copy_out:
578 srcs.append(':' + copy_out)
579 self.dump_android_property_list('srcs', '"%s"', srcs)
580
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700581 def dump_defaults_module(self):
582 """Dump a rust_defaults module to be shared by other modules."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700583 name = self.build_default_name()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700584 self.defaults = name
585 self.write('\nrust_defaults {')
586 self.write(' name: "' + name + '",')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700587 if self.runner.args.global_defaults:
588 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700589 self.write(' crate_name: "' + self.crate_name + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700590 if len(self.srcs) == 1: # only one source file; share it in defaults
591 self.default_srcs = True
592 if self.has_warning and not self.cap_lints:
593 self.write(' // has rustc warnings')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700594 self.dump_srcs_list()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700595 if 'test' in self.crate_types:
596 self.write(' test_suites: ["general-tests"],')
597 self.write(' auto_gen_config: true,')
598 self.dump_edition_flags_libs()
599 self.write('}')
600
601 def dump_single_type_android_module(self):
602 """Dump one simple Android module, which has only one crate_type."""
603 crate_type = self.crate_types[0]
604 if crate_type != 'test':
605 # do not change self.stem or self.module_name
606 self.dump_one_android_module(crate_type)
607 return
608 # Dump one test module per source file, and separate host and device tests.
609 # crate_type == 'test'
610 if (self.host_supported and self.device_supported) or len(self.srcs) > 1:
611 self.srcs = sorted(set(self.srcs))
612 self.dump_defaults_module()
613 saved_srcs = self.srcs
614 for src in saved_srcs:
615 self.srcs = [src]
616 saved_device_supported = self.device_supported
617 saved_host_supported = self.host_supported
618 saved_main_src = self.main_src
619 self.main_src = src
620 if saved_host_supported:
621 self.device_supported = False
622 self.host_supported = True
623 self.module_name = self.test_module_name()
624 self.decide_one_module_type(crate_type)
625 self.dump_one_android_module(crate_type)
626 if saved_device_supported:
627 self.device_supported = True
628 self.host_supported = False
629 self.module_name = self.test_module_name()
630 self.decide_one_module_type(crate_type)
631 self.dump_one_android_module(crate_type)
632 self.host_supported = saved_host_supported
633 self.device_supported = saved_device_supported
634 self.main_src = saved_main_src
635 self.srcs = saved_srcs
636
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700637 def dump_one_android_module(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800638 """Dump one Android module definition."""
639 if not self.module_type:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700640 self.write('\nERROR: unknown crate_type ' + crate_type)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800641 return
642 self.write('\n' + self.module_type + ' {')
643 self.dump_android_core_properties()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700644 if not self.defaults:
645 self.dump_edition_flags_libs()
646 if self.runner.args.host_first_multilib and self.host_supported and crate_type != 'test':
647 self.write(' compile_multilib: "first",')
648 self.write('}')
649
650 def dump_android_flags(self):
651 """Dump Android module flags property."""
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700652 if not self.cfgs and not self.codegens and not self.cap_lints:
653 return
654 self.write(' flags: [')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800655 if self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700656 self.write(' "--cap-lints ' + self.cap_lints + '",')
657 cfg_fmt = '"--cfg %s"'
658 codegens_fmt = '"-C %s"'
659 self.dump_android_property_list_items(cfg_fmt, self.cfgs)
660 self.dump_android_property_list_items(codegens_fmt, self.codegens)
661 self.write(' ],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700662
663 def dump_edition_flags_libs(self):
664 if self.edition:
665 self.write(' edition: "' + self.edition + '",')
666 self.dump_android_property_list('features', '"%s"', self.features)
667 self.dump_android_flags()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800668 if self.externs:
669 self.dump_android_externs()
670 self.dump_android_property_list('static_libs', '"lib%s"', self.static_libs)
671 self.dump_android_property_list('shared_libs', '"lib%s"', self.shared_libs)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800672
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700673 def main_src_basename_path(self):
674 return re.sub('/', '_', re.sub('.rs$', '', self.main_src))
675
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800676 def test_module_name(self):
677 """Return a unique name for a test module."""
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700678 # root_pkg+(_host|_device) + '_test_'+source_file_name
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700679 suffix = self.main_src_basename_path()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700680 host_device = '_host'
681 if self.device_supported:
682 host_device = '_device'
683 return self.root_pkg + host_device + '_test_' + suffix
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800684
685 def decide_module_type(self):
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700686 # Use the first crate type for the default/first module.
687 crate_type = self.crate_types[0] if self.crate_types else ''
688 self.decide_one_module_type(crate_type)
689
690 def decide_one_module_type(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800691 """Decide which Android module type to use."""
692 host = '' if self.device_supported else '_host'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700693 if crate_type == 'bin': # rust_binary[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800694 self.module_type = 'rust_binary' + host
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700695 # In rare cases like protobuf-codegen, the output binary name must
696 # be renamed to use as a plugin for protoc.
697 self.stem = altered_stem(self.crate_name)
698 self.module_name = altered_name(self.crate_name)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700699 elif crate_type == 'lib': # rust_library[_host]
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700700 # TODO(chh): should this be rust_library[_host]?
701 # Assuming that Cargo.toml do not use both 'lib' and 'rlib',
702 # because we map them both to rlib.
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700703 self.module_type = 'rust_library' + host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800704 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700705 self.module_name = altered_name(self.stem)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700706 elif crate_type == 'rlib': # rust_library[_host]
707 self.module_type = 'rust_library' + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700708 self.stem = 'lib' + self.crate_name
709 self.module_name = altered_name(self.stem)
710 elif crate_type == 'dylib': # rust_library[_host]_dylib
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800711 self.module_type = 'rust_library' + host + '_dylib'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700712 self.stem = 'lib' + self.crate_name
713 self.module_name = altered_name(self.stem) + '_dylib'
714 elif crate_type == 'cdylib': # rust_library[_host]_shared
Ivan Lozano0c057ad2020-12-15 10:41:26 -0500715 self.module_type = 'rust_ffi' + host + '_shared'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700716 self.stem = 'lib' + self.crate_name
717 self.module_name = altered_name(self.stem) + '_shared'
718 elif crate_type == 'staticlib': # rust_library[_host]_static
Ivan Lozano0c057ad2020-12-15 10:41:26 -0500719 self.module_type = 'rust_ffi' + host + '_static'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700720 self.stem = 'lib' + self.crate_name
721 self.module_name = altered_name(self.stem) + '_static'
722 elif crate_type == 'test': # rust_test[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800723 self.module_type = 'rust_test' + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700724 # Before do_merge, stem name is based on the --crate-name parameter.
725 # and test module name is based on stem.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800726 self.stem = self.test_module_name()
727 # self.stem will be changed after merging with other tests.
728 # self.stem is NOT used for final test binary name.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700729 # rust_test uses each source file base name as part of output file name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700730 # In do_merge, this function is called again, with a module_name.
731 # We make sure that the module name is unique in each package.
732 if self.module_name:
733 # Cargo uses "-C extra-filename=..." and "-C metadata=..." to add
734 # different suffixes and distinguish multiple tests of the same
735 # crate name. We ignore -C and use claim_module_name to get
736 # unique sequential suffix.
737 self.module_name = self.runner.claim_module_name(
738 self.module_name, self, 0)
739 # Now the module name is unique, stem should also match and unique.
740 self.stem = self.module_name
741 elif crate_type == 'proc-macro': # rust_proc_macro
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800742 self.module_type = 'rust_proc_macro'
743 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700744 self.module_name = altered_name(self.stem)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800745 else: # unknown module type, rust_prebuilt_dylib? rust_library[_host]?
746 self.module_type = ''
747 self.stem = ''
748
749 def dump_android_property_list_items(self, fmt, values):
750 for v in values:
751 # fmt has quotes, so we need escape_quotes(v)
752 self.write(' ' + (fmt % escape_quotes(v)) + ',')
753
754 def dump_android_property_list(self, name, fmt, values):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700755 if not values:
756 return
757 if len(values) > 1:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800758 self.write(' ' + name + ': [')
759 self.dump_android_property_list_items(fmt, values)
760 self.write(' ],')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700761 else:
762 self.write(' ' + name + ': [' +
763 (fmt % escape_quotes(values[0])) + '],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800764
765 def dump_android_core_properties(self):
766 """Dump the module header, name, stem, etc."""
767 self.write(' name: "' + self.module_name + '",')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700768 # see properties shared by dump_defaults_module
769 if self.defaults:
770 self.write(' defaults: ["' + self.defaults + '"],')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700771 elif self.runner.args.global_defaults:
772 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800773 if self.stem != self.module_name:
774 self.write(' stem: "' + self.stem + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700775 if self.has_warning and not self.cap_lints and not self.default_srcs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700776 self.write(' // has rustc warnings')
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700777 if self.host_supported and self.device_supported and self.module_type != 'rust_proc_macro':
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800778 self.write(' host_supported: true,')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700779 if not self.defaults:
780 self.write(' crate_name: "' + self.crate_name + '",')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700781 if not self.default_srcs:
782 self.dump_srcs_list()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700783 if 'test' in self.crate_types and not self.defaults:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800784 # self.root_pkg can have multiple test modules, with different *_tests[n]
785 # names, but their executables can all be installed under the same _tests
786 # directory. When built from Cargo.toml, all tests should have different
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700787 # file or crate names. So we used (root_pkg + '_tests') name as the
788 # relative_install_path.
789 # However, some package like 'slab' can have non-mergeable tests that
790 # must be separated by different module names. So, here we no longer
791 # emit relative_install_path.
792 # self.write(' relative_install_path: "' + self.root_pkg + '_tests",')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800793 self.write(' test_suites: ["general-tests"],')
794 self.write(' auto_gen_config: true,')
Joel Galensone261a152021-01-12 11:31:53 -0800795 if 'test' in self.crate_types and self.host_supported:
796 self.write(' test_options: {')
797 self.write(' unit_test: true,')
798 self.write(' },')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800799
800 def dump_android_externs(self):
801 """Dump the dependent rlibs and dylibs property."""
802 so_libs = list()
803 rust_libs = ''
804 deps_libname = re.compile('^.* = lib(.*)-[0-9a-f]*.(rlib|so|rmeta)$')
805 for lib in self.externs:
806 # normal value of lib: "libc = liblibc-*.rlib"
807 # strange case in rand crate: "getrandom_package = libgetrandom-*.rlib"
808 # we should use "libgetrandom", not "lib" + "getrandom_package"
809 groups = deps_libname.match(lib)
810 if groups is not None:
811 lib_name = groups.group(1)
812 else:
813 lib_name = re.sub(' .*$', '', lib)
814 if lib.endswith('.rlib') or lib.endswith('.rmeta'):
815 # On MacOS .rmeta is used when Linux uses .rlib or .rmeta.
816 rust_libs += ' "' + altered_name('lib' + lib_name) + '",\n'
817 elif lib.endswith('.so'):
818 so_libs.append(lib_name)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700819 elif lib != 'proc_macro': # --extern proc_macro is special and ignored
820 rust_libs += ' // ERROR: unknown type of lib ' + lib + '\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800821 if rust_libs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700822 self.write(' rustlibs: [\n' + rust_libs + ' ],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800823 # Are all dependent .so files proc_macros?
824 # TODO(chh): Separate proc_macros and dylib.
825 self.dump_android_property_list('proc_macros', '"lib%s"', so_libs)
826
827
828class ARObject(object):
829 """Information of an "ar" link command."""
830
831 def __init__(self, runner, outf_name):
832 # Remembered global runner and its members.
833 self.runner = runner
834 self.pkg = ''
835 self.outf_name = outf_name # path to Android.bp
836 # "ar" arguments
837 self.line_num = 1
838 self.line = ''
839 self.flags = '' # e.g. "crs"
840 self.lib = '' # e.g. "/.../out/lib*.a"
841 self.objs = list() # e.g. "/.../out/.../*.o"
842
843 def parse(self, pkg, line_num, args_line):
844 """Collect ar obj/lib file names."""
845 self.pkg = pkg
846 self.line_num = line_num
847 self.line = args_line
848 args = args_line.split()
849 num_args = len(args)
850 if num_args < 3:
851 print('ERROR: "ar" command has too few arguments', args_line)
852 else:
853 self.flags = unquote(args[0])
854 self.lib = unquote(args[1])
855 self.objs = sorted(set(map(unquote, args[2:])))
856 return self
857
858 def write(self, s):
859 self.outf.write(s + '\n')
860
861 def dump_debug_info(self):
862 self.write('\n// Line ' + str(self.line_num) + ' "ar" ' + self.line)
863 self.write('// ar_object for %12s' % self.pkg)
864 self.write('// flags = %s' % self.flags)
865 self.write('// lib = %s' % short_out_name(self.pkg, self.lib))
866 for o in self.objs:
867 self.write('// obj = %s' % short_out_name(self.pkg, o))
868
869 def dump_android_lib(self):
870 """Write cc_library_static into Android.bp."""
871 self.write('\ncc_library_static {')
872 self.write(' name: "' + file_base_name(self.lib) + '",')
873 self.write(' host_supported: true,')
874 if self.flags != 'crs':
875 self.write(' // ar flags = %s' % self.flags)
876 if self.pkg not in self.runner.pkg_obj2cc:
877 self.write(' ERROR: cannot find source files.\n}')
878 return
879 self.write(' srcs: [')
880 obj2cc = self.runner.pkg_obj2cc[self.pkg]
881 # Note: wflags are ignored.
882 dflags = list()
883 fflags = list()
884 for obj in self.objs:
885 self.write(' "' + short_out_name(self.pkg, obj2cc[obj].src) + '",')
886 # TODO(chh): union of dflags and flags of all obj
887 # Now, just a temporary hack that uses the last obj's flags
888 dflags = obj2cc[obj].dflags
889 fflags = obj2cc[obj].fflags
890 self.write(' ],')
891 self.write(' cflags: [')
892 self.write(' "-O3",') # TODO(chh): is this default correct?
893 self.write(' "-Wno-error",')
894 for x in fflags:
895 self.write(' "-f' + x + '",')
896 for x in dflags:
897 self.write(' "-D' + x + '",')
898 self.write(' ],')
899 self.write('}')
900
901 def dump(self):
902 """Dump error/debug/module info to the output .bp file."""
903 self.runner.init_bp_file(self.outf_name)
904 with open(self.outf_name, 'a') as outf:
905 self.outf = outf
906 if self.runner.args.debug:
907 self.dump_debug_info()
908 self.dump_android_lib()
909
910
911class CCObject(object):
912 """Information of a "cc" compilation command."""
913
914 def __init__(self, runner, outf_name):
915 # Remembered global runner and its members.
916 self.runner = runner
917 self.pkg = ''
918 self.outf_name = outf_name # path to Android.bp
919 # "cc" arguments
920 self.line_num = 1
921 self.line = ''
922 self.src = ''
923 self.obj = ''
924 self.dflags = list() # -D flags
925 self.fflags = list() # -f flags
926 self.iflags = list() # -I flags
927 self.wflags = list() # -W flags
928 self.other_args = list()
929
930 def parse(self, pkg, line_num, args_line):
931 """Collect cc compilation flags and src/out file names."""
932 self.pkg = pkg
933 self.line_num = line_num
934 self.line = args_line
935 args = args_line.split()
936 i = 0
937 while i < len(args):
938 arg = args[i]
939 if arg == '"-c"':
940 i += 1
941 if args[i].startswith('"-o'):
942 # ring-0.13.5 dumps: ... "-c" "-o/.../*.o" ".../*.c"
943 self.obj = unquote(args[i])[2:]
944 i += 1
945 self.src = unquote(args[i])
946 else:
947 self.src = unquote(args[i])
948 elif arg == '"-o"':
949 i += 1
950 self.obj = unquote(args[i])
951 elif arg == '"-I"':
952 i += 1
953 self.iflags.append(unquote(args[i]))
954 elif arg.startswith('"-D'):
955 self.dflags.append(unquote(args[i])[2:])
956 elif arg.startswith('"-f'):
957 self.fflags.append(unquote(args[i])[2:])
958 elif arg.startswith('"-W'):
959 self.wflags.append(unquote(args[i])[2:])
960 elif not (arg.startswith('"-O') or arg == '"-m64"' or arg == '"-g"' or
961 arg == '"-g3"'):
962 # ignore -O -m64 -g
963 self.other_args.append(unquote(args[i]))
964 i += 1
965 self.dflags = sorted(set(self.dflags))
966 self.fflags = sorted(set(self.fflags))
967 # self.wflags is not sorted because some are order sensitive
968 # and we ignore them anyway.
969 if self.pkg not in self.runner.pkg_obj2cc:
970 self.runner.pkg_obj2cc[self.pkg] = {}
971 self.runner.pkg_obj2cc[self.pkg][self.obj] = self
972 return self
973
974 def write(self, s):
975 self.outf.write(s + '\n')
976
977 def dump_debug_flags(self, name, flags):
978 self.write('// ' + name + ':')
979 for f in flags:
980 self.write('// %s' % f)
981
982 def dump(self):
983 """Dump only error/debug info to the output .bp file."""
984 if not self.runner.args.debug:
985 return
986 self.runner.init_bp_file(self.outf_name)
987 with open(self.outf_name, 'a') as outf:
988 self.outf = outf
989 self.write('\n// Line ' + str(self.line_num) + ' "cc" ' + self.line)
990 self.write('// cc_object for %12s' % self.pkg)
991 self.write('// src = %s' % short_out_name(self.pkg, self.src))
992 self.write('// obj = %s' % short_out_name(self.pkg, self.obj))
993 self.dump_debug_flags('-I flags', self.iflags)
994 self.dump_debug_flags('-D flags', self.dflags)
995 self.dump_debug_flags('-f flags', self.fflags)
996 self.dump_debug_flags('-W flags', self.wflags)
997 if self.other_args:
998 self.dump_debug_flags('other args', self.other_args)
999
1000
1001class Runner(object):
1002 """Main class to parse cargo -v output and print Android module definitions."""
1003
1004 def __init__(self, args):
1005 self.bp_files = set() # Remember all output Android.bp files.
1006 self.root_pkg = '' # name of package in ./Cargo.toml
1007 # Saved flags, modes, and data.
1008 self.args = args
1009 self.dry_run = not args.run
1010 self.skip_cargo = args.skipcargo
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001011 self.cargo_path = './cargo' # path to cargo, will be set later
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001012 self.checked_out_files = False # to check only once
1013 self.build_out_files = [] # output files generated by build.rs
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001014 # All cc/ar objects, crates, dependencies, and warning files
1015 self.cc_objects = list()
1016 self.pkg_obj2cc = {}
1017 # pkg_obj2cc[cc_object[i].pkg][cc_objects[i].obj] = cc_objects[i]
1018 self.ar_objects = list()
1019 self.crates = list()
1020 self.dependencies = list() # dependent and build script crates
1021 self.warning_files = set()
1022 # Keep a unique mapping from (module name) to crate
1023 self.name_owners = {}
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001024 # Save and dump all errors from cargo to Android.bp.
1025 self.errors = ''
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001026 self.setup_cargo_path()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001027 # Default action is cargo clean, followed by build or user given actions.
1028 if args.cargo:
1029 self.cargo = ['clean'] + args.cargo
1030 else:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001031 default_target = '--target x86_64-unknown-linux-gnu'
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -07001032 # Use the same target for both host and default device builds.
1033 # Same target is used as default in host x86_64 Android compilation.
1034 # Note: b/169872957, prebuilt cargo failed to build vsock
1035 # on x86_64-unknown-linux-musl systems.
1036 self.cargo = ['clean', 'build ' + default_target]
1037 if args.tests:
1038 self.cargo.append('build --tests ' + default_target)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001039
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001040 def setup_cargo_path(self):
1041 """Find cargo in the --cargo_bin or prebuilt rust bin directory."""
1042 if self.args.cargo_bin:
1043 self.cargo_path = os.path.join(self.args.cargo_bin, 'cargo')
1044 if not os.path.isfile(self.cargo_path):
1045 sys.exit('ERROR: cannot find cargo in ' + self.args.cargo_bin)
1046 print('WARNING: using cargo in ' + self.args.cargo_bin)
1047 return
1048 # We have only tested this on Linux.
1049 if platform.system() != 'Linux':
1050 sys.exit('ERROR: this script has only been tested on Linux with cargo.')
1051 # Assuming that this script is in development/scripts.
1052 my_dir = os.path.dirname(os.path.abspath(__file__))
1053 linux_dir = os.path.join(my_dir, '..', '..',
1054 'prebuilts', 'rust', 'linux-x86')
1055 if not os.path.isdir(linux_dir):
1056 sys.exit('ERROR: cannot find directory ' + linux_dir)
1057 rust_version = self.find_rust_version(my_dir, linux_dir)
1058 cargo_bin = os.path.join(linux_dir, rust_version, 'bin')
1059 self.cargo_path = os.path.join(cargo_bin, 'cargo')
1060 if not os.path.isfile(self.cargo_path):
1061 sys.exit('ERROR: cannot find cargo in ' + cargo_bin
1062 + '; please try --cargo_bin= flag.')
1063 return
1064
1065 def find_rust_version(self, my_dir, linux_dir):
1066 """Use my script directory, find prebuilt rust version."""
1067 # First look up build/soong/rust/config/global.go.
1068 path2global = os.path.join(my_dir, '..', '..',
1069 'build', 'soong', 'rust', 'config', 'global.go')
1070 if os.path.isfile(path2global):
1071 # try to find: RustDefaultVersion = "1.44.0"
1072 version_pat = re.compile(
1073 r'\s*RustDefaultVersion\s*=\s*"([0-9]+\.[0-9]+\.[0-9]+)".*$')
1074 with open(path2global, 'r') as inf:
1075 for line in inf:
1076 result = version_pat.match(line)
1077 if result:
1078 return result.group(1)
1079 print('WARNING: cannot find RustDefaultVersion in ' + path2global)
1080 # Otherwise, find the newest (largest) version number in linux_dir.
1081 rust_version = (0, 0, 0) # the prebuilt version to use
1082 version_pat = re.compile(r'([0-9]+)\.([0-9]+)\.([0-9]+)$')
1083 for dir_name in os.listdir(linux_dir):
1084 result = version_pat.match(dir_name)
1085 if not result:
1086 continue
1087 version = (result.group(1), result.group(2), result.group(3))
1088 if version > rust_version:
1089 rust_version = version
1090 return '.'.join(rust_version)
1091
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001092 def find_out_files(self):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001093 # list1 has build.rs output for normal crates
1094 list1 = glob.glob(TARGET_TMP + '/*/*/build/' + self.root_pkg + '-*/out/*')
1095 # list2 has build.rs output for proc-macro crates
1096 list2 = glob.glob(TARGET_TMP + '/*/build/' + self.root_pkg + '-*/out/*')
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001097 return list1 + list2
1098
1099 def copy_out_files(self):
1100 """Copy build.rs output files to ./out and set up build_out_files."""
1101 if self.checked_out_files:
1102 return
1103 self.checked_out_files = True
1104 cargo_out_files = self.find_out_files()
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001105 out_files = set()
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001106 if cargo_out_files:
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001107 os.makedirs('out', exist_ok=True)
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001108 for path in cargo_out_files:
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001109 file_name = path.split('/')[-1]
1110 out_files.add(file_name)
1111 shutil.copy(path, 'out/' + file_name)
1112 self.build_out_files = sorted(out_files)
1113
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001114 def has_used_out_dir(self):
1115 """Returns true if env!("OUT_DIR") is found."""
1116 return 0 == os.system('grep -rl --exclude build.rs --include \\*.rs' +
1117 ' \'env!("OUT_DIR")\' * > /dev/null')
1118
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001119 def copy_out_module_name(self):
1120 if self.args.copy_out and self.build_out_files:
1121 return 'copy_' + self.root_pkg + '_build_out'
1122 else:
1123 return ''
1124
Haibo Huang0f72c952021-03-19 11:34:15 -07001125 def read_license(self, name):
1126 if not os.path.isfile(name):
1127 return ''
1128 license = ''
1129 with open(name, 'r') as intf:
1130 line = intf.readline()
1131 # Firstly skip ANDROID_BP_HEADER
1132 while line.startswith('//'):
1133 line = intf.readline()
1134 # Read all lines until we see a rust_* rule.
1135 while line != '' and not line.startswith('rust_'):
1136 license += line
1137 line = intf.readline()
1138 return license.strip()
1139
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001140 def dump_copy_out_module(self, outf):
1141 """Output the genrule module to copy out/* to $(genDir)."""
1142 copy_out = self.copy_out_module_name()
1143 if not copy_out:
1144 return
1145 outf.write('\ngenrule {\n')
1146 outf.write(' name: "' + copy_out + '",\n')
1147 outf.write(' srcs: ["out/*"],\n')
1148 outf.write(' cmd: "cp $(in) $(genDir)",\n')
1149 if len(self.build_out_files) > 1:
1150 outf.write(' out: [\n')
1151 for f in self.build_out_files:
1152 outf.write(' "' + f + '",\n')
1153 outf.write(' ],\n')
1154 else:
1155 outf.write(' out: ["' + self.build_out_files[0] + '"],\n')
1156 outf.write('}\n')
1157
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001158 def init_bp_file(self, name):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001159 # name could be Android.bp or sub_dir_path/Android.bp
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001160 if name not in self.bp_files:
1161 self.bp_files.add(name)
Haibo Huang0f72c952021-03-19 11:34:15 -07001162 license_section = self.read_license(name)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001163 with open(name, 'w') as outf:
Andrew Walbran80e90be2020-06-09 14:33:18 +01001164 outf.write(ANDROID_BP_HEADER.format(args=' '.join(sys.argv[1:])))
Haibo Huang0f72c952021-03-19 11:34:15 -07001165 outf.write('\n')
1166 outf.write(license_section)
1167 outf.write('\n')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001168 # at most one copy_out module per .bp file
1169 self.dump_copy_out_module(outf)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001170
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001171 def dump_test_mapping_files(self):
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001172 """Dump all TEST_MAPPING files."""
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001173 if self.dry_run:
1174 print('Dry-run skip dump of TEST_MAPPING')
Joel Galensoncf4ebb02021-04-07 08:39:51 -07001175 elif self.args.no_test_mapping:
1176 print('Skipping generation of TEST_MAPPING')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001177 else:
Jeff Vander Stoep1b24dc32021-02-03 18:52:42 +01001178 test_mapping = TestMapping(None)
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +01001179 for bp_file_name in self.bp_files:
1180 test_mapping.create_test_mapping(os.path.dirname(bp_file_name))
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001181 return self
1182
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001183 def try_claim_module_name(self, name, owner):
1184 """Reserve and return True if it has not been reserved yet."""
1185 if name not in self.name_owners or owner == self.name_owners[name]:
1186 self.name_owners[name] = owner
1187 return True
1188 return False
1189
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001190 def claim_module_name(self, prefix, owner, counter):
1191 """Return prefix if not owned yet, otherwise, prefix+str(counter)."""
1192 while True:
1193 name = prefix
1194 if counter > 0:
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001195 name += '_' + str(counter)
1196 if self.try_claim_module_name(name, owner):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001197 return name
1198 counter += 1
1199
1200 def find_root_pkg(self):
1201 """Read name of [package] in ./Cargo.toml."""
1202 if not os.path.exists('./Cargo.toml'):
1203 return
1204 with open('./Cargo.toml', 'r') as inf:
1205 pkg_section = re.compile(r'^ *\[package\]')
1206 name = re.compile('^ *name *= * "([^"]*)"')
1207 in_pkg = False
1208 for line in inf:
1209 if in_pkg:
1210 if name.match(line):
1211 self.root_pkg = name.match(line).group(1)
1212 break
1213 else:
1214 in_pkg = pkg_section.match(line) is not None
1215
1216 def run_cargo(self):
1217 """Calls cargo -v and save its output to ./cargo.out."""
1218 if self.skip_cargo:
1219 return self
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001220 cargo_toml = './Cargo.toml'
1221 cargo_out = './cargo.out'
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001222 # Do not use Cargo.lock, because .bp rules are designed to
1223 # run with "latest" crates avaialable on Android.
1224 cargo_lock = './Cargo.lock'
1225 cargo_lock_saved = './cargo.lock.saved'
1226 had_cargo_lock = os.path.exists(cargo_lock)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001227 if not os.access(cargo_toml, os.R_OK):
1228 print('ERROR: Cannot find or read', cargo_toml)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001229 return self
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001230 if not self.dry_run:
1231 if os.path.exists(cargo_out):
1232 os.remove(cargo_out)
1233 if not self.args.use_cargo_lock and had_cargo_lock: # save it
1234 os.rename(cargo_lock, cargo_lock_saved)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001235 cmd_tail = ' --target-dir ' + TARGET_TMP + ' >> ' + cargo_out + ' 2>&1'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001236 # set up search PATH for cargo to find the correct rustc
1237 saved_path = os.environ['PATH']
1238 os.environ['PATH'] = os.path.dirname(self.cargo_path) + ':' + saved_path
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001239 # Add [workspace] to Cargo.toml if it is not there.
1240 added_workspace = False
1241 if self.args.add_workspace:
1242 with open(cargo_toml, 'r') as in_file:
1243 cargo_toml_lines = in_file.readlines()
1244 found_workspace = '[workspace]\n' in cargo_toml_lines
1245 if found_workspace:
1246 print('### WARNING: found [workspace] in Cargo.toml')
1247 else:
1248 with open(cargo_toml, 'a') as out_file:
1249 out_file.write('[workspace]\n')
1250 added_workspace = True
1251 if self.args.verbose:
1252 print('### INFO: added [workspace] to Cargo.toml')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001253 for c in self.cargo:
1254 features = ''
Chih-Hung Hsieh6c8d52f2020-03-30 18:28:52 -07001255 if c != 'clean':
1256 if self.args.features is not None:
1257 features = ' --no-default-features'
1258 if self.args.features:
1259 features += ' --features ' + self.args.features
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001260 cmd_v_flag = ' -vv ' if self.args.vv else ' -v '
1261 cmd = self.cargo_path + cmd_v_flag
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001262 cmd += c + features + cmd_tail
1263 if self.args.rustflags and c != 'clean':
1264 cmd = 'RUSTFLAGS="' + self.args.rustflags + '" ' + cmd
1265 if self.dry_run:
1266 print('Dry-run skip:', cmd)
1267 else:
1268 if self.args.verbose:
1269 print('Running:', cmd)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001270 with open(cargo_out, 'a') as out_file:
1271 out_file.write('### Running: ' + cmd + '\n')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001272 os.system(cmd)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001273 if added_workspace: # restore original Cargo.toml
1274 with open(cargo_toml, 'w') as out_file:
1275 out_file.writelines(cargo_toml_lines)
1276 if self.args.verbose:
1277 print('### INFO: restored original Cargo.toml')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001278 os.environ['PATH'] = saved_path
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001279 if not self.dry_run:
1280 if not had_cargo_lock: # restore to no Cargo.lock state
1281 os.remove(cargo_lock)
1282 elif not self.args.use_cargo_lock: # restore saved Cargo.lock
1283 os.rename(cargo_lock_saved, cargo_lock)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001284 return self
1285
1286 def dump_dependencies(self):
1287 """Append dependencies and their features to Android.bp."""
1288 if not self.dependencies:
1289 return
1290 dependent_list = list()
1291 for c in self.dependencies:
1292 dependent_list.append(c.feature_list())
1293 sorted_dependencies = sorted(set(dependent_list))
1294 self.init_bp_file('Android.bp')
1295 with open('Android.bp', 'a') as outf:
1296 outf.write('\n// dependent_library ["feature_list"]\n')
1297 for s in sorted_dependencies:
1298 outf.write('// ' + s + '\n')
1299
1300 def dump_pkg_obj2cc(self):
1301 """Dump debug info of the pkg_obj2cc map."""
1302 if not self.args.debug:
1303 return
1304 self.init_bp_file('Android.bp')
1305 with open('Android.bp', 'a') as outf:
1306 sorted_pkgs = sorted(self.pkg_obj2cc.keys())
1307 for pkg in sorted_pkgs:
1308 if not self.pkg_obj2cc[pkg]:
1309 continue
1310 outf.write('\n// obj => src for %s\n' % pkg)
1311 obj2cc = self.pkg_obj2cc[pkg]
1312 for obj in sorted(obj2cc.keys()):
1313 outf.write('// ' + short_out_name(pkg, obj) + ' => ' +
1314 short_out_name(pkg, obj2cc[obj].src) + '\n')
1315
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001316 def apply_patch(self):
1317 """Apply local patch file if it is given."""
1318 if self.args.patch:
1319 if self.dry_run:
1320 print('Dry-run skip patch file:', self.args.patch)
1321 else:
1322 if not os.path.exists(self.args.patch):
1323 self.append_to_bp('ERROR cannot find patch file: ' + self.args.patch)
1324 return self
1325 if self.args.verbose:
1326 print('### INFO: applying local patch file:', self.args.patch)
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001327 os.system('patch -s --no-backup-if-mismatch ./Android.bp ' +
1328 self.args.patch)
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001329 return self
1330
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001331 def gen_bp(self):
1332 """Parse cargo.out and generate Android.bp files."""
1333 if self.dry_run:
1334 print('Dry-run skip: read', CARGO_OUT, 'write Android.bp')
1335 elif os.path.exists(CARGO_OUT):
1336 self.find_root_pkg()
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001337 if self.args.copy_out:
1338 self.copy_out_files()
1339 elif self.find_out_files() and self.has_used_out_dir():
1340 print('WARNING: ' + self.root_pkg + ' has cargo output files; ' +
1341 'please rerun with the --copy-out flag.')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001342 with open(CARGO_OUT, 'r') as cargo_out:
1343 self.parse(cargo_out, 'Android.bp')
1344 self.crates.sort(key=get_module_name)
1345 for obj in self.cc_objects:
1346 obj.dump()
1347 self.dump_pkg_obj2cc()
1348 for crate in self.crates:
1349 crate.dump()
1350 dumped_libs = set()
1351 for lib in self.ar_objects:
1352 if lib.pkg == self.root_pkg:
1353 lib_name = file_base_name(lib.lib)
1354 if lib_name not in dumped_libs:
1355 dumped_libs.add(lib_name)
1356 lib.dump()
1357 if self.args.dependencies and self.dependencies:
1358 self.dump_dependencies()
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001359 if self.errors:
1360 self.append_to_bp('\nErrors in ' + CARGO_OUT + ':\n' + self.errors)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001361 return self
1362
1363 def add_ar_object(self, obj):
1364 self.ar_objects.append(obj)
1365
1366 def add_cc_object(self, obj):
1367 self.cc_objects.append(obj)
1368
1369 def add_crate(self, crate):
1370 """Merge crate with someone in crates, or append to it. Return crates."""
1371 if crate.skip_crate():
1372 if self.args.debug: # include debug info of all crates
1373 self.crates.append(crate)
1374 if self.args.dependencies: # include only dependent crates
1375 if (is_dependent_file_path(crate.main_src) and
1376 not is_build_crate_name(crate.crate_name)):
1377 self.dependencies.append(crate)
1378 else:
1379 for c in self.crates:
1380 if c.merge(crate, 'Android.bp'):
1381 return
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001382 # If not merged, decide module type and name now.
1383 crate.decide_module_type()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001384 self.crates.append(crate)
1385
1386 def find_warning_owners(self):
1387 """For each warning file, find its owner crate."""
1388 missing_owner = False
1389 for f in self.warning_files:
1390 cargo_dir = '' # find lowest crate, with longest path
1391 owner = None # owner crate of this warning
1392 for c in self.crates:
1393 if (f.startswith(c.cargo_dir + '/') and
1394 len(cargo_dir) < len(c.cargo_dir)):
1395 cargo_dir = c.cargo_dir
1396 owner = c
1397 if owner:
1398 owner.has_warning = True
1399 else:
1400 missing_owner = True
1401 if missing_owner and os.path.exists('Cargo.toml'):
1402 # owner is the root cargo, with empty cargo_dir
1403 for c in self.crates:
1404 if not c.cargo_dir:
1405 c.has_warning = True
1406
1407 def rustc_command(self, n, rustc_line, line, outf_name):
1408 """Process a rustc command line from cargo -vv output."""
1409 # cargo build -vv output can have multiple lines for a rustc command
1410 # due to '\n' in strings for environment variables.
1411 # strip removes leading spaces and '\n' at the end
1412 new_rustc = (rustc_line.strip() + line) if rustc_line else line
1413 # Use an heuristic to detect the completions of a multi-line command.
1414 # This might fail for some very rare case, but easy to fix manually.
1415 if not line.endswith('`\n') or (new_rustc.count('`') % 2) != 0:
1416 return new_rustc
1417 if RUSTC_VV_CMD_ARGS.match(new_rustc):
1418 args = RUSTC_VV_CMD_ARGS.match(new_rustc).group(1)
1419 self.add_crate(Crate(self, outf_name).parse(n, args))
1420 else:
1421 self.assert_empty_vv_line(new_rustc)
1422 return ''
1423
1424 def cc_ar_command(self, n, groups, outf_name):
1425 pkg = groups.group(1)
1426 line = groups.group(3)
1427 if groups.group(2) == 'cc':
1428 self.add_cc_object(CCObject(self, outf_name).parse(pkg, n, line))
1429 else:
1430 self.add_ar_object(ARObject(self, outf_name).parse(pkg, n, line))
1431
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001432 def append_to_bp(self, line):
1433 self.init_bp_file('Android.bp')
1434 with open('Android.bp', 'a') as outf:
1435 outf.write(line)
1436
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001437 def assert_empty_vv_line(self, line):
1438 if line: # report error if line is not empty
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001439 self.append_to_bp('ERROR -vv line: ' + line)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001440 return ''
1441
1442 def parse(self, inf, outf_name):
1443 """Parse rustc and warning messages in inf, return a list of Crates."""
1444 n = 0 # line number
1445 prev_warning = False # true if the previous line was warning: ...
1446 rustc_line = '' # previous line(s) matching RUSTC_VV_PAT
1447 for line in inf:
1448 n += 1
1449 if line.startswith('warning: '):
1450 prev_warning = True
1451 rustc_line = self.assert_empty_vv_line(rustc_line)
1452 continue
1453 new_rustc = ''
1454 if RUSTC_PAT.match(line):
1455 args_line = RUSTC_PAT.match(line).group(1)
1456 self.add_crate(Crate(self, outf_name).parse(n, args_line))
1457 self.assert_empty_vv_line(rustc_line)
1458 elif rustc_line or RUSTC_VV_PAT.match(line):
1459 new_rustc = self.rustc_command(n, rustc_line, line, outf_name)
1460 elif CC_AR_VV_PAT.match(line):
1461 self.cc_ar_command(n, CC_AR_VV_PAT.match(line), outf_name)
1462 elif prev_warning and WARNING_FILE_PAT.match(line):
1463 self.assert_empty_vv_line(rustc_line)
1464 fpath = WARNING_FILE_PAT.match(line).group(1)
1465 if fpath[0] != '/': # ignore absolute path
1466 self.warning_files.add(fpath)
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001467 elif line.startswith('error: ') or line.startswith('error[E'):
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001468 if not self.args.ignore_cargo_errors:
1469 self.errors += line
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001470 prev_warning = False
1471 rustc_line = new_rustc
1472 self.find_warning_owners()
1473
1474
1475def parse_args():
1476 """Parse main arguments."""
1477 parser = argparse.ArgumentParser('cargo2android')
1478 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001479 '--add_workspace',
1480 action='store_true',
1481 default=False,
1482 help=('append [workspace] to Cargo.toml before calling cargo,' +
1483 ' to treat current directory as root of package source;' +
1484 ' otherwise the relative source file path in generated' +
1485 ' .bp file will be from the parent directory.'))
1486 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001487 '--cargo',
1488 action='append',
1489 metavar='args_string',
1490 help=('extra cargo build -v args in a string, ' +
1491 'each --cargo flag calls cargo build -v once'))
1492 parser.add_argument(
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001493 '--cargo_bin',
1494 type=str,
1495 help='use cargo in the cargo_bin directory instead of the prebuilt one')
1496 parser.add_argument(
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001497 '--copy-out',
1498 action='store_true',
1499 default=False,
1500 help=('only for root directory, ' +
1501 'copy build.rs output to ./out/* and add a genrule to copy ' +
1502 './out/* to genrule output; for crates with code pattern: ' +
1503 'include!(concat!(env!("OUT_DIR"), "/<some_file>.rs"))'))
1504 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001505 '--debug',
1506 action='store_true',
1507 default=False,
1508 help='dump debug info into Android.bp')
1509 parser.add_argument(
1510 '--dependencies',
1511 action='store_true',
1512 default=False,
1513 help='dump debug info of dependent crates')
1514 parser.add_argument(
1515 '--device',
1516 action='store_true',
1517 default=False,
1518 help='run cargo also for a default device target')
1519 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001520 '--features',
1521 type=str,
1522 help=('pass features to cargo build, ' +
1523 'empty string means no default features'))
1524 parser.add_argument(
1525 '--global_defaults',
1526 type=str,
1527 help='add a defaults name to every module')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001528 parser.add_argument(
1529 '--host-first-multilib',
1530 action='store_true',
1531 default=False,
1532 help=('add a compile_multilib:"first" property ' +
1533 'to Android.bp host modules.'))
1534 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001535 '--ignore-cargo-errors',
1536 action='store_true',
1537 default=False,
1538 help='do not append cargo/rustc error messages to Android.bp')
1539 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001540 '--no-host',
1541 action='store_true',
1542 default=False,
1543 help='do not run cargo for the host; only for the device target')
1544 parser.add_argument(
1545 '--no-subdir',
1546 action='store_true',
1547 default=False,
1548 help='do not output anything for sub-directories')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001549 parser.add_argument(
1550 '--onefile',
1551 action='store_true',
1552 default=False,
1553 help=('output all into one ./Android.bp, default will generate ' +
1554 'one Android.bp per Cargo.toml in subdirectories'))
1555 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001556 '--patch',
1557 type=str,
1558 help='apply the given patch file to generated ./Android.bp')
1559 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001560 '--run',
1561 action='store_true',
1562 default=False,
1563 help='run it, default is dry-run')
1564 parser.add_argument('--rustflags', type=str, help='passing flags to rustc')
1565 parser.add_argument(
1566 '--skipcargo',
1567 action='store_true',
1568 default=False,
1569 help='skip cargo command, parse cargo.out, and generate Android.bp')
1570 parser.add_argument(
1571 '--tests',
1572 action='store_true',
1573 default=False,
1574 help='run cargo build --tests after normal build')
1575 parser.add_argument(
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001576 '--use-cargo-lock',
1577 action='store_true',
1578 default=False,
1579 help=('run cargo build with existing Cargo.lock ' +
1580 '(used when some latest dependent crates failed)'))
1581 parser.add_argument(
Joel Galensoncf4ebb02021-04-07 08:39:51 -07001582 '--no-test-mapping',
1583 action='store_true',
1584 default=False,
1585 help='Do not generate a TEST_MAPPING file. Use only to speed up debugging.')
1586 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001587 '--verbose',
1588 action='store_true',
1589 default=False,
1590 help='echo executed commands')
1591 parser.add_argument(
1592 '--vv',
1593 action='store_true',
1594 default=False,
1595 help='run cargo with -vv instead of default -v')
1596 return parser.parse_args()
1597
1598
1599def main():
1600 args = parse_args()
1601 if not args.run: # default is dry-run
1602 print(DRY_RUN_NOTE)
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001603 Runner(args).run_cargo().gen_bp().apply_patch().dump_test_mapping_files()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001604
1605
1606if __name__ == '__main__':
1607 main()