blob: 2da6dcb7eaf02d5ae651676b0ead7f295b271467 [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
Joel Galenson3f42f802021-04-07 12:42:17 -070098# This should be kept in sync with tools/external_updater/crates_updater.py.
99ERRORS_LINE = 'Errors in ' + CARGO_OUT + ':'
100
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800101TARGET_TMP = 'target.tmp' # Name of temporary output directory.
102
103# Message to be displayed when this script is called without the --run flag.
104DRY_RUN_NOTE = (
105 'Dry-run: This script uses ./' + TARGET_TMP + ' for output directory,\n' +
106 'runs cargo clean, runs cargo build -v, saves output to ./cargo.out,\n' +
107 'and writes to Android.bp in the current and subdirectories.\n\n' +
108 'To do do all of the above, use the --run flag.\n' +
109 'See --help for other flags, and more usage notes in this script.\n')
110
111# Cargo -v output of a call to rustc.
112RUSTC_PAT = re.compile('^ +Running `rustc (.*)`$')
113
114# Cargo -vv output of a call to rustc could be split into multiple lines.
115# Assume that the first line will contain some CARGO_* env definition.
116RUSTC_VV_PAT = re.compile('^ +Running `.*CARGO_.*=.*$')
117# The combined -vv output rustc command line pattern.
118RUSTC_VV_CMD_ARGS = re.compile('^ *Running `.*CARGO_.*=.* rustc (.*)`$')
119
120# Cargo -vv output of a "cc" or "ar" command; all in one line.
121CC_AR_VV_PAT = re.compile(r'^\[([^ ]*)[^\]]*\] running:? "(cc|ar)" (.*)$')
122# Some package, such as ring-0.13.5, has pattern '... running "cc"'.
123
124# Rustc output of file location path pattern for a warning message.
125WARNING_FILE_PAT = re.compile('^ *--> ([^:]*):[0-9]+')
126
127# Rust package name with suffix -d1.d2.d3.
128VERSION_SUFFIX_PAT = re.compile(r'^(.*)-[0-9]+\.[0-9]+\.[0-9]+$')
129
130
131def altered_name(name):
132 return RENAME_MAP[name] if (name in RENAME_MAP) else name
133
134
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700135def altered_stem(name):
136 return RENAME_STEM_MAP[name] if (name in RENAME_STEM_MAP) else name
137
138
139def altered_defaults(name):
140 return RENAME_DEFAULTS_MAP[name] if (name in RENAME_DEFAULTS_MAP) else name
141
142
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800143def is_build_crate_name(name):
144 # We added special prefix to build script crate names.
145 return name.startswith('build_script_')
146
147
148def is_dependent_file_path(path):
149 # Absolute or dependent '.../' paths are not main files of this crate.
150 return path.startswith('/') or path.startswith('.../')
151
152
153def get_module_name(crate): # to sort crates in a list
154 return crate.module_name
155
156
157def pkg2crate_name(s):
158 return s.replace('-', '_').replace('.', '_')
159
160
161def file_base_name(path):
162 return os.path.splitext(os.path.basename(path))[0]
163
164
165def test_base_name(path):
166 return pkg2crate_name(file_base_name(path))
167
168
169def unquote(s): # remove quotes around str
170 if s and len(s) > 1 and s[0] == '"' and s[-1] == '"':
171 return s[1:-1]
172 return s
173
174
175def remove_version_suffix(s): # remove -d1.d2.d3 suffix
176 if VERSION_SUFFIX_PAT.match(s):
177 return VERSION_SUFFIX_PAT.match(s).group(1)
178 return s
179
180
181def short_out_name(pkg, s): # replace /.../pkg-*/out/* with .../out/*
182 return re.sub('^/.*/' + pkg + '-[0-9a-f]*/out/', '.../out/', s)
183
184
185def escape_quotes(s): # replace '"' with '\\"'
186 return s.replace('"', '\\"')
187
188
189class Crate(object):
190 """Information of a Rust crate to collect/emit for an Android.bp module."""
191
192 def __init__(self, runner, outf_name):
193 # Remembered global runner and its members.
194 self.runner = runner
195 self.debug = runner.args.debug
196 self.cargo_dir = '' # directory of my Cargo.toml
197 self.outf_name = outf_name # path to Android.bp
198 self.outf = None # open file handle of outf_name during dump*
199 # Variants/results that could be merged from multiple rustc lines.
200 self.host_supported = False
201 self.device_supported = False
202 self.has_warning = False
203 # Android module properties derived from rustc parameters.
204 self.module_name = '' # unique in Android build system
205 self.module_type = '' # rust_{binary,library,test}[_host] etc.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700206 self.defaults = '' # rust_defaults used by rust_test* modules
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700207 self.default_srcs = False # use 'srcs' defined in self.defaults
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800208 self.root_pkg = '' # parent package name of a sub/test packge, from -L
209 self.srcs = list() # main_src or merged multiple source files
210 self.stem = '' # real base name of output file
211 # Kept parsed status
212 self.errors = '' # all errors found during parsing
213 self.line_num = 1 # runner told input source line number
214 self.line = '' # original rustc command line parameters
215 # Parameters collected from rustc command line.
216 self.crate_name = '' # follows --crate-name
217 self.main_src = '' # follows crate_name parameter, shortened
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700218 self.crate_types = list() # follows --crate-type
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800219 self.cfgs = list() # follows --cfg, without feature= prefix
220 self.features = list() # follows --cfg, name in 'feature="..."'
221 self.codegens = list() # follows -C, some ignored
222 self.externs = list() # follows --extern
223 self.core_externs = list() # first part of self.externs elements
224 self.static_libs = list() # e.g. -l static=host_cpuid
225 self.shared_libs = list() # e.g. -l dylib=wayland-client, -l z
226 self.cap_lints = '' # follows --cap-lints
227 self.emit_list = '' # e.g., --emit=dep-info,metadata,link
228 self.edition = '2015' # rustc default, e.g., --edition=2018
229 self.target = '' # follows --target
230
231 def write(self, s):
232 # convenient way to output one line at a time with EOL.
233 self.outf.write(s + '\n')
234
235 def same_flags(self, other):
236 # host_supported, device_supported, has_warning are not compared but merged
237 # target is not compared, to merge different target/host modules
238 # externs is not compared; only core_externs is compared
239 return (not self.errors and not other.errors and
240 self.edition == other.edition and
241 self.cap_lints == other.cap_lints and
242 self.emit_list == other.emit_list and
243 self.core_externs == other.core_externs and
244 self.codegens == other.codegens and
245 self.features == other.features and
246 self.static_libs == other.static_libs and
247 self.shared_libs == other.shared_libs and self.cfgs == other.cfgs)
248
249 def merge_host_device(self, other):
250 """Returns true if attributes are the same except host/device support."""
251 return (self.crate_name == other.crate_name and
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700252 self.crate_types == other.crate_types and
253 self.main_src == other.main_src and
254 # before merge, each test module has an unique module name and stem
255 (self.stem == other.stem or self.crate_types == ['test']) and
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800256 self.root_pkg == other.root_pkg and not self.skip_crate() and
257 self.same_flags(other))
258
259 def merge_test(self, other):
260 """Returns true if self and other are tests of same root_pkg."""
261 # Before merger, each test has its own crate_name.
262 # A merged test uses its source file base name as output file name,
263 # so a test is mergeable only if its base name equals to its crate name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700264 return (self.crate_types == other.crate_types and
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700265 self.crate_types == ['test'] and self.root_pkg == other.root_pkg and
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800266 not self.skip_crate() and
267 other.crate_name == test_base_name(other.main_src) and
268 (len(self.srcs) > 1 or
269 (self.crate_name == test_base_name(self.main_src)) and
270 self.host_supported == other.host_supported and
271 self.device_supported == other.device_supported) and
272 self.same_flags(other))
273
274 def merge(self, other, outf_name):
275 """Try to merge crate into self."""
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700276 # Cargo build --tests could recompile a library for tests.
277 # We need to merge such duplicated calls to rustc, with
278 # the algorithm in merge_host_device.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800279 should_merge_host_device = self.merge_host_device(other)
280 should_merge_test = False
281 if not should_merge_host_device:
282 should_merge_test = self.merge_test(other)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800283 if should_merge_host_device or should_merge_test:
284 self.runner.init_bp_file(outf_name)
285 with open(outf_name, 'a') as outf: # to write debug info
286 self.outf = outf
287 other.outf = outf
288 self.do_merge(other, should_merge_test)
289 return True
290 return False
291
292 def do_merge(self, other, should_merge_test):
293 """Merge attributes of other to self."""
294 if self.debug:
295 self.write('\n// Before merge definition (1):')
296 self.dump_debug_info()
297 self.write('\n// Before merge definition (2):')
298 other.dump_debug_info()
299 # Merge properties of other to self.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800300 self.has_warning = self.has_warning or other.has_warning
301 if not self.target: # okay to keep only the first target triple
302 self.target = other.target
303 # decide_module_type sets up default self.stem,
304 # which can be changed if self is a merged test module.
305 self.decide_module_type()
306 if should_merge_test:
307 self.srcs.append(other.main_src)
308 # use a short unique name as the merged module name.
309 prefix = self.root_pkg + '_tests'
310 self.module_name = self.runner.claim_module_name(prefix, self, 0)
311 self.stem = self.module_name
312 # This normalized root_pkg name although might be the same
313 # as other module's crate_name, it is not actually used for
314 # output file name. A merged test module always have multiple
315 # source files and each source file base name is used as
316 # its output file name.
317 self.crate_name = pkg2crate_name(self.root_pkg)
318 if self.debug:
319 self.write('\n// After merge definition (1):')
320 self.dump_debug_info()
321
322 def find_cargo_dir(self):
323 """Deepest directory with Cargo.toml and contains the main_src."""
324 if not is_dependent_file_path(self.main_src):
325 dir_name = os.path.dirname(self.main_src)
326 while dir_name:
327 if os.path.exists(dir_name + '/Cargo.toml'):
328 self.cargo_dir = dir_name
329 return
330 dir_name = os.path.dirname(dir_name)
331
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700332 def add_codegens_flag(self, flag):
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700333 """Ignore options not used in Android."""
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700334 # 'prefer-dynamic' does not work with common flag -C lto
Chih-Hung Hsieh63459ed2020-08-26 11:51:15 -0700335 # 'embed-bitcode' is ignored; we might control LTO with other .bp flag
Chih-Hung Hsieh6c13b722020-09-11 21:24:03 -0700336 # 'codegen-units' is set in Android global config or by default
337 if not (flag.startswith('codegen-units=') or
338 flag.startswith('debuginfo=') or
Chih-Hung Hsieh63459ed2020-08-26 11:51:15 -0700339 flag.startswith('embed-bitcode=') or
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700340 flag.startswith('extra-filename=') or
341 flag.startswith('incremental=') or
342 flag.startswith('metadata=') or
343 flag == 'prefer-dynamic'):
344 self.codegens.append(flag)
345
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800346 def parse(self, line_num, line):
347 """Find important rustc arguments to convert to Android.bp properties."""
348 self.line_num = line_num
349 self.line = line
350 args = line.split() # Loop through every argument of rustc.
351 i = 0
352 while i < len(args):
353 arg = args[i]
354 if arg == '--crate-name':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700355 i += 1
356 self.crate_name = args[i]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800357 elif arg == '--crate-type':
358 i += 1
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700359 # cargo calls rustc with multiple --crate-type flags.
360 # rustc can accept:
361 # --crate-type [bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]
362 self.crate_types.append(args[i])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800363 elif arg == '--test':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700364 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800365 elif arg == '--target':
366 i += 1
367 self.target = args[i]
368 elif arg == '--cfg':
369 i += 1
370 if args[i].startswith('\'feature='):
371 self.features.append(unquote(args[i].replace('\'feature=', '')[:-1]))
372 else:
373 self.cfgs.append(args[i])
374 elif arg == '--extern':
375 i += 1
376 extern_names = re.sub('=/[^ ]*/deps/', ' = ', args[i])
377 self.externs.append(extern_names)
378 self.core_externs.append(re.sub(' = .*', '', extern_names))
379 elif arg == '-C': # codegen options
380 i += 1
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700381 self.add_codegens_flag(args[i])
382 elif arg.startswith('-C'):
383 # cargo has been passing "-C <xyz>" flag to rustc,
384 # but newer cargo could pass '-Cembed-bitcode=no' to rustc.
385 self.add_codegens_flag(arg[2:])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800386 elif arg == '--cap-lints':
387 i += 1
388 self.cap_lints = args[i]
389 elif arg == '-L':
390 i += 1
391 if args[i].startswith('dependency=') and args[i].endswith('/deps'):
392 if '/' + TARGET_TMP + '/' in args[i]:
393 self.root_pkg = re.sub(
394 '^.*/', '', re.sub('/' + TARGET_TMP + '/.*/deps$', '', args[i]))
395 else:
396 self.root_pkg = re.sub('^.*/', '',
397 re.sub('/[^/]+/[^/]+/deps$', '', args[i]))
398 self.root_pkg = remove_version_suffix(self.root_pkg)
399 elif arg == '-l':
400 i += 1
401 if args[i].startswith('static='):
402 self.static_libs.append(re.sub('static=', '', args[i]))
403 elif args[i].startswith('dylib='):
404 self.shared_libs.append(re.sub('dylib=', '', args[i]))
405 else:
406 self.shared_libs.append(args[i])
407 elif arg == '--out-dir' or arg == '--color': # ignored
408 i += 1
409 elif arg.startswith('--error-format=') or arg.startswith('--json='):
410 _ = arg # ignored
411 elif arg.startswith('--emit='):
412 self.emit_list = arg.replace('--emit=', '')
413 elif arg.startswith('--edition='):
414 self.edition = arg.replace('--edition=', '')
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700415 elif not arg.startswith('-'):
416 # shorten imported crate main source paths like $HOME/.cargo/
417 # registry/src/github.com-1ecc6299db9ec823/memchr-2.3.3/src/lib.rs
418 self.main_src = re.sub(r'^/[^ ]*/registry/src/', '.../', args[i])
419 self.main_src = re.sub(r'^\.\.\./github.com-[0-9a-f]*/', '.../',
420 self.main_src)
421 self.find_cargo_dir()
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700422 if self.cargo_dir: # for a subdirectory
423 if self.runner.args.no_subdir: # all .bp content to /dev/null
424 self.outf_name = '/dev/null'
425 elif not self.runner.args.onefile:
426 # Write to Android.bp in the subdirectory with Cargo.toml.
427 self.outf_name = self.cargo_dir + '/Android.bp'
428 self.main_src = self.main_src[len(self.cargo_dir) + 1:]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800429 else:
430 self.errors += 'ERROR: unknown ' + arg + '\n'
431 i += 1
432 if not self.crate_name:
433 self.errors += 'ERROR: missing --crate-name\n'
434 if not self.main_src:
435 self.errors += 'ERROR: missing main source file\n'
436 else:
437 self.srcs.append(self.main_src)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700438 if not self.crate_types:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800439 # Treat "--cfg test" as "--test"
440 if 'test' in self.cfgs:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700441 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800442 else:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700443 self.errors += 'ERROR: missing --crate-type or --test\n'
444 elif len(self.crate_types) > 1:
445 if 'test' in self.crate_types:
446 self.errors += 'ERROR: cannot handle both --crate-type and --test\n'
447 if 'lib' in self.crate_types and 'rlib' in self.crate_types:
448 self.errors += 'ERROR: cannot generate both lib and rlib crate types\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800449 if not self.root_pkg:
450 self.root_pkg = self.crate_name
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700451 self.device_supported = self.runner.args.device
452 self.host_supported = not self.runner.args.no_host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800453 self.cfgs = sorted(set(self.cfgs))
454 self.features = sorted(set(self.features))
455 self.codegens = sorted(set(self.codegens))
456 self.externs = sorted(set(self.externs))
457 self.core_externs = sorted(set(self.core_externs))
458 self.static_libs = sorted(set(self.static_libs))
459 self.shared_libs = sorted(set(self.shared_libs))
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700460 self.crate_types = sorted(set(self.crate_types))
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800461 self.decide_module_type()
462 self.module_name = altered_name(self.stem)
463 return self
464
465 def dump_line(self):
466 self.write('\n// Line ' + str(self.line_num) + ' ' + self.line)
467
468 def feature_list(self):
469 """Return a string of main_src + "feature_list"."""
470 pkg = self.main_src
471 if pkg.startswith('.../'): # keep only the main package name
472 pkg = re.sub('/.*', '', pkg[4:])
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700473 elif pkg.startswith('/'): # use relative path for a local package
474 pkg = os.path.relpath(pkg)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800475 if not self.features:
476 return pkg
477 return pkg + ' "' + ','.join(self.features) + '"'
478
479 def dump_skip_crate(self, kind):
480 if self.debug:
481 self.write('\n// IGNORED: ' + kind + ' ' + self.main_src)
482 return self
483
484 def skip_crate(self):
485 """Return crate_name or a message if this crate should be skipped."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700486 if (is_build_crate_name(self.crate_name) or
487 self.crate_name in EXCLUDED_CRATES):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800488 return self.crate_name
489 if is_dependent_file_path(self.main_src):
490 return 'dependent crate'
491 return ''
492
493 def dump(self):
494 """Dump all error/debug/module code to the output .bp file."""
495 self.runner.init_bp_file(self.outf_name)
496 with open(self.outf_name, 'a') as outf:
497 self.outf = outf
498 if self.errors:
499 self.dump_line()
500 self.write(self.errors)
501 elif self.skip_crate():
502 self.dump_skip_crate(self.skip_crate())
503 else:
504 if self.debug:
505 self.dump_debug_info()
506 self.dump_android_module()
507
508 def dump_debug_info(self):
509 """Dump parsed data, when cargo2android is called with --debug."""
510
511 def dump(name, value):
512 self.write('//%12s = %s' % (name, value))
513
514 def opt_dump(name, value):
515 if value:
516 dump(name, value)
517
518 def dump_list(fmt, values):
519 for v in values:
520 self.write(fmt % v)
521
522 self.dump_line()
523 dump('module_name', self.module_name)
524 dump('crate_name', self.crate_name)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700525 dump('crate_types', self.crate_types)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800526 dump('main_src', self.main_src)
527 dump('has_warning', self.has_warning)
528 dump('for_host', self.host_supported)
529 dump('for_device', self.device_supported)
530 dump('module_type', self.module_type)
531 opt_dump('target', self.target)
532 opt_dump('edition', self.edition)
533 opt_dump('emit_list', self.emit_list)
534 opt_dump('cap_lints', self.cap_lints)
535 dump_list('// cfg = %s', self.cfgs)
536 dump_list('// cfg = \'feature "%s"\'', self.features)
537 # TODO(chh): escape quotes in self.features, but not in other dump_list
538 dump_list('// codegen = %s', self.codegens)
539 dump_list('// externs = %s', self.externs)
540 dump_list('// -l static = %s', self.static_libs)
541 dump_list('// -l (dylib) = %s', self.shared_libs)
542
543 def dump_android_module(self):
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700544 """Dump one or more Android module definition, depending on crate_types."""
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700545 if len(self.crate_types) == 1:
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700546 self.dump_single_type_android_module()
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700547 return
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700548 if 'test' in self.crate_types:
549 self.write('\nERROR: multiple crate types cannot include test type')
550 return
551 # Dump one Android module per crate_type.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700552 for crate_type in self.crate_types:
553 self.decide_one_module_type(crate_type)
554 self.dump_one_android_module(crate_type)
555
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700556 def build_default_name(self):
557 """Return a short and readable name for the rust_defaults module."""
558 # Choices: (1) root_pkg + '_defaults',
559 # (2) root_pkg + '_defaults_' + crate_name
560 # (3) root_pkg + '_defaults_' + main_src_basename_path
561 # (4) root_pkg + '_defaults_' + a_positive_sequence_number
562 name1 = altered_defaults(self.root_pkg) + '_defaults'
563 if self.runner.try_claim_module_name(name1, self):
564 return name1
565 name2 = name1 + '_' + self.crate_name
566 if self.runner.try_claim_module_name(name2, self):
567 return name2
568 name3 = name1 + '_' + self.main_src_basename_path()
569 if self.runner.try_claim_module_name(name3, self):
570 return name3
571 return self.runner.claim_module_name(name1, self, 0)
572
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700573 def dump_srcs_list(self):
574 """Dump the srcs list, for defaults or regular modules."""
575 if len(self.srcs) > 1:
576 srcs = sorted(set(self.srcs)) # make a copy and dedup
577 else:
578 srcs = [self.main_src]
579 copy_out = self.runner.copy_out_module_name()
580 if copy_out:
581 srcs.append(':' + copy_out)
582 self.dump_android_property_list('srcs', '"%s"', srcs)
583
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700584 def dump_defaults_module(self):
585 """Dump a rust_defaults module to be shared by other modules."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700586 name = self.build_default_name()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700587 self.defaults = name
588 self.write('\nrust_defaults {')
589 self.write(' name: "' + name + '",')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700590 if self.runner.args.global_defaults:
591 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700592 self.write(' crate_name: "' + self.crate_name + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700593 if len(self.srcs) == 1: # only one source file; share it in defaults
594 self.default_srcs = True
595 if self.has_warning and not self.cap_lints:
596 self.write(' // has rustc warnings')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700597 self.dump_srcs_list()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700598 if 'test' in self.crate_types:
599 self.write(' test_suites: ["general-tests"],')
600 self.write(' auto_gen_config: true,')
601 self.dump_edition_flags_libs()
602 self.write('}')
603
604 def dump_single_type_android_module(self):
605 """Dump one simple Android module, which has only one crate_type."""
606 crate_type = self.crate_types[0]
607 if crate_type != 'test':
608 # do not change self.stem or self.module_name
609 self.dump_one_android_module(crate_type)
610 return
611 # Dump one test module per source file, and separate host and device tests.
612 # crate_type == 'test'
613 if (self.host_supported and self.device_supported) or len(self.srcs) > 1:
614 self.srcs = sorted(set(self.srcs))
615 self.dump_defaults_module()
616 saved_srcs = self.srcs
617 for src in saved_srcs:
618 self.srcs = [src]
619 saved_device_supported = self.device_supported
620 saved_host_supported = self.host_supported
621 saved_main_src = self.main_src
622 self.main_src = src
623 if saved_host_supported:
624 self.device_supported = False
625 self.host_supported = True
626 self.module_name = self.test_module_name()
627 self.decide_one_module_type(crate_type)
628 self.dump_one_android_module(crate_type)
629 if saved_device_supported:
630 self.device_supported = True
631 self.host_supported = False
632 self.module_name = self.test_module_name()
633 self.decide_one_module_type(crate_type)
634 self.dump_one_android_module(crate_type)
635 self.host_supported = saved_host_supported
636 self.device_supported = saved_device_supported
637 self.main_src = saved_main_src
638 self.srcs = saved_srcs
639
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700640 def dump_one_android_module(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800641 """Dump one Android module definition."""
642 if not self.module_type:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700643 self.write('\nERROR: unknown crate_type ' + crate_type)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800644 return
645 self.write('\n' + self.module_type + ' {')
646 self.dump_android_core_properties()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700647 if not self.defaults:
648 self.dump_edition_flags_libs()
649 if self.runner.args.host_first_multilib and self.host_supported and crate_type != 'test':
650 self.write(' compile_multilib: "first",')
651 self.write('}')
652
653 def dump_android_flags(self):
654 """Dump Android module flags property."""
Thiébaud Weksteena5a728b2021-04-08 14:23:49 +0200655 if not self.codegens and not self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700656 return
657 self.write(' flags: [')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800658 if self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700659 self.write(' "--cap-lints ' + self.cap_lints + '",')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700660 codegens_fmt = '"-C %s"'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700661 self.dump_android_property_list_items(codegens_fmt, self.codegens)
662 self.write(' ],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700663
664 def dump_edition_flags_libs(self):
665 if self.edition:
666 self.write(' edition: "' + self.edition + '",')
667 self.dump_android_property_list('features', '"%s"', self.features)
Thiébaud Weksteena5a728b2021-04-08 14:23:49 +0200668 self.dump_android_property_list('cfgs', '"%s"', self.cfgs)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700669 self.dump_android_flags()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800670 if self.externs:
671 self.dump_android_externs()
672 self.dump_android_property_list('static_libs', '"lib%s"', self.static_libs)
673 self.dump_android_property_list('shared_libs', '"lib%s"', self.shared_libs)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800674
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700675 def main_src_basename_path(self):
676 return re.sub('/', '_', re.sub('.rs$', '', self.main_src))
677
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800678 def test_module_name(self):
679 """Return a unique name for a test module."""
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700680 # root_pkg+(_host|_device) + '_test_'+source_file_name
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700681 suffix = self.main_src_basename_path()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700682 host_device = '_host'
683 if self.device_supported:
684 host_device = '_device'
685 return self.root_pkg + host_device + '_test_' + suffix
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800686
687 def decide_module_type(self):
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700688 # Use the first crate type for the default/first module.
689 crate_type = self.crate_types[0] if self.crate_types else ''
690 self.decide_one_module_type(crate_type)
691
692 def decide_one_module_type(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800693 """Decide which Android module type to use."""
694 host = '' if self.device_supported else '_host'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700695 if crate_type == 'bin': # rust_binary[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800696 self.module_type = 'rust_binary' + host
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700697 # In rare cases like protobuf-codegen, the output binary name must
698 # be renamed to use as a plugin for protoc.
699 self.stem = altered_stem(self.crate_name)
700 self.module_name = altered_name(self.crate_name)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700701 elif crate_type == 'lib': # rust_library[_host]
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700702 # TODO(chh): should this be rust_library[_host]?
703 # Assuming that Cargo.toml do not use both 'lib' and 'rlib',
704 # because we map them both to rlib.
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700705 self.module_type = 'rust_library' + host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800706 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700707 self.module_name = altered_name(self.stem)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700708 elif crate_type == 'rlib': # rust_library[_host]
709 self.module_type = 'rust_library' + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700710 self.stem = 'lib' + self.crate_name
711 self.module_name = altered_name(self.stem)
712 elif crate_type == 'dylib': # rust_library[_host]_dylib
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800713 self.module_type = 'rust_library' + host + '_dylib'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700714 self.stem = 'lib' + self.crate_name
715 self.module_name = altered_name(self.stem) + '_dylib'
716 elif crate_type == 'cdylib': # rust_library[_host]_shared
Ivan Lozano0c057ad2020-12-15 10:41:26 -0500717 self.module_type = 'rust_ffi' + host + '_shared'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700718 self.stem = 'lib' + self.crate_name
719 self.module_name = altered_name(self.stem) + '_shared'
720 elif crate_type == 'staticlib': # rust_library[_host]_static
Ivan Lozano0c057ad2020-12-15 10:41:26 -0500721 self.module_type = 'rust_ffi' + host + '_static'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700722 self.stem = 'lib' + self.crate_name
723 self.module_name = altered_name(self.stem) + '_static'
724 elif crate_type == 'test': # rust_test[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800725 self.module_type = 'rust_test' + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700726 # Before do_merge, stem name is based on the --crate-name parameter.
727 # and test module name is based on stem.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800728 self.stem = self.test_module_name()
729 # self.stem will be changed after merging with other tests.
730 # self.stem is NOT used for final test binary name.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700731 # rust_test uses each source file base name as part of output file name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700732 # In do_merge, this function is called again, with a module_name.
733 # We make sure that the module name is unique in each package.
734 if self.module_name:
735 # Cargo uses "-C extra-filename=..." and "-C metadata=..." to add
736 # different suffixes and distinguish multiple tests of the same
737 # crate name. We ignore -C and use claim_module_name to get
738 # unique sequential suffix.
739 self.module_name = self.runner.claim_module_name(
740 self.module_name, self, 0)
741 # Now the module name is unique, stem should also match and unique.
742 self.stem = self.module_name
743 elif crate_type == 'proc-macro': # rust_proc_macro
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800744 self.module_type = 'rust_proc_macro'
745 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700746 self.module_name = altered_name(self.stem)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800747 else: # unknown module type, rust_prebuilt_dylib? rust_library[_host]?
748 self.module_type = ''
749 self.stem = ''
750
751 def dump_android_property_list_items(self, fmt, values):
752 for v in values:
753 # fmt has quotes, so we need escape_quotes(v)
754 self.write(' ' + (fmt % escape_quotes(v)) + ',')
755
756 def dump_android_property_list(self, name, fmt, values):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700757 if not values:
758 return
759 if len(values) > 1:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800760 self.write(' ' + name + ': [')
761 self.dump_android_property_list_items(fmt, values)
762 self.write(' ],')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700763 else:
764 self.write(' ' + name + ': [' +
765 (fmt % escape_quotes(values[0])) + '],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800766
767 def dump_android_core_properties(self):
768 """Dump the module header, name, stem, etc."""
769 self.write(' name: "' + self.module_name + '",')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700770 # see properties shared by dump_defaults_module
771 if self.defaults:
772 self.write(' defaults: ["' + self.defaults + '"],')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700773 elif self.runner.args.global_defaults:
774 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800775 if self.stem != self.module_name:
776 self.write(' stem: "' + self.stem + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700777 if self.has_warning and not self.cap_lints and not self.default_srcs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700778 self.write(' // has rustc warnings')
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700779 if self.host_supported and self.device_supported and self.module_type != 'rust_proc_macro':
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800780 self.write(' host_supported: true,')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700781 if not self.defaults:
782 self.write(' crate_name: "' + self.crate_name + '",')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700783 if not self.default_srcs:
784 self.dump_srcs_list()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700785 if 'test' in self.crate_types and not self.defaults:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800786 # self.root_pkg can have multiple test modules, with different *_tests[n]
787 # names, but their executables can all be installed under the same _tests
788 # directory. When built from Cargo.toml, all tests should have different
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700789 # file or crate names. So we used (root_pkg + '_tests') name as the
790 # relative_install_path.
791 # However, some package like 'slab' can have non-mergeable tests that
792 # must be separated by different module names. So, here we no longer
793 # emit relative_install_path.
794 # self.write(' relative_install_path: "' + self.root_pkg + '_tests",')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800795 self.write(' test_suites: ["general-tests"],')
796 self.write(' auto_gen_config: true,')
Joel Galensone261a152021-01-12 11:31:53 -0800797 if 'test' in self.crate_types and self.host_supported:
798 self.write(' test_options: {')
799 self.write(' unit_test: true,')
800 self.write(' },')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800801
802 def dump_android_externs(self):
803 """Dump the dependent rlibs and dylibs property."""
804 so_libs = list()
805 rust_libs = ''
806 deps_libname = re.compile('^.* = lib(.*)-[0-9a-f]*.(rlib|so|rmeta)$')
807 for lib in self.externs:
808 # normal value of lib: "libc = liblibc-*.rlib"
809 # strange case in rand crate: "getrandom_package = libgetrandom-*.rlib"
810 # we should use "libgetrandom", not "lib" + "getrandom_package"
811 groups = deps_libname.match(lib)
812 if groups is not None:
813 lib_name = groups.group(1)
814 else:
815 lib_name = re.sub(' .*$', '', lib)
816 if lib.endswith('.rlib') or lib.endswith('.rmeta'):
817 # On MacOS .rmeta is used when Linux uses .rlib or .rmeta.
818 rust_libs += ' "' + altered_name('lib' + lib_name) + '",\n'
819 elif lib.endswith('.so'):
820 so_libs.append(lib_name)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700821 elif lib != 'proc_macro': # --extern proc_macro is special and ignored
822 rust_libs += ' // ERROR: unknown type of lib ' + lib + '\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800823 if rust_libs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700824 self.write(' rustlibs: [\n' + rust_libs + ' ],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800825 # Are all dependent .so files proc_macros?
826 # TODO(chh): Separate proc_macros and dylib.
827 self.dump_android_property_list('proc_macros', '"lib%s"', so_libs)
828
829
830class ARObject(object):
831 """Information of an "ar" link command."""
832
833 def __init__(self, runner, outf_name):
834 # Remembered global runner and its members.
835 self.runner = runner
836 self.pkg = ''
837 self.outf_name = outf_name # path to Android.bp
838 # "ar" arguments
839 self.line_num = 1
840 self.line = ''
841 self.flags = '' # e.g. "crs"
842 self.lib = '' # e.g. "/.../out/lib*.a"
843 self.objs = list() # e.g. "/.../out/.../*.o"
844
845 def parse(self, pkg, line_num, args_line):
846 """Collect ar obj/lib file names."""
847 self.pkg = pkg
848 self.line_num = line_num
849 self.line = args_line
850 args = args_line.split()
851 num_args = len(args)
852 if num_args < 3:
853 print('ERROR: "ar" command has too few arguments', args_line)
854 else:
855 self.flags = unquote(args[0])
856 self.lib = unquote(args[1])
857 self.objs = sorted(set(map(unquote, args[2:])))
858 return self
859
860 def write(self, s):
861 self.outf.write(s + '\n')
862
863 def dump_debug_info(self):
864 self.write('\n// Line ' + str(self.line_num) + ' "ar" ' + self.line)
865 self.write('// ar_object for %12s' % self.pkg)
866 self.write('// flags = %s' % self.flags)
867 self.write('// lib = %s' % short_out_name(self.pkg, self.lib))
868 for o in self.objs:
869 self.write('// obj = %s' % short_out_name(self.pkg, o))
870
871 def dump_android_lib(self):
872 """Write cc_library_static into Android.bp."""
873 self.write('\ncc_library_static {')
874 self.write(' name: "' + file_base_name(self.lib) + '",')
875 self.write(' host_supported: true,')
876 if self.flags != 'crs':
877 self.write(' // ar flags = %s' % self.flags)
878 if self.pkg not in self.runner.pkg_obj2cc:
879 self.write(' ERROR: cannot find source files.\n}')
880 return
881 self.write(' srcs: [')
882 obj2cc = self.runner.pkg_obj2cc[self.pkg]
883 # Note: wflags are ignored.
884 dflags = list()
885 fflags = list()
886 for obj in self.objs:
887 self.write(' "' + short_out_name(self.pkg, obj2cc[obj].src) + '",')
888 # TODO(chh): union of dflags and flags of all obj
889 # Now, just a temporary hack that uses the last obj's flags
890 dflags = obj2cc[obj].dflags
891 fflags = obj2cc[obj].fflags
892 self.write(' ],')
893 self.write(' cflags: [')
894 self.write(' "-O3",') # TODO(chh): is this default correct?
895 self.write(' "-Wno-error",')
896 for x in fflags:
897 self.write(' "-f' + x + '",')
898 for x in dflags:
899 self.write(' "-D' + x + '",')
900 self.write(' ],')
901 self.write('}')
902
903 def dump(self):
904 """Dump error/debug/module info to the output .bp file."""
905 self.runner.init_bp_file(self.outf_name)
906 with open(self.outf_name, 'a') as outf:
907 self.outf = outf
908 if self.runner.args.debug:
909 self.dump_debug_info()
910 self.dump_android_lib()
911
912
913class CCObject(object):
914 """Information of a "cc" compilation command."""
915
916 def __init__(self, runner, outf_name):
917 # Remembered global runner and its members.
918 self.runner = runner
919 self.pkg = ''
920 self.outf_name = outf_name # path to Android.bp
921 # "cc" arguments
922 self.line_num = 1
923 self.line = ''
924 self.src = ''
925 self.obj = ''
926 self.dflags = list() # -D flags
927 self.fflags = list() # -f flags
928 self.iflags = list() # -I flags
929 self.wflags = list() # -W flags
930 self.other_args = list()
931
932 def parse(self, pkg, line_num, args_line):
933 """Collect cc compilation flags and src/out file names."""
934 self.pkg = pkg
935 self.line_num = line_num
936 self.line = args_line
937 args = args_line.split()
938 i = 0
939 while i < len(args):
940 arg = args[i]
941 if arg == '"-c"':
942 i += 1
943 if args[i].startswith('"-o'):
944 # ring-0.13.5 dumps: ... "-c" "-o/.../*.o" ".../*.c"
945 self.obj = unquote(args[i])[2:]
946 i += 1
947 self.src = unquote(args[i])
948 else:
949 self.src = unquote(args[i])
950 elif arg == '"-o"':
951 i += 1
952 self.obj = unquote(args[i])
953 elif arg == '"-I"':
954 i += 1
955 self.iflags.append(unquote(args[i]))
956 elif arg.startswith('"-D'):
957 self.dflags.append(unquote(args[i])[2:])
958 elif arg.startswith('"-f'):
959 self.fflags.append(unquote(args[i])[2:])
960 elif arg.startswith('"-W'):
961 self.wflags.append(unquote(args[i])[2:])
962 elif not (arg.startswith('"-O') or arg == '"-m64"' or arg == '"-g"' or
963 arg == '"-g3"'):
964 # ignore -O -m64 -g
965 self.other_args.append(unquote(args[i]))
966 i += 1
967 self.dflags = sorted(set(self.dflags))
968 self.fflags = sorted(set(self.fflags))
969 # self.wflags is not sorted because some are order sensitive
970 # and we ignore them anyway.
971 if self.pkg not in self.runner.pkg_obj2cc:
972 self.runner.pkg_obj2cc[self.pkg] = {}
973 self.runner.pkg_obj2cc[self.pkg][self.obj] = self
974 return self
975
976 def write(self, s):
977 self.outf.write(s + '\n')
978
979 def dump_debug_flags(self, name, flags):
980 self.write('// ' + name + ':')
981 for f in flags:
982 self.write('// %s' % f)
983
984 def dump(self):
985 """Dump only error/debug info to the output .bp file."""
986 if not self.runner.args.debug:
987 return
988 self.runner.init_bp_file(self.outf_name)
989 with open(self.outf_name, 'a') as outf:
990 self.outf = outf
991 self.write('\n// Line ' + str(self.line_num) + ' "cc" ' + self.line)
992 self.write('// cc_object for %12s' % self.pkg)
993 self.write('// src = %s' % short_out_name(self.pkg, self.src))
994 self.write('// obj = %s' % short_out_name(self.pkg, self.obj))
995 self.dump_debug_flags('-I flags', self.iflags)
996 self.dump_debug_flags('-D flags', self.dflags)
997 self.dump_debug_flags('-f flags', self.fflags)
998 self.dump_debug_flags('-W flags', self.wflags)
999 if self.other_args:
1000 self.dump_debug_flags('other args', self.other_args)
1001
1002
1003class Runner(object):
1004 """Main class to parse cargo -v output and print Android module definitions."""
1005
1006 def __init__(self, args):
1007 self.bp_files = set() # Remember all output Android.bp files.
1008 self.root_pkg = '' # name of package in ./Cargo.toml
1009 # Saved flags, modes, and data.
1010 self.args = args
1011 self.dry_run = not args.run
1012 self.skip_cargo = args.skipcargo
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001013 self.cargo_path = './cargo' # path to cargo, will be set later
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001014 self.checked_out_files = False # to check only once
1015 self.build_out_files = [] # output files generated by build.rs
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001016 # All cc/ar objects, crates, dependencies, and warning files
1017 self.cc_objects = list()
1018 self.pkg_obj2cc = {}
1019 # pkg_obj2cc[cc_object[i].pkg][cc_objects[i].obj] = cc_objects[i]
1020 self.ar_objects = list()
1021 self.crates = list()
1022 self.dependencies = list() # dependent and build script crates
1023 self.warning_files = set()
1024 # Keep a unique mapping from (module name) to crate
1025 self.name_owners = {}
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001026 # Save and dump all errors from cargo to Android.bp.
1027 self.errors = ''
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001028 self.setup_cargo_path()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001029 # Default action is cargo clean, followed by build or user given actions.
1030 if args.cargo:
1031 self.cargo = ['clean'] + args.cargo
1032 else:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001033 default_target = '--target x86_64-unknown-linux-gnu'
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -07001034 # Use the same target for both host and default device builds.
1035 # Same target is used as default in host x86_64 Android compilation.
1036 # Note: b/169872957, prebuilt cargo failed to build vsock
1037 # on x86_64-unknown-linux-musl systems.
1038 self.cargo = ['clean', 'build ' + default_target]
1039 if args.tests:
1040 self.cargo.append('build --tests ' + default_target)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001041
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001042 def setup_cargo_path(self):
1043 """Find cargo in the --cargo_bin or prebuilt rust bin directory."""
1044 if self.args.cargo_bin:
1045 self.cargo_path = os.path.join(self.args.cargo_bin, 'cargo')
1046 if not os.path.isfile(self.cargo_path):
1047 sys.exit('ERROR: cannot find cargo in ' + self.args.cargo_bin)
1048 print('WARNING: using cargo in ' + self.args.cargo_bin)
1049 return
1050 # We have only tested this on Linux.
1051 if platform.system() != 'Linux':
1052 sys.exit('ERROR: this script has only been tested on Linux with cargo.')
1053 # Assuming that this script is in development/scripts.
1054 my_dir = os.path.dirname(os.path.abspath(__file__))
1055 linux_dir = os.path.join(my_dir, '..', '..',
1056 'prebuilts', 'rust', 'linux-x86')
1057 if not os.path.isdir(linux_dir):
1058 sys.exit('ERROR: cannot find directory ' + linux_dir)
1059 rust_version = self.find_rust_version(my_dir, linux_dir)
1060 cargo_bin = os.path.join(linux_dir, rust_version, 'bin')
1061 self.cargo_path = os.path.join(cargo_bin, 'cargo')
1062 if not os.path.isfile(self.cargo_path):
1063 sys.exit('ERROR: cannot find cargo in ' + cargo_bin
1064 + '; please try --cargo_bin= flag.')
1065 return
1066
1067 def find_rust_version(self, my_dir, linux_dir):
1068 """Use my script directory, find prebuilt rust version."""
1069 # First look up build/soong/rust/config/global.go.
1070 path2global = os.path.join(my_dir, '..', '..',
1071 'build', 'soong', 'rust', 'config', 'global.go')
1072 if os.path.isfile(path2global):
1073 # try to find: RustDefaultVersion = "1.44.0"
1074 version_pat = re.compile(
1075 r'\s*RustDefaultVersion\s*=\s*"([0-9]+\.[0-9]+\.[0-9]+)".*$')
1076 with open(path2global, 'r') as inf:
1077 for line in inf:
1078 result = version_pat.match(line)
1079 if result:
1080 return result.group(1)
1081 print('WARNING: cannot find RustDefaultVersion in ' + path2global)
1082 # Otherwise, find the newest (largest) version number in linux_dir.
1083 rust_version = (0, 0, 0) # the prebuilt version to use
1084 version_pat = re.compile(r'([0-9]+)\.([0-9]+)\.([0-9]+)$')
1085 for dir_name in os.listdir(linux_dir):
1086 result = version_pat.match(dir_name)
1087 if not result:
1088 continue
1089 version = (result.group(1), result.group(2), result.group(3))
1090 if version > rust_version:
1091 rust_version = version
1092 return '.'.join(rust_version)
1093
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001094 def find_out_files(self):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001095 # list1 has build.rs output for normal crates
1096 list1 = glob.glob(TARGET_TMP + '/*/*/build/' + self.root_pkg + '-*/out/*')
1097 # list2 has build.rs output for proc-macro crates
1098 list2 = glob.glob(TARGET_TMP + '/*/build/' + self.root_pkg + '-*/out/*')
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001099 return list1 + list2
1100
1101 def copy_out_files(self):
1102 """Copy build.rs output files to ./out and set up build_out_files."""
1103 if self.checked_out_files:
1104 return
1105 self.checked_out_files = True
1106 cargo_out_files = self.find_out_files()
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001107 out_files = set()
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001108 if cargo_out_files:
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001109 os.makedirs('out', exist_ok=True)
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001110 for path in cargo_out_files:
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001111 file_name = path.split('/')[-1]
1112 out_files.add(file_name)
1113 shutil.copy(path, 'out/' + file_name)
1114 self.build_out_files = sorted(out_files)
1115
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001116 def has_used_out_dir(self):
1117 """Returns true if env!("OUT_DIR") is found."""
1118 return 0 == os.system('grep -rl --exclude build.rs --include \\*.rs' +
1119 ' \'env!("OUT_DIR")\' * > /dev/null')
1120
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001121 def copy_out_module_name(self):
1122 if self.args.copy_out and self.build_out_files:
1123 return 'copy_' + self.root_pkg + '_build_out'
1124 else:
1125 return ''
1126
Haibo Huang0f72c952021-03-19 11:34:15 -07001127 def read_license(self, name):
1128 if not os.path.isfile(name):
1129 return ''
1130 license = ''
1131 with open(name, 'r') as intf:
1132 line = intf.readline()
1133 # Firstly skip ANDROID_BP_HEADER
1134 while line.startswith('//'):
1135 line = intf.readline()
Joel Galensond9d13b82021-04-05 11:27:55 -07001136 # Read all lines until we see a rust_* or genrule rule.
1137 while line != '' and not (line.startswith('rust_') or line.startswith('genrule {')):
Haibo Huang0f72c952021-03-19 11:34:15 -07001138 license += line
1139 line = intf.readline()
1140 return license.strip()
1141
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001142 def dump_copy_out_module(self, outf):
1143 """Output the genrule module to copy out/* to $(genDir)."""
1144 copy_out = self.copy_out_module_name()
1145 if not copy_out:
1146 return
1147 outf.write('\ngenrule {\n')
1148 outf.write(' name: "' + copy_out + '",\n')
1149 outf.write(' srcs: ["out/*"],\n')
1150 outf.write(' cmd: "cp $(in) $(genDir)",\n')
1151 if len(self.build_out_files) > 1:
1152 outf.write(' out: [\n')
1153 for f in self.build_out_files:
1154 outf.write(' "' + f + '",\n')
1155 outf.write(' ],\n')
1156 else:
1157 outf.write(' out: ["' + self.build_out_files[0] + '"],\n')
1158 outf.write('}\n')
1159
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001160 def init_bp_file(self, name):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001161 # name could be Android.bp or sub_dir_path/Android.bp
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001162 if name not in self.bp_files:
1163 self.bp_files.add(name)
Haibo Huang0f72c952021-03-19 11:34:15 -07001164 license_section = self.read_license(name)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001165 with open(name, 'w') as outf:
Andrew Walbran80e90be2020-06-09 14:33:18 +01001166 outf.write(ANDROID_BP_HEADER.format(args=' '.join(sys.argv[1:])))
Haibo Huang0f72c952021-03-19 11:34:15 -07001167 outf.write('\n')
1168 outf.write(license_section)
1169 outf.write('\n')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001170 # at most one copy_out module per .bp file
1171 self.dump_copy_out_module(outf)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001172
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001173 def dump_test_mapping_files(self):
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001174 """Dump all TEST_MAPPING files."""
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001175 if self.dry_run:
1176 print('Dry-run skip dump of TEST_MAPPING')
Joel Galensoncf4ebb02021-04-07 08:39:51 -07001177 elif self.args.no_test_mapping:
1178 print('Skipping generation of TEST_MAPPING')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001179 else:
Jeff Vander Stoep1b24dc32021-02-03 18:52:42 +01001180 test_mapping = TestMapping(None)
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +01001181 for bp_file_name in self.bp_files:
1182 test_mapping.create_test_mapping(os.path.dirname(bp_file_name))
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001183 return self
1184
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001185 def try_claim_module_name(self, name, owner):
1186 """Reserve and return True if it has not been reserved yet."""
1187 if name not in self.name_owners or owner == self.name_owners[name]:
1188 self.name_owners[name] = owner
1189 return True
1190 return False
1191
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001192 def claim_module_name(self, prefix, owner, counter):
1193 """Return prefix if not owned yet, otherwise, prefix+str(counter)."""
1194 while True:
1195 name = prefix
1196 if counter > 0:
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001197 name += '_' + str(counter)
1198 if self.try_claim_module_name(name, owner):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001199 return name
1200 counter += 1
1201
1202 def find_root_pkg(self):
1203 """Read name of [package] in ./Cargo.toml."""
1204 if not os.path.exists('./Cargo.toml'):
1205 return
1206 with open('./Cargo.toml', 'r') as inf:
1207 pkg_section = re.compile(r'^ *\[package\]')
1208 name = re.compile('^ *name *= * "([^"]*)"')
1209 in_pkg = False
1210 for line in inf:
1211 if in_pkg:
1212 if name.match(line):
1213 self.root_pkg = name.match(line).group(1)
1214 break
1215 else:
1216 in_pkg = pkg_section.match(line) is not None
1217
1218 def run_cargo(self):
1219 """Calls cargo -v and save its output to ./cargo.out."""
1220 if self.skip_cargo:
1221 return self
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001222 cargo_toml = './Cargo.toml'
1223 cargo_out = './cargo.out'
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001224 # Do not use Cargo.lock, because .bp rules are designed to
1225 # run with "latest" crates avaialable on Android.
1226 cargo_lock = './Cargo.lock'
1227 cargo_lock_saved = './cargo.lock.saved'
1228 had_cargo_lock = os.path.exists(cargo_lock)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001229 if not os.access(cargo_toml, os.R_OK):
1230 print('ERROR: Cannot find or read', cargo_toml)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001231 return self
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001232 if not self.dry_run:
1233 if os.path.exists(cargo_out):
1234 os.remove(cargo_out)
1235 if not self.args.use_cargo_lock and had_cargo_lock: # save it
1236 os.rename(cargo_lock, cargo_lock_saved)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001237 cmd_tail = ' --target-dir ' + TARGET_TMP + ' >> ' + cargo_out + ' 2>&1'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001238 # set up search PATH for cargo to find the correct rustc
1239 saved_path = os.environ['PATH']
1240 os.environ['PATH'] = os.path.dirname(self.cargo_path) + ':' + saved_path
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001241 # Add [workspace] to Cargo.toml if it is not there.
1242 added_workspace = False
1243 if self.args.add_workspace:
1244 with open(cargo_toml, 'r') as in_file:
1245 cargo_toml_lines = in_file.readlines()
1246 found_workspace = '[workspace]\n' in cargo_toml_lines
1247 if found_workspace:
1248 print('### WARNING: found [workspace] in Cargo.toml')
1249 else:
1250 with open(cargo_toml, 'a') as out_file:
1251 out_file.write('[workspace]\n')
1252 added_workspace = True
1253 if self.args.verbose:
1254 print('### INFO: added [workspace] to Cargo.toml')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001255 for c in self.cargo:
1256 features = ''
Chih-Hung Hsieh6c8d52f2020-03-30 18:28:52 -07001257 if c != 'clean':
1258 if self.args.features is not None:
1259 features = ' --no-default-features'
1260 if self.args.features:
1261 features += ' --features ' + self.args.features
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001262 cmd_v_flag = ' -vv ' if self.args.vv else ' -v '
1263 cmd = self.cargo_path + cmd_v_flag
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001264 cmd += c + features + cmd_tail
1265 if self.args.rustflags and c != 'clean':
1266 cmd = 'RUSTFLAGS="' + self.args.rustflags + '" ' + cmd
1267 if self.dry_run:
1268 print('Dry-run skip:', cmd)
1269 else:
1270 if self.args.verbose:
1271 print('Running:', cmd)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001272 with open(cargo_out, 'a') as out_file:
1273 out_file.write('### Running: ' + cmd + '\n')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001274 os.system(cmd)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001275 if added_workspace: # restore original Cargo.toml
1276 with open(cargo_toml, 'w') as out_file:
1277 out_file.writelines(cargo_toml_lines)
1278 if self.args.verbose:
1279 print('### INFO: restored original Cargo.toml')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001280 os.environ['PATH'] = saved_path
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001281 if not self.dry_run:
1282 if not had_cargo_lock: # restore to no Cargo.lock state
1283 os.remove(cargo_lock)
1284 elif not self.args.use_cargo_lock: # restore saved Cargo.lock
1285 os.rename(cargo_lock_saved, cargo_lock)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001286 return self
1287
1288 def dump_dependencies(self):
1289 """Append dependencies and their features to Android.bp."""
1290 if not self.dependencies:
1291 return
1292 dependent_list = list()
1293 for c in self.dependencies:
1294 dependent_list.append(c.feature_list())
1295 sorted_dependencies = sorted(set(dependent_list))
1296 self.init_bp_file('Android.bp')
1297 with open('Android.bp', 'a') as outf:
1298 outf.write('\n// dependent_library ["feature_list"]\n')
1299 for s in sorted_dependencies:
1300 outf.write('// ' + s + '\n')
1301
1302 def dump_pkg_obj2cc(self):
1303 """Dump debug info of the pkg_obj2cc map."""
1304 if not self.args.debug:
1305 return
1306 self.init_bp_file('Android.bp')
1307 with open('Android.bp', 'a') as outf:
1308 sorted_pkgs = sorted(self.pkg_obj2cc.keys())
1309 for pkg in sorted_pkgs:
1310 if not self.pkg_obj2cc[pkg]:
1311 continue
1312 outf.write('\n// obj => src for %s\n' % pkg)
1313 obj2cc = self.pkg_obj2cc[pkg]
1314 for obj in sorted(obj2cc.keys()):
1315 outf.write('// ' + short_out_name(pkg, obj) + ' => ' +
1316 short_out_name(pkg, obj2cc[obj].src) + '\n')
1317
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001318 def apply_patch(self):
1319 """Apply local patch file if it is given."""
1320 if self.args.patch:
1321 if self.dry_run:
1322 print('Dry-run skip patch file:', self.args.patch)
1323 else:
1324 if not os.path.exists(self.args.patch):
1325 self.append_to_bp('ERROR cannot find patch file: ' + self.args.patch)
1326 return self
1327 if self.args.verbose:
1328 print('### INFO: applying local patch file:', self.args.patch)
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001329 os.system('patch -s --no-backup-if-mismatch ./Android.bp ' +
1330 self.args.patch)
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001331 return self
1332
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001333 def gen_bp(self):
1334 """Parse cargo.out and generate Android.bp files."""
1335 if self.dry_run:
1336 print('Dry-run skip: read', CARGO_OUT, 'write Android.bp')
1337 elif os.path.exists(CARGO_OUT):
1338 self.find_root_pkg()
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001339 if self.args.copy_out:
1340 self.copy_out_files()
1341 elif self.find_out_files() and self.has_used_out_dir():
1342 print('WARNING: ' + self.root_pkg + ' has cargo output files; ' +
1343 'please rerun with the --copy-out flag.')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001344 with open(CARGO_OUT, 'r') as cargo_out:
1345 self.parse(cargo_out, 'Android.bp')
1346 self.crates.sort(key=get_module_name)
1347 for obj in self.cc_objects:
1348 obj.dump()
1349 self.dump_pkg_obj2cc()
1350 for crate in self.crates:
1351 crate.dump()
1352 dumped_libs = set()
1353 for lib in self.ar_objects:
1354 if lib.pkg == self.root_pkg:
1355 lib_name = file_base_name(lib.lib)
1356 if lib_name not in dumped_libs:
1357 dumped_libs.add(lib_name)
1358 lib.dump()
1359 if self.args.dependencies and self.dependencies:
1360 self.dump_dependencies()
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001361 if self.errors:
Joel Galenson3f42f802021-04-07 12:42:17 -07001362 self.append_to_bp('\n' + ERRORS_LINE + '\n' + self.errors)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001363 return self
1364
1365 def add_ar_object(self, obj):
1366 self.ar_objects.append(obj)
1367
1368 def add_cc_object(self, obj):
1369 self.cc_objects.append(obj)
1370
1371 def add_crate(self, crate):
1372 """Merge crate with someone in crates, or append to it. Return crates."""
1373 if crate.skip_crate():
1374 if self.args.debug: # include debug info of all crates
1375 self.crates.append(crate)
1376 if self.args.dependencies: # include only dependent crates
1377 if (is_dependent_file_path(crate.main_src) and
1378 not is_build_crate_name(crate.crate_name)):
1379 self.dependencies.append(crate)
1380 else:
1381 for c in self.crates:
1382 if c.merge(crate, 'Android.bp'):
1383 return
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001384 # If not merged, decide module type and name now.
1385 crate.decide_module_type()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001386 self.crates.append(crate)
1387
1388 def find_warning_owners(self):
1389 """For each warning file, find its owner crate."""
1390 missing_owner = False
1391 for f in self.warning_files:
1392 cargo_dir = '' # find lowest crate, with longest path
1393 owner = None # owner crate of this warning
1394 for c in self.crates:
1395 if (f.startswith(c.cargo_dir + '/') and
1396 len(cargo_dir) < len(c.cargo_dir)):
1397 cargo_dir = c.cargo_dir
1398 owner = c
1399 if owner:
1400 owner.has_warning = True
1401 else:
1402 missing_owner = True
1403 if missing_owner and os.path.exists('Cargo.toml'):
1404 # owner is the root cargo, with empty cargo_dir
1405 for c in self.crates:
1406 if not c.cargo_dir:
1407 c.has_warning = True
1408
1409 def rustc_command(self, n, rustc_line, line, outf_name):
1410 """Process a rustc command line from cargo -vv output."""
1411 # cargo build -vv output can have multiple lines for a rustc command
1412 # due to '\n' in strings for environment variables.
1413 # strip removes leading spaces and '\n' at the end
1414 new_rustc = (rustc_line.strip() + line) if rustc_line else line
1415 # Use an heuristic to detect the completions of a multi-line command.
1416 # This might fail for some very rare case, but easy to fix manually.
1417 if not line.endswith('`\n') or (new_rustc.count('`') % 2) != 0:
1418 return new_rustc
1419 if RUSTC_VV_CMD_ARGS.match(new_rustc):
1420 args = RUSTC_VV_CMD_ARGS.match(new_rustc).group(1)
1421 self.add_crate(Crate(self, outf_name).parse(n, args))
1422 else:
1423 self.assert_empty_vv_line(new_rustc)
1424 return ''
1425
1426 def cc_ar_command(self, n, groups, outf_name):
1427 pkg = groups.group(1)
1428 line = groups.group(3)
1429 if groups.group(2) == 'cc':
1430 self.add_cc_object(CCObject(self, outf_name).parse(pkg, n, line))
1431 else:
1432 self.add_ar_object(ARObject(self, outf_name).parse(pkg, n, line))
1433
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001434 def append_to_bp(self, line):
1435 self.init_bp_file('Android.bp')
1436 with open('Android.bp', 'a') as outf:
1437 outf.write(line)
1438
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001439 def assert_empty_vv_line(self, line):
1440 if line: # report error if line is not empty
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001441 self.append_to_bp('ERROR -vv line: ' + line)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001442 return ''
1443
1444 def parse(self, inf, outf_name):
1445 """Parse rustc and warning messages in inf, return a list of Crates."""
1446 n = 0 # line number
1447 prev_warning = False # true if the previous line was warning: ...
1448 rustc_line = '' # previous line(s) matching RUSTC_VV_PAT
1449 for line in inf:
1450 n += 1
1451 if line.startswith('warning: '):
1452 prev_warning = True
1453 rustc_line = self.assert_empty_vv_line(rustc_line)
1454 continue
1455 new_rustc = ''
1456 if RUSTC_PAT.match(line):
1457 args_line = RUSTC_PAT.match(line).group(1)
1458 self.add_crate(Crate(self, outf_name).parse(n, args_line))
1459 self.assert_empty_vv_line(rustc_line)
1460 elif rustc_line or RUSTC_VV_PAT.match(line):
1461 new_rustc = self.rustc_command(n, rustc_line, line, outf_name)
1462 elif CC_AR_VV_PAT.match(line):
1463 self.cc_ar_command(n, CC_AR_VV_PAT.match(line), outf_name)
1464 elif prev_warning and WARNING_FILE_PAT.match(line):
1465 self.assert_empty_vv_line(rustc_line)
1466 fpath = WARNING_FILE_PAT.match(line).group(1)
1467 if fpath[0] != '/': # ignore absolute path
1468 self.warning_files.add(fpath)
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001469 elif line.startswith('error: ') or line.startswith('error[E'):
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001470 if not self.args.ignore_cargo_errors:
1471 self.errors += line
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001472 prev_warning = False
1473 rustc_line = new_rustc
1474 self.find_warning_owners()
1475
1476
1477def parse_args():
1478 """Parse main arguments."""
1479 parser = argparse.ArgumentParser('cargo2android')
1480 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001481 '--add_workspace',
1482 action='store_true',
1483 default=False,
1484 help=('append [workspace] to Cargo.toml before calling cargo,' +
1485 ' to treat current directory as root of package source;' +
1486 ' otherwise the relative source file path in generated' +
1487 ' .bp file will be from the parent directory.'))
1488 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001489 '--cargo',
1490 action='append',
1491 metavar='args_string',
1492 help=('extra cargo build -v args in a string, ' +
1493 'each --cargo flag calls cargo build -v once'))
1494 parser.add_argument(
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001495 '--cargo_bin',
1496 type=str,
1497 help='use cargo in the cargo_bin directory instead of the prebuilt one')
1498 parser.add_argument(
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001499 '--copy-out',
1500 action='store_true',
1501 default=False,
1502 help=('only for root directory, ' +
1503 'copy build.rs output to ./out/* and add a genrule to copy ' +
1504 './out/* to genrule output; for crates with code pattern: ' +
1505 'include!(concat!(env!("OUT_DIR"), "/<some_file>.rs"))'))
1506 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001507 '--debug',
1508 action='store_true',
1509 default=False,
1510 help='dump debug info into Android.bp')
1511 parser.add_argument(
1512 '--dependencies',
1513 action='store_true',
1514 default=False,
1515 help='dump debug info of dependent crates')
1516 parser.add_argument(
1517 '--device',
1518 action='store_true',
1519 default=False,
1520 help='run cargo also for a default device target')
1521 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001522 '--features',
1523 type=str,
1524 help=('pass features to cargo build, ' +
1525 'empty string means no default features'))
1526 parser.add_argument(
1527 '--global_defaults',
1528 type=str,
1529 help='add a defaults name to every module')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001530 parser.add_argument(
1531 '--host-first-multilib',
1532 action='store_true',
1533 default=False,
1534 help=('add a compile_multilib:"first" property ' +
1535 'to Android.bp host modules.'))
1536 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001537 '--ignore-cargo-errors',
1538 action='store_true',
1539 default=False,
1540 help='do not append cargo/rustc error messages to Android.bp')
1541 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001542 '--no-host',
1543 action='store_true',
1544 default=False,
1545 help='do not run cargo for the host; only for the device target')
1546 parser.add_argument(
1547 '--no-subdir',
1548 action='store_true',
1549 default=False,
1550 help='do not output anything for sub-directories')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001551 parser.add_argument(
1552 '--onefile',
1553 action='store_true',
1554 default=False,
1555 help=('output all into one ./Android.bp, default will generate ' +
1556 'one Android.bp per Cargo.toml in subdirectories'))
1557 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001558 '--patch',
1559 type=str,
1560 help='apply the given patch file to generated ./Android.bp')
1561 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001562 '--run',
1563 action='store_true',
1564 default=False,
1565 help='run it, default is dry-run')
1566 parser.add_argument('--rustflags', type=str, help='passing flags to rustc')
1567 parser.add_argument(
1568 '--skipcargo',
1569 action='store_true',
1570 default=False,
1571 help='skip cargo command, parse cargo.out, and generate Android.bp')
1572 parser.add_argument(
1573 '--tests',
1574 action='store_true',
1575 default=False,
1576 help='run cargo build --tests after normal build')
1577 parser.add_argument(
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001578 '--use-cargo-lock',
1579 action='store_true',
1580 default=False,
1581 help=('run cargo build with existing Cargo.lock ' +
1582 '(used when some latest dependent crates failed)'))
1583 parser.add_argument(
Joel Galensoncf4ebb02021-04-07 08:39:51 -07001584 '--no-test-mapping',
1585 action='store_true',
1586 default=False,
1587 help='Do not generate a TEST_MAPPING file. Use only to speed up debugging.')
1588 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001589 '--verbose',
1590 action='store_true',
1591 default=False,
1592 help='echo executed commands')
1593 parser.add_argument(
1594 '--vv',
1595 action='store_true',
1596 default=False,
1597 help='run cargo with -vv instead of default -v')
1598 return parser.parse_args()
1599
1600
1601def main():
1602 args = parse_args()
1603 if not args.run: # default is dry-run
1604 print(DRY_RUN_NOTE)
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001605 Runner(args).run_cargo().gen_bp().apply_patch().dump_test_mapping_files()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001606
1607
1608if __name__ == '__main__':
1609 main()