blob: c5585f3a8579e9db9545eac530f2cc9bb4e05772 [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."""
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700655 if not self.cfgs and not self.codegens and not self.cap_lints:
656 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 + '",')
660 cfg_fmt = '"--cfg %s"'
661 codegens_fmt = '"-C %s"'
662 self.dump_android_property_list_items(cfg_fmt, self.cfgs)
663 self.dump_android_property_list_items(codegens_fmt, self.codegens)
664 self.write(' ],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700665
666 def dump_edition_flags_libs(self):
667 if self.edition:
668 self.write(' edition: "' + self.edition + '",')
669 self.dump_android_property_list('features', '"%s"', self.features)
670 self.dump_android_flags()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800671 if self.externs:
672 self.dump_android_externs()
673 self.dump_android_property_list('static_libs', '"lib%s"', self.static_libs)
674 self.dump_android_property_list('shared_libs', '"lib%s"', self.shared_libs)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800675
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700676 def main_src_basename_path(self):
677 return re.sub('/', '_', re.sub('.rs$', '', self.main_src))
678
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800679 def test_module_name(self):
680 """Return a unique name for a test module."""
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700681 # root_pkg+(_host|_device) + '_test_'+source_file_name
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700682 suffix = self.main_src_basename_path()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700683 host_device = '_host'
684 if self.device_supported:
685 host_device = '_device'
686 return self.root_pkg + host_device + '_test_' + suffix
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800687
688 def decide_module_type(self):
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700689 # Use the first crate type for the default/first module.
690 crate_type = self.crate_types[0] if self.crate_types else ''
691 self.decide_one_module_type(crate_type)
692
693 def decide_one_module_type(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800694 """Decide which Android module type to use."""
695 host = '' if self.device_supported else '_host'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700696 if crate_type == 'bin': # rust_binary[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800697 self.module_type = 'rust_binary' + host
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700698 # In rare cases like protobuf-codegen, the output binary name must
699 # be renamed to use as a plugin for protoc.
700 self.stem = altered_stem(self.crate_name)
701 self.module_name = altered_name(self.crate_name)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700702 elif crate_type == 'lib': # rust_library[_host]
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700703 # TODO(chh): should this be rust_library[_host]?
704 # Assuming that Cargo.toml do not use both 'lib' and 'rlib',
705 # because we map them both to rlib.
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700706 self.module_type = 'rust_library' + host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800707 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700708 self.module_name = altered_name(self.stem)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700709 elif crate_type == 'rlib': # rust_library[_host]
710 self.module_type = 'rust_library' + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700711 self.stem = 'lib' + self.crate_name
712 self.module_name = altered_name(self.stem)
713 elif crate_type == 'dylib': # rust_library[_host]_dylib
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800714 self.module_type = 'rust_library' + host + '_dylib'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700715 self.stem = 'lib' + self.crate_name
716 self.module_name = altered_name(self.stem) + '_dylib'
717 elif crate_type == 'cdylib': # rust_library[_host]_shared
Ivan Lozano0c057ad2020-12-15 10:41:26 -0500718 self.module_type = 'rust_ffi' + host + '_shared'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700719 self.stem = 'lib' + self.crate_name
720 self.module_name = altered_name(self.stem) + '_shared'
721 elif crate_type == 'staticlib': # rust_library[_host]_static
Ivan Lozano0c057ad2020-12-15 10:41:26 -0500722 self.module_type = 'rust_ffi' + host + '_static'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700723 self.stem = 'lib' + self.crate_name
724 self.module_name = altered_name(self.stem) + '_static'
725 elif crate_type == 'test': # rust_test[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800726 self.module_type = 'rust_test' + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700727 # Before do_merge, stem name is based on the --crate-name parameter.
728 # and test module name is based on stem.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800729 self.stem = self.test_module_name()
730 # self.stem will be changed after merging with other tests.
731 # self.stem is NOT used for final test binary name.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700732 # rust_test uses each source file base name as part of output file name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700733 # In do_merge, this function is called again, with a module_name.
734 # We make sure that the module name is unique in each package.
735 if self.module_name:
736 # Cargo uses "-C extra-filename=..." and "-C metadata=..." to add
737 # different suffixes and distinguish multiple tests of the same
738 # crate name. We ignore -C and use claim_module_name to get
739 # unique sequential suffix.
740 self.module_name = self.runner.claim_module_name(
741 self.module_name, self, 0)
742 # Now the module name is unique, stem should also match and unique.
743 self.stem = self.module_name
744 elif crate_type == 'proc-macro': # rust_proc_macro
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800745 self.module_type = 'rust_proc_macro'
746 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700747 self.module_name = altered_name(self.stem)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800748 else: # unknown module type, rust_prebuilt_dylib? rust_library[_host]?
749 self.module_type = ''
750 self.stem = ''
751
752 def dump_android_property_list_items(self, fmt, values):
753 for v in values:
754 # fmt has quotes, so we need escape_quotes(v)
755 self.write(' ' + (fmt % escape_quotes(v)) + ',')
756
757 def dump_android_property_list(self, name, fmt, values):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700758 if not values:
759 return
760 if len(values) > 1:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800761 self.write(' ' + name + ': [')
762 self.dump_android_property_list_items(fmt, values)
763 self.write(' ],')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700764 else:
765 self.write(' ' + name + ': [' +
766 (fmt % escape_quotes(values[0])) + '],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800767
768 def dump_android_core_properties(self):
769 """Dump the module header, name, stem, etc."""
770 self.write(' name: "' + self.module_name + '",')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700771 # see properties shared by dump_defaults_module
772 if self.defaults:
773 self.write(' defaults: ["' + self.defaults + '"],')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700774 elif self.runner.args.global_defaults:
775 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800776 if self.stem != self.module_name:
777 self.write(' stem: "' + self.stem + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700778 if self.has_warning and not self.cap_lints and not self.default_srcs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700779 self.write(' // has rustc warnings')
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700780 if self.host_supported and self.device_supported and self.module_type != 'rust_proc_macro':
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800781 self.write(' host_supported: true,')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700782 if not self.defaults:
783 self.write(' crate_name: "' + self.crate_name + '",')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700784 if not self.default_srcs:
785 self.dump_srcs_list()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700786 if 'test' in self.crate_types and not self.defaults:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800787 # self.root_pkg can have multiple test modules, with different *_tests[n]
788 # names, but their executables can all be installed under the same _tests
789 # directory. When built from Cargo.toml, all tests should have different
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700790 # file or crate names. So we used (root_pkg + '_tests') name as the
791 # relative_install_path.
792 # However, some package like 'slab' can have non-mergeable tests that
793 # must be separated by different module names. So, here we no longer
794 # emit relative_install_path.
795 # self.write(' relative_install_path: "' + self.root_pkg + '_tests",')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800796 self.write(' test_suites: ["general-tests"],')
797 self.write(' auto_gen_config: true,')
Joel Galensone261a152021-01-12 11:31:53 -0800798 if 'test' in self.crate_types and self.host_supported:
799 self.write(' test_options: {')
800 self.write(' unit_test: true,')
801 self.write(' },')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800802
803 def dump_android_externs(self):
804 """Dump the dependent rlibs and dylibs property."""
805 so_libs = list()
806 rust_libs = ''
807 deps_libname = re.compile('^.* = lib(.*)-[0-9a-f]*.(rlib|so|rmeta)$')
808 for lib in self.externs:
809 # normal value of lib: "libc = liblibc-*.rlib"
810 # strange case in rand crate: "getrandom_package = libgetrandom-*.rlib"
811 # we should use "libgetrandom", not "lib" + "getrandom_package"
812 groups = deps_libname.match(lib)
813 if groups is not None:
814 lib_name = groups.group(1)
815 else:
816 lib_name = re.sub(' .*$', '', lib)
817 if lib.endswith('.rlib') or lib.endswith('.rmeta'):
818 # On MacOS .rmeta is used when Linux uses .rlib or .rmeta.
819 rust_libs += ' "' + altered_name('lib' + lib_name) + '",\n'
820 elif lib.endswith('.so'):
821 so_libs.append(lib_name)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700822 elif lib != 'proc_macro': # --extern proc_macro is special and ignored
823 rust_libs += ' // ERROR: unknown type of lib ' + lib + '\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800824 if rust_libs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700825 self.write(' rustlibs: [\n' + rust_libs + ' ],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800826 # Are all dependent .so files proc_macros?
827 # TODO(chh): Separate proc_macros and dylib.
828 self.dump_android_property_list('proc_macros', '"lib%s"', so_libs)
829
830
831class ARObject(object):
832 """Information of an "ar" link command."""
833
834 def __init__(self, runner, outf_name):
835 # Remembered global runner and its members.
836 self.runner = runner
837 self.pkg = ''
838 self.outf_name = outf_name # path to Android.bp
839 # "ar" arguments
840 self.line_num = 1
841 self.line = ''
842 self.flags = '' # e.g. "crs"
843 self.lib = '' # e.g. "/.../out/lib*.a"
844 self.objs = list() # e.g. "/.../out/.../*.o"
845
846 def parse(self, pkg, line_num, args_line):
847 """Collect ar obj/lib file names."""
848 self.pkg = pkg
849 self.line_num = line_num
850 self.line = args_line
851 args = args_line.split()
852 num_args = len(args)
853 if num_args < 3:
854 print('ERROR: "ar" command has too few arguments', args_line)
855 else:
856 self.flags = unquote(args[0])
857 self.lib = unquote(args[1])
858 self.objs = sorted(set(map(unquote, args[2:])))
859 return self
860
861 def write(self, s):
862 self.outf.write(s + '\n')
863
864 def dump_debug_info(self):
865 self.write('\n// Line ' + str(self.line_num) + ' "ar" ' + self.line)
866 self.write('// ar_object for %12s' % self.pkg)
867 self.write('// flags = %s' % self.flags)
868 self.write('// lib = %s' % short_out_name(self.pkg, self.lib))
869 for o in self.objs:
870 self.write('// obj = %s' % short_out_name(self.pkg, o))
871
872 def dump_android_lib(self):
873 """Write cc_library_static into Android.bp."""
874 self.write('\ncc_library_static {')
875 self.write(' name: "' + file_base_name(self.lib) + '",')
876 self.write(' host_supported: true,')
877 if self.flags != 'crs':
878 self.write(' // ar flags = %s' % self.flags)
879 if self.pkg not in self.runner.pkg_obj2cc:
880 self.write(' ERROR: cannot find source files.\n}')
881 return
882 self.write(' srcs: [')
883 obj2cc = self.runner.pkg_obj2cc[self.pkg]
884 # Note: wflags are ignored.
885 dflags = list()
886 fflags = list()
887 for obj in self.objs:
888 self.write(' "' + short_out_name(self.pkg, obj2cc[obj].src) + '",')
889 # TODO(chh): union of dflags and flags of all obj
890 # Now, just a temporary hack that uses the last obj's flags
891 dflags = obj2cc[obj].dflags
892 fflags = obj2cc[obj].fflags
893 self.write(' ],')
894 self.write(' cflags: [')
895 self.write(' "-O3",') # TODO(chh): is this default correct?
896 self.write(' "-Wno-error",')
897 for x in fflags:
898 self.write(' "-f' + x + '",')
899 for x in dflags:
900 self.write(' "-D' + x + '",')
901 self.write(' ],')
902 self.write('}')
903
904 def dump(self):
905 """Dump error/debug/module info to the output .bp file."""
906 self.runner.init_bp_file(self.outf_name)
907 with open(self.outf_name, 'a') as outf:
908 self.outf = outf
909 if self.runner.args.debug:
910 self.dump_debug_info()
911 self.dump_android_lib()
912
913
914class CCObject(object):
915 """Information of a "cc" compilation command."""
916
917 def __init__(self, runner, outf_name):
918 # Remembered global runner and its members.
919 self.runner = runner
920 self.pkg = ''
921 self.outf_name = outf_name # path to Android.bp
922 # "cc" arguments
923 self.line_num = 1
924 self.line = ''
925 self.src = ''
926 self.obj = ''
927 self.dflags = list() # -D flags
928 self.fflags = list() # -f flags
929 self.iflags = list() # -I flags
930 self.wflags = list() # -W flags
931 self.other_args = list()
932
933 def parse(self, pkg, line_num, args_line):
934 """Collect cc compilation flags and src/out file names."""
935 self.pkg = pkg
936 self.line_num = line_num
937 self.line = args_line
938 args = args_line.split()
939 i = 0
940 while i < len(args):
941 arg = args[i]
942 if arg == '"-c"':
943 i += 1
944 if args[i].startswith('"-o'):
945 # ring-0.13.5 dumps: ... "-c" "-o/.../*.o" ".../*.c"
946 self.obj = unquote(args[i])[2:]
947 i += 1
948 self.src = unquote(args[i])
949 else:
950 self.src = unquote(args[i])
951 elif arg == '"-o"':
952 i += 1
953 self.obj = unquote(args[i])
954 elif arg == '"-I"':
955 i += 1
956 self.iflags.append(unquote(args[i]))
957 elif arg.startswith('"-D'):
958 self.dflags.append(unquote(args[i])[2:])
959 elif arg.startswith('"-f'):
960 self.fflags.append(unquote(args[i])[2:])
961 elif arg.startswith('"-W'):
962 self.wflags.append(unquote(args[i])[2:])
963 elif not (arg.startswith('"-O') or arg == '"-m64"' or arg == '"-g"' or
964 arg == '"-g3"'):
965 # ignore -O -m64 -g
966 self.other_args.append(unquote(args[i]))
967 i += 1
968 self.dflags = sorted(set(self.dflags))
969 self.fflags = sorted(set(self.fflags))
970 # self.wflags is not sorted because some are order sensitive
971 # and we ignore them anyway.
972 if self.pkg not in self.runner.pkg_obj2cc:
973 self.runner.pkg_obj2cc[self.pkg] = {}
974 self.runner.pkg_obj2cc[self.pkg][self.obj] = self
975 return self
976
977 def write(self, s):
978 self.outf.write(s + '\n')
979
980 def dump_debug_flags(self, name, flags):
981 self.write('// ' + name + ':')
982 for f in flags:
983 self.write('// %s' % f)
984
985 def dump(self):
986 """Dump only error/debug info to the output .bp file."""
987 if not self.runner.args.debug:
988 return
989 self.runner.init_bp_file(self.outf_name)
990 with open(self.outf_name, 'a') as outf:
991 self.outf = outf
992 self.write('\n// Line ' + str(self.line_num) + ' "cc" ' + self.line)
993 self.write('// cc_object for %12s' % self.pkg)
994 self.write('// src = %s' % short_out_name(self.pkg, self.src))
995 self.write('// obj = %s' % short_out_name(self.pkg, self.obj))
996 self.dump_debug_flags('-I flags', self.iflags)
997 self.dump_debug_flags('-D flags', self.dflags)
998 self.dump_debug_flags('-f flags', self.fflags)
999 self.dump_debug_flags('-W flags', self.wflags)
1000 if self.other_args:
1001 self.dump_debug_flags('other args', self.other_args)
1002
1003
1004class Runner(object):
1005 """Main class to parse cargo -v output and print Android module definitions."""
1006
1007 def __init__(self, args):
1008 self.bp_files = set() # Remember all output Android.bp files.
1009 self.root_pkg = '' # name of package in ./Cargo.toml
1010 # Saved flags, modes, and data.
1011 self.args = args
1012 self.dry_run = not args.run
1013 self.skip_cargo = args.skipcargo
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001014 self.cargo_path = './cargo' # path to cargo, will be set later
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001015 self.checked_out_files = False # to check only once
1016 self.build_out_files = [] # output files generated by build.rs
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001017 # All cc/ar objects, crates, dependencies, and warning files
1018 self.cc_objects = list()
1019 self.pkg_obj2cc = {}
1020 # pkg_obj2cc[cc_object[i].pkg][cc_objects[i].obj] = cc_objects[i]
1021 self.ar_objects = list()
1022 self.crates = list()
1023 self.dependencies = list() # dependent and build script crates
1024 self.warning_files = set()
1025 # Keep a unique mapping from (module name) to crate
1026 self.name_owners = {}
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001027 # Save and dump all errors from cargo to Android.bp.
1028 self.errors = ''
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001029 self.setup_cargo_path()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001030 # Default action is cargo clean, followed by build or user given actions.
1031 if args.cargo:
1032 self.cargo = ['clean'] + args.cargo
1033 else:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001034 default_target = '--target x86_64-unknown-linux-gnu'
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -07001035 # Use the same target for both host and default device builds.
1036 # Same target is used as default in host x86_64 Android compilation.
1037 # Note: b/169872957, prebuilt cargo failed to build vsock
1038 # on x86_64-unknown-linux-musl systems.
1039 self.cargo = ['clean', 'build ' + default_target]
1040 if args.tests:
1041 self.cargo.append('build --tests ' + default_target)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001042
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001043 def setup_cargo_path(self):
1044 """Find cargo in the --cargo_bin or prebuilt rust bin directory."""
1045 if self.args.cargo_bin:
1046 self.cargo_path = os.path.join(self.args.cargo_bin, 'cargo')
1047 if not os.path.isfile(self.cargo_path):
1048 sys.exit('ERROR: cannot find cargo in ' + self.args.cargo_bin)
1049 print('WARNING: using cargo in ' + self.args.cargo_bin)
1050 return
1051 # We have only tested this on Linux.
1052 if platform.system() != 'Linux':
1053 sys.exit('ERROR: this script has only been tested on Linux with cargo.')
1054 # Assuming that this script is in development/scripts.
1055 my_dir = os.path.dirname(os.path.abspath(__file__))
1056 linux_dir = os.path.join(my_dir, '..', '..',
1057 'prebuilts', 'rust', 'linux-x86')
1058 if not os.path.isdir(linux_dir):
1059 sys.exit('ERROR: cannot find directory ' + linux_dir)
1060 rust_version = self.find_rust_version(my_dir, linux_dir)
1061 cargo_bin = os.path.join(linux_dir, rust_version, 'bin')
1062 self.cargo_path = os.path.join(cargo_bin, 'cargo')
1063 if not os.path.isfile(self.cargo_path):
1064 sys.exit('ERROR: cannot find cargo in ' + cargo_bin
1065 + '; please try --cargo_bin= flag.')
1066 return
1067
1068 def find_rust_version(self, my_dir, linux_dir):
1069 """Use my script directory, find prebuilt rust version."""
1070 # First look up build/soong/rust/config/global.go.
1071 path2global = os.path.join(my_dir, '..', '..',
1072 'build', 'soong', 'rust', 'config', 'global.go')
1073 if os.path.isfile(path2global):
1074 # try to find: RustDefaultVersion = "1.44.0"
1075 version_pat = re.compile(
1076 r'\s*RustDefaultVersion\s*=\s*"([0-9]+\.[0-9]+\.[0-9]+)".*$')
1077 with open(path2global, 'r') as inf:
1078 for line in inf:
1079 result = version_pat.match(line)
1080 if result:
1081 return result.group(1)
1082 print('WARNING: cannot find RustDefaultVersion in ' + path2global)
1083 # Otherwise, find the newest (largest) version number in linux_dir.
1084 rust_version = (0, 0, 0) # the prebuilt version to use
1085 version_pat = re.compile(r'([0-9]+)\.([0-9]+)\.([0-9]+)$')
1086 for dir_name in os.listdir(linux_dir):
1087 result = version_pat.match(dir_name)
1088 if not result:
1089 continue
1090 version = (result.group(1), result.group(2), result.group(3))
1091 if version > rust_version:
1092 rust_version = version
1093 return '.'.join(rust_version)
1094
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001095 def find_out_files(self):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001096 # list1 has build.rs output for normal crates
1097 list1 = glob.glob(TARGET_TMP + '/*/*/build/' + self.root_pkg + '-*/out/*')
1098 # list2 has build.rs output for proc-macro crates
1099 list2 = glob.glob(TARGET_TMP + '/*/build/' + self.root_pkg + '-*/out/*')
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001100 return list1 + list2
1101
1102 def copy_out_files(self):
1103 """Copy build.rs output files to ./out and set up build_out_files."""
1104 if self.checked_out_files:
1105 return
1106 self.checked_out_files = True
1107 cargo_out_files = self.find_out_files()
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001108 out_files = set()
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001109 if cargo_out_files:
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001110 os.makedirs('out', exist_ok=True)
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001111 for path in cargo_out_files:
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001112 file_name = path.split('/')[-1]
1113 out_files.add(file_name)
1114 shutil.copy(path, 'out/' + file_name)
1115 self.build_out_files = sorted(out_files)
1116
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001117 def has_used_out_dir(self):
1118 """Returns true if env!("OUT_DIR") is found."""
1119 return 0 == os.system('grep -rl --exclude build.rs --include \\*.rs' +
1120 ' \'env!("OUT_DIR")\' * > /dev/null')
1121
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001122 def copy_out_module_name(self):
1123 if self.args.copy_out and self.build_out_files:
1124 return 'copy_' + self.root_pkg + '_build_out'
1125 else:
1126 return ''
1127
Haibo Huang0f72c952021-03-19 11:34:15 -07001128 def read_license(self, name):
1129 if not os.path.isfile(name):
1130 return ''
1131 license = ''
1132 with open(name, 'r') as intf:
1133 line = intf.readline()
1134 # Firstly skip ANDROID_BP_HEADER
1135 while line.startswith('//'):
1136 line = intf.readline()
1137 # Read all lines until we see a rust_* rule.
1138 while line != '' and not line.startswith('rust_'):
1139 license += line
1140 line = intf.readline()
1141 return license.strip()
1142
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001143 def dump_copy_out_module(self, outf):
1144 """Output the genrule module to copy out/* to $(genDir)."""
1145 copy_out = self.copy_out_module_name()
1146 if not copy_out:
1147 return
1148 outf.write('\ngenrule {\n')
1149 outf.write(' name: "' + copy_out + '",\n')
1150 outf.write(' srcs: ["out/*"],\n')
1151 outf.write(' cmd: "cp $(in) $(genDir)",\n')
1152 if len(self.build_out_files) > 1:
1153 outf.write(' out: [\n')
1154 for f in self.build_out_files:
1155 outf.write(' "' + f + '",\n')
1156 outf.write(' ],\n')
1157 else:
1158 outf.write(' out: ["' + self.build_out_files[0] + '"],\n')
1159 outf.write('}\n')
1160
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001161 def init_bp_file(self, name):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001162 # name could be Android.bp or sub_dir_path/Android.bp
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001163 if name not in self.bp_files:
1164 self.bp_files.add(name)
Haibo Huang0f72c952021-03-19 11:34:15 -07001165 license_section = self.read_license(name)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001166 with open(name, 'w') as outf:
Andrew Walbran80e90be2020-06-09 14:33:18 +01001167 outf.write(ANDROID_BP_HEADER.format(args=' '.join(sys.argv[1:])))
Haibo Huang0f72c952021-03-19 11:34:15 -07001168 outf.write('\n')
1169 outf.write(license_section)
1170 outf.write('\n')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001171 # at most one copy_out module per .bp file
1172 self.dump_copy_out_module(outf)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001173
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001174 def dump_test_mapping_files(self):
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001175 """Dump all TEST_MAPPING files."""
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001176 if self.dry_run:
1177 print('Dry-run skip dump of TEST_MAPPING')
1178 else:
Jeff Vander Stoep1b24dc32021-02-03 18:52:42 +01001179 test_mapping = TestMapping(None)
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +01001180 for bp_file_name in self.bp_files:
1181 test_mapping.create_test_mapping(os.path.dirname(bp_file_name))
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001182 return self
1183
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001184 def try_claim_module_name(self, name, owner):
1185 """Reserve and return True if it has not been reserved yet."""
1186 if name not in self.name_owners or owner == self.name_owners[name]:
1187 self.name_owners[name] = owner
1188 return True
1189 return False
1190
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001191 def claim_module_name(self, prefix, owner, counter):
1192 """Return prefix if not owned yet, otherwise, prefix+str(counter)."""
1193 while True:
1194 name = prefix
1195 if counter > 0:
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001196 name += '_' + str(counter)
1197 if self.try_claim_module_name(name, owner):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001198 return name
1199 counter += 1
1200
1201 def find_root_pkg(self):
1202 """Read name of [package] in ./Cargo.toml."""
1203 if not os.path.exists('./Cargo.toml'):
1204 return
1205 with open('./Cargo.toml', 'r') as inf:
1206 pkg_section = re.compile(r'^ *\[package\]')
1207 name = re.compile('^ *name *= * "([^"]*)"')
1208 in_pkg = False
1209 for line in inf:
1210 if in_pkg:
1211 if name.match(line):
1212 self.root_pkg = name.match(line).group(1)
1213 break
1214 else:
1215 in_pkg = pkg_section.match(line) is not None
1216
1217 def run_cargo(self):
1218 """Calls cargo -v and save its output to ./cargo.out."""
1219 if self.skip_cargo:
1220 return self
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001221 cargo_toml = './Cargo.toml'
1222 cargo_out = './cargo.out'
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001223 # Do not use Cargo.lock, because .bp rules are designed to
1224 # run with "latest" crates avaialable on Android.
1225 cargo_lock = './Cargo.lock'
1226 cargo_lock_saved = './cargo.lock.saved'
1227 had_cargo_lock = os.path.exists(cargo_lock)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001228 if not os.access(cargo_toml, os.R_OK):
1229 print('ERROR: Cannot find or read', cargo_toml)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001230 return self
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001231 if not self.dry_run:
1232 if os.path.exists(cargo_out):
1233 os.remove(cargo_out)
1234 if not self.args.use_cargo_lock and had_cargo_lock: # save it
1235 os.rename(cargo_lock, cargo_lock_saved)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001236 cmd_tail = ' --target-dir ' + TARGET_TMP + ' >> ' + cargo_out + ' 2>&1'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001237 # set up search PATH for cargo to find the correct rustc
1238 saved_path = os.environ['PATH']
1239 os.environ['PATH'] = os.path.dirname(self.cargo_path) + ':' + saved_path
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001240 # Add [workspace] to Cargo.toml if it is not there.
1241 added_workspace = False
1242 if self.args.add_workspace:
1243 with open(cargo_toml, 'r') as in_file:
1244 cargo_toml_lines = in_file.readlines()
1245 found_workspace = '[workspace]\n' in cargo_toml_lines
1246 if found_workspace:
1247 print('### WARNING: found [workspace] in Cargo.toml')
1248 else:
1249 with open(cargo_toml, 'a') as out_file:
1250 out_file.write('[workspace]\n')
1251 added_workspace = True
1252 if self.args.verbose:
1253 print('### INFO: added [workspace] to Cargo.toml')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001254 for c in self.cargo:
1255 features = ''
Chih-Hung Hsieh6c8d52f2020-03-30 18:28:52 -07001256 if c != 'clean':
1257 if self.args.features is not None:
1258 features = ' --no-default-features'
1259 if self.args.features:
1260 features += ' --features ' + self.args.features
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001261 cmd_v_flag = ' -vv ' if self.args.vv else ' -v '
1262 cmd = self.cargo_path + cmd_v_flag
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001263 cmd += c + features + cmd_tail
1264 if self.args.rustflags and c != 'clean':
1265 cmd = 'RUSTFLAGS="' + self.args.rustflags + '" ' + cmd
1266 if self.dry_run:
1267 print('Dry-run skip:', cmd)
1268 else:
1269 if self.args.verbose:
1270 print('Running:', cmd)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001271 with open(cargo_out, 'a') as out_file:
1272 out_file.write('### Running: ' + cmd + '\n')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001273 os.system(cmd)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001274 if added_workspace: # restore original Cargo.toml
1275 with open(cargo_toml, 'w') as out_file:
1276 out_file.writelines(cargo_toml_lines)
1277 if self.args.verbose:
1278 print('### INFO: restored original Cargo.toml')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001279 os.environ['PATH'] = saved_path
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001280 if not self.dry_run:
1281 if not had_cargo_lock: # restore to no Cargo.lock state
1282 os.remove(cargo_lock)
1283 elif not self.args.use_cargo_lock: # restore saved Cargo.lock
1284 os.rename(cargo_lock_saved, cargo_lock)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001285 return self
1286
1287 def dump_dependencies(self):
1288 """Append dependencies and their features to Android.bp."""
1289 if not self.dependencies:
1290 return
1291 dependent_list = list()
1292 for c in self.dependencies:
1293 dependent_list.append(c.feature_list())
1294 sorted_dependencies = sorted(set(dependent_list))
1295 self.init_bp_file('Android.bp')
1296 with open('Android.bp', 'a') as outf:
1297 outf.write('\n// dependent_library ["feature_list"]\n')
1298 for s in sorted_dependencies:
1299 outf.write('// ' + s + '\n')
1300
1301 def dump_pkg_obj2cc(self):
1302 """Dump debug info of the pkg_obj2cc map."""
1303 if not self.args.debug:
1304 return
1305 self.init_bp_file('Android.bp')
1306 with open('Android.bp', 'a') as outf:
1307 sorted_pkgs = sorted(self.pkg_obj2cc.keys())
1308 for pkg in sorted_pkgs:
1309 if not self.pkg_obj2cc[pkg]:
1310 continue
1311 outf.write('\n// obj => src for %s\n' % pkg)
1312 obj2cc = self.pkg_obj2cc[pkg]
1313 for obj in sorted(obj2cc.keys()):
1314 outf.write('// ' + short_out_name(pkg, obj) + ' => ' +
1315 short_out_name(pkg, obj2cc[obj].src) + '\n')
1316
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001317 def apply_patch(self):
1318 """Apply local patch file if it is given."""
1319 if self.args.patch:
1320 if self.dry_run:
1321 print('Dry-run skip patch file:', self.args.patch)
1322 else:
1323 if not os.path.exists(self.args.patch):
1324 self.append_to_bp('ERROR cannot find patch file: ' + self.args.patch)
1325 return self
1326 if self.args.verbose:
1327 print('### INFO: applying local patch file:', self.args.patch)
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001328 os.system('patch -s --no-backup-if-mismatch ./Android.bp ' +
1329 self.args.patch)
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001330 return self
1331
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001332 def gen_bp(self):
1333 """Parse cargo.out and generate Android.bp files."""
1334 if self.dry_run:
1335 print('Dry-run skip: read', CARGO_OUT, 'write Android.bp')
1336 elif os.path.exists(CARGO_OUT):
1337 self.find_root_pkg()
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001338 if self.args.copy_out:
1339 self.copy_out_files()
1340 elif self.find_out_files() and self.has_used_out_dir():
1341 print('WARNING: ' + self.root_pkg + ' has cargo output files; ' +
1342 'please rerun with the --copy-out flag.')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001343 with open(CARGO_OUT, 'r') as cargo_out:
1344 self.parse(cargo_out, 'Android.bp')
1345 self.crates.sort(key=get_module_name)
1346 for obj in self.cc_objects:
1347 obj.dump()
1348 self.dump_pkg_obj2cc()
1349 for crate in self.crates:
1350 crate.dump()
1351 dumped_libs = set()
1352 for lib in self.ar_objects:
1353 if lib.pkg == self.root_pkg:
1354 lib_name = file_base_name(lib.lib)
1355 if lib_name not in dumped_libs:
1356 dumped_libs.add(lib_name)
1357 lib.dump()
1358 if self.args.dependencies and self.dependencies:
1359 self.dump_dependencies()
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001360 if self.errors:
Joel Galenson3f42f802021-04-07 12:42:17 -07001361 self.append_to_bp('\n' + ERRORS_LINE + '\n' + self.errors)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001362 return self
1363
1364 def add_ar_object(self, obj):
1365 self.ar_objects.append(obj)
1366
1367 def add_cc_object(self, obj):
1368 self.cc_objects.append(obj)
1369
1370 def add_crate(self, crate):
1371 """Merge crate with someone in crates, or append to it. Return crates."""
1372 if crate.skip_crate():
1373 if self.args.debug: # include debug info of all crates
1374 self.crates.append(crate)
1375 if self.args.dependencies: # include only dependent crates
1376 if (is_dependent_file_path(crate.main_src) and
1377 not is_build_crate_name(crate.crate_name)):
1378 self.dependencies.append(crate)
1379 else:
1380 for c in self.crates:
1381 if c.merge(crate, 'Android.bp'):
1382 return
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001383 # If not merged, decide module type and name now.
1384 crate.decide_module_type()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001385 self.crates.append(crate)
1386
1387 def find_warning_owners(self):
1388 """For each warning file, find its owner crate."""
1389 missing_owner = False
1390 for f in self.warning_files:
1391 cargo_dir = '' # find lowest crate, with longest path
1392 owner = None # owner crate of this warning
1393 for c in self.crates:
1394 if (f.startswith(c.cargo_dir + '/') and
1395 len(cargo_dir) < len(c.cargo_dir)):
1396 cargo_dir = c.cargo_dir
1397 owner = c
1398 if owner:
1399 owner.has_warning = True
1400 else:
1401 missing_owner = True
1402 if missing_owner and os.path.exists('Cargo.toml'):
1403 # owner is the root cargo, with empty cargo_dir
1404 for c in self.crates:
1405 if not c.cargo_dir:
1406 c.has_warning = True
1407
1408 def rustc_command(self, n, rustc_line, line, outf_name):
1409 """Process a rustc command line from cargo -vv output."""
1410 # cargo build -vv output can have multiple lines for a rustc command
1411 # due to '\n' in strings for environment variables.
1412 # strip removes leading spaces and '\n' at the end
1413 new_rustc = (rustc_line.strip() + line) if rustc_line else line
1414 # Use an heuristic to detect the completions of a multi-line command.
1415 # This might fail for some very rare case, but easy to fix manually.
1416 if not line.endswith('`\n') or (new_rustc.count('`') % 2) != 0:
1417 return new_rustc
1418 if RUSTC_VV_CMD_ARGS.match(new_rustc):
1419 args = RUSTC_VV_CMD_ARGS.match(new_rustc).group(1)
1420 self.add_crate(Crate(self, outf_name).parse(n, args))
1421 else:
1422 self.assert_empty_vv_line(new_rustc)
1423 return ''
1424
1425 def cc_ar_command(self, n, groups, outf_name):
1426 pkg = groups.group(1)
1427 line = groups.group(3)
1428 if groups.group(2) == 'cc':
1429 self.add_cc_object(CCObject(self, outf_name).parse(pkg, n, line))
1430 else:
1431 self.add_ar_object(ARObject(self, outf_name).parse(pkg, n, line))
1432
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001433 def append_to_bp(self, line):
1434 self.init_bp_file('Android.bp')
1435 with open('Android.bp', 'a') as outf:
1436 outf.write(line)
1437
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001438 def assert_empty_vv_line(self, line):
1439 if line: # report error if line is not empty
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001440 self.append_to_bp('ERROR -vv line: ' + line)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001441 return ''
1442
1443 def parse(self, inf, outf_name):
1444 """Parse rustc and warning messages in inf, return a list of Crates."""
1445 n = 0 # line number
1446 prev_warning = False # true if the previous line was warning: ...
1447 rustc_line = '' # previous line(s) matching RUSTC_VV_PAT
1448 for line in inf:
1449 n += 1
1450 if line.startswith('warning: '):
1451 prev_warning = True
1452 rustc_line = self.assert_empty_vv_line(rustc_line)
1453 continue
1454 new_rustc = ''
1455 if RUSTC_PAT.match(line):
1456 args_line = RUSTC_PAT.match(line).group(1)
1457 self.add_crate(Crate(self, outf_name).parse(n, args_line))
1458 self.assert_empty_vv_line(rustc_line)
1459 elif rustc_line or RUSTC_VV_PAT.match(line):
1460 new_rustc = self.rustc_command(n, rustc_line, line, outf_name)
1461 elif CC_AR_VV_PAT.match(line):
1462 self.cc_ar_command(n, CC_AR_VV_PAT.match(line), outf_name)
1463 elif prev_warning and WARNING_FILE_PAT.match(line):
1464 self.assert_empty_vv_line(rustc_line)
1465 fpath = WARNING_FILE_PAT.match(line).group(1)
1466 if fpath[0] != '/': # ignore absolute path
1467 self.warning_files.add(fpath)
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001468 elif line.startswith('error: ') or line.startswith('error[E'):
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001469 if not self.args.ignore_cargo_errors:
1470 self.errors += line
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001471 prev_warning = False
1472 rustc_line = new_rustc
1473 self.find_warning_owners()
1474
1475
1476def parse_args():
1477 """Parse main arguments."""
1478 parser = argparse.ArgumentParser('cargo2android')
1479 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001480 '--add_workspace',
1481 action='store_true',
1482 default=False,
1483 help=('append [workspace] to Cargo.toml before calling cargo,' +
1484 ' to treat current directory as root of package source;' +
1485 ' otherwise the relative source file path in generated' +
1486 ' .bp file will be from the parent directory.'))
1487 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001488 '--cargo',
1489 action='append',
1490 metavar='args_string',
1491 help=('extra cargo build -v args in a string, ' +
1492 'each --cargo flag calls cargo build -v once'))
1493 parser.add_argument(
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001494 '--cargo_bin',
1495 type=str,
1496 help='use cargo in the cargo_bin directory instead of the prebuilt one')
1497 parser.add_argument(
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001498 '--copy-out',
1499 action='store_true',
1500 default=False,
1501 help=('only for root directory, ' +
1502 'copy build.rs output to ./out/* and add a genrule to copy ' +
1503 './out/* to genrule output; for crates with code pattern: ' +
1504 'include!(concat!(env!("OUT_DIR"), "/<some_file>.rs"))'))
1505 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001506 '--debug',
1507 action='store_true',
1508 default=False,
1509 help='dump debug info into Android.bp')
1510 parser.add_argument(
1511 '--dependencies',
1512 action='store_true',
1513 default=False,
1514 help='dump debug info of dependent crates')
1515 parser.add_argument(
1516 '--device',
1517 action='store_true',
1518 default=False,
1519 help='run cargo also for a default device target')
1520 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001521 '--features',
1522 type=str,
1523 help=('pass features to cargo build, ' +
1524 'empty string means no default features'))
1525 parser.add_argument(
1526 '--global_defaults',
1527 type=str,
1528 help='add a defaults name to every module')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001529 parser.add_argument(
1530 '--host-first-multilib',
1531 action='store_true',
1532 default=False,
1533 help=('add a compile_multilib:"first" property ' +
1534 'to Android.bp host modules.'))
1535 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001536 '--ignore-cargo-errors',
1537 action='store_true',
1538 default=False,
1539 help='do not append cargo/rustc error messages to Android.bp')
1540 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001541 '--no-host',
1542 action='store_true',
1543 default=False,
1544 help='do not run cargo for the host; only for the device target')
1545 parser.add_argument(
1546 '--no-subdir',
1547 action='store_true',
1548 default=False,
1549 help='do not output anything for sub-directories')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001550 parser.add_argument(
1551 '--onefile',
1552 action='store_true',
1553 default=False,
1554 help=('output all into one ./Android.bp, default will generate ' +
1555 'one Android.bp per Cargo.toml in subdirectories'))
1556 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001557 '--patch',
1558 type=str,
1559 help='apply the given patch file to generated ./Android.bp')
1560 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001561 '--run',
1562 action='store_true',
1563 default=False,
1564 help='run it, default is dry-run')
1565 parser.add_argument('--rustflags', type=str, help='passing flags to rustc')
1566 parser.add_argument(
1567 '--skipcargo',
1568 action='store_true',
1569 default=False,
1570 help='skip cargo command, parse cargo.out, and generate Android.bp')
1571 parser.add_argument(
1572 '--tests',
1573 action='store_true',
1574 default=False,
1575 help='run cargo build --tests after normal build')
1576 parser.add_argument(
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001577 '--use-cargo-lock',
1578 action='store_true',
1579 default=False,
1580 help=('run cargo build with existing Cargo.lock ' +
1581 '(used when some latest dependent crates failed)'))
1582 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001583 '--verbose',
1584 action='store_true',
1585 default=False,
1586 help='echo executed commands')
1587 parser.add_argument(
1588 '--vv',
1589 action='store_true',
1590 default=False,
1591 help='run cargo with -vv instead of default -v')
1592 return parser.parse_args()
1593
1594
1595def main():
1596 args = parse_args()
1597 if not args.run: # default is dry-run
1598 print(DRY_RUN_NOTE)
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001599 Runner(args).run_cargo().gen_bp().apply_patch().dump_test_mapping_files()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001600
1601
1602if __name__ == '__main__':
1603 main()