blob: ecdb4513a8c180c62bb81b7f3136283b1f017639 [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
Joel Galenson0fbdafe2021-04-21 16:33:33 -070054import json
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080055import os
56import os.path
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -070057import platform
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080058import re
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -070059import shutil
Andrew Walbran80e90be2020-06-09 14:33:18 +010060import sys
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080061
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -070062# Some Rust packages include extra unwanted crates.
63# This set contains all such excluded crate names.
64EXCLUDED_CRATES = set(['protobuf_bin_gen_rust_do_not_use'])
65
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080066RENAME_MAP = {
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -070067 # This map includes all changes to the default rust module names
68 # to resolve name conflicts, avoid confusion, or work as plugin.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080069 'libbacktrace': 'libbacktrace_rust',
Andrew Walbrane51f1042020-08-11 16:42:48 +010070 'libbase': 'libbase_rust',
Victor Hsieh21bea792020-12-04 10:59:16 -080071 'libfuse': 'libfuse_rust',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080072 'libgcc': 'libgcc_rust',
73 'liblog': 'liblog_rust',
Chih-Hung Hsieh07119862020-07-24 15:34:06 -070074 'libminijail': 'libminijail_rust',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080075 'libsync': 'libsync_rust',
76 'libx86_64': 'libx86_64_rust',
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -070077 'protoc_gen_rust': 'protoc-gen-rust',
78}
79
80RENAME_STEM_MAP = {
81 # This map includes all changes to the default rust module stem names,
82 # which is used for output files when different from the module name.
83 'protoc_gen_rust': 'protoc-gen-rust',
84}
85
86RENAME_DEFAULTS_MAP = {
87 # This map includes all changes to the default prefix of rust_default
88 # module names, to avoid conflict with existing Android modules.
89 'libc': 'rust_libc',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080090}
91
92# Header added to all generated Android.bp files.
Joel Galenson56446742021-02-18 08:27:48 -080093ANDROID_BP_HEADER = (
94 '// This file is generated by cargo2android.py {args}.\n' +
95 '// Do not modify this file as changes will be overridden on upgrade.\n')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080096
97CARGO_OUT = 'cargo.out' # Name of file to keep cargo build -v output.
98
Joel Galenson3f42f802021-04-07 12:42:17 -070099# This should be kept in sync with tools/external_updater/crates_updater.py.
100ERRORS_LINE = 'Errors in ' + CARGO_OUT + ':'
101
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800102TARGET_TMP = 'target.tmp' # Name of temporary output directory.
103
104# Message to be displayed when this script is called without the --run flag.
105DRY_RUN_NOTE = (
106 'Dry-run: This script uses ./' + TARGET_TMP + ' for output directory,\n' +
107 'runs cargo clean, runs cargo build -v, saves output to ./cargo.out,\n' +
108 'and writes to Android.bp in the current and subdirectories.\n\n' +
109 'To do do all of the above, use the --run flag.\n' +
110 'See --help for other flags, and more usage notes in this script.\n')
111
112# Cargo -v output of a call to rustc.
113RUSTC_PAT = re.compile('^ +Running `rustc (.*)`$')
114
115# Cargo -vv output of a call to rustc could be split into multiple lines.
116# Assume that the first line will contain some CARGO_* env definition.
117RUSTC_VV_PAT = re.compile('^ +Running `.*CARGO_.*=.*$')
118# The combined -vv output rustc command line pattern.
119RUSTC_VV_CMD_ARGS = re.compile('^ *Running `.*CARGO_.*=.* rustc (.*)`$')
120
121# Cargo -vv output of a "cc" or "ar" command; all in one line.
122CC_AR_VV_PAT = re.compile(r'^\[([^ ]*)[^\]]*\] running:? "(cc|ar)" (.*)$')
123# Some package, such as ring-0.13.5, has pattern '... running "cc"'.
124
125# Rustc output of file location path pattern for a warning message.
126WARNING_FILE_PAT = re.compile('^ *--> ([^:]*):[0-9]+')
127
128# Rust package name with suffix -d1.d2.d3.
129VERSION_SUFFIX_PAT = re.compile(r'^(.*)-[0-9]+\.[0-9]+\.[0-9]+$')
130
131
132def altered_name(name):
133 return RENAME_MAP[name] if (name in RENAME_MAP) else name
134
135
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700136def altered_stem(name):
137 return RENAME_STEM_MAP[name] if (name in RENAME_STEM_MAP) else name
138
139
140def altered_defaults(name):
141 return RENAME_DEFAULTS_MAP[name] if (name in RENAME_DEFAULTS_MAP) else name
142
143
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800144def is_build_crate_name(name):
145 # We added special prefix to build script crate names.
146 return name.startswith('build_script_')
147
148
149def is_dependent_file_path(path):
150 # Absolute or dependent '.../' paths are not main files of this crate.
151 return path.startswith('/') or path.startswith('.../')
152
153
154def get_module_name(crate): # to sort crates in a list
155 return crate.module_name
156
157
158def pkg2crate_name(s):
159 return s.replace('-', '_').replace('.', '_')
160
161
162def file_base_name(path):
163 return os.path.splitext(os.path.basename(path))[0]
164
165
166def test_base_name(path):
167 return pkg2crate_name(file_base_name(path))
168
169
170def unquote(s): # remove quotes around str
171 if s and len(s) > 1 and s[0] == '"' and s[-1] == '"':
172 return s[1:-1]
173 return s
174
175
176def remove_version_suffix(s): # remove -d1.d2.d3 suffix
177 if VERSION_SUFFIX_PAT.match(s):
178 return VERSION_SUFFIX_PAT.match(s).group(1)
179 return s
180
181
182def short_out_name(pkg, s): # replace /.../pkg-*/out/* with .../out/*
183 return re.sub('^/.*/' + pkg + '-[0-9a-f]*/out/', '.../out/', s)
184
185
186def escape_quotes(s): # replace '"' with '\\"'
187 return s.replace('"', '\\"')
188
189
190class Crate(object):
191 """Information of a Rust crate to collect/emit for an Android.bp module."""
192
193 def __init__(self, runner, outf_name):
194 # Remembered global runner and its members.
195 self.runner = runner
196 self.debug = runner.args.debug
197 self.cargo_dir = '' # directory of my Cargo.toml
198 self.outf_name = outf_name # path to Android.bp
199 self.outf = None # open file handle of outf_name during dump*
200 # Variants/results that could be merged from multiple rustc lines.
201 self.host_supported = False
202 self.device_supported = False
203 self.has_warning = False
204 # Android module properties derived from rustc parameters.
205 self.module_name = '' # unique in Android build system
206 self.module_type = '' # rust_{binary,library,test}[_host] etc.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700207 self.defaults = '' # rust_defaults used by rust_test* modules
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700208 self.default_srcs = False # use 'srcs' defined in self.defaults
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800209 self.root_pkg = '' # parent package name of a sub/test packge, from -L
210 self.srcs = list() # main_src or merged multiple source files
211 self.stem = '' # real base name of output file
212 # Kept parsed status
213 self.errors = '' # all errors found during parsing
214 self.line_num = 1 # runner told input source line number
215 self.line = '' # original rustc command line parameters
216 # Parameters collected from rustc command line.
217 self.crate_name = '' # follows --crate-name
218 self.main_src = '' # follows crate_name parameter, shortened
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700219 self.crate_types = list() # follows --crate-type
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800220 self.cfgs = list() # follows --cfg, without feature= prefix
221 self.features = list() # follows --cfg, name in 'feature="..."'
222 self.codegens = list() # follows -C, some ignored
223 self.externs = list() # follows --extern
224 self.core_externs = list() # first part of self.externs elements
225 self.static_libs = list() # e.g. -l static=host_cpuid
226 self.shared_libs = list() # e.g. -l dylib=wayland-client, -l z
227 self.cap_lints = '' # follows --cap-lints
228 self.emit_list = '' # e.g., --emit=dep-info,metadata,link
229 self.edition = '2015' # rustc default, e.g., --edition=2018
230 self.target = '' # follows --target
231
232 def write(self, s):
233 # convenient way to output one line at a time with EOL.
234 self.outf.write(s + '\n')
235
236 def same_flags(self, other):
237 # host_supported, device_supported, has_warning are not compared but merged
238 # target is not compared, to merge different target/host modules
239 # externs is not compared; only core_externs is compared
240 return (not self.errors and not other.errors and
241 self.edition == other.edition and
242 self.cap_lints == other.cap_lints and
243 self.emit_list == other.emit_list and
244 self.core_externs == other.core_externs and
245 self.codegens == other.codegens and
246 self.features == other.features and
247 self.static_libs == other.static_libs and
248 self.shared_libs == other.shared_libs and self.cfgs == other.cfgs)
249
250 def merge_host_device(self, other):
251 """Returns true if attributes are the same except host/device support."""
252 return (self.crate_name == other.crate_name and
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700253 self.crate_types == other.crate_types and
254 self.main_src == other.main_src and
255 # before merge, each test module has an unique module name and stem
256 (self.stem == other.stem or self.crate_types == ['test']) and
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800257 self.root_pkg == other.root_pkg and not self.skip_crate() and
258 self.same_flags(other))
259
260 def merge_test(self, other):
261 """Returns true if self and other are tests of same root_pkg."""
262 # Before merger, each test has its own crate_name.
263 # A merged test uses its source file base name as output file name,
264 # so a test is mergeable only if its base name equals to its crate name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700265 return (self.crate_types == other.crate_types and
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700266 self.crate_types == ['test'] and self.root_pkg == other.root_pkg and
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800267 not self.skip_crate() and
268 other.crate_name == test_base_name(other.main_src) and
269 (len(self.srcs) > 1 or
270 (self.crate_name == test_base_name(self.main_src)) and
271 self.host_supported == other.host_supported and
272 self.device_supported == other.device_supported) and
273 self.same_flags(other))
274
275 def merge(self, other, outf_name):
276 """Try to merge crate into self."""
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700277 # Cargo build --tests could recompile a library for tests.
278 # We need to merge such duplicated calls to rustc, with
279 # the algorithm in merge_host_device.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800280 should_merge_host_device = self.merge_host_device(other)
281 should_merge_test = False
282 if not should_merge_host_device:
283 should_merge_test = self.merge_test(other)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800284 if should_merge_host_device or should_merge_test:
285 self.runner.init_bp_file(outf_name)
286 with open(outf_name, 'a') as outf: # to write debug info
287 self.outf = outf
288 other.outf = outf
289 self.do_merge(other, should_merge_test)
290 return True
291 return False
292
293 def do_merge(self, other, should_merge_test):
294 """Merge attributes of other to self."""
295 if self.debug:
296 self.write('\n// Before merge definition (1):')
297 self.dump_debug_info()
298 self.write('\n// Before merge definition (2):')
299 other.dump_debug_info()
300 # Merge properties of other to self.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800301 self.has_warning = self.has_warning or other.has_warning
302 if not self.target: # okay to keep only the first target triple
303 self.target = other.target
304 # decide_module_type sets up default self.stem,
305 # which can be changed if self is a merged test module.
306 self.decide_module_type()
307 if should_merge_test:
308 self.srcs.append(other.main_src)
309 # use a short unique name as the merged module name.
310 prefix = self.root_pkg + '_tests'
311 self.module_name = self.runner.claim_module_name(prefix, self, 0)
312 self.stem = self.module_name
313 # This normalized root_pkg name although might be the same
314 # as other module's crate_name, it is not actually used for
315 # output file name. A merged test module always have multiple
316 # source files and each source file base name is used as
317 # its output file name.
318 self.crate_name = pkg2crate_name(self.root_pkg)
319 if self.debug:
320 self.write('\n// After merge definition (1):')
321 self.dump_debug_info()
322
323 def find_cargo_dir(self):
324 """Deepest directory with Cargo.toml and contains the main_src."""
325 if not is_dependent_file_path(self.main_src):
326 dir_name = os.path.dirname(self.main_src)
327 while dir_name:
328 if os.path.exists(dir_name + '/Cargo.toml'):
329 self.cargo_dir = dir_name
330 return
331 dir_name = os.path.dirname(dir_name)
332
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700333 def add_codegens_flag(self, flag):
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700334 """Ignore options not used in Android."""
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700335 # 'prefer-dynamic' does not work with common flag -C lto
Chih-Hung Hsieh63459ed2020-08-26 11:51:15 -0700336 # 'embed-bitcode' is ignored; we might control LTO with other .bp flag
Chih-Hung Hsieh6c13b722020-09-11 21:24:03 -0700337 # 'codegen-units' is set in Android global config or by default
338 if not (flag.startswith('codegen-units=') or
339 flag.startswith('debuginfo=') or
Chih-Hung Hsieh63459ed2020-08-26 11:51:15 -0700340 flag.startswith('embed-bitcode=') or
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700341 flag.startswith('extra-filename=') or
342 flag.startswith('incremental=') or
343 flag.startswith('metadata=') or
344 flag == 'prefer-dynamic'):
345 self.codegens.append(flag)
346
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800347 def parse(self, line_num, line):
348 """Find important rustc arguments to convert to Android.bp properties."""
349 self.line_num = line_num
350 self.line = line
351 args = line.split() # Loop through every argument of rustc.
352 i = 0
353 while i < len(args):
354 arg = args[i]
355 if arg == '--crate-name':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700356 i += 1
357 self.crate_name = args[i]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800358 elif arg == '--crate-type':
359 i += 1
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700360 # cargo calls rustc with multiple --crate-type flags.
361 # rustc can accept:
362 # --crate-type [bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]
363 self.crate_types.append(args[i])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800364 elif arg == '--test':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700365 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800366 elif arg == '--target':
367 i += 1
368 self.target = args[i]
369 elif arg == '--cfg':
370 i += 1
371 if args[i].startswith('\'feature='):
372 self.features.append(unquote(args[i].replace('\'feature=', '')[:-1]))
373 else:
374 self.cfgs.append(args[i])
375 elif arg == '--extern':
376 i += 1
377 extern_names = re.sub('=/[^ ]*/deps/', ' = ', args[i])
378 self.externs.append(extern_names)
379 self.core_externs.append(re.sub(' = .*', '', extern_names))
380 elif arg == '-C': # codegen options
381 i += 1
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700382 self.add_codegens_flag(args[i])
383 elif arg.startswith('-C'):
384 # cargo has been passing "-C <xyz>" flag to rustc,
385 # but newer cargo could pass '-Cembed-bitcode=no' to rustc.
386 self.add_codegens_flag(arg[2:])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800387 elif arg == '--cap-lints':
388 i += 1
389 self.cap_lints = args[i]
390 elif arg == '-L':
391 i += 1
392 if args[i].startswith('dependency=') and args[i].endswith('/deps'):
393 if '/' + TARGET_TMP + '/' in args[i]:
394 self.root_pkg = re.sub(
395 '^.*/', '', re.sub('/' + TARGET_TMP + '/.*/deps$', '', args[i]))
396 else:
397 self.root_pkg = re.sub('^.*/', '',
398 re.sub('/[^/]+/[^/]+/deps$', '', args[i]))
399 self.root_pkg = remove_version_suffix(self.root_pkg)
400 elif arg == '-l':
401 i += 1
402 if args[i].startswith('static='):
403 self.static_libs.append(re.sub('static=', '', args[i]))
404 elif args[i].startswith('dylib='):
405 self.shared_libs.append(re.sub('dylib=', '', args[i]))
406 else:
407 self.shared_libs.append(args[i])
408 elif arg == '--out-dir' or arg == '--color': # ignored
409 i += 1
410 elif arg.startswith('--error-format=') or arg.startswith('--json='):
411 _ = arg # ignored
412 elif arg.startswith('--emit='):
413 self.emit_list = arg.replace('--emit=', '')
414 elif arg.startswith('--edition='):
415 self.edition = arg.replace('--edition=', '')
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700416 elif not arg.startswith('-'):
417 # shorten imported crate main source paths like $HOME/.cargo/
418 # registry/src/github.com-1ecc6299db9ec823/memchr-2.3.3/src/lib.rs
419 self.main_src = re.sub(r'^/[^ ]*/registry/src/', '.../', args[i])
420 self.main_src = re.sub(r'^\.\.\./github.com-[0-9a-f]*/', '.../',
421 self.main_src)
422 self.find_cargo_dir()
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700423 if self.cargo_dir: # for a subdirectory
424 if self.runner.args.no_subdir: # all .bp content to /dev/null
425 self.outf_name = '/dev/null'
426 elif not self.runner.args.onefile:
427 # Write to Android.bp in the subdirectory with Cargo.toml.
428 self.outf_name = self.cargo_dir + '/Android.bp'
429 self.main_src = self.main_src[len(self.cargo_dir) + 1:]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800430 else:
431 self.errors += 'ERROR: unknown ' + arg + '\n'
432 i += 1
433 if not self.crate_name:
434 self.errors += 'ERROR: missing --crate-name\n'
435 if not self.main_src:
436 self.errors += 'ERROR: missing main source file\n'
437 else:
438 self.srcs.append(self.main_src)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700439 if not self.crate_types:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800440 # Treat "--cfg test" as "--test"
441 if 'test' in self.cfgs:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700442 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800443 else:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700444 self.errors += 'ERROR: missing --crate-type or --test\n'
445 elif len(self.crate_types) > 1:
446 if 'test' in self.crate_types:
447 self.errors += 'ERROR: cannot handle both --crate-type and --test\n'
448 if 'lib' in self.crate_types and 'rlib' in self.crate_types:
449 self.errors += 'ERROR: cannot generate both lib and rlib crate types\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800450 if not self.root_pkg:
451 self.root_pkg = self.crate_name
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700452 self.device_supported = self.runner.args.device
453 self.host_supported = not self.runner.args.no_host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800454 self.cfgs = sorted(set(self.cfgs))
455 self.features = sorted(set(self.features))
456 self.codegens = sorted(set(self.codegens))
457 self.externs = sorted(set(self.externs))
458 self.core_externs = sorted(set(self.core_externs))
459 self.static_libs = sorted(set(self.static_libs))
460 self.shared_libs = sorted(set(self.shared_libs))
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700461 self.crate_types = sorted(set(self.crate_types))
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800462 self.decide_module_type()
463 self.module_name = altered_name(self.stem)
464 return self
465
466 def dump_line(self):
467 self.write('\n// Line ' + str(self.line_num) + ' ' + self.line)
468
469 def feature_list(self):
470 """Return a string of main_src + "feature_list"."""
471 pkg = self.main_src
472 if pkg.startswith('.../'): # keep only the main package name
473 pkg = re.sub('/.*', '', pkg[4:])
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700474 elif pkg.startswith('/'): # use relative path for a local package
475 pkg = os.path.relpath(pkg)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800476 if not self.features:
477 return pkg
478 return pkg + ' "' + ','.join(self.features) + '"'
479
480 def dump_skip_crate(self, kind):
481 if self.debug:
482 self.write('\n// IGNORED: ' + kind + ' ' + self.main_src)
483 return self
484
485 def skip_crate(self):
486 """Return crate_name or a message if this crate should be skipped."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700487 if (is_build_crate_name(self.crate_name) or
488 self.crate_name in EXCLUDED_CRATES):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800489 return self.crate_name
490 if is_dependent_file_path(self.main_src):
491 return 'dependent crate'
492 return ''
493
494 def dump(self):
495 """Dump all error/debug/module code to the output .bp file."""
496 self.runner.init_bp_file(self.outf_name)
497 with open(self.outf_name, 'a') as outf:
498 self.outf = outf
499 if self.errors:
500 self.dump_line()
501 self.write(self.errors)
502 elif self.skip_crate():
503 self.dump_skip_crate(self.skip_crate())
504 else:
505 if self.debug:
506 self.dump_debug_info()
507 self.dump_android_module()
508
509 def dump_debug_info(self):
510 """Dump parsed data, when cargo2android is called with --debug."""
511
512 def dump(name, value):
513 self.write('//%12s = %s' % (name, value))
514
515 def opt_dump(name, value):
516 if value:
517 dump(name, value)
518
519 def dump_list(fmt, values):
520 for v in values:
521 self.write(fmt % v)
522
523 self.dump_line()
524 dump('module_name', self.module_name)
525 dump('crate_name', self.crate_name)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700526 dump('crate_types', self.crate_types)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800527 dump('main_src', self.main_src)
528 dump('has_warning', self.has_warning)
529 dump('for_host', self.host_supported)
530 dump('for_device', self.device_supported)
531 dump('module_type', self.module_type)
532 opt_dump('target', self.target)
533 opt_dump('edition', self.edition)
534 opt_dump('emit_list', self.emit_list)
535 opt_dump('cap_lints', self.cap_lints)
536 dump_list('// cfg = %s', self.cfgs)
537 dump_list('// cfg = \'feature "%s"\'', self.features)
538 # TODO(chh): escape quotes in self.features, but not in other dump_list
539 dump_list('// codegen = %s', self.codegens)
540 dump_list('// externs = %s', self.externs)
541 dump_list('// -l static = %s', self.static_libs)
542 dump_list('// -l (dylib) = %s', self.shared_libs)
543
544 def dump_android_module(self):
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700545 """Dump one or more Android module definition, depending on crate_types."""
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700546 if len(self.crate_types) == 1:
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700547 self.dump_single_type_android_module()
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700548 return
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700549 if 'test' in self.crate_types:
550 self.write('\nERROR: multiple crate types cannot include test type')
551 return
552 # Dump one Android module per crate_type.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700553 for crate_type in self.crate_types:
554 self.decide_one_module_type(crate_type)
555 self.dump_one_android_module(crate_type)
556
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700557 def build_default_name(self):
558 """Return a short and readable name for the rust_defaults module."""
559 # Choices: (1) root_pkg + '_defaults',
560 # (2) root_pkg + '_defaults_' + crate_name
561 # (3) root_pkg + '_defaults_' + main_src_basename_path
562 # (4) root_pkg + '_defaults_' + a_positive_sequence_number
563 name1 = altered_defaults(self.root_pkg) + '_defaults'
564 if self.runner.try_claim_module_name(name1, self):
565 return name1
566 name2 = name1 + '_' + self.crate_name
567 if self.runner.try_claim_module_name(name2, self):
568 return name2
569 name3 = name1 + '_' + self.main_src_basename_path()
570 if self.runner.try_claim_module_name(name3, self):
571 return name3
572 return self.runner.claim_module_name(name1, self, 0)
573
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700574 def dump_srcs_list(self):
575 """Dump the srcs list, for defaults or regular modules."""
576 if len(self.srcs) > 1:
577 srcs = sorted(set(self.srcs)) # make a copy and dedup
578 else:
579 srcs = [self.main_src]
580 copy_out = self.runner.copy_out_module_name()
581 if copy_out:
582 srcs.append(':' + copy_out)
583 self.dump_android_property_list('srcs', '"%s"', srcs)
584
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700585 def dump_defaults_module(self):
586 """Dump a rust_defaults module to be shared by other modules."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700587 name = self.build_default_name()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700588 self.defaults = name
589 self.write('\nrust_defaults {')
590 self.write(' name: "' + name + '",')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700591 if self.runner.args.global_defaults:
592 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700593 self.write(' crate_name: "' + self.crate_name + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700594 if len(self.srcs) == 1: # only one source file; share it in defaults
595 self.default_srcs = True
596 if self.has_warning and not self.cap_lints:
597 self.write(' // has rustc warnings')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700598 self.dump_srcs_list()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700599 if 'test' in self.crate_types:
600 self.write(' test_suites: ["general-tests"],')
601 self.write(' auto_gen_config: true,')
602 self.dump_edition_flags_libs()
603 self.write('}')
604
605 def dump_single_type_android_module(self):
606 """Dump one simple Android module, which has only one crate_type."""
607 crate_type = self.crate_types[0]
608 if crate_type != 'test':
609 # do not change self.stem or self.module_name
610 self.dump_one_android_module(crate_type)
611 return
612 # Dump one test module per source file, and separate host and device tests.
613 # crate_type == 'test'
614 if (self.host_supported and self.device_supported) or len(self.srcs) > 1:
615 self.srcs = sorted(set(self.srcs))
616 self.dump_defaults_module()
617 saved_srcs = self.srcs
618 for src in saved_srcs:
619 self.srcs = [src]
620 saved_device_supported = self.device_supported
621 saved_host_supported = self.host_supported
622 saved_main_src = self.main_src
623 self.main_src = src
624 if saved_host_supported:
625 self.device_supported = False
626 self.host_supported = True
627 self.module_name = self.test_module_name()
628 self.decide_one_module_type(crate_type)
629 self.dump_one_android_module(crate_type)
630 if saved_device_supported:
631 self.device_supported = True
632 self.host_supported = False
633 self.module_name = self.test_module_name()
634 self.decide_one_module_type(crate_type)
635 self.dump_one_android_module(crate_type)
636 self.host_supported = saved_host_supported
637 self.device_supported = saved_device_supported
638 self.main_src = saved_main_src
639 self.srcs = saved_srcs
640
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700641 def dump_one_android_module(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800642 """Dump one Android module definition."""
643 if not self.module_type:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700644 self.write('\nERROR: unknown crate_type ' + crate_type)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800645 return
646 self.write('\n' + self.module_type + ' {')
647 self.dump_android_core_properties()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700648 if not self.defaults:
649 self.dump_edition_flags_libs()
650 if self.runner.args.host_first_multilib and self.host_supported and crate_type != 'test':
651 self.write(' compile_multilib: "first",')
652 self.write('}')
653
654 def dump_android_flags(self):
655 """Dump Android module flags property."""
Thiébaud Weksteena5a728b2021-04-08 14:23:49 +0200656 if not self.codegens and not self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700657 return
658 self.write(' flags: [')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800659 if self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700660 self.write(' "--cap-lints ' + self.cap_lints + '",')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700661 codegens_fmt = '"-C %s"'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700662 self.dump_android_property_list_items(codegens_fmt, self.codegens)
663 self.write(' ],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700664
665 def dump_edition_flags_libs(self):
666 if self.edition:
667 self.write(' edition: "' + self.edition + '",')
668 self.dump_android_property_list('features', '"%s"', self.features)
Thiébaud Weksteena5a728b2021-04-08 14:23:49 +0200669 self.dump_android_property_list('cfgs', '"%s"', self.cfgs)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700670 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()
Joel Galensond9d13b82021-04-05 11:27:55 -07001137 # Read all lines until we see a rust_* or genrule rule.
1138 while line != '' and not (line.startswith('rust_') or line.startswith('genrule {')):
Haibo Huang0f72c952021-03-19 11:34:15 -07001139 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')
Joel Galensoncf4ebb02021-04-07 08:39:51 -07001178 elif self.args.no_test_mapping:
1179 print('Skipping generation of TEST_MAPPING')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001180 else:
Jeff Vander Stoep1b24dc32021-02-03 18:52:42 +01001181 test_mapping = TestMapping(None)
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +01001182 for bp_file_name in self.bp_files:
1183 test_mapping.create_test_mapping(os.path.dirname(bp_file_name))
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -07001184 return self
1185
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001186 def try_claim_module_name(self, name, owner):
1187 """Reserve and return True if it has not been reserved yet."""
1188 if name not in self.name_owners or owner == self.name_owners[name]:
1189 self.name_owners[name] = owner
1190 return True
1191 return False
1192
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001193 def claim_module_name(self, prefix, owner, counter):
1194 """Return prefix if not owned yet, otherwise, prefix+str(counter)."""
1195 while True:
1196 name = prefix
1197 if counter > 0:
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001198 name += '_' + str(counter)
1199 if self.try_claim_module_name(name, owner):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001200 return name
1201 counter += 1
1202
1203 def find_root_pkg(self):
1204 """Read name of [package] in ./Cargo.toml."""
1205 if not os.path.exists('./Cargo.toml'):
1206 return
1207 with open('./Cargo.toml', 'r') as inf:
1208 pkg_section = re.compile(r'^ *\[package\]')
1209 name = re.compile('^ *name *= * "([^"]*)"')
1210 in_pkg = False
1211 for line in inf:
1212 if in_pkg:
1213 if name.match(line):
1214 self.root_pkg = name.match(line).group(1)
1215 break
1216 else:
1217 in_pkg = pkg_section.match(line) is not None
1218
1219 def run_cargo(self):
1220 """Calls cargo -v and save its output to ./cargo.out."""
1221 if self.skip_cargo:
1222 return self
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001223 cargo_toml = './Cargo.toml'
1224 cargo_out = './cargo.out'
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001225 # Do not use Cargo.lock, because .bp rules are designed to
1226 # run with "latest" crates avaialable on Android.
1227 cargo_lock = './Cargo.lock'
1228 cargo_lock_saved = './cargo.lock.saved'
1229 had_cargo_lock = os.path.exists(cargo_lock)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001230 if not os.access(cargo_toml, os.R_OK):
1231 print('ERROR: Cannot find or read', cargo_toml)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001232 return self
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001233 if not self.dry_run:
1234 if os.path.exists(cargo_out):
1235 os.remove(cargo_out)
1236 if not self.args.use_cargo_lock and had_cargo_lock: # save it
1237 os.rename(cargo_lock, cargo_lock_saved)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001238 cmd_tail = ' --target-dir ' + TARGET_TMP + ' >> ' + cargo_out + ' 2>&1'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001239 # set up search PATH for cargo to find the correct rustc
1240 saved_path = os.environ['PATH']
1241 os.environ['PATH'] = os.path.dirname(self.cargo_path) + ':' + saved_path
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001242 # Add [workspace] to Cargo.toml if it is not there.
1243 added_workspace = False
1244 if self.args.add_workspace:
1245 with open(cargo_toml, 'r') as in_file:
1246 cargo_toml_lines = in_file.readlines()
1247 found_workspace = '[workspace]\n' in cargo_toml_lines
1248 if found_workspace:
1249 print('### WARNING: found [workspace] in Cargo.toml')
1250 else:
1251 with open(cargo_toml, 'a') as out_file:
1252 out_file.write('[workspace]\n')
1253 added_workspace = True
1254 if self.args.verbose:
1255 print('### INFO: added [workspace] to Cargo.toml')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001256 for c in self.cargo:
1257 features = ''
Chih-Hung Hsieh6c8d52f2020-03-30 18:28:52 -07001258 if c != 'clean':
1259 if self.args.features is not None:
1260 features = ' --no-default-features'
1261 if self.args.features:
1262 features += ' --features ' + self.args.features
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001263 cmd_v_flag = ' -vv ' if self.args.vv else ' -v '
1264 cmd = self.cargo_path + cmd_v_flag
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001265 cmd += c + features + cmd_tail
1266 if self.args.rustflags and c != 'clean':
1267 cmd = 'RUSTFLAGS="' + self.args.rustflags + '" ' + cmd
1268 if self.dry_run:
1269 print('Dry-run skip:', cmd)
1270 else:
1271 if self.args.verbose:
1272 print('Running:', cmd)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001273 with open(cargo_out, 'a') as out_file:
1274 out_file.write('### Running: ' + cmd + '\n')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001275 os.system(cmd)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001276 if added_workspace: # restore original Cargo.toml
1277 with open(cargo_toml, 'w') as out_file:
1278 out_file.writelines(cargo_toml_lines)
1279 if self.args.verbose:
1280 print('### INFO: restored original Cargo.toml')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001281 os.environ['PATH'] = saved_path
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001282 if not self.dry_run:
1283 if not had_cargo_lock: # restore to no Cargo.lock state
1284 os.remove(cargo_lock)
1285 elif not self.args.use_cargo_lock: # restore saved Cargo.lock
1286 os.rename(cargo_lock_saved, cargo_lock)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001287 return self
1288
1289 def dump_dependencies(self):
1290 """Append dependencies and their features to Android.bp."""
1291 if not self.dependencies:
1292 return
1293 dependent_list = list()
1294 for c in self.dependencies:
1295 dependent_list.append(c.feature_list())
1296 sorted_dependencies = sorted(set(dependent_list))
1297 self.init_bp_file('Android.bp')
1298 with open('Android.bp', 'a') as outf:
1299 outf.write('\n// dependent_library ["feature_list"]\n')
1300 for s in sorted_dependencies:
1301 outf.write('// ' + s + '\n')
1302
1303 def dump_pkg_obj2cc(self):
1304 """Dump debug info of the pkg_obj2cc map."""
1305 if not self.args.debug:
1306 return
1307 self.init_bp_file('Android.bp')
1308 with open('Android.bp', 'a') as outf:
1309 sorted_pkgs = sorted(self.pkg_obj2cc.keys())
1310 for pkg in sorted_pkgs:
1311 if not self.pkg_obj2cc[pkg]:
1312 continue
1313 outf.write('\n// obj => src for %s\n' % pkg)
1314 obj2cc = self.pkg_obj2cc[pkg]
1315 for obj in sorted(obj2cc.keys()):
1316 outf.write('// ' + short_out_name(pkg, obj) + ' => ' +
1317 short_out_name(pkg, obj2cc[obj].src) + '\n')
1318
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001319 def apply_patch(self):
1320 """Apply local patch file if it is given."""
1321 if self.args.patch:
1322 if self.dry_run:
1323 print('Dry-run skip patch file:', self.args.patch)
1324 else:
1325 if not os.path.exists(self.args.patch):
1326 self.append_to_bp('ERROR cannot find patch file: ' + self.args.patch)
1327 return self
1328 if self.args.verbose:
1329 print('### INFO: applying local patch file:', self.args.patch)
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001330 os.system('patch -s --no-backup-if-mismatch ./Android.bp ' +
1331 self.args.patch)
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001332 return self
1333
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001334 def gen_bp(self):
1335 """Parse cargo.out and generate Android.bp files."""
1336 if self.dry_run:
1337 print('Dry-run skip: read', CARGO_OUT, 'write Android.bp')
1338 elif os.path.exists(CARGO_OUT):
1339 self.find_root_pkg()
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001340 if self.args.copy_out:
1341 self.copy_out_files()
1342 elif self.find_out_files() and self.has_used_out_dir():
1343 print('WARNING: ' + self.root_pkg + ' has cargo output files; ' +
1344 'please rerun with the --copy-out flag.')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001345 with open(CARGO_OUT, 'r') as cargo_out:
1346 self.parse(cargo_out, 'Android.bp')
1347 self.crates.sort(key=get_module_name)
1348 for obj in self.cc_objects:
1349 obj.dump()
1350 self.dump_pkg_obj2cc()
1351 for crate in self.crates:
1352 crate.dump()
1353 dumped_libs = set()
1354 for lib in self.ar_objects:
1355 if lib.pkg == self.root_pkg:
1356 lib_name = file_base_name(lib.lib)
1357 if lib_name not in dumped_libs:
1358 dumped_libs.add(lib_name)
1359 lib.dump()
1360 if self.args.dependencies and self.dependencies:
1361 self.dump_dependencies()
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001362 if self.errors:
Joel Galenson3f42f802021-04-07 12:42:17 -07001363 self.append_to_bp('\n' + ERRORS_LINE + '\n' + self.errors)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001364 return self
1365
1366 def add_ar_object(self, obj):
1367 self.ar_objects.append(obj)
1368
1369 def add_cc_object(self, obj):
1370 self.cc_objects.append(obj)
1371
1372 def add_crate(self, crate):
1373 """Merge crate with someone in crates, or append to it. Return crates."""
1374 if crate.skip_crate():
1375 if self.args.debug: # include debug info of all crates
1376 self.crates.append(crate)
1377 if self.args.dependencies: # include only dependent crates
1378 if (is_dependent_file_path(crate.main_src) and
1379 not is_build_crate_name(crate.crate_name)):
1380 self.dependencies.append(crate)
1381 else:
1382 for c in self.crates:
1383 if c.merge(crate, 'Android.bp'):
1384 return
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001385 # If not merged, decide module type and name now.
1386 crate.decide_module_type()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001387 self.crates.append(crate)
1388
1389 def find_warning_owners(self):
1390 """For each warning file, find its owner crate."""
1391 missing_owner = False
1392 for f in self.warning_files:
1393 cargo_dir = '' # find lowest crate, with longest path
1394 owner = None # owner crate of this warning
1395 for c in self.crates:
1396 if (f.startswith(c.cargo_dir + '/') and
1397 len(cargo_dir) < len(c.cargo_dir)):
1398 cargo_dir = c.cargo_dir
1399 owner = c
1400 if owner:
1401 owner.has_warning = True
1402 else:
1403 missing_owner = True
1404 if missing_owner and os.path.exists('Cargo.toml'):
1405 # owner is the root cargo, with empty cargo_dir
1406 for c in self.crates:
1407 if not c.cargo_dir:
1408 c.has_warning = True
1409
1410 def rustc_command(self, n, rustc_line, line, outf_name):
1411 """Process a rustc command line from cargo -vv output."""
1412 # cargo build -vv output can have multiple lines for a rustc command
1413 # due to '\n' in strings for environment variables.
1414 # strip removes leading spaces and '\n' at the end
1415 new_rustc = (rustc_line.strip() + line) if rustc_line else line
1416 # Use an heuristic to detect the completions of a multi-line command.
1417 # This might fail for some very rare case, but easy to fix manually.
1418 if not line.endswith('`\n') or (new_rustc.count('`') % 2) != 0:
1419 return new_rustc
1420 if RUSTC_VV_CMD_ARGS.match(new_rustc):
1421 args = RUSTC_VV_CMD_ARGS.match(new_rustc).group(1)
1422 self.add_crate(Crate(self, outf_name).parse(n, args))
1423 else:
1424 self.assert_empty_vv_line(new_rustc)
1425 return ''
1426
1427 def cc_ar_command(self, n, groups, outf_name):
1428 pkg = groups.group(1)
1429 line = groups.group(3)
1430 if groups.group(2) == 'cc':
1431 self.add_cc_object(CCObject(self, outf_name).parse(pkg, n, line))
1432 else:
1433 self.add_ar_object(ARObject(self, outf_name).parse(pkg, n, line))
1434
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001435 def append_to_bp(self, line):
1436 self.init_bp_file('Android.bp')
1437 with open('Android.bp', 'a') as outf:
1438 outf.write(line)
1439
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001440 def assert_empty_vv_line(self, line):
1441 if line: # report error if line is not empty
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001442 self.append_to_bp('ERROR -vv line: ' + line)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001443 return ''
1444
1445 def parse(self, inf, outf_name):
1446 """Parse rustc and warning messages in inf, return a list of Crates."""
1447 n = 0 # line number
1448 prev_warning = False # true if the previous line was warning: ...
1449 rustc_line = '' # previous line(s) matching RUSTC_VV_PAT
1450 for line in inf:
1451 n += 1
1452 if line.startswith('warning: '):
1453 prev_warning = True
1454 rustc_line = self.assert_empty_vv_line(rustc_line)
1455 continue
1456 new_rustc = ''
1457 if RUSTC_PAT.match(line):
1458 args_line = RUSTC_PAT.match(line).group(1)
1459 self.add_crate(Crate(self, outf_name).parse(n, args_line))
1460 self.assert_empty_vv_line(rustc_line)
1461 elif rustc_line or RUSTC_VV_PAT.match(line):
1462 new_rustc = self.rustc_command(n, rustc_line, line, outf_name)
1463 elif CC_AR_VV_PAT.match(line):
1464 self.cc_ar_command(n, CC_AR_VV_PAT.match(line), outf_name)
1465 elif prev_warning and WARNING_FILE_PAT.match(line):
1466 self.assert_empty_vv_line(rustc_line)
1467 fpath = WARNING_FILE_PAT.match(line).group(1)
1468 if fpath[0] != '/': # ignore absolute path
1469 self.warning_files.add(fpath)
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001470 elif line.startswith('error: ') or line.startswith('error[E'):
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001471 if not self.args.ignore_cargo_errors:
1472 self.errors += line
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001473 prev_warning = False
1474 rustc_line = new_rustc
1475 self.find_warning_owners()
1476
1477
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001478def get_parser():
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001479 """Parse main arguments."""
1480 parser = argparse.ArgumentParser('cargo2android')
1481 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001482 '--add_workspace',
1483 action='store_true',
1484 default=False,
1485 help=('append [workspace] to Cargo.toml before calling cargo,' +
1486 ' to treat current directory as root of package source;' +
1487 ' otherwise the relative source file path in generated' +
1488 ' .bp file will be from the parent directory.'))
1489 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001490 '--cargo',
1491 action='append',
1492 metavar='args_string',
1493 help=('extra cargo build -v args in a string, ' +
1494 'each --cargo flag calls cargo build -v once'))
1495 parser.add_argument(
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001496 '--cargo_bin',
1497 type=str,
1498 help='use cargo in the cargo_bin directory instead of the prebuilt one')
1499 parser.add_argument(
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001500 '--copy-out',
1501 action='store_true',
1502 default=False,
1503 help=('only for root directory, ' +
1504 'copy build.rs output to ./out/* and add a genrule to copy ' +
1505 './out/* to genrule output; for crates with code pattern: ' +
1506 'include!(concat!(env!("OUT_DIR"), "/<some_file>.rs"))'))
1507 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001508 '--debug',
1509 action='store_true',
1510 default=False,
1511 help='dump debug info into Android.bp')
1512 parser.add_argument(
1513 '--dependencies',
1514 action='store_true',
1515 default=False,
1516 help='dump debug info of dependent crates')
1517 parser.add_argument(
1518 '--device',
1519 action='store_true',
1520 default=False,
1521 help='run cargo also for a default device target')
1522 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001523 '--features',
1524 type=str,
1525 help=('pass features to cargo build, ' +
1526 'empty string means no default features'))
1527 parser.add_argument(
1528 '--global_defaults',
1529 type=str,
1530 help='add a defaults name to every module')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001531 parser.add_argument(
1532 '--host-first-multilib',
1533 action='store_true',
1534 default=False,
1535 help=('add a compile_multilib:"first" property ' +
1536 'to Android.bp host modules.'))
1537 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001538 '--ignore-cargo-errors',
1539 action='store_true',
1540 default=False,
1541 help='do not append cargo/rustc error messages to Android.bp')
1542 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001543 '--no-host',
1544 action='store_true',
1545 default=False,
1546 help='do not run cargo for the host; only for the device target')
1547 parser.add_argument(
1548 '--no-subdir',
1549 action='store_true',
1550 default=False,
1551 help='do not output anything for sub-directories')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001552 parser.add_argument(
1553 '--onefile',
1554 action='store_true',
1555 default=False,
1556 help=('output all into one ./Android.bp, default will generate ' +
1557 'one Android.bp per Cargo.toml in subdirectories'))
1558 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001559 '--patch',
1560 type=str,
1561 help='apply the given patch file to generated ./Android.bp')
1562 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001563 '--run',
1564 action='store_true',
1565 default=False,
1566 help='run it, default is dry-run')
1567 parser.add_argument('--rustflags', type=str, help='passing flags to rustc')
1568 parser.add_argument(
1569 '--skipcargo',
1570 action='store_true',
1571 default=False,
1572 help='skip cargo command, parse cargo.out, and generate Android.bp')
1573 parser.add_argument(
1574 '--tests',
1575 action='store_true',
1576 default=False,
1577 help='run cargo build --tests after normal build')
1578 parser.add_argument(
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001579 '--use-cargo-lock',
1580 action='store_true',
1581 default=False,
1582 help=('run cargo build with existing Cargo.lock ' +
1583 '(used when some latest dependent crates failed)'))
1584 parser.add_argument(
Joel Galensoncf4ebb02021-04-07 08:39:51 -07001585 '--no-test-mapping',
1586 action='store_true',
1587 default=False,
1588 help='Do not generate a TEST_MAPPING file. Use only to speed up debugging.')
1589 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001590 '--verbose',
1591 action='store_true',
1592 default=False,
1593 help='echo executed commands')
1594 parser.add_argument(
1595 '--vv',
1596 action='store_true',
1597 default=False,
1598 help='run cargo with -vv instead of default -v')
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001599 parser.add_argument(
1600 '--dump-config-and-exit',
1601 type=str,
1602 help=('Dump command-line arguments (minus this flag) to a config file and exit. ' +
1603 'This is intended to help migrate from command line options to config files.'))
1604 parser.add_argument(
1605 '--config',
1606 type=str,
1607 help=('Load command-line options from the given config file. ' +
1608 'Options in this file will override those passed on the command line.'))
1609 return parser
1610
1611
1612def parse_args(parser):
1613 """Parses command-line options."""
1614 args = parser.parse_args()
1615 # Use the values specified in a config file if one was found.
1616 if args.config:
1617 with open(args.config, 'r') as f:
1618 config = json.load(f)
1619 args_dict = vars(args)
1620 for arg in config:
1621 args_dict[arg.replace('-', '_')] = config[arg]
1622 return args
1623
1624
1625def dump_config(parser, args):
1626 """Writes the non-default command-line options to the specified file."""
1627 args_dict = vars(args)
1628 # Filter out the arguments that have their default value.
1629 non_default_args = {}
1630 for arg in args_dict:
1631 if args_dict[arg] != parser.get_default(arg) and arg != 'dump_config_and_exit':
1632 non_default_args[arg.replace('_', '-')] = args_dict[arg]
1633 # Write to the specified file.
1634 with open(args.dump_config_and_exit, 'w') as f:
1635 json.dump(non_default_args, f, indent=2, sort_keys=True)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001636
1637
1638def main():
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001639 parser = get_parser()
1640 args = parse_args(parser)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001641 if not args.run: # default is dry-run
1642 print(DRY_RUN_NOTE)
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001643 if args.dump_config_and_exit:
1644 dump_config(parser, args)
1645 else:
1646 Runner(args).run_cargo().gen_bp().apply_patch().dump_test_mapping_files()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001647
1648
1649if __name__ == '__main__':
1650 main()