blob: 766759111fe7c44fce4a9f43f5997336493f49c0 [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
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -070042If there are rustc warning messages, this script will add
43a warning comment to the owner crate module in Android.bp.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080044"""
45
46from __future__ import print_function
47
48import argparse
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -070049import glob
Joel Galenson0fbdafe2021-04-21 16:33:33 -070050import json
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080051import os
52import os.path
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -070053import platform
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080054import re
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -070055import shutil
Joel Galenson7e8247e2021-05-20 18:51:42 -070056import subprocess
Andrew Walbran80e90be2020-06-09 14:33:18 +010057import sys
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080058
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -070059# Some Rust packages include extra unwanted crates.
60# This set contains all such excluded crate names.
61EXCLUDED_CRATES = set(['protobuf_bin_gen_rust_do_not_use'])
62
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080063RENAME_MAP = {
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -070064 # This map includes all changes to the default rust module names
65 # to resolve name conflicts, avoid confusion, or work as plugin.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080066 'libbacktrace': 'libbacktrace_rust',
Andrew Walbrane51f1042020-08-11 16:42:48 +010067 'libbase': 'libbase_rust',
Luke Huanga1371af2021-06-29 18:04:40 +080068 'libbase64': 'libbase64_rust',
Victor Hsieh21bea792020-12-04 10:59:16 -080069 'libfuse': 'libfuse_rust',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080070 'libgcc': 'libgcc_rust',
71 'liblog': 'liblog_rust',
Chih-Hung Hsieh07119862020-07-24 15:34:06 -070072 'libminijail': 'libminijail_rust',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080073 'libsync': 'libsync_rust',
74 'libx86_64': 'libx86_64_rust',
Jooyung Hana427c9b2021-07-16 08:53:14 +090075 'libxml': 'libxml_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."""
Joel Galensond37d7e62021-07-13 09:03:01 -0700558 # Choices: (1) root_pkg + '_test'? + '_defaults',
559 # (2) root_pkg + '_test'? + '_defaults_' + crate_name
560 # (3) root_pkg + '_test'? + '_defaults_' + main_src_basename_path
561 # (4) root_pkg + '_test'? + '_defaults_' + a_positive_sequence_number
562 test = "_test" if self.crate_types == ['test'] else ""
563 name1 = altered_defaults(self.root_pkg) + test + '_defaults'
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700564 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'
Joel Galensonf6b3c912021-06-03 16:00:54 -0700614 self.srcs = [src for src in self.srcs if not src in self.runner.args.test_blocklist]
615 if ((self.host_supported and self.device_supported and len(self.srcs) > 0) or
616 len(self.srcs) > 1):
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700617 self.srcs = sorted(set(self.srcs))
618 self.dump_defaults_module()
619 saved_srcs = self.srcs
620 for src in saved_srcs:
621 self.srcs = [src]
622 saved_device_supported = self.device_supported
623 saved_host_supported = self.host_supported
624 saved_main_src = self.main_src
625 self.main_src = src
626 if saved_host_supported:
627 self.device_supported = False
628 self.host_supported = True
629 self.module_name = self.test_module_name()
630 self.decide_one_module_type(crate_type)
631 self.dump_one_android_module(crate_type)
632 if saved_device_supported:
633 self.device_supported = True
634 self.host_supported = False
635 self.module_name = self.test_module_name()
636 self.decide_one_module_type(crate_type)
637 self.dump_one_android_module(crate_type)
638 self.host_supported = saved_host_supported
639 self.device_supported = saved_device_supported
640 self.main_src = saved_main_src
641 self.srcs = saved_srcs
642
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700643 def dump_one_android_module(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800644 """Dump one Android module definition."""
645 if not self.module_type:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700646 self.write('\nERROR: unknown crate_type ' + crate_type)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800647 return
648 self.write('\n' + self.module_type + ' {')
649 self.dump_android_core_properties()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700650 if not self.defaults:
651 self.dump_edition_flags_libs()
652 if self.runner.args.host_first_multilib and self.host_supported and crate_type != 'test':
653 self.write(' compile_multilib: "first",')
Joel Galensond9c4de62021-04-23 10:26:40 -0700654 if self.runner.args.apex_available and crate_type == 'lib':
655 self.write(' apex_available: [')
656 for apex in self.runner.args.apex_available:
657 self.write(' "%s",' % apex)
658 self.write(' ],')
659 if self.runner.args.min_sdk_version and crate_type == 'lib':
660 self.write(' min_sdk_version: "%s",' % self.runner.args.min_sdk_version)
Joel Galenson5664f2a2021-06-10 10:13:49 -0700661 if self.runner.args.add_module_block:
662 with open(self.runner.args.add_module_block, 'r') as f:
663 self.write(' %s,' % f.read().replace('\n', '\n '))
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700664 self.write('}')
665
666 def dump_android_flags(self):
667 """Dump Android module flags property."""
ThiƩbaud Weksteena5a728b2021-04-08 14:23:49 +0200668 if not self.codegens and not self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700669 return
670 self.write(' flags: [')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800671 if self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700672 self.write(' "--cap-lints ' + self.cap_lints + '",')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700673 codegens_fmt = '"-C %s"'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700674 self.dump_android_property_list_items(codegens_fmt, self.codegens)
675 self.write(' ],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700676
677 def dump_edition_flags_libs(self):
678 if self.edition:
679 self.write(' edition: "' + self.edition + '",')
680 self.dump_android_property_list('features', '"%s"', self.features)
Joel Galenson3d6d1e72021-06-07 15:00:24 -0700681 cfgs = [cfg for cfg in self.cfgs if not cfg in self.runner.args.cfg_blocklist]
682 self.dump_android_property_list('cfgs', '"%s"', cfgs)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700683 self.dump_android_flags()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800684 if self.externs:
685 self.dump_android_externs()
Joel Galenson12467e52021-07-12 14:33:28 -0700686 all_static_libs = [lib for lib in self.static_libs if not lib in self.runner.args.lib_blocklist]
687 static_libs = [lib for lib in all_static_libs if not lib in self.runner.args.whole_static_libs]
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700688 self.dump_android_property_list('static_libs', '"lib%s"', static_libs)
Joel Galenson12467e52021-07-12 14:33:28 -0700689 whole_static_libs = [lib for lib in all_static_libs if lib in self.runner.args.whole_static_libs]
690 self.dump_android_property_list('whole_static_libs', '"lib%s"', whole_static_libs)
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700691 shared_libs = [lib for lib in self.shared_libs if not lib in self.runner.args.lib_blocklist]
692 self.dump_android_property_list('shared_libs', '"lib%s"', shared_libs)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800693
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700694 def main_src_basename_path(self):
695 return re.sub('/', '_', re.sub('.rs$', '', self.main_src))
696
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800697 def test_module_name(self):
698 """Return a unique name for a test module."""
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700699 # root_pkg+(_host|_device) + '_test_'+source_file_name
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700700 suffix = self.main_src_basename_path()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700701 host_device = '_host'
702 if self.device_supported:
703 host_device = '_device'
704 return self.root_pkg + host_device + '_test_' + suffix
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800705
706 def decide_module_type(self):
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700707 # Use the first crate type for the default/first module.
708 crate_type = self.crate_types[0] if self.crate_types else ''
709 self.decide_one_module_type(crate_type)
710
711 def decide_one_module_type(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800712 """Decide which Android module type to use."""
713 host = '' if self.device_supported else '_host'
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700714 rlib = '_rlib' if self.runner.args.force_rlib else ''
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700715 if crate_type == 'bin': # rust_binary[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800716 self.module_type = 'rust_binary' + host
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700717 # In rare cases like protobuf-codegen, the output binary name must
718 # be renamed to use as a plugin for protoc.
719 self.stem = altered_stem(self.crate_name)
720 self.module_name = altered_name(self.crate_name)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700721 elif crate_type == 'lib': # rust_library[_host]
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700722 # TODO(chh): should this be rust_library[_host]?
723 # Assuming that Cargo.toml do not use both 'lib' and 'rlib',
724 # because we map them both to rlib.
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700725 self.module_type = 'rust_library' + rlib + host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800726 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700727 self.module_name = altered_name(self.stem)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700728 elif crate_type == 'rlib': # rust_library[_host]
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700729 self.module_type = 'rust_library' + rlib + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700730 self.stem = 'lib' + self.crate_name
731 self.module_name = altered_name(self.stem)
732 elif crate_type == 'dylib': # rust_library[_host]_dylib
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800733 self.module_type = 'rust_library' + host + '_dylib'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700734 self.stem = 'lib' + self.crate_name
735 self.module_name = altered_name(self.stem) + '_dylib'
736 elif crate_type == 'cdylib': # rust_library[_host]_shared
Ivan Lozano0c057ad2020-12-15 10:41:26 -0500737 self.module_type = 'rust_ffi' + host + '_shared'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700738 self.stem = 'lib' + self.crate_name
739 self.module_name = altered_name(self.stem) + '_shared'
740 elif crate_type == 'staticlib': # rust_library[_host]_static
Ivan Lozano0c057ad2020-12-15 10:41:26 -0500741 self.module_type = 'rust_ffi' + host + '_static'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700742 self.stem = 'lib' + self.crate_name
743 self.module_name = altered_name(self.stem) + '_static'
744 elif crate_type == 'test': # rust_test[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800745 self.module_type = 'rust_test' + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700746 # Before do_merge, stem name is based on the --crate-name parameter.
747 # and test module name is based on stem.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800748 self.stem = self.test_module_name()
749 # self.stem will be changed after merging with other tests.
750 # self.stem is NOT used for final test binary name.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700751 # rust_test uses each source file base name as part of output file name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700752 # In do_merge, this function is called again, with a module_name.
753 # We make sure that the module name is unique in each package.
754 if self.module_name:
755 # Cargo uses "-C extra-filename=..." and "-C metadata=..." to add
756 # different suffixes and distinguish multiple tests of the same
757 # crate name. We ignore -C and use claim_module_name to get
758 # unique sequential suffix.
759 self.module_name = self.runner.claim_module_name(
760 self.module_name, self, 0)
761 # Now the module name is unique, stem should also match and unique.
762 self.stem = self.module_name
763 elif crate_type == 'proc-macro': # rust_proc_macro
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800764 self.module_type = 'rust_proc_macro'
765 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700766 self.module_name = altered_name(self.stem)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800767 else: # unknown module type, rust_prebuilt_dylib? rust_library[_host]?
768 self.module_type = ''
769 self.stem = ''
770
771 def dump_android_property_list_items(self, fmt, values):
772 for v in values:
773 # fmt has quotes, so we need escape_quotes(v)
774 self.write(' ' + (fmt % escape_quotes(v)) + ',')
775
776 def dump_android_property_list(self, name, fmt, values):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700777 if not values:
778 return
779 if len(values) > 1:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800780 self.write(' ' + name + ': [')
781 self.dump_android_property_list_items(fmt, values)
782 self.write(' ],')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700783 else:
784 self.write(' ' + name + ': [' +
785 (fmt % escape_quotes(values[0])) + '],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800786
787 def dump_android_core_properties(self):
788 """Dump the module header, name, stem, etc."""
789 self.write(' name: "' + self.module_name + '",')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700790 # see properties shared by dump_defaults_module
791 if self.defaults:
792 self.write(' defaults: ["' + self.defaults + '"],')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700793 elif self.runner.args.global_defaults:
794 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800795 if self.stem != self.module_name:
796 self.write(' stem: "' + self.stem + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700797 if self.has_warning and not self.cap_lints and not self.default_srcs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700798 self.write(' // has rustc warnings')
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700799 if self.host_supported and self.device_supported and self.module_type != 'rust_proc_macro':
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800800 self.write(' host_supported: true,')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700801 if not self.defaults:
802 self.write(' crate_name: "' + self.crate_name + '",')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700803 if not self.default_srcs:
804 self.dump_srcs_list()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700805 if 'test' in self.crate_types and not self.defaults:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800806 # self.root_pkg can have multiple test modules, with different *_tests[n]
807 # names, but their executables can all be installed under the same _tests
808 # directory. When built from Cargo.toml, all tests should have different
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700809 # file or crate names. So we used (root_pkg + '_tests') name as the
810 # relative_install_path.
811 # However, some package like 'slab' can have non-mergeable tests that
812 # must be separated by different module names. So, here we no longer
813 # emit relative_install_path.
814 # self.write(' relative_install_path: "' + self.root_pkg + '_tests",')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800815 self.write(' test_suites: ["general-tests"],')
816 self.write(' auto_gen_config: true,')
Joel Galensone261a152021-01-12 11:31:53 -0800817 if 'test' in self.crate_types and self.host_supported:
818 self.write(' test_options: {')
819 self.write(' unit_test: true,')
820 self.write(' },')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800821
822 def dump_android_externs(self):
823 """Dump the dependent rlibs and dylibs property."""
824 so_libs = list()
825 rust_libs = ''
826 deps_libname = re.compile('^.* = lib(.*)-[0-9a-f]*.(rlib|so|rmeta)$')
827 for lib in self.externs:
828 # normal value of lib: "libc = liblibc-*.rlib"
829 # strange case in rand crate: "getrandom_package = libgetrandom-*.rlib"
830 # we should use "libgetrandom", not "lib" + "getrandom_package"
831 groups = deps_libname.match(lib)
832 if groups is not None:
833 lib_name = groups.group(1)
834 else:
835 lib_name = re.sub(' .*$', '', lib)
Joel Galenson97e414a2021-05-27 09:42:32 -0700836 if lib_name in self.runner.args.dependency_blocklist:
837 continue
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800838 if lib.endswith('.rlib') or lib.endswith('.rmeta'):
839 # On MacOS .rmeta is used when Linux uses .rlib or .rmeta.
840 rust_libs += ' "' + altered_name('lib' + lib_name) + '",\n'
841 elif lib.endswith('.so'):
842 so_libs.append(lib_name)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700843 elif lib != 'proc_macro': # --extern proc_macro is special and ignored
844 rust_libs += ' // ERROR: unknown type of lib ' + lib + '\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800845 if rust_libs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700846 self.write(' rustlibs: [\n' + rust_libs + ' ],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800847 # Are all dependent .so files proc_macros?
848 # TODO(chh): Separate proc_macros and dylib.
849 self.dump_android_property_list('proc_macros', '"lib%s"', so_libs)
850
851
852class ARObject(object):
853 """Information of an "ar" link command."""
854
855 def __init__(self, runner, outf_name):
856 # Remembered global runner and its members.
857 self.runner = runner
858 self.pkg = ''
859 self.outf_name = outf_name # path to Android.bp
860 # "ar" arguments
861 self.line_num = 1
862 self.line = ''
863 self.flags = '' # e.g. "crs"
864 self.lib = '' # e.g. "/.../out/lib*.a"
865 self.objs = list() # e.g. "/.../out/.../*.o"
866
867 def parse(self, pkg, line_num, args_line):
868 """Collect ar obj/lib file names."""
869 self.pkg = pkg
870 self.line_num = line_num
871 self.line = args_line
872 args = args_line.split()
873 num_args = len(args)
874 if num_args < 3:
875 print('ERROR: "ar" command has too few arguments', args_line)
876 else:
877 self.flags = unquote(args[0])
878 self.lib = unquote(args[1])
879 self.objs = sorted(set(map(unquote, args[2:])))
880 return self
881
882 def write(self, s):
883 self.outf.write(s + '\n')
884
885 def dump_debug_info(self):
886 self.write('\n// Line ' + str(self.line_num) + ' "ar" ' + self.line)
887 self.write('// ar_object for %12s' % self.pkg)
888 self.write('// flags = %s' % self.flags)
889 self.write('// lib = %s' % short_out_name(self.pkg, self.lib))
890 for o in self.objs:
891 self.write('// obj = %s' % short_out_name(self.pkg, o))
892
893 def dump_android_lib(self):
894 """Write cc_library_static into Android.bp."""
895 self.write('\ncc_library_static {')
896 self.write(' name: "' + file_base_name(self.lib) + '",')
897 self.write(' host_supported: true,')
898 if self.flags != 'crs':
899 self.write(' // ar flags = %s' % self.flags)
900 if self.pkg not in self.runner.pkg_obj2cc:
901 self.write(' ERROR: cannot find source files.\n}')
902 return
903 self.write(' srcs: [')
904 obj2cc = self.runner.pkg_obj2cc[self.pkg]
905 # Note: wflags are ignored.
906 dflags = list()
907 fflags = list()
908 for obj in self.objs:
909 self.write(' "' + short_out_name(self.pkg, obj2cc[obj].src) + '",')
910 # TODO(chh): union of dflags and flags of all obj
911 # Now, just a temporary hack that uses the last obj's flags
912 dflags = obj2cc[obj].dflags
913 fflags = obj2cc[obj].fflags
914 self.write(' ],')
915 self.write(' cflags: [')
916 self.write(' "-O3",') # TODO(chh): is this default correct?
917 self.write(' "-Wno-error",')
918 for x in fflags:
919 self.write(' "-f' + x + '",')
920 for x in dflags:
921 self.write(' "-D' + x + '",')
922 self.write(' ],')
923 self.write('}')
924
925 def dump(self):
926 """Dump error/debug/module info to the output .bp file."""
927 self.runner.init_bp_file(self.outf_name)
928 with open(self.outf_name, 'a') as outf:
929 self.outf = outf
930 if self.runner.args.debug:
931 self.dump_debug_info()
932 self.dump_android_lib()
933
934
935class CCObject(object):
936 """Information of a "cc" compilation command."""
937
938 def __init__(self, runner, outf_name):
939 # Remembered global runner and its members.
940 self.runner = runner
941 self.pkg = ''
942 self.outf_name = outf_name # path to Android.bp
943 # "cc" arguments
944 self.line_num = 1
945 self.line = ''
946 self.src = ''
947 self.obj = ''
948 self.dflags = list() # -D flags
949 self.fflags = list() # -f flags
950 self.iflags = list() # -I flags
951 self.wflags = list() # -W flags
952 self.other_args = list()
953
954 def parse(self, pkg, line_num, args_line):
955 """Collect cc compilation flags and src/out file names."""
956 self.pkg = pkg
957 self.line_num = line_num
958 self.line = args_line
959 args = args_line.split()
960 i = 0
961 while i < len(args):
962 arg = args[i]
963 if arg == '"-c"':
964 i += 1
965 if args[i].startswith('"-o'):
966 # ring-0.13.5 dumps: ... "-c" "-o/.../*.o" ".../*.c"
967 self.obj = unquote(args[i])[2:]
968 i += 1
969 self.src = unquote(args[i])
970 else:
971 self.src = unquote(args[i])
972 elif arg == '"-o"':
973 i += 1
974 self.obj = unquote(args[i])
975 elif arg == '"-I"':
976 i += 1
977 self.iflags.append(unquote(args[i]))
978 elif arg.startswith('"-D'):
979 self.dflags.append(unquote(args[i])[2:])
980 elif arg.startswith('"-f'):
981 self.fflags.append(unquote(args[i])[2:])
982 elif arg.startswith('"-W'):
983 self.wflags.append(unquote(args[i])[2:])
984 elif not (arg.startswith('"-O') or arg == '"-m64"' or arg == '"-g"' or
985 arg == '"-g3"'):
986 # ignore -O -m64 -g
987 self.other_args.append(unquote(args[i]))
988 i += 1
989 self.dflags = sorted(set(self.dflags))
990 self.fflags = sorted(set(self.fflags))
991 # self.wflags is not sorted because some are order sensitive
992 # and we ignore them anyway.
993 if self.pkg not in self.runner.pkg_obj2cc:
994 self.runner.pkg_obj2cc[self.pkg] = {}
995 self.runner.pkg_obj2cc[self.pkg][self.obj] = self
996 return self
997
998 def write(self, s):
999 self.outf.write(s + '\n')
1000
1001 def dump_debug_flags(self, name, flags):
1002 self.write('// ' + name + ':')
1003 for f in flags:
1004 self.write('// %s' % f)
1005
1006 def dump(self):
1007 """Dump only error/debug info to the output .bp file."""
1008 if not self.runner.args.debug:
1009 return
1010 self.runner.init_bp_file(self.outf_name)
1011 with open(self.outf_name, 'a') as outf:
1012 self.outf = outf
1013 self.write('\n// Line ' + str(self.line_num) + ' "cc" ' + self.line)
1014 self.write('// cc_object for %12s' % self.pkg)
1015 self.write('// src = %s' % short_out_name(self.pkg, self.src))
1016 self.write('// obj = %s' % short_out_name(self.pkg, self.obj))
1017 self.dump_debug_flags('-I flags', self.iflags)
1018 self.dump_debug_flags('-D flags', self.dflags)
1019 self.dump_debug_flags('-f flags', self.fflags)
1020 self.dump_debug_flags('-W flags', self.wflags)
1021 if self.other_args:
1022 self.dump_debug_flags('other args', self.other_args)
1023
1024
1025class Runner(object):
1026 """Main class to parse cargo -v output and print Android module definitions."""
1027
1028 def __init__(self, args):
1029 self.bp_files = set() # Remember all output Android.bp files.
1030 self.root_pkg = '' # name of package in ./Cargo.toml
1031 # Saved flags, modes, and data.
1032 self.args = args
1033 self.dry_run = not args.run
1034 self.skip_cargo = args.skipcargo
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001035 self.cargo_path = './cargo' # path to cargo, will be set later
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001036 self.checked_out_files = False # to check only once
1037 self.build_out_files = [] # output files generated by build.rs
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001038 # All cc/ar objects, crates, dependencies, and warning files
1039 self.cc_objects = list()
1040 self.pkg_obj2cc = {}
1041 # pkg_obj2cc[cc_object[i].pkg][cc_objects[i].obj] = cc_objects[i]
1042 self.ar_objects = list()
1043 self.crates = list()
1044 self.dependencies = list() # dependent and build script crates
1045 self.warning_files = set()
1046 # Keep a unique mapping from (module name) to crate
1047 self.name_owners = {}
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001048 # Save and dump all errors from cargo to Android.bp.
1049 self.errors = ''
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001050 self.setup_cargo_path()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001051 # Default action is cargo clean, followed by build or user given actions.
1052 if args.cargo:
1053 self.cargo = ['clean'] + args.cargo
1054 else:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001055 default_target = '--target x86_64-unknown-linux-gnu'
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -07001056 # Use the same target for both host and default device builds.
1057 # Same target is used as default in host x86_64 Android compilation.
1058 # Note: b/169872957, prebuilt cargo failed to build vsock
1059 # on x86_64-unknown-linux-musl systems.
1060 self.cargo = ['clean', 'build ' + default_target]
1061 if args.tests:
1062 self.cargo.append('build --tests ' + default_target)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001063
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001064 def setup_cargo_path(self):
1065 """Find cargo in the --cargo_bin or prebuilt rust bin directory."""
1066 if self.args.cargo_bin:
1067 self.cargo_path = os.path.join(self.args.cargo_bin, 'cargo')
1068 if not os.path.isfile(self.cargo_path):
1069 sys.exit('ERROR: cannot find cargo in ' + self.args.cargo_bin)
1070 print('WARNING: using cargo in ' + self.args.cargo_bin)
1071 return
1072 # We have only tested this on Linux.
1073 if platform.system() != 'Linux':
1074 sys.exit('ERROR: this script has only been tested on Linux with cargo.')
1075 # Assuming that this script is in development/scripts.
1076 my_dir = os.path.dirname(os.path.abspath(__file__))
1077 linux_dir = os.path.join(my_dir, '..', '..',
1078 'prebuilts', 'rust', 'linux-x86')
1079 if not os.path.isdir(linux_dir):
1080 sys.exit('ERROR: cannot find directory ' + linux_dir)
1081 rust_version = self.find_rust_version(my_dir, linux_dir)
1082 cargo_bin = os.path.join(linux_dir, rust_version, 'bin')
1083 self.cargo_path = os.path.join(cargo_bin, 'cargo')
1084 if not os.path.isfile(self.cargo_path):
1085 sys.exit('ERROR: cannot find cargo in ' + cargo_bin
1086 + '; please try --cargo_bin= flag.')
1087 return
1088
1089 def find_rust_version(self, my_dir, linux_dir):
1090 """Use my script directory, find prebuilt rust version."""
1091 # First look up build/soong/rust/config/global.go.
1092 path2global = os.path.join(my_dir, '..', '..',
1093 'build', 'soong', 'rust', 'config', 'global.go')
1094 if os.path.isfile(path2global):
1095 # try to find: RustDefaultVersion = "1.44.0"
1096 version_pat = re.compile(
1097 r'\s*RustDefaultVersion\s*=\s*"([0-9]+\.[0-9]+\.[0-9]+)".*$')
1098 with open(path2global, 'r') as inf:
1099 for line in inf:
1100 result = version_pat.match(line)
1101 if result:
1102 return result.group(1)
1103 print('WARNING: cannot find RustDefaultVersion in ' + path2global)
1104 # Otherwise, find the newest (largest) version number in linux_dir.
1105 rust_version = (0, 0, 0) # the prebuilt version to use
1106 version_pat = re.compile(r'([0-9]+)\.([0-9]+)\.([0-9]+)$')
1107 for dir_name in os.listdir(linux_dir):
1108 result = version_pat.match(dir_name)
1109 if not result:
1110 continue
1111 version = (result.group(1), result.group(2), result.group(3))
1112 if version > rust_version:
1113 rust_version = version
1114 return '.'.join(rust_version)
1115
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001116 def find_out_files(self):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001117 # list1 has build.rs output for normal crates
1118 list1 = glob.glob(TARGET_TMP + '/*/*/build/' + self.root_pkg + '-*/out/*')
1119 # list2 has build.rs output for proc-macro crates
1120 list2 = glob.glob(TARGET_TMP + '/*/build/' + self.root_pkg + '-*/out/*')
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001121 return list1 + list2
1122
1123 def copy_out_files(self):
1124 """Copy build.rs output files to ./out and set up build_out_files."""
1125 if self.checked_out_files:
1126 return
1127 self.checked_out_files = True
1128 cargo_out_files = self.find_out_files()
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001129 out_files = set()
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001130 if cargo_out_files:
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001131 os.makedirs('out', exist_ok=True)
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001132 for path in cargo_out_files:
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001133 file_name = path.split('/')[-1]
1134 out_files.add(file_name)
1135 shutil.copy(path, 'out/' + file_name)
1136 self.build_out_files = sorted(out_files)
1137
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001138 def has_used_out_dir(self):
1139 """Returns true if env!("OUT_DIR") is found."""
1140 return 0 == os.system('grep -rl --exclude build.rs --include \\*.rs' +
1141 ' \'env!("OUT_DIR")\' * > /dev/null')
1142
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001143 def copy_out_module_name(self):
1144 if self.args.copy_out and self.build_out_files:
1145 return 'copy_' + self.root_pkg + '_build_out'
1146 else:
1147 return ''
1148
Haibo Huang0f72c952021-03-19 11:34:15 -07001149 def read_license(self, name):
1150 if not os.path.isfile(name):
1151 return ''
1152 license = ''
1153 with open(name, 'r') as intf:
1154 line = intf.readline()
1155 # Firstly skip ANDROID_BP_HEADER
1156 while line.startswith('//'):
1157 line = intf.readline()
Joel Galensond9d13b82021-04-05 11:27:55 -07001158 # Read all lines until we see a rust_* or genrule rule.
1159 while line != '' and not (line.startswith('rust_') or line.startswith('genrule {')):
Haibo Huang0f72c952021-03-19 11:34:15 -07001160 license += line
1161 line = intf.readline()
1162 return license.strip()
1163
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001164 def dump_copy_out_module(self, outf):
1165 """Output the genrule module to copy out/* to $(genDir)."""
1166 copy_out = self.copy_out_module_name()
1167 if not copy_out:
1168 return
1169 outf.write('\ngenrule {\n')
1170 outf.write(' name: "' + copy_out + '",\n')
1171 outf.write(' srcs: ["out/*"],\n')
1172 outf.write(' cmd: "cp $(in) $(genDir)",\n')
1173 if len(self.build_out_files) > 1:
1174 outf.write(' out: [\n')
1175 for f in self.build_out_files:
1176 outf.write(' "' + f + '",\n')
1177 outf.write(' ],\n')
1178 else:
1179 outf.write(' out: ["' + self.build_out_files[0] + '"],\n')
1180 outf.write('}\n')
1181
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001182 def init_bp_file(self, name):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001183 # name could be Android.bp or sub_dir_path/Android.bp
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001184 if name not in self.bp_files:
1185 self.bp_files.add(name)
Haibo Huang0f72c952021-03-19 11:34:15 -07001186 license_section = self.read_license(name)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001187 with open(name, 'w') as outf:
Joel Galenson367360c2021-04-29 14:31:43 -07001188 print_args = filter(lambda x: x != "--no-test-mapping", sys.argv[1:])
1189 outf.write(ANDROID_BP_HEADER.format(args=' '.join(print_args)))
Haibo Huang0f72c952021-03-19 11:34:15 -07001190 outf.write('\n')
1191 outf.write(license_section)
1192 outf.write('\n')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001193 # at most one copy_out module per .bp file
1194 self.dump_copy_out_module(outf)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001195
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001196 def try_claim_module_name(self, name, owner):
1197 """Reserve and return True if it has not been reserved yet."""
1198 if name not in self.name_owners or owner == self.name_owners[name]:
1199 self.name_owners[name] = owner
1200 return True
1201 return False
1202
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001203 def claim_module_name(self, prefix, owner, counter):
1204 """Return prefix if not owned yet, otherwise, prefix+str(counter)."""
1205 while True:
1206 name = prefix
1207 if counter > 0:
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001208 name += '_' + str(counter)
1209 if self.try_claim_module_name(name, owner):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001210 return name
1211 counter += 1
1212
1213 def find_root_pkg(self):
1214 """Read name of [package] in ./Cargo.toml."""
1215 if not os.path.exists('./Cargo.toml'):
1216 return
1217 with open('./Cargo.toml', 'r') as inf:
1218 pkg_section = re.compile(r'^ *\[package\]')
1219 name = re.compile('^ *name *= * "([^"]*)"')
1220 in_pkg = False
1221 for line in inf:
1222 if in_pkg:
1223 if name.match(line):
1224 self.root_pkg = name.match(line).group(1)
1225 break
1226 else:
1227 in_pkg = pkg_section.match(line) is not None
1228
1229 def run_cargo(self):
1230 """Calls cargo -v and save its output to ./cargo.out."""
1231 if self.skip_cargo:
1232 return self
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001233 cargo_toml = './Cargo.toml'
1234 cargo_out = './cargo.out'
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001235 # Do not use Cargo.lock, because .bp rules are designed to
1236 # run with "latest" crates avaialable on Android.
1237 cargo_lock = './Cargo.lock'
1238 cargo_lock_saved = './cargo.lock.saved'
1239 had_cargo_lock = os.path.exists(cargo_lock)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001240 if not os.access(cargo_toml, os.R_OK):
1241 print('ERROR: Cannot find or read', cargo_toml)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001242 return self
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001243 if not self.dry_run:
1244 if os.path.exists(cargo_out):
1245 os.remove(cargo_out)
1246 if not self.args.use_cargo_lock and had_cargo_lock: # save it
1247 os.rename(cargo_lock, cargo_lock_saved)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001248 cmd_tail = ' --target-dir ' + TARGET_TMP + ' >> ' + cargo_out + ' 2>&1'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001249 # set up search PATH for cargo to find the correct rustc
1250 saved_path = os.environ['PATH']
1251 os.environ['PATH'] = os.path.dirname(self.cargo_path) + ':' + saved_path
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001252 # Add [workspace] to Cargo.toml if it is not there.
1253 added_workspace = False
1254 if self.args.add_workspace:
1255 with open(cargo_toml, 'r') as in_file:
1256 cargo_toml_lines = in_file.readlines()
1257 found_workspace = '[workspace]\n' in cargo_toml_lines
1258 if found_workspace:
1259 print('### WARNING: found [workspace] in Cargo.toml')
1260 else:
1261 with open(cargo_toml, 'a') as out_file:
1262 out_file.write('[workspace]\n')
1263 added_workspace = True
1264 if self.args.verbose:
1265 print('### INFO: added [workspace] to Cargo.toml')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001266 for c in self.cargo:
1267 features = ''
Chih-Hung Hsieh6c8d52f2020-03-30 18:28:52 -07001268 if c != 'clean':
1269 if self.args.features is not None:
1270 features = ' --no-default-features'
1271 if self.args.features:
1272 features += ' --features ' + self.args.features
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001273 cmd_v_flag = ' -vv ' if self.args.vv else ' -v '
1274 cmd = self.cargo_path + cmd_v_flag
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001275 cmd += c + features + cmd_tail
1276 if self.args.rustflags and c != 'clean':
1277 cmd = 'RUSTFLAGS="' + self.args.rustflags + '" ' + cmd
1278 if self.dry_run:
1279 print('Dry-run skip:', cmd)
1280 else:
1281 if self.args.verbose:
1282 print('Running:', cmd)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001283 with open(cargo_out, 'a') as out_file:
1284 out_file.write('### Running: ' + cmd + '\n')
Joel Galenson6bf54e32021-05-17 10:54:50 -07001285 ret = os.system(cmd)
1286 if ret != 0:
1287 print('*** There was an error while running cargo. ' +
1288 'See the cargo.out file for details.')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001289 if added_workspace: # restore original Cargo.toml
1290 with open(cargo_toml, 'w') as out_file:
1291 out_file.writelines(cargo_toml_lines)
1292 if self.args.verbose:
1293 print('### INFO: restored original Cargo.toml')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001294 os.environ['PATH'] = saved_path
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001295 if not self.dry_run:
1296 if not had_cargo_lock: # restore to no Cargo.lock state
1297 os.remove(cargo_lock)
1298 elif not self.args.use_cargo_lock: # restore saved Cargo.lock
1299 os.rename(cargo_lock_saved, cargo_lock)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001300 return self
1301
1302 def dump_dependencies(self):
1303 """Append dependencies and their features to Android.bp."""
1304 if not self.dependencies:
1305 return
1306 dependent_list = list()
1307 for c in self.dependencies:
1308 dependent_list.append(c.feature_list())
1309 sorted_dependencies = sorted(set(dependent_list))
1310 self.init_bp_file('Android.bp')
1311 with open('Android.bp', 'a') as outf:
1312 outf.write('\n// dependent_library ["feature_list"]\n')
1313 for s in sorted_dependencies:
1314 outf.write('// ' + s + '\n')
1315
1316 def dump_pkg_obj2cc(self):
1317 """Dump debug info of the pkg_obj2cc map."""
1318 if not self.args.debug:
1319 return
1320 self.init_bp_file('Android.bp')
1321 with open('Android.bp', 'a') as outf:
1322 sorted_pkgs = sorted(self.pkg_obj2cc.keys())
1323 for pkg in sorted_pkgs:
1324 if not self.pkg_obj2cc[pkg]:
1325 continue
1326 outf.write('\n// obj => src for %s\n' % pkg)
1327 obj2cc = self.pkg_obj2cc[pkg]
1328 for obj in sorted(obj2cc.keys()):
1329 outf.write('// ' + short_out_name(pkg, obj) + ' => ' +
1330 short_out_name(pkg, obj2cc[obj].src) + '\n')
1331
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001332 def apply_patch(self):
1333 """Apply local patch file if it is given."""
1334 if self.args.patch:
1335 if self.dry_run:
1336 print('Dry-run skip patch file:', self.args.patch)
1337 else:
1338 if not os.path.exists(self.args.patch):
1339 self.append_to_bp('ERROR cannot find patch file: ' + self.args.patch)
1340 return self
1341 if self.args.verbose:
1342 print('### INFO: applying local patch file:', self.args.patch)
Joel Galenson7e8247e2021-05-20 18:51:42 -07001343 subprocess.run(['patch', '-s', '--no-backup-if-mismatch', './Android.bp',
1344 self.args.patch], check=True)
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001345 return self
1346
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001347 def gen_bp(self):
1348 """Parse cargo.out and generate Android.bp files."""
1349 if self.dry_run:
1350 print('Dry-run skip: read', CARGO_OUT, 'write Android.bp')
1351 elif os.path.exists(CARGO_OUT):
1352 self.find_root_pkg()
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001353 if self.args.copy_out:
1354 self.copy_out_files()
1355 elif self.find_out_files() and self.has_used_out_dir():
1356 print('WARNING: ' + self.root_pkg + ' has cargo output files; ' +
1357 'please rerun with the --copy-out flag.')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001358 with open(CARGO_OUT, 'r') as cargo_out:
1359 self.parse(cargo_out, 'Android.bp')
1360 self.crates.sort(key=get_module_name)
1361 for obj in self.cc_objects:
1362 obj.dump()
1363 self.dump_pkg_obj2cc()
1364 for crate in self.crates:
1365 crate.dump()
1366 dumped_libs = set()
1367 for lib in self.ar_objects:
1368 if lib.pkg == self.root_pkg:
1369 lib_name = file_base_name(lib.lib)
1370 if lib_name not in dumped_libs:
1371 dumped_libs.add(lib_name)
1372 lib.dump()
Joel Galenson5664f2a2021-06-10 10:13:49 -07001373 if self.args.add_toplevel_block:
1374 with open(self.args.add_toplevel_block, 'r') as f:
1375 self.append_to_bp('\n' + f.read() + '\n')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001376 if self.args.dependencies and self.dependencies:
1377 self.dump_dependencies()
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001378 if self.errors:
Joel Galenson3f42f802021-04-07 12:42:17 -07001379 self.append_to_bp('\n' + ERRORS_LINE + '\n' + self.errors)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001380 return self
1381
1382 def add_ar_object(self, obj):
1383 self.ar_objects.append(obj)
1384
1385 def add_cc_object(self, obj):
1386 self.cc_objects.append(obj)
1387
1388 def add_crate(self, crate):
1389 """Merge crate with someone in crates, or append to it. Return crates."""
1390 if crate.skip_crate():
1391 if self.args.debug: # include debug info of all crates
1392 self.crates.append(crate)
1393 if self.args.dependencies: # include only dependent crates
1394 if (is_dependent_file_path(crate.main_src) and
1395 not is_build_crate_name(crate.crate_name)):
1396 self.dependencies.append(crate)
1397 else:
1398 for c in self.crates:
1399 if c.merge(crate, 'Android.bp'):
1400 return
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001401 # If not merged, decide module type and name now.
1402 crate.decide_module_type()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001403 self.crates.append(crate)
1404
1405 def find_warning_owners(self):
1406 """For each warning file, find its owner crate."""
1407 missing_owner = False
1408 for f in self.warning_files:
1409 cargo_dir = '' # find lowest crate, with longest path
1410 owner = None # owner crate of this warning
1411 for c in self.crates:
1412 if (f.startswith(c.cargo_dir + '/') and
1413 len(cargo_dir) < len(c.cargo_dir)):
1414 cargo_dir = c.cargo_dir
1415 owner = c
1416 if owner:
1417 owner.has_warning = True
1418 else:
1419 missing_owner = True
1420 if missing_owner and os.path.exists('Cargo.toml'):
1421 # owner is the root cargo, with empty cargo_dir
1422 for c in self.crates:
1423 if not c.cargo_dir:
1424 c.has_warning = True
1425
1426 def rustc_command(self, n, rustc_line, line, outf_name):
1427 """Process a rustc command line from cargo -vv output."""
1428 # cargo build -vv output can have multiple lines for a rustc command
1429 # due to '\n' in strings for environment variables.
1430 # strip removes leading spaces and '\n' at the end
1431 new_rustc = (rustc_line.strip() + line) if rustc_line else line
1432 # Use an heuristic to detect the completions of a multi-line command.
1433 # This might fail for some very rare case, but easy to fix manually.
1434 if not line.endswith('`\n') or (new_rustc.count('`') % 2) != 0:
1435 return new_rustc
1436 if RUSTC_VV_CMD_ARGS.match(new_rustc):
1437 args = RUSTC_VV_CMD_ARGS.match(new_rustc).group(1)
1438 self.add_crate(Crate(self, outf_name).parse(n, args))
1439 else:
1440 self.assert_empty_vv_line(new_rustc)
1441 return ''
1442
1443 def cc_ar_command(self, n, groups, outf_name):
1444 pkg = groups.group(1)
1445 line = groups.group(3)
1446 if groups.group(2) == 'cc':
1447 self.add_cc_object(CCObject(self, outf_name).parse(pkg, n, line))
1448 else:
1449 self.add_ar_object(ARObject(self, outf_name).parse(pkg, n, line))
1450
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001451 def append_to_bp(self, line):
1452 self.init_bp_file('Android.bp')
1453 with open('Android.bp', 'a') as outf:
1454 outf.write(line)
1455
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001456 def assert_empty_vv_line(self, line):
1457 if line: # report error if line is not empty
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001458 self.append_to_bp('ERROR -vv line: ' + line)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001459 return ''
1460
1461 def parse(self, inf, outf_name):
1462 """Parse rustc and warning messages in inf, return a list of Crates."""
1463 n = 0 # line number
1464 prev_warning = False # true if the previous line was warning: ...
1465 rustc_line = '' # previous line(s) matching RUSTC_VV_PAT
1466 for line in inf:
1467 n += 1
1468 if line.startswith('warning: '):
1469 prev_warning = True
1470 rustc_line = self.assert_empty_vv_line(rustc_line)
1471 continue
1472 new_rustc = ''
1473 if RUSTC_PAT.match(line):
1474 args_line = RUSTC_PAT.match(line).group(1)
1475 self.add_crate(Crate(self, outf_name).parse(n, args_line))
1476 self.assert_empty_vv_line(rustc_line)
1477 elif rustc_line or RUSTC_VV_PAT.match(line):
1478 new_rustc = self.rustc_command(n, rustc_line, line, outf_name)
1479 elif CC_AR_VV_PAT.match(line):
1480 self.cc_ar_command(n, CC_AR_VV_PAT.match(line), outf_name)
1481 elif prev_warning and WARNING_FILE_PAT.match(line):
1482 self.assert_empty_vv_line(rustc_line)
1483 fpath = WARNING_FILE_PAT.match(line).group(1)
1484 if fpath[0] != '/': # ignore absolute path
1485 self.warning_files.add(fpath)
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001486 elif line.startswith('error: ') or line.startswith('error[E'):
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001487 if not self.args.ignore_cargo_errors:
1488 self.errors += line
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001489 prev_warning = False
1490 rustc_line = new_rustc
1491 self.find_warning_owners()
1492
1493
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001494def get_parser():
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001495 """Parse main arguments."""
1496 parser = argparse.ArgumentParser('cargo2android')
1497 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001498 '--add_workspace',
1499 action='store_true',
1500 default=False,
1501 help=('append [workspace] to Cargo.toml before calling cargo,' +
1502 ' to treat current directory as root of package source;' +
1503 ' otherwise the relative source file path in generated' +
1504 ' .bp file will be from the parent directory.'))
1505 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001506 '--cargo',
1507 action='append',
1508 metavar='args_string',
1509 help=('extra cargo build -v args in a string, ' +
1510 'each --cargo flag calls cargo build -v once'))
1511 parser.add_argument(
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001512 '--cargo_bin',
1513 type=str,
1514 help='use cargo in the cargo_bin directory instead of the prebuilt one')
1515 parser.add_argument(
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001516 '--copy-out',
1517 action='store_true',
1518 default=False,
1519 help=('only for root directory, ' +
1520 'copy build.rs output to ./out/* and add a genrule to copy ' +
1521 './out/* to genrule output; for crates with code pattern: ' +
1522 'include!(concat!(env!("OUT_DIR"), "/<some_file>.rs"))'))
1523 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001524 '--debug',
1525 action='store_true',
1526 default=False,
1527 help='dump debug info into Android.bp')
1528 parser.add_argument(
1529 '--dependencies',
1530 action='store_true',
1531 default=False,
1532 help='dump debug info of dependent crates')
1533 parser.add_argument(
1534 '--device',
1535 action='store_true',
1536 default=False,
1537 help='run cargo also for a default device target')
1538 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001539 '--features',
1540 type=str,
1541 help=('pass features to cargo build, ' +
1542 'empty string means no default features'))
1543 parser.add_argument(
1544 '--global_defaults',
1545 type=str,
1546 help='add a defaults name to every module')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001547 parser.add_argument(
1548 '--host-first-multilib',
1549 action='store_true',
1550 default=False,
1551 help=('add a compile_multilib:"first" property ' +
1552 'to Android.bp host modules.'))
1553 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001554 '--ignore-cargo-errors',
1555 action='store_true',
1556 default=False,
1557 help='do not append cargo/rustc error messages to Android.bp')
1558 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001559 '--no-host',
1560 action='store_true',
1561 default=False,
1562 help='do not run cargo for the host; only for the device target')
1563 parser.add_argument(
1564 '--no-subdir',
1565 action='store_true',
1566 default=False,
1567 help='do not output anything for sub-directories')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001568 parser.add_argument(
1569 '--onefile',
1570 action='store_true',
1571 default=False,
1572 help=('output all into one ./Android.bp, default will generate ' +
1573 'one Android.bp per Cargo.toml in subdirectories'))
1574 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001575 '--patch',
1576 type=str,
1577 help='apply the given patch file to generated ./Android.bp')
1578 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001579 '--run',
1580 action='store_true',
1581 default=False,
1582 help='run it, default is dry-run')
1583 parser.add_argument('--rustflags', type=str, help='passing flags to rustc')
1584 parser.add_argument(
1585 '--skipcargo',
1586 action='store_true',
1587 default=False,
1588 help='skip cargo command, parse cargo.out, and generate Android.bp')
1589 parser.add_argument(
1590 '--tests',
1591 action='store_true',
1592 default=False,
1593 help='run cargo build --tests after normal build')
1594 parser.add_argument(
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001595 '--use-cargo-lock',
1596 action='store_true',
1597 default=False,
1598 help=('run cargo build with existing Cargo.lock ' +
1599 '(used when some latest dependent crates failed)'))
1600 parser.add_argument(
Joel Galensond9c4de62021-04-23 10:26:40 -07001601 '--min-sdk-version',
1602 type=str,
1603 help='Minimum SDK version')
1604 parser.add_argument(
1605 '--apex-available',
1606 nargs='*',
1607 help='Mark the main library as apex_available with the given apexes.')
1608 parser.add_argument(
Joel Galensoncb5f2f02021-06-08 14:47:55 -07001609 '--force-rlib',
1610 action='store_true',
1611 default=False,
1612 help='Make the main library an rlib.')
1613 parser.add_argument(
Joel Galenson12467e52021-07-12 14:33:28 -07001614 '--whole-static-libs',
1615 nargs='*',
1616 default=[],
1617 help='Make the given libraries (without lib prefixes) whole_static_libs.')
1618 parser.add_argument(
Joel Galenson97e414a2021-05-27 09:42:32 -07001619 '--dependency-blocklist',
1620 nargs='*',
1621 default=[],
Joel Galenson12467e52021-07-12 14:33:28 -07001622 help='Do not emit the given dependencies (without lib prefixes).')
Joel Galenson97e414a2021-05-27 09:42:32 -07001623 parser.add_argument(
Joel Galensoncb5f2f02021-06-08 14:47:55 -07001624 '--lib-blocklist',
1625 nargs='*',
1626 default=[],
Joel Galenson12467e52021-07-12 14:33:28 -07001627 help='Do not emit the given C libraries as dependencies (without lib prefixes).')
Joel Galensoncb5f2f02021-06-08 14:47:55 -07001628 parser.add_argument(
Joel Galensonf6b3c912021-06-03 16:00:54 -07001629 '--test-blocklist',
1630 nargs='*',
1631 default=[],
1632 help=('Do not emit the given tests. ' +
1633 'Pass the path to the test file to exclude.'))
1634 parser.add_argument(
Joel Galenson3d6d1e72021-06-07 15:00:24 -07001635 '--cfg-blocklist',
1636 nargs='*',
1637 default=[],
1638 help='Do not emit the given cfg.')
1639 parser.add_argument(
Joel Galenson5664f2a2021-06-10 10:13:49 -07001640 '--add-toplevel-block',
1641 type=str,
1642 help='Add the contents of the given file to the top level of the Android.bp.')
1643 parser.add_argument(
1644 '--add-module-block',
1645 type=str,
1646 help='Add the contents of the given file to the main module.')
1647 parser.add_argument(
Joel Galensoncf4ebb02021-04-07 08:39:51 -07001648 '--no-test-mapping',
1649 action='store_true',
1650 default=False,
ThiƩbaud Weksteen198e93f2021-07-02 14:49:19 +02001651 help='Deprecated. Has no effect.')
Joel Galensoncf4ebb02021-04-07 08:39:51 -07001652 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001653 '--verbose',
1654 action='store_true',
1655 default=False,
1656 help='echo executed commands')
1657 parser.add_argument(
1658 '--vv',
1659 action='store_true',
1660 default=False,
1661 help='run cargo with -vv instead of default -v')
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001662 parser.add_argument(
1663 '--dump-config-and-exit',
1664 type=str,
1665 help=('Dump command-line arguments (minus this flag) to a config file and exit. ' +
1666 'This is intended to help migrate from command line options to config files.'))
1667 parser.add_argument(
1668 '--config',
1669 type=str,
1670 help=('Load command-line options from the given config file. ' +
1671 'Options in this file will override those passed on the command line.'))
1672 return parser
1673
1674
1675def parse_args(parser):
1676 """Parses command-line options."""
1677 args = parser.parse_args()
1678 # Use the values specified in a config file if one was found.
1679 if args.config:
1680 with open(args.config, 'r') as f:
1681 config = json.load(f)
1682 args_dict = vars(args)
1683 for arg in config:
1684 args_dict[arg.replace('-', '_')] = config[arg]
1685 return args
1686
1687
1688def dump_config(parser, args):
1689 """Writes the non-default command-line options to the specified file."""
1690 args_dict = vars(args)
1691 # Filter out the arguments that have their default value.
Joel Galenson367360c2021-04-29 14:31:43 -07001692 # Also filter certain "temporary" arguments.
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001693 non_default_args = {}
1694 for arg in args_dict:
Joel Galenson367360c2021-04-29 14:31:43 -07001695 if args_dict[arg] != parser.get_default(
1696 arg) and arg != 'dump_config_and_exit' and arg != 'no_test_mapping':
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001697 non_default_args[arg.replace('_', '-')] = args_dict[arg]
1698 # Write to the specified file.
1699 with open(args.dump_config_and_exit, 'w') as f:
1700 json.dump(non_default_args, f, indent=2, sort_keys=True)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001701
1702
1703def main():
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001704 parser = get_parser()
1705 args = parse_args(parser)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001706 if not args.run: # default is dry-run
1707 print(DRY_RUN_NOTE)
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001708 if args.dump_config_and_exit:
1709 dump_config(parser, args)
1710 else:
ThiƩbaud Weksteen198e93f2021-07-02 14:49:19 +02001711 Runner(args).run_cargo().gen_bp().apply_patch()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001712
1713
1714if __name__ == '__main__':
1715 main()