blob: f32061e727180c6ddc6aa731f2b1b27c0e01a9c1 [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.
Andrew Walbran80e90be2020-06-09 14:33:18 +010092ANDROID_BP_HEADER = '// This file is generated by cargo2android.py {args}.\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080093
94CARGO_OUT = 'cargo.out' # Name of file to keep cargo build -v output.
95
96TARGET_TMP = 'target.tmp' # Name of temporary output directory.
97
98# Message to be displayed when this script is called without the --run flag.
99DRY_RUN_NOTE = (
100 'Dry-run: This script uses ./' + TARGET_TMP + ' for output directory,\n' +
101 'runs cargo clean, runs cargo build -v, saves output to ./cargo.out,\n' +
102 'and writes to Android.bp in the current and subdirectories.\n\n' +
103 'To do do all of the above, use the --run flag.\n' +
104 'See --help for other flags, and more usage notes in this script.\n')
105
106# Cargo -v output of a call to rustc.
107RUSTC_PAT = re.compile('^ +Running `rustc (.*)`$')
108
109# Cargo -vv output of a call to rustc could be split into multiple lines.
110# Assume that the first line will contain some CARGO_* env definition.
111RUSTC_VV_PAT = re.compile('^ +Running `.*CARGO_.*=.*$')
112# The combined -vv output rustc command line pattern.
113RUSTC_VV_CMD_ARGS = re.compile('^ *Running `.*CARGO_.*=.* rustc (.*)`$')
114
115# Cargo -vv output of a "cc" or "ar" command; all in one line.
116CC_AR_VV_PAT = re.compile(r'^\[([^ ]*)[^\]]*\] running:? "(cc|ar)" (.*)$')
117# Some package, such as ring-0.13.5, has pattern '... running "cc"'.
118
119# Rustc output of file location path pattern for a warning message.
120WARNING_FILE_PAT = re.compile('^ *--> ([^:]*):[0-9]+')
121
122# Rust package name with suffix -d1.d2.d3.
123VERSION_SUFFIX_PAT = re.compile(r'^(.*)-[0-9]+\.[0-9]+\.[0-9]+$')
124
125
126def altered_name(name):
127 return RENAME_MAP[name] if (name in RENAME_MAP) else name
128
129
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700130def altered_stem(name):
131 return RENAME_STEM_MAP[name] if (name in RENAME_STEM_MAP) else name
132
133
134def altered_defaults(name):
135 return RENAME_DEFAULTS_MAP[name] if (name in RENAME_DEFAULTS_MAP) else name
136
137
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800138def is_build_crate_name(name):
139 # We added special prefix to build script crate names.
140 return name.startswith('build_script_')
141
142
143def is_dependent_file_path(path):
144 # Absolute or dependent '.../' paths are not main files of this crate.
145 return path.startswith('/') or path.startswith('.../')
146
147
148def get_module_name(crate): # to sort crates in a list
149 return crate.module_name
150
151
152def pkg2crate_name(s):
153 return s.replace('-', '_').replace('.', '_')
154
155
156def file_base_name(path):
157 return os.path.splitext(os.path.basename(path))[0]
158
159
160def test_base_name(path):
161 return pkg2crate_name(file_base_name(path))
162
163
164def unquote(s): # remove quotes around str
165 if s and len(s) > 1 and s[0] == '"' and s[-1] == '"':
166 return s[1:-1]
167 return s
168
169
170def remove_version_suffix(s): # remove -d1.d2.d3 suffix
171 if VERSION_SUFFIX_PAT.match(s):
172 return VERSION_SUFFIX_PAT.match(s).group(1)
173 return s
174
175
176def short_out_name(pkg, s): # replace /.../pkg-*/out/* with .../out/*
177 return re.sub('^/.*/' + pkg + '-[0-9a-f]*/out/', '.../out/', s)
178
179
180def escape_quotes(s): # replace '"' with '\\"'
181 return s.replace('"', '\\"')
182
183
184class Crate(object):
185 """Information of a Rust crate to collect/emit for an Android.bp module."""
186
187 def __init__(self, runner, outf_name):
188 # Remembered global runner and its members.
189 self.runner = runner
190 self.debug = runner.args.debug
191 self.cargo_dir = '' # directory of my Cargo.toml
192 self.outf_name = outf_name # path to Android.bp
193 self.outf = None # open file handle of outf_name during dump*
194 # Variants/results that could be merged from multiple rustc lines.
195 self.host_supported = False
196 self.device_supported = False
197 self.has_warning = False
198 # Android module properties derived from rustc parameters.
199 self.module_name = '' # unique in Android build system
200 self.module_type = '' # rust_{binary,library,test}[_host] etc.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700201 self.defaults = '' # rust_defaults used by rust_test* modules
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700202 self.default_srcs = False # use 'srcs' defined in self.defaults
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800203 self.root_pkg = '' # parent package name of a sub/test packge, from -L
204 self.srcs = list() # main_src or merged multiple source files
205 self.stem = '' # real base name of output file
206 # Kept parsed status
207 self.errors = '' # all errors found during parsing
208 self.line_num = 1 # runner told input source line number
209 self.line = '' # original rustc command line parameters
210 # Parameters collected from rustc command line.
211 self.crate_name = '' # follows --crate-name
212 self.main_src = '' # follows crate_name parameter, shortened
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700213 self.crate_types = list() # follows --crate-type
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800214 self.cfgs = list() # follows --cfg, without feature= prefix
215 self.features = list() # follows --cfg, name in 'feature="..."'
216 self.codegens = list() # follows -C, some ignored
217 self.externs = list() # follows --extern
218 self.core_externs = list() # first part of self.externs elements
219 self.static_libs = list() # e.g. -l static=host_cpuid
220 self.shared_libs = list() # e.g. -l dylib=wayland-client, -l z
221 self.cap_lints = '' # follows --cap-lints
222 self.emit_list = '' # e.g., --emit=dep-info,metadata,link
223 self.edition = '2015' # rustc default, e.g., --edition=2018
224 self.target = '' # follows --target
225
226 def write(self, s):
227 # convenient way to output one line at a time with EOL.
228 self.outf.write(s + '\n')
229
230 def same_flags(self, other):
231 # host_supported, device_supported, has_warning are not compared but merged
232 # target is not compared, to merge different target/host modules
233 # externs is not compared; only core_externs is compared
234 return (not self.errors and not other.errors and
235 self.edition == other.edition and
236 self.cap_lints == other.cap_lints and
237 self.emit_list == other.emit_list and
238 self.core_externs == other.core_externs and
239 self.codegens == other.codegens and
240 self.features == other.features and
241 self.static_libs == other.static_libs and
242 self.shared_libs == other.shared_libs and self.cfgs == other.cfgs)
243
244 def merge_host_device(self, other):
245 """Returns true if attributes are the same except host/device support."""
246 return (self.crate_name == other.crate_name and
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700247 self.crate_types == other.crate_types and
248 self.main_src == other.main_src and
249 # before merge, each test module has an unique module name and stem
250 (self.stem == other.stem or self.crate_types == ['test']) and
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800251 self.root_pkg == other.root_pkg and not self.skip_crate() and
252 self.same_flags(other))
253
254 def merge_test(self, other):
255 """Returns true if self and other are tests of same root_pkg."""
256 # Before merger, each test has its own crate_name.
257 # A merged test uses its source file base name as output file name,
258 # so a test is mergeable only if its base name equals to its crate name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700259 return (self.crate_types == other.crate_types and
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700260 self.crate_types == ['test'] and self.root_pkg == other.root_pkg and
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800261 not self.skip_crate() and
262 other.crate_name == test_base_name(other.main_src) and
263 (len(self.srcs) > 1 or
264 (self.crate_name == test_base_name(self.main_src)) and
265 self.host_supported == other.host_supported and
266 self.device_supported == other.device_supported) and
267 self.same_flags(other))
268
269 def merge(self, other, outf_name):
270 """Try to merge crate into self."""
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700271 # Cargo build --tests could recompile a library for tests.
272 # We need to merge such duplicated calls to rustc, with
273 # the algorithm in merge_host_device.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800274 should_merge_host_device = self.merge_host_device(other)
275 should_merge_test = False
276 if not should_merge_host_device:
277 should_merge_test = self.merge_test(other)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800278 if should_merge_host_device or should_merge_test:
279 self.runner.init_bp_file(outf_name)
280 with open(outf_name, 'a') as outf: # to write debug info
281 self.outf = outf
282 other.outf = outf
283 self.do_merge(other, should_merge_test)
284 return True
285 return False
286
287 def do_merge(self, other, should_merge_test):
288 """Merge attributes of other to self."""
289 if self.debug:
290 self.write('\n// Before merge definition (1):')
291 self.dump_debug_info()
292 self.write('\n// Before merge definition (2):')
293 other.dump_debug_info()
294 # Merge properties of other to self.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800295 self.has_warning = self.has_warning or other.has_warning
296 if not self.target: # okay to keep only the first target triple
297 self.target = other.target
298 # decide_module_type sets up default self.stem,
299 # which can be changed if self is a merged test module.
300 self.decide_module_type()
301 if should_merge_test:
302 self.srcs.append(other.main_src)
303 # use a short unique name as the merged module name.
304 prefix = self.root_pkg + '_tests'
305 self.module_name = self.runner.claim_module_name(prefix, self, 0)
306 self.stem = self.module_name
307 # This normalized root_pkg name although might be the same
308 # as other module's crate_name, it is not actually used for
309 # output file name. A merged test module always have multiple
310 # source files and each source file base name is used as
311 # its output file name.
312 self.crate_name = pkg2crate_name(self.root_pkg)
313 if self.debug:
314 self.write('\n// After merge definition (1):')
315 self.dump_debug_info()
316
317 def find_cargo_dir(self):
318 """Deepest directory with Cargo.toml and contains the main_src."""
319 if not is_dependent_file_path(self.main_src):
320 dir_name = os.path.dirname(self.main_src)
321 while dir_name:
322 if os.path.exists(dir_name + '/Cargo.toml'):
323 self.cargo_dir = dir_name
324 return
325 dir_name = os.path.dirname(dir_name)
326
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700327 def add_codegens_flag(self, flag):
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700328 """Ignore options not used in Android."""
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700329 # 'prefer-dynamic' does not work with common flag -C lto
Chih-Hung Hsieh63459ed2020-08-26 11:51:15 -0700330 # 'embed-bitcode' is ignored; we might control LTO with other .bp flag
Chih-Hung Hsieh6c13b722020-09-11 21:24:03 -0700331 # 'codegen-units' is set in Android global config or by default
332 if not (flag.startswith('codegen-units=') or
333 flag.startswith('debuginfo=') or
Chih-Hung Hsieh63459ed2020-08-26 11:51:15 -0700334 flag.startswith('embed-bitcode=') or
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700335 flag.startswith('extra-filename=') or
336 flag.startswith('incremental=') or
337 flag.startswith('metadata=') or
338 flag == 'prefer-dynamic'):
339 self.codegens.append(flag)
340
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800341 def parse(self, line_num, line):
342 """Find important rustc arguments to convert to Android.bp properties."""
343 self.line_num = line_num
344 self.line = line
345 args = line.split() # Loop through every argument of rustc.
346 i = 0
347 while i < len(args):
348 arg = args[i]
349 if arg == '--crate-name':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700350 i += 1
351 self.crate_name = args[i]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800352 elif arg == '--crate-type':
353 i += 1
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700354 # cargo calls rustc with multiple --crate-type flags.
355 # rustc can accept:
356 # --crate-type [bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]
357 self.crate_types.append(args[i])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800358 elif arg == '--test':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700359 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800360 elif arg == '--target':
361 i += 1
362 self.target = args[i]
363 elif arg == '--cfg':
364 i += 1
365 if args[i].startswith('\'feature='):
366 self.features.append(unquote(args[i].replace('\'feature=', '')[:-1]))
367 else:
368 self.cfgs.append(args[i])
369 elif arg == '--extern':
370 i += 1
371 extern_names = re.sub('=/[^ ]*/deps/', ' = ', args[i])
372 self.externs.append(extern_names)
373 self.core_externs.append(re.sub(' = .*', '', extern_names))
374 elif arg == '-C': # codegen options
375 i += 1
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700376 self.add_codegens_flag(args[i])
377 elif arg.startswith('-C'):
378 # cargo has been passing "-C <xyz>" flag to rustc,
379 # but newer cargo could pass '-Cembed-bitcode=no' to rustc.
380 self.add_codegens_flag(arg[2:])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800381 elif arg == '--cap-lints':
382 i += 1
383 self.cap_lints = args[i]
384 elif arg == '-L':
385 i += 1
386 if args[i].startswith('dependency=') and args[i].endswith('/deps'):
387 if '/' + TARGET_TMP + '/' in args[i]:
388 self.root_pkg = re.sub(
389 '^.*/', '', re.sub('/' + TARGET_TMP + '/.*/deps$', '', args[i]))
390 else:
391 self.root_pkg = re.sub('^.*/', '',
392 re.sub('/[^/]+/[^/]+/deps$', '', args[i]))
393 self.root_pkg = remove_version_suffix(self.root_pkg)
394 elif arg == '-l':
395 i += 1
396 if args[i].startswith('static='):
397 self.static_libs.append(re.sub('static=', '', args[i]))
398 elif args[i].startswith('dylib='):
399 self.shared_libs.append(re.sub('dylib=', '', args[i]))
400 else:
401 self.shared_libs.append(args[i])
402 elif arg == '--out-dir' or arg == '--color': # ignored
403 i += 1
404 elif arg.startswith('--error-format=') or arg.startswith('--json='):
405 _ = arg # ignored
406 elif arg.startswith('--emit='):
407 self.emit_list = arg.replace('--emit=', '')
408 elif arg.startswith('--edition='):
409 self.edition = arg.replace('--edition=', '')
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700410 elif not arg.startswith('-'):
411 # shorten imported crate main source paths like $HOME/.cargo/
412 # registry/src/github.com-1ecc6299db9ec823/memchr-2.3.3/src/lib.rs
413 self.main_src = re.sub(r'^/[^ ]*/registry/src/', '.../', args[i])
414 self.main_src = re.sub(r'^\.\.\./github.com-[0-9a-f]*/', '.../',
415 self.main_src)
416 self.find_cargo_dir()
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700417 if self.cargo_dir: # for a subdirectory
418 if self.runner.args.no_subdir: # all .bp content to /dev/null
419 self.outf_name = '/dev/null'
420 elif not self.runner.args.onefile:
421 # Write to Android.bp in the subdirectory with Cargo.toml.
422 self.outf_name = self.cargo_dir + '/Android.bp'
423 self.main_src = self.main_src[len(self.cargo_dir) + 1:]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800424 else:
425 self.errors += 'ERROR: unknown ' + arg + '\n'
426 i += 1
427 if not self.crate_name:
428 self.errors += 'ERROR: missing --crate-name\n'
429 if not self.main_src:
430 self.errors += 'ERROR: missing main source file\n'
431 else:
432 self.srcs.append(self.main_src)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700433 if not self.crate_types:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800434 # Treat "--cfg test" as "--test"
435 if 'test' in self.cfgs:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700436 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800437 else:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700438 self.errors += 'ERROR: missing --crate-type or --test\n'
439 elif len(self.crate_types) > 1:
440 if 'test' in self.crate_types:
441 self.errors += 'ERROR: cannot handle both --crate-type and --test\n'
442 if 'lib' in self.crate_types and 'rlib' in self.crate_types:
443 self.errors += 'ERROR: cannot generate both lib and rlib crate types\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800444 if not self.root_pkg:
445 self.root_pkg = self.crate_name
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700446 self.device_supported = self.runner.args.device
447 self.host_supported = not self.runner.args.no_host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800448 self.cfgs = sorted(set(self.cfgs))
449 self.features = sorted(set(self.features))
450 self.codegens = sorted(set(self.codegens))
451 self.externs = sorted(set(self.externs))
452 self.core_externs = sorted(set(self.core_externs))
453 self.static_libs = sorted(set(self.static_libs))
454 self.shared_libs = sorted(set(self.shared_libs))
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700455 self.crate_types = sorted(set(self.crate_types))
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800456 self.decide_module_type()
457 self.module_name = altered_name(self.stem)
458 return self
459
460 def dump_line(self):
461 self.write('\n// Line ' + str(self.line_num) + ' ' + self.line)
462
463 def feature_list(self):
464 """Return a string of main_src + "feature_list"."""
465 pkg = self.main_src
466 if pkg.startswith('.../'): # keep only the main package name
467 pkg = re.sub('/.*', '', pkg[4:])
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700468 elif pkg.startswith('/'): # use relative path for a local package
469 pkg = os.path.relpath(pkg)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800470 if not self.features:
471 return pkg
472 return pkg + ' "' + ','.join(self.features) + '"'
473
474 def dump_skip_crate(self, kind):
475 if self.debug:
476 self.write('\n// IGNORED: ' + kind + ' ' + self.main_src)
477 return self
478
479 def skip_crate(self):
480 """Return crate_name or a message if this crate should be skipped."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700481 if (is_build_crate_name(self.crate_name) or
482 self.crate_name in EXCLUDED_CRATES):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800483 return self.crate_name
484 if is_dependent_file_path(self.main_src):
485 return 'dependent crate'
486 return ''
487
488 def dump(self):
489 """Dump all error/debug/module code to the output .bp file."""
490 self.runner.init_bp_file(self.outf_name)
491 with open(self.outf_name, 'a') as outf:
492 self.outf = outf
493 if self.errors:
494 self.dump_line()
495 self.write(self.errors)
496 elif self.skip_crate():
497 self.dump_skip_crate(self.skip_crate())
498 else:
499 if self.debug:
500 self.dump_debug_info()
501 self.dump_android_module()
502
503 def dump_debug_info(self):
504 """Dump parsed data, when cargo2android is called with --debug."""
505
506 def dump(name, value):
507 self.write('//%12s = %s' % (name, value))
508
509 def opt_dump(name, value):
510 if value:
511 dump(name, value)
512
513 def dump_list(fmt, values):
514 for v in values:
515 self.write(fmt % v)
516
517 self.dump_line()
518 dump('module_name', self.module_name)
519 dump('crate_name', self.crate_name)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700520 dump('crate_types', self.crate_types)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800521 dump('main_src', self.main_src)
522 dump('has_warning', self.has_warning)
523 dump('for_host', self.host_supported)
524 dump('for_device', self.device_supported)
525 dump('module_type', self.module_type)
526 opt_dump('target', self.target)
527 opt_dump('edition', self.edition)
528 opt_dump('emit_list', self.emit_list)
529 opt_dump('cap_lints', self.cap_lints)
530 dump_list('// cfg = %s', self.cfgs)
531 dump_list('// cfg = \'feature "%s"\'', self.features)
532 # TODO(chh): escape quotes in self.features, but not in other dump_list
533 dump_list('// codegen = %s', self.codegens)
534 dump_list('// externs = %s', self.externs)
535 dump_list('// -l static = %s', self.static_libs)
536 dump_list('// -l (dylib) = %s', self.shared_libs)
537
538 def dump_android_module(self):
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700539 """Dump one or more Android module definition, depending on crate_types."""
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700540 if len(self.crate_types) == 1:
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700541 self.dump_single_type_android_module()
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700542 return
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700543 if 'test' in self.crate_types:
544 self.write('\nERROR: multiple crate types cannot include test type')
545 return
546 # Dump one Android module per crate_type.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700547 for crate_type in self.crate_types:
548 self.decide_one_module_type(crate_type)
549 self.dump_one_android_module(crate_type)
550
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700551 def build_default_name(self):
552 """Return a short and readable name for the rust_defaults module."""
553 # Choices: (1) root_pkg + '_defaults',
554 # (2) root_pkg + '_defaults_' + crate_name
555 # (3) root_pkg + '_defaults_' + main_src_basename_path
556 # (4) root_pkg + '_defaults_' + a_positive_sequence_number
557 name1 = altered_defaults(self.root_pkg) + '_defaults'
558 if self.runner.try_claim_module_name(name1, self):
559 return name1
560 name2 = name1 + '_' + self.crate_name
561 if self.runner.try_claim_module_name(name2, self):
562 return name2
563 name3 = name1 + '_' + self.main_src_basename_path()
564 if self.runner.try_claim_module_name(name3, self):
565 return name3
566 return self.runner.claim_module_name(name1, self, 0)
567
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700568 def dump_srcs_list(self):
569 """Dump the srcs list, for defaults or regular modules."""
570 if len(self.srcs) > 1:
571 srcs = sorted(set(self.srcs)) # make a copy and dedup
572 else:
573 srcs = [self.main_src]
574 copy_out = self.runner.copy_out_module_name()
575 if copy_out:
576 srcs.append(':' + copy_out)
577 self.dump_android_property_list('srcs', '"%s"', srcs)
578
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700579 def dump_defaults_module(self):
580 """Dump a rust_defaults module to be shared by other modules."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700581 name = self.build_default_name()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700582 self.defaults = name
583 self.write('\nrust_defaults {')
584 self.write(' name: "' + name + '",')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700585 if self.runner.args.global_defaults:
586 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700587 self.write(' crate_name: "' + self.crate_name + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700588 if len(self.srcs) == 1: # only one source file; share it in defaults
589 self.default_srcs = True
590 if self.has_warning and not self.cap_lints:
591 self.write(' // has rustc warnings')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700592 self.dump_srcs_list()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700593 if 'test' in self.crate_types:
594 self.write(' test_suites: ["general-tests"],')
595 self.write(' auto_gen_config: true,')
596 self.dump_edition_flags_libs()
597 self.write('}')
598
599 def dump_single_type_android_module(self):
600 """Dump one simple Android module, which has only one crate_type."""
601 crate_type = self.crate_types[0]
602 if crate_type != 'test':
603 # do not change self.stem or self.module_name
604 self.dump_one_android_module(crate_type)
605 return
606 # Dump one test module per source file, and separate host and device tests.
607 # crate_type == 'test'
608 if (self.host_supported and self.device_supported) or len(self.srcs) > 1:
609 self.srcs = sorted(set(self.srcs))
610 self.dump_defaults_module()
611 saved_srcs = self.srcs
612 for src in saved_srcs:
613 self.srcs = [src]
614 saved_device_supported = self.device_supported
615 saved_host_supported = self.host_supported
616 saved_main_src = self.main_src
617 self.main_src = src
618 if saved_host_supported:
619 self.device_supported = False
620 self.host_supported = True
621 self.module_name = self.test_module_name()
622 self.decide_one_module_type(crate_type)
623 self.dump_one_android_module(crate_type)
624 if saved_device_supported:
625 self.device_supported = True
626 self.host_supported = False
627 self.module_name = self.test_module_name()
628 self.decide_one_module_type(crate_type)
629 self.dump_one_android_module(crate_type)
630 self.host_supported = saved_host_supported
631 self.device_supported = saved_device_supported
632 self.main_src = saved_main_src
633 self.srcs = saved_srcs
634
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700635 def dump_one_android_module(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800636 """Dump one Android module definition."""
637 if not self.module_type:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700638 self.write('\nERROR: unknown crate_type ' + crate_type)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800639 return
640 self.write('\n' + self.module_type + ' {')
641 self.dump_android_core_properties()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700642 if not self.defaults:
643 self.dump_edition_flags_libs()
644 if self.runner.args.host_first_multilib and self.host_supported and crate_type != 'test':
645 self.write(' compile_multilib: "first",')
646 self.write('}')
647
648 def dump_android_flags(self):
649 """Dump Android module flags property."""
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700650 if not self.cfgs and not self.codegens and not self.cap_lints:
651 return
652 self.write(' flags: [')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800653 if self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700654 self.write(' "--cap-lints ' + self.cap_lints + '",')
655 cfg_fmt = '"--cfg %s"'
656 codegens_fmt = '"-C %s"'
657 self.dump_android_property_list_items(cfg_fmt, self.cfgs)
658 self.dump_android_property_list_items(codegens_fmt, self.codegens)
659 self.write(' ],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700660
661 def dump_edition_flags_libs(self):
662 if self.edition:
663 self.write(' edition: "' + self.edition + '",')
664 self.dump_android_property_list('features', '"%s"', self.features)
665 self.dump_android_flags()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800666 if self.externs:
667 self.dump_android_externs()
668 self.dump_android_property_list('static_libs', '"lib%s"', self.static_libs)
669 self.dump_android_property_list('shared_libs', '"lib%s"', self.shared_libs)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800670
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700671 def main_src_basename_path(self):
672 return re.sub('/', '_', re.sub('.rs$', '', self.main_src))
673
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800674 def test_module_name(self):
675 """Return a unique name for a test module."""
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700676 # root_pkg+(_host|_device) + '_test_'+source_file_name
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700677 suffix = self.main_src_basename_path()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700678 host_device = '_host'
679 if self.device_supported:
680 host_device = '_device'
681 return self.root_pkg + host_device + '_test_' + suffix
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800682
683 def decide_module_type(self):
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700684 # Use the first crate type for the default/first module.
685 crate_type = self.crate_types[0] if self.crate_types else ''
686 self.decide_one_module_type(crate_type)
687
688 def decide_one_module_type(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800689 """Decide which Android module type to use."""
690 host = '' if self.device_supported else '_host'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700691 if crate_type == 'bin': # rust_binary[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800692 self.module_type = 'rust_binary' + host
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700693 # In rare cases like protobuf-codegen, the output binary name must
694 # be renamed to use as a plugin for protoc.
695 self.stem = altered_stem(self.crate_name)
696 self.module_name = altered_name(self.crate_name)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700697 elif crate_type == 'lib': # rust_library[_host]
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700698 # TODO(chh): should this be rust_library[_host]?
699 # Assuming that Cargo.toml do not use both 'lib' and 'rlib',
700 # because we map them both to rlib.
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700701 self.module_type = 'rust_library' + host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800702 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700703 self.module_name = altered_name(self.stem)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700704 elif crate_type == 'rlib': # rust_library[_host]
705 self.module_type = 'rust_library' + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700706 self.stem = 'lib' + self.crate_name
707 self.module_name = altered_name(self.stem)
708 elif crate_type == 'dylib': # rust_library[_host]_dylib
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800709 self.module_type = 'rust_library' + host + '_dylib'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700710 self.stem = 'lib' + self.crate_name
711 self.module_name = altered_name(self.stem) + '_dylib'
712 elif crate_type == 'cdylib': # rust_library[_host]_shared
Ivan Lozano0c057ad2020-12-15 10:41:26 -0500713 self.module_type = 'rust_ffi' + host + '_shared'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700714 self.stem = 'lib' + self.crate_name
715 self.module_name = altered_name(self.stem) + '_shared'
716 elif crate_type == 'staticlib': # rust_library[_host]_static
Ivan Lozano0c057ad2020-12-15 10:41:26 -0500717 self.module_type = 'rust_ffi' + host + '_static'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700718 self.stem = 'lib' + self.crate_name
719 self.module_name = altered_name(self.stem) + '_static'
720 elif crate_type == 'test': # rust_test[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800721 self.module_type = 'rust_test' + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700722 # Before do_merge, stem name is based on the --crate-name parameter.
723 # and test module name is based on stem.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800724 self.stem = self.test_module_name()
725 # self.stem will be changed after merging with other tests.
726 # self.stem is NOT used for final test binary name.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700727 # rust_test uses each source file base name as part of output file name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700728 # In do_merge, this function is called again, with a module_name.
729 # We make sure that the module name is unique in each package.
730 if self.module_name:
731 # Cargo uses "-C extra-filename=..." and "-C metadata=..." to add
732 # different suffixes and distinguish multiple tests of the same
733 # crate name. We ignore -C and use claim_module_name to get
734 # unique sequential suffix.
735 self.module_name = self.runner.claim_module_name(
736 self.module_name, self, 0)
737 # Now the module name is unique, stem should also match and unique.
738 self.stem = self.module_name
739 elif crate_type == 'proc-macro': # rust_proc_macro
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800740 self.module_type = 'rust_proc_macro'
741 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700742 self.module_name = altered_name(self.stem)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800743 else: # unknown module type, rust_prebuilt_dylib? rust_library[_host]?
744 self.module_type = ''
745 self.stem = ''
746
747 def dump_android_property_list_items(self, fmt, values):
748 for v in values:
749 # fmt has quotes, so we need escape_quotes(v)
750 self.write(' ' + (fmt % escape_quotes(v)) + ',')
751
752 def dump_android_property_list(self, name, fmt, values):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700753 if not values:
754 return
755 if len(values) > 1:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800756 self.write(' ' + name + ': [')
757 self.dump_android_property_list_items(fmt, values)
758 self.write(' ],')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700759 else:
760 self.write(' ' + name + ': [' +
761 (fmt % escape_quotes(values[0])) + '],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800762
763 def dump_android_core_properties(self):
764 """Dump the module header, name, stem, etc."""
765 self.write(' name: "' + self.module_name + '",')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700766 # see properties shared by dump_defaults_module
767 if self.defaults:
768 self.write(' defaults: ["' + self.defaults + '"],')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700769 elif self.runner.args.global_defaults:
770 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800771 if self.stem != self.module_name:
772 self.write(' stem: "' + self.stem + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700773 if self.has_warning and not self.cap_lints and not self.default_srcs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700774 self.write(' // has rustc warnings')
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700775 if self.host_supported and self.device_supported and self.module_type != 'rust_proc_macro':
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800776 self.write(' host_supported: true,')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700777 if not self.defaults:
778 self.write(' crate_name: "' + self.crate_name + '",')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700779 if not self.default_srcs:
780 self.dump_srcs_list()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700781 if 'test' in self.crate_types and not self.defaults:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800782 # self.root_pkg can have multiple test modules, with different *_tests[n]
783 # names, but their executables can all be installed under the same _tests
784 # directory. When built from Cargo.toml, all tests should have different
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700785 # file or crate names. So we used (root_pkg + '_tests') name as the
786 # relative_install_path.
787 # However, some package like 'slab' can have non-mergeable tests that
788 # must be separated by different module names. So, here we no longer
789 # emit relative_install_path.
790 # self.write(' relative_install_path: "' + self.root_pkg + '_tests",')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800791 self.write(' test_suites: ["general-tests"],')
792 self.write(' auto_gen_config: true,')
Joel Galensone261a152021-01-12 11:31:53 -0800793 if 'test' in self.crate_types and self.host_supported:
794 self.write(' test_options: {')
795 self.write(' unit_test: true,')
796 self.write(' },')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800797
798 def dump_android_externs(self):
799 """Dump the dependent rlibs and dylibs property."""
800 so_libs = list()
801 rust_libs = ''
802 deps_libname = re.compile('^.* = lib(.*)-[0-9a-f]*.(rlib|so|rmeta)$')
803 for lib in self.externs:
804 # normal value of lib: "libc = liblibc-*.rlib"
805 # strange case in rand crate: "getrandom_package = libgetrandom-*.rlib"
806 # we should use "libgetrandom", not "lib" + "getrandom_package"
807 groups = deps_libname.match(lib)
808 if groups is not None:
809 lib_name = groups.group(1)
810 else:
811 lib_name = re.sub(' .*$', '', lib)
812 if lib.endswith('.rlib') or lib.endswith('.rmeta'):
813 # On MacOS .rmeta is used when Linux uses .rlib or .rmeta.
814 rust_libs += ' "' + altered_name('lib' + lib_name) + '",\n'
815 elif lib.endswith('.so'):
816 so_libs.append(lib_name)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700817 elif lib != 'proc_macro': # --extern proc_macro is special and ignored
818 rust_libs += ' // ERROR: unknown type of lib ' + lib + '\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800819 if rust_libs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700820 self.write(' rustlibs: [\n' + rust_libs + ' ],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800821 # Are all dependent .so files proc_macros?
822 # TODO(chh): Separate proc_macros and dylib.
823 self.dump_android_property_list('proc_macros', '"lib%s"', so_libs)
824
825
826class ARObject(object):
827 """Information of an "ar" link command."""
828
829 def __init__(self, runner, outf_name):
830 # Remembered global runner and its members.
831 self.runner = runner
832 self.pkg = ''
833 self.outf_name = outf_name # path to Android.bp
834 # "ar" arguments
835 self.line_num = 1
836 self.line = ''
837 self.flags = '' # e.g. "crs"
838 self.lib = '' # e.g. "/.../out/lib*.a"
839 self.objs = list() # e.g. "/.../out/.../*.o"
840
841 def parse(self, pkg, line_num, args_line):
842 """Collect ar obj/lib file names."""
843 self.pkg = pkg
844 self.line_num = line_num
845 self.line = args_line
846 args = args_line.split()
847 num_args = len(args)
848 if num_args < 3:
849 print('ERROR: "ar" command has too few arguments', args_line)
850 else:
851 self.flags = unquote(args[0])
852 self.lib = unquote(args[1])
853 self.objs = sorted(set(map(unquote, args[2:])))
854 return self
855
856 def write(self, s):
857 self.outf.write(s + '\n')
858
859 def dump_debug_info(self):
860 self.write('\n// Line ' + str(self.line_num) + ' "ar" ' + self.line)
861 self.write('// ar_object for %12s' % self.pkg)
862 self.write('// flags = %s' % self.flags)
863 self.write('// lib = %s' % short_out_name(self.pkg, self.lib))
864 for o in self.objs:
865 self.write('// obj = %s' % short_out_name(self.pkg, o))
866
867 def dump_android_lib(self):
868 """Write cc_library_static into Android.bp."""
869 self.write('\ncc_library_static {')
870 self.write(' name: "' + file_base_name(self.lib) + '",')
871 self.write(' host_supported: true,')
872 if self.flags != 'crs':
873 self.write(' // ar flags = %s' % self.flags)
874 if self.pkg not in self.runner.pkg_obj2cc:
875 self.write(' ERROR: cannot find source files.\n}')
876 return
877 self.write(' srcs: [')
878 obj2cc = self.runner.pkg_obj2cc[self.pkg]
879 # Note: wflags are ignored.
880 dflags = list()
881 fflags = list()
882 for obj in self.objs:
883 self.write(' "' + short_out_name(self.pkg, obj2cc[obj].src) + '",')
884 # TODO(chh): union of dflags and flags of all obj
885 # Now, just a temporary hack that uses the last obj's flags
886 dflags = obj2cc[obj].dflags
887 fflags = obj2cc[obj].fflags
888 self.write(' ],')
889 self.write(' cflags: [')
890 self.write(' "-O3",') # TODO(chh): is this default correct?
891 self.write(' "-Wno-error",')
892 for x in fflags:
893 self.write(' "-f' + x + '",')
894 for x in dflags:
895 self.write(' "-D' + x + '",')
896 self.write(' ],')
897 self.write('}')
898
899 def dump(self):
900 """Dump error/debug/module info to the output .bp file."""
901 self.runner.init_bp_file(self.outf_name)
902 with open(self.outf_name, 'a') as outf:
903 self.outf = outf
904 if self.runner.args.debug:
905 self.dump_debug_info()
906 self.dump_android_lib()
907
908
909class CCObject(object):
910 """Information of a "cc" compilation command."""
911
912 def __init__(self, runner, outf_name):
913 # Remembered global runner and its members.
914 self.runner = runner
915 self.pkg = ''
916 self.outf_name = outf_name # path to Android.bp
917 # "cc" arguments
918 self.line_num = 1
919 self.line = ''
920 self.src = ''
921 self.obj = ''
922 self.dflags = list() # -D flags
923 self.fflags = list() # -f flags
924 self.iflags = list() # -I flags
925 self.wflags = list() # -W flags
926 self.other_args = list()
927
928 def parse(self, pkg, line_num, args_line):
929 """Collect cc compilation flags and src/out file names."""
930 self.pkg = pkg
931 self.line_num = line_num
932 self.line = args_line
933 args = args_line.split()
934 i = 0
935 while i < len(args):
936 arg = args[i]
937 if arg == '"-c"':
938 i += 1
939 if args[i].startswith('"-o'):
940 # ring-0.13.5 dumps: ... "-c" "-o/.../*.o" ".../*.c"
941 self.obj = unquote(args[i])[2:]
942 i += 1
943 self.src = unquote(args[i])
944 else:
945 self.src = unquote(args[i])
946 elif arg == '"-o"':
947 i += 1
948 self.obj = unquote(args[i])
949 elif arg == '"-I"':
950 i += 1
951 self.iflags.append(unquote(args[i]))
952 elif arg.startswith('"-D'):
953 self.dflags.append(unquote(args[i])[2:])
954 elif arg.startswith('"-f'):
955 self.fflags.append(unquote(args[i])[2:])
956 elif arg.startswith('"-W'):
957 self.wflags.append(unquote(args[i])[2:])
958 elif not (arg.startswith('"-O') or arg == '"-m64"' or arg == '"-g"' or
959 arg == '"-g3"'):
960 # ignore -O -m64 -g
961 self.other_args.append(unquote(args[i]))
962 i += 1
963 self.dflags = sorted(set(self.dflags))
964 self.fflags = sorted(set(self.fflags))
965 # self.wflags is not sorted because some are order sensitive
966 # and we ignore them anyway.
967 if self.pkg not in self.runner.pkg_obj2cc:
968 self.runner.pkg_obj2cc[self.pkg] = {}
969 self.runner.pkg_obj2cc[self.pkg][self.obj] = self
970 return self
971
972 def write(self, s):
973 self.outf.write(s + '\n')
974
975 def dump_debug_flags(self, name, flags):
976 self.write('// ' + name + ':')
977 for f in flags:
978 self.write('// %s' % f)
979
980 def dump(self):
981 """Dump only error/debug info to the output .bp file."""
982 if not self.runner.args.debug:
983 return
984 self.runner.init_bp_file(self.outf_name)
985 with open(self.outf_name, 'a') as outf:
986 self.outf = outf
987 self.write('\n// Line ' + str(self.line_num) + ' "cc" ' + self.line)
988 self.write('// cc_object for %12s' % self.pkg)
989 self.write('// src = %s' % short_out_name(self.pkg, self.src))
990 self.write('// obj = %s' % short_out_name(self.pkg, self.obj))
991 self.dump_debug_flags('-I flags', self.iflags)
992 self.dump_debug_flags('-D flags', self.dflags)
993 self.dump_debug_flags('-f flags', self.fflags)
994 self.dump_debug_flags('-W flags', self.wflags)
995 if self.other_args:
996 self.dump_debug_flags('other args', self.other_args)
997
998
999class Runner(object):
1000 """Main class to parse cargo -v output and print Android module definitions."""
1001
1002 def __init__(self, args):
1003 self.bp_files = set() # Remember all output Android.bp files.
1004 self.root_pkg = '' # name of package in ./Cargo.toml
1005 # Saved flags, modes, and data.
1006 self.args = args
1007 self.dry_run = not args.run
1008 self.skip_cargo = args.skipcargo
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001009 self.cargo_path = './cargo' # path to cargo, will be set later
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001010 self.checked_out_files = False # to check only once
1011 self.build_out_files = [] # output files generated by build.rs
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001012 # All cc/ar objects, crates, dependencies, and warning files
1013 self.cc_objects = list()
1014 self.pkg_obj2cc = {}
1015 # pkg_obj2cc[cc_object[i].pkg][cc_objects[i].obj] = cc_objects[i]
1016 self.ar_objects = list()
1017 self.crates = list()
1018 self.dependencies = list() # dependent and build script crates
1019 self.warning_files = set()
1020 # Keep a unique mapping from (module name) to crate
1021 self.name_owners = {}
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001022 # Save and dump all errors from cargo to Android.bp.
1023 self.errors = ''
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001024 self.setup_cargo_path()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001025 # Default action is cargo clean, followed by build or user given actions.
1026 if args.cargo:
1027 self.cargo = ['clean'] + args.cargo
1028 else:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001029 default_target = '--target x86_64-unknown-linux-gnu'
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -07001030 # Use the same target for both host and default device builds.
1031 # Same target is used as default in host x86_64 Android compilation.
1032 # Note: b/169872957, prebuilt cargo failed to build vsock
1033 # on x86_64-unknown-linux-musl systems.
1034 self.cargo = ['clean', 'build ' + default_target]
1035 if args.tests:
1036 self.cargo.append('build --tests ' + default_target)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001037
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001038 def setup_cargo_path(self):
1039 """Find cargo in the --cargo_bin or prebuilt rust bin directory."""
1040 if self.args.cargo_bin:
1041 self.cargo_path = os.path.join(self.args.cargo_bin, 'cargo')
1042 if not os.path.isfile(self.cargo_path):
1043 sys.exit('ERROR: cannot find cargo in ' + self.args.cargo_bin)
1044 print('WARNING: using cargo in ' + self.args.cargo_bin)
1045 return
1046 # We have only tested this on Linux.
1047 if platform.system() != 'Linux':
1048 sys.exit('ERROR: this script has only been tested on Linux with cargo.')
1049 # Assuming that this script is in development/scripts.
1050 my_dir = os.path.dirname(os.path.abspath(__file__))
1051 linux_dir = os.path.join(my_dir, '..', '..',
1052 'prebuilts', 'rust', 'linux-x86')
1053 if not os.path.isdir(linux_dir):
1054 sys.exit('ERROR: cannot find directory ' + linux_dir)
1055 rust_version = self.find_rust_version(my_dir, linux_dir)
1056 cargo_bin = os.path.join(linux_dir, rust_version, 'bin')
1057 self.cargo_path = os.path.join(cargo_bin, 'cargo')
1058 if not os.path.isfile(self.cargo_path):
1059 sys.exit('ERROR: cannot find cargo in ' + cargo_bin
1060 + '; please try --cargo_bin= flag.')
1061 return
1062
1063 def find_rust_version(self, my_dir, linux_dir):
1064 """Use my script directory, find prebuilt rust version."""
1065 # First look up build/soong/rust/config/global.go.
1066 path2global = os.path.join(my_dir, '..', '..',
1067 'build', 'soong', 'rust', 'config', 'global.go')
1068 if os.path.isfile(path2global):
1069 # try to find: RustDefaultVersion = "1.44.0"
1070 version_pat = re.compile(
1071 r'\s*RustDefaultVersion\s*=\s*"([0-9]+\.[0-9]+\.[0-9]+)".*$')
1072 with open(path2global, 'r') as inf:
1073 for line in inf:
1074 result = version_pat.match(line)
1075 if result:
1076 return result.group(1)
1077 print('WARNING: cannot find RustDefaultVersion in ' + path2global)
1078 # Otherwise, find the newest (largest) version number in linux_dir.
1079 rust_version = (0, 0, 0) # the prebuilt version to use
1080 version_pat = re.compile(r'([0-9]+)\.([0-9]+)\.([0-9]+)$')
1081 for dir_name in os.listdir(linux_dir):
1082 result = version_pat.match(dir_name)
1083 if not result:
1084 continue
1085 version = (result.group(1), result.group(2), result.group(3))
1086 if version > rust_version:
1087 rust_version = version
1088 return '.'.join(rust_version)
1089
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001090 def find_out_files(self):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001091 # list1 has build.rs output for normal crates
1092 list1 = glob.glob(TARGET_TMP + '/*/*/build/' + self.root_pkg + '-*/out/*')
1093 # list2 has build.rs output for proc-macro crates
1094 list2 = glob.glob(TARGET_TMP + '/*/build/' + self.root_pkg + '-*/out/*')
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001095 return list1 + list2
1096
1097 def copy_out_files(self):
1098 """Copy build.rs output files to ./out and set up build_out_files."""
1099 if self.checked_out_files:
1100 return
1101 self.checked_out_files = True
1102 cargo_out_files = self.find_out_files()
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001103 out_files = set()
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001104 if cargo_out_files:
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001105 os.makedirs('out', exist_ok=True)
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001106 for path in cargo_out_files:
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001107 file_name = path.split('/')[-1]
1108 out_files.add(file_name)
1109 shutil.copy(path, 'out/' + file_name)
1110 self.build_out_files = sorted(out_files)
1111
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001112 def has_used_out_dir(self):
1113 """Returns true if env!("OUT_DIR") is found."""
1114 return 0 == os.system('grep -rl --exclude build.rs --include \\*.rs' +
1115 ' \'env!("OUT_DIR")\' * > /dev/null')
1116
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001117 def copy_out_module_name(self):
1118 if self.args.copy_out and self.build_out_files:
1119 return 'copy_' + self.root_pkg + '_build_out'
1120 else:
1121 return ''
1122
1123 def dump_copy_out_module(self, outf):
1124 """Output the genrule module to copy out/* to $(genDir)."""
1125 copy_out = self.copy_out_module_name()
1126 if not copy_out:
1127 return
1128 outf.write('\ngenrule {\n')
1129 outf.write(' name: "' + copy_out + '",\n')
1130 outf.write(' srcs: ["out/*"],\n')
1131 outf.write(' cmd: "cp $(in) $(genDir)",\n')
1132 if len(self.build_out_files) > 1:
1133 outf.write(' out: [\n')
1134 for f in self.build_out_files:
1135 outf.write(' "' + f + '",\n')
1136 outf.write(' ],\n')
1137 else:
1138 outf.write(' out: ["' + self.build_out_files[0] + '"],\n')
1139 outf.write('}\n')
1140
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001141 def init_bp_file(self, name):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001142 # name could be Android.bp or sub_dir_path/Android.bp
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001143 if name not in self.bp_files:
1144 self.bp_files.add(name)
1145 with open(name, 'w') as outf:
Andrew Walbran80e90be2020-06-09 14:33:18 +01001146 outf.write(ANDROID_BP_HEADER.format(args=' '.join(sys.argv[1:])))
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001147 # at most one copy_out module per .bp file
1148 self.dump_copy_out_module(outf)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001149
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001150 def dump_test_mapping_files(self):
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001151 """Dump all TEST_MAPPING files."""
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001152 if self.dry_run:
1153 print('Dry-run skip dump of TEST_MAPPING')
1154 else:
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +01001155 test_mapping = TestMapping()
1156 for bp_file_name in self.bp_files:
1157 test_mapping.create_test_mapping(os.path.dirname(bp_file_name))
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001158 return self
1159
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001160 def try_claim_module_name(self, name, owner):
1161 """Reserve and return True if it has not been reserved yet."""
1162 if name not in self.name_owners or owner == self.name_owners[name]:
1163 self.name_owners[name] = owner
1164 return True
1165 return False
1166
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001167 def claim_module_name(self, prefix, owner, counter):
1168 """Return prefix if not owned yet, otherwise, prefix+str(counter)."""
1169 while True:
1170 name = prefix
1171 if counter > 0:
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001172 name += '_' + str(counter)
1173 if self.try_claim_module_name(name, owner):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001174 return name
1175 counter += 1
1176
1177 def find_root_pkg(self):
1178 """Read name of [package] in ./Cargo.toml."""
1179 if not os.path.exists('./Cargo.toml'):
1180 return
1181 with open('./Cargo.toml', 'r') as inf:
1182 pkg_section = re.compile(r'^ *\[package\]')
1183 name = re.compile('^ *name *= * "([^"]*)"')
1184 in_pkg = False
1185 for line in inf:
1186 if in_pkg:
1187 if name.match(line):
1188 self.root_pkg = name.match(line).group(1)
1189 break
1190 else:
1191 in_pkg = pkg_section.match(line) is not None
1192
1193 def run_cargo(self):
1194 """Calls cargo -v and save its output to ./cargo.out."""
1195 if self.skip_cargo:
1196 return self
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001197 cargo_toml = './Cargo.toml'
1198 cargo_out = './cargo.out'
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001199 # Do not use Cargo.lock, because .bp rules are designed to
1200 # run with "latest" crates avaialable on Android.
1201 cargo_lock = './Cargo.lock'
1202 cargo_lock_saved = './cargo.lock.saved'
1203 had_cargo_lock = os.path.exists(cargo_lock)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001204 if not os.access(cargo_toml, os.R_OK):
1205 print('ERROR: Cannot find or read', cargo_toml)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001206 return self
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001207 if not self.dry_run:
1208 if os.path.exists(cargo_out):
1209 os.remove(cargo_out)
1210 if not self.args.use_cargo_lock and had_cargo_lock: # save it
1211 os.rename(cargo_lock, cargo_lock_saved)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001212 cmd_tail = ' --target-dir ' + TARGET_TMP + ' >> ' + cargo_out + ' 2>&1'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001213 # set up search PATH for cargo to find the correct rustc
1214 saved_path = os.environ['PATH']
1215 os.environ['PATH'] = os.path.dirname(self.cargo_path) + ':' + saved_path
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001216 # Add [workspace] to Cargo.toml if it is not there.
1217 added_workspace = False
1218 if self.args.add_workspace:
1219 with open(cargo_toml, 'r') as in_file:
1220 cargo_toml_lines = in_file.readlines()
1221 found_workspace = '[workspace]\n' in cargo_toml_lines
1222 if found_workspace:
1223 print('### WARNING: found [workspace] in Cargo.toml')
1224 else:
1225 with open(cargo_toml, 'a') as out_file:
1226 out_file.write('[workspace]\n')
1227 added_workspace = True
1228 if self.args.verbose:
1229 print('### INFO: added [workspace] to Cargo.toml')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001230 for c in self.cargo:
1231 features = ''
Chih-Hung Hsieh6c8d52f2020-03-30 18:28:52 -07001232 if c != 'clean':
1233 if self.args.features is not None:
1234 features = ' --no-default-features'
1235 if self.args.features:
1236 features += ' --features ' + self.args.features
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001237 cmd_v_flag = ' -vv ' if self.args.vv else ' -v '
1238 cmd = self.cargo_path + cmd_v_flag
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001239 cmd += c + features + cmd_tail
1240 if self.args.rustflags and c != 'clean':
1241 cmd = 'RUSTFLAGS="' + self.args.rustflags + '" ' + cmd
1242 if self.dry_run:
1243 print('Dry-run skip:', cmd)
1244 else:
1245 if self.args.verbose:
1246 print('Running:', cmd)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001247 with open(cargo_out, 'a') as out_file:
1248 out_file.write('### Running: ' + cmd + '\n')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001249 os.system(cmd)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001250 if added_workspace: # restore original Cargo.toml
1251 with open(cargo_toml, 'w') as out_file:
1252 out_file.writelines(cargo_toml_lines)
1253 if self.args.verbose:
1254 print('### INFO: restored original Cargo.toml')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001255 os.environ['PATH'] = saved_path
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001256 if not self.dry_run:
1257 if not had_cargo_lock: # restore to no Cargo.lock state
1258 os.remove(cargo_lock)
1259 elif not self.args.use_cargo_lock: # restore saved Cargo.lock
1260 os.rename(cargo_lock_saved, cargo_lock)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001261 return self
1262
1263 def dump_dependencies(self):
1264 """Append dependencies and their features to Android.bp."""
1265 if not self.dependencies:
1266 return
1267 dependent_list = list()
1268 for c in self.dependencies:
1269 dependent_list.append(c.feature_list())
1270 sorted_dependencies = sorted(set(dependent_list))
1271 self.init_bp_file('Android.bp')
1272 with open('Android.bp', 'a') as outf:
1273 outf.write('\n// dependent_library ["feature_list"]\n')
1274 for s in sorted_dependencies:
1275 outf.write('// ' + s + '\n')
1276
1277 def dump_pkg_obj2cc(self):
1278 """Dump debug info of the pkg_obj2cc map."""
1279 if not self.args.debug:
1280 return
1281 self.init_bp_file('Android.bp')
1282 with open('Android.bp', 'a') as outf:
1283 sorted_pkgs = sorted(self.pkg_obj2cc.keys())
1284 for pkg in sorted_pkgs:
1285 if not self.pkg_obj2cc[pkg]:
1286 continue
1287 outf.write('\n// obj => src for %s\n' % pkg)
1288 obj2cc = self.pkg_obj2cc[pkg]
1289 for obj in sorted(obj2cc.keys()):
1290 outf.write('// ' + short_out_name(pkg, obj) + ' => ' +
1291 short_out_name(pkg, obj2cc[obj].src) + '\n')
1292
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001293 def apply_patch(self):
1294 """Apply local patch file if it is given."""
1295 if self.args.patch:
1296 if self.dry_run:
1297 print('Dry-run skip patch file:', self.args.patch)
1298 else:
1299 if not os.path.exists(self.args.patch):
1300 self.append_to_bp('ERROR cannot find patch file: ' + self.args.patch)
1301 return self
1302 if self.args.verbose:
1303 print('### INFO: applying local patch file:', self.args.patch)
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001304 os.system('patch -s --no-backup-if-mismatch ./Android.bp ' +
1305 self.args.patch)
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001306 return self
1307
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001308 def gen_bp(self):
1309 """Parse cargo.out and generate Android.bp files."""
1310 if self.dry_run:
1311 print('Dry-run skip: read', CARGO_OUT, 'write Android.bp')
1312 elif os.path.exists(CARGO_OUT):
1313 self.find_root_pkg()
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001314 if self.args.copy_out:
1315 self.copy_out_files()
1316 elif self.find_out_files() and self.has_used_out_dir():
1317 print('WARNING: ' + self.root_pkg + ' has cargo output files; ' +
1318 'please rerun with the --copy-out flag.')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001319 with open(CARGO_OUT, 'r') as cargo_out:
1320 self.parse(cargo_out, 'Android.bp')
1321 self.crates.sort(key=get_module_name)
1322 for obj in self.cc_objects:
1323 obj.dump()
1324 self.dump_pkg_obj2cc()
1325 for crate in self.crates:
1326 crate.dump()
1327 dumped_libs = set()
1328 for lib in self.ar_objects:
1329 if lib.pkg == self.root_pkg:
1330 lib_name = file_base_name(lib.lib)
1331 if lib_name not in dumped_libs:
1332 dumped_libs.add(lib_name)
1333 lib.dump()
1334 if self.args.dependencies and self.dependencies:
1335 self.dump_dependencies()
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001336 if self.errors:
1337 self.append_to_bp('\nErrors in ' + CARGO_OUT + ':\n' + self.errors)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001338 return self
1339
1340 def add_ar_object(self, obj):
1341 self.ar_objects.append(obj)
1342
1343 def add_cc_object(self, obj):
1344 self.cc_objects.append(obj)
1345
1346 def add_crate(self, crate):
1347 """Merge crate with someone in crates, or append to it. Return crates."""
1348 if crate.skip_crate():
1349 if self.args.debug: # include debug info of all crates
1350 self.crates.append(crate)
1351 if self.args.dependencies: # include only dependent crates
1352 if (is_dependent_file_path(crate.main_src) and
1353 not is_build_crate_name(crate.crate_name)):
1354 self.dependencies.append(crate)
1355 else:
1356 for c in self.crates:
1357 if c.merge(crate, 'Android.bp'):
1358 return
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001359 # If not merged, decide module type and name now.
1360 crate.decide_module_type()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001361 self.crates.append(crate)
1362
1363 def find_warning_owners(self):
1364 """For each warning file, find its owner crate."""
1365 missing_owner = False
1366 for f in self.warning_files:
1367 cargo_dir = '' # find lowest crate, with longest path
1368 owner = None # owner crate of this warning
1369 for c in self.crates:
1370 if (f.startswith(c.cargo_dir + '/') and
1371 len(cargo_dir) < len(c.cargo_dir)):
1372 cargo_dir = c.cargo_dir
1373 owner = c
1374 if owner:
1375 owner.has_warning = True
1376 else:
1377 missing_owner = True
1378 if missing_owner and os.path.exists('Cargo.toml'):
1379 # owner is the root cargo, with empty cargo_dir
1380 for c in self.crates:
1381 if not c.cargo_dir:
1382 c.has_warning = True
1383
1384 def rustc_command(self, n, rustc_line, line, outf_name):
1385 """Process a rustc command line from cargo -vv output."""
1386 # cargo build -vv output can have multiple lines for a rustc command
1387 # due to '\n' in strings for environment variables.
1388 # strip removes leading spaces and '\n' at the end
1389 new_rustc = (rustc_line.strip() + line) if rustc_line else line
1390 # Use an heuristic to detect the completions of a multi-line command.
1391 # This might fail for some very rare case, but easy to fix manually.
1392 if not line.endswith('`\n') or (new_rustc.count('`') % 2) != 0:
1393 return new_rustc
1394 if RUSTC_VV_CMD_ARGS.match(new_rustc):
1395 args = RUSTC_VV_CMD_ARGS.match(new_rustc).group(1)
1396 self.add_crate(Crate(self, outf_name).parse(n, args))
1397 else:
1398 self.assert_empty_vv_line(new_rustc)
1399 return ''
1400
1401 def cc_ar_command(self, n, groups, outf_name):
1402 pkg = groups.group(1)
1403 line = groups.group(3)
1404 if groups.group(2) == 'cc':
1405 self.add_cc_object(CCObject(self, outf_name).parse(pkg, n, line))
1406 else:
1407 self.add_ar_object(ARObject(self, outf_name).parse(pkg, n, line))
1408
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001409 def append_to_bp(self, line):
1410 self.init_bp_file('Android.bp')
1411 with open('Android.bp', 'a') as outf:
1412 outf.write(line)
1413
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001414 def assert_empty_vv_line(self, line):
1415 if line: # report error if line is not empty
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001416 self.append_to_bp('ERROR -vv line: ' + line)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001417 return ''
1418
1419 def parse(self, inf, outf_name):
1420 """Parse rustc and warning messages in inf, return a list of Crates."""
1421 n = 0 # line number
1422 prev_warning = False # true if the previous line was warning: ...
1423 rustc_line = '' # previous line(s) matching RUSTC_VV_PAT
1424 for line in inf:
1425 n += 1
1426 if line.startswith('warning: '):
1427 prev_warning = True
1428 rustc_line = self.assert_empty_vv_line(rustc_line)
1429 continue
1430 new_rustc = ''
1431 if RUSTC_PAT.match(line):
1432 args_line = RUSTC_PAT.match(line).group(1)
1433 self.add_crate(Crate(self, outf_name).parse(n, args_line))
1434 self.assert_empty_vv_line(rustc_line)
1435 elif rustc_line or RUSTC_VV_PAT.match(line):
1436 new_rustc = self.rustc_command(n, rustc_line, line, outf_name)
1437 elif CC_AR_VV_PAT.match(line):
1438 self.cc_ar_command(n, CC_AR_VV_PAT.match(line), outf_name)
1439 elif prev_warning and WARNING_FILE_PAT.match(line):
1440 self.assert_empty_vv_line(rustc_line)
1441 fpath = WARNING_FILE_PAT.match(line).group(1)
1442 if fpath[0] != '/': # ignore absolute path
1443 self.warning_files.add(fpath)
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001444 elif line.startswith('error: ') or line.startswith('error[E'):
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001445 if not self.args.ignore_cargo_errors:
1446 self.errors += line
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001447 prev_warning = False
1448 rustc_line = new_rustc
1449 self.find_warning_owners()
1450
1451
1452def parse_args():
1453 """Parse main arguments."""
1454 parser = argparse.ArgumentParser('cargo2android')
1455 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001456 '--add_workspace',
1457 action='store_true',
1458 default=False,
1459 help=('append [workspace] to Cargo.toml before calling cargo,' +
1460 ' to treat current directory as root of package source;' +
1461 ' otherwise the relative source file path in generated' +
1462 ' .bp file will be from the parent directory.'))
1463 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001464 '--cargo',
1465 action='append',
1466 metavar='args_string',
1467 help=('extra cargo build -v args in a string, ' +
1468 'each --cargo flag calls cargo build -v once'))
1469 parser.add_argument(
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001470 '--cargo_bin',
1471 type=str,
1472 help='use cargo in the cargo_bin directory instead of the prebuilt one')
1473 parser.add_argument(
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001474 '--copy-out',
1475 action='store_true',
1476 default=False,
1477 help=('only for root directory, ' +
1478 'copy build.rs output to ./out/* and add a genrule to copy ' +
1479 './out/* to genrule output; for crates with code pattern: ' +
1480 'include!(concat!(env!("OUT_DIR"), "/<some_file>.rs"))'))
1481 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001482 '--debug',
1483 action='store_true',
1484 default=False,
1485 help='dump debug info into Android.bp')
1486 parser.add_argument(
1487 '--dependencies',
1488 action='store_true',
1489 default=False,
1490 help='dump debug info of dependent crates')
1491 parser.add_argument(
1492 '--device',
1493 action='store_true',
1494 default=False,
1495 help='run cargo also for a default device target')
1496 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001497 '--features',
1498 type=str,
1499 help=('pass features to cargo build, ' +
1500 'empty string means no default features'))
1501 parser.add_argument(
1502 '--global_defaults',
1503 type=str,
1504 help='add a defaults name to every module')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001505 parser.add_argument(
1506 '--host-first-multilib',
1507 action='store_true',
1508 default=False,
1509 help=('add a compile_multilib:"first" property ' +
1510 'to Android.bp host modules.'))
1511 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001512 '--ignore-cargo-errors',
1513 action='store_true',
1514 default=False,
1515 help='do not append cargo/rustc error messages to Android.bp')
1516 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001517 '--no-host',
1518 action='store_true',
1519 default=False,
1520 help='do not run cargo for the host; only for the device target')
1521 parser.add_argument(
1522 '--no-subdir',
1523 action='store_true',
1524 default=False,
1525 help='do not output anything for sub-directories')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001526 parser.add_argument(
1527 '--onefile',
1528 action='store_true',
1529 default=False,
1530 help=('output all into one ./Android.bp, default will generate ' +
1531 'one Android.bp per Cargo.toml in subdirectories'))
1532 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001533 '--patch',
1534 type=str,
1535 help='apply the given patch file to generated ./Android.bp')
1536 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001537 '--run',
1538 action='store_true',
1539 default=False,
1540 help='run it, default is dry-run')
1541 parser.add_argument('--rustflags', type=str, help='passing flags to rustc')
1542 parser.add_argument(
1543 '--skipcargo',
1544 action='store_true',
1545 default=False,
1546 help='skip cargo command, parse cargo.out, and generate Android.bp')
1547 parser.add_argument(
1548 '--tests',
1549 action='store_true',
1550 default=False,
1551 help='run cargo build --tests after normal build')
1552 parser.add_argument(
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001553 '--use-cargo-lock',
1554 action='store_true',
1555 default=False,
1556 help=('run cargo build with existing Cargo.lock ' +
1557 '(used when some latest dependent crates failed)'))
1558 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001559 '--verbose',
1560 action='store_true',
1561 default=False,
1562 help='echo executed commands')
1563 parser.add_argument(
1564 '--vv',
1565 action='store_true',
1566 default=False,
1567 help='run cargo with -vv instead of default -v')
1568 return parser.parse_args()
1569
1570
1571def main():
1572 args = parse_args()
1573 if not args.run: # default is dry-run
1574 print(DRY_RUN_NOTE)
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001575 Runner(args).run_cargo().gen_bp().apply_patch().dump_test_mapping_files()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001576
1577
1578if __name__ == '__main__':
1579 main()