blob: 70a4dc3bea0e2bb2eabbb247ed379ba58aeb34fc [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
Matthew Maurer062709c2021-08-17 11:27:36 -0700130# Crate types corresponding to a C ABI library
131C_LIBRARY_CRATE_TYPES = ['staticlib', 'cdylib']
132# Crate types corresponding to a Rust ABI library
133RUST_LIBRARY_CRATE_TYPES = ['lib', 'rlib', 'dylib']
134# Crate types corresponding to a library
135LIBRARY_CRATE_TYPES = C_LIBRARY_CRATE_TYPES + RUST_LIBRARY_CRATE_TYPES
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800136
137def altered_name(name):
138 return RENAME_MAP[name] if (name in RENAME_MAP) else name
139
140
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700141def altered_stem(name):
142 return RENAME_STEM_MAP[name] if (name in RENAME_STEM_MAP) else name
143
144
145def altered_defaults(name):
146 return RENAME_DEFAULTS_MAP[name] if (name in RENAME_DEFAULTS_MAP) else name
147
148
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800149def is_build_crate_name(name):
150 # We added special prefix to build script crate names.
151 return name.startswith('build_script_')
152
153
154def is_dependent_file_path(path):
155 # Absolute or dependent '.../' paths are not main files of this crate.
156 return path.startswith('/') or path.startswith('.../')
157
158
159def get_module_name(crate): # to sort crates in a list
160 return crate.module_name
161
162
163def pkg2crate_name(s):
164 return s.replace('-', '_').replace('.', '_')
165
166
167def file_base_name(path):
168 return os.path.splitext(os.path.basename(path))[0]
169
170
171def test_base_name(path):
172 return pkg2crate_name(file_base_name(path))
173
174
175def unquote(s): # remove quotes around str
176 if s and len(s) > 1 and s[0] == '"' and s[-1] == '"':
177 return s[1:-1]
178 return s
179
180
181def remove_version_suffix(s): # remove -d1.d2.d3 suffix
182 if VERSION_SUFFIX_PAT.match(s):
183 return VERSION_SUFFIX_PAT.match(s).group(1)
184 return s
185
186
187def short_out_name(pkg, s): # replace /.../pkg-*/out/* with .../out/*
188 return re.sub('^/.*/' + pkg + '-[0-9a-f]*/out/', '.../out/', s)
189
190
191def escape_quotes(s): # replace '"' with '\\"'
192 return s.replace('"', '\\"')
193
194
195class Crate(object):
196 """Information of a Rust crate to collect/emit for an Android.bp module."""
197
198 def __init__(self, runner, outf_name):
199 # Remembered global runner and its members.
200 self.runner = runner
201 self.debug = runner.args.debug
202 self.cargo_dir = '' # directory of my Cargo.toml
203 self.outf_name = outf_name # path to Android.bp
204 self.outf = None # open file handle of outf_name during dump*
205 # Variants/results that could be merged from multiple rustc lines.
206 self.host_supported = False
207 self.device_supported = False
208 self.has_warning = False
209 # Android module properties derived from rustc parameters.
210 self.module_name = '' # unique in Android build system
211 self.module_type = '' # rust_{binary,library,test}[_host] etc.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700212 self.defaults = '' # rust_defaults used by rust_test* modules
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700213 self.default_srcs = False # use 'srcs' defined in self.defaults
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800214 self.root_pkg = '' # parent package name of a sub/test packge, from -L
215 self.srcs = list() # main_src or merged multiple source files
216 self.stem = '' # real base name of output file
217 # Kept parsed status
218 self.errors = '' # all errors found during parsing
219 self.line_num = 1 # runner told input source line number
220 self.line = '' # original rustc command line parameters
221 # Parameters collected from rustc command line.
222 self.crate_name = '' # follows --crate-name
223 self.main_src = '' # follows crate_name parameter, shortened
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700224 self.crate_types = list() # follows --crate-type
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800225 self.cfgs = list() # follows --cfg, without feature= prefix
226 self.features = list() # follows --cfg, name in 'feature="..."'
227 self.codegens = list() # follows -C, some ignored
228 self.externs = list() # follows --extern
229 self.core_externs = list() # first part of self.externs elements
230 self.static_libs = list() # e.g. -l static=host_cpuid
231 self.shared_libs = list() # e.g. -l dylib=wayland-client, -l z
232 self.cap_lints = '' # follows --cap-lints
233 self.emit_list = '' # e.g., --emit=dep-info,metadata,link
234 self.edition = '2015' # rustc default, e.g., --edition=2018
235 self.target = '' # follows --target
Ivan Lozanocc660f12021-08-11 16:49:46 -0400236 self.cargo_env_compat = True
237 self.cargo_pkg_version = '' # value extracted from Cargo.toml version field
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800238
239 def write(self, s):
240 # convenient way to output one line at a time with EOL.
241 self.outf.write(s + '\n')
242
243 def same_flags(self, other):
244 # host_supported, device_supported, has_warning are not compared but merged
245 # target is not compared, to merge different target/host modules
246 # externs is not compared; only core_externs is compared
247 return (not self.errors and not other.errors and
248 self.edition == other.edition and
249 self.cap_lints == other.cap_lints and
250 self.emit_list == other.emit_list and
251 self.core_externs == other.core_externs and
252 self.codegens == other.codegens and
253 self.features == other.features and
254 self.static_libs == other.static_libs and
255 self.shared_libs == other.shared_libs and self.cfgs == other.cfgs)
256
257 def merge_host_device(self, other):
258 """Returns true if attributes are the same except host/device support."""
259 return (self.crate_name == other.crate_name and
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700260 self.crate_types == other.crate_types and
261 self.main_src == other.main_src and
262 # before merge, each test module has an unique module name and stem
263 (self.stem == other.stem or self.crate_types == ['test']) and
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800264 self.root_pkg == other.root_pkg and not self.skip_crate() and
265 self.same_flags(other))
266
267 def merge_test(self, other):
268 """Returns true if self and other are tests of same root_pkg."""
269 # Before merger, each test has its own crate_name.
270 # A merged test uses its source file base name as output file name,
271 # so a test is mergeable only if its base name equals to its crate name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700272 return (self.crate_types == other.crate_types and
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700273 self.crate_types == ['test'] and self.root_pkg == other.root_pkg and
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800274 not self.skip_crate() and
275 other.crate_name == test_base_name(other.main_src) and
276 (len(self.srcs) > 1 or
277 (self.crate_name == test_base_name(self.main_src)) and
278 self.host_supported == other.host_supported and
279 self.device_supported == other.device_supported) and
280 self.same_flags(other))
281
282 def merge(self, other, outf_name):
283 """Try to merge crate into self."""
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700284 # Cargo build --tests could recompile a library for tests.
285 # We need to merge such duplicated calls to rustc, with
286 # the algorithm in merge_host_device.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800287 should_merge_host_device = self.merge_host_device(other)
288 should_merge_test = False
289 if not should_merge_host_device:
290 should_merge_test = self.merge_test(other)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800291 if should_merge_host_device or should_merge_test:
292 self.runner.init_bp_file(outf_name)
293 with open(outf_name, 'a') as outf: # to write debug info
294 self.outf = outf
295 other.outf = outf
296 self.do_merge(other, should_merge_test)
297 return True
298 return False
299
300 def do_merge(self, other, should_merge_test):
301 """Merge attributes of other to self."""
302 if self.debug:
303 self.write('\n// Before merge definition (1):')
304 self.dump_debug_info()
305 self.write('\n// Before merge definition (2):')
306 other.dump_debug_info()
307 # Merge properties of other to self.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800308 self.has_warning = self.has_warning or other.has_warning
309 if not self.target: # okay to keep only the first target triple
310 self.target = other.target
311 # decide_module_type sets up default self.stem,
312 # which can be changed if self is a merged test module.
313 self.decide_module_type()
314 if should_merge_test:
Joel Galenson57fa23a2021-07-15 10:47:35 -0700315 if (self.main_src in self.runner.args.test_blocklist and
316 not other.main_src in self.runner.args.test_blocklist):
317 self.main_src = other.main_src
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800318 self.srcs.append(other.main_src)
319 # use a short unique name as the merged module name.
320 prefix = self.root_pkg + '_tests'
321 self.module_name = self.runner.claim_module_name(prefix, self, 0)
322 self.stem = self.module_name
323 # This normalized root_pkg name although might be the same
324 # as other module's crate_name, it is not actually used for
325 # output file name. A merged test module always have multiple
326 # source files and each source file base name is used as
327 # its output file name.
328 self.crate_name = pkg2crate_name(self.root_pkg)
329 if self.debug:
330 self.write('\n// After merge definition (1):')
331 self.dump_debug_info()
332
333 def find_cargo_dir(self):
334 """Deepest directory with Cargo.toml and contains the main_src."""
335 if not is_dependent_file_path(self.main_src):
336 dir_name = os.path.dirname(self.main_src)
337 while dir_name:
338 if os.path.exists(dir_name + '/Cargo.toml'):
339 self.cargo_dir = dir_name
340 return
341 dir_name = os.path.dirname(dir_name)
342
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700343 def add_codegens_flag(self, flag):
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700344 """Ignore options not used in Android."""
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700345 # 'prefer-dynamic' does not work with common flag -C lto
Chih-Hung Hsieh63459ed2020-08-26 11:51:15 -0700346 # 'embed-bitcode' is ignored; we might control LTO with other .bp flag
Chih-Hung Hsieh6c13b722020-09-11 21:24:03 -0700347 # 'codegen-units' is set in Android global config or by default
348 if not (flag.startswith('codegen-units=') or
349 flag.startswith('debuginfo=') or
Chih-Hung Hsieh63459ed2020-08-26 11:51:15 -0700350 flag.startswith('embed-bitcode=') or
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700351 flag.startswith('extra-filename=') or
352 flag.startswith('incremental=') or
353 flag.startswith('metadata=') or
354 flag == 'prefer-dynamic'):
355 self.codegens.append(flag)
356
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800357 def parse(self, line_num, line):
358 """Find important rustc arguments to convert to Android.bp properties."""
359 self.line_num = line_num
360 self.line = line
361 args = line.split() # Loop through every argument of rustc.
362 i = 0
363 while i < len(args):
364 arg = args[i]
365 if arg == '--crate-name':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700366 i += 1
367 self.crate_name = args[i]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800368 elif arg == '--crate-type':
369 i += 1
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700370 # cargo calls rustc with multiple --crate-type flags.
371 # rustc can accept:
372 # --crate-type [bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]
373 self.crate_types.append(args[i])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800374 elif arg == '--test':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700375 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800376 elif arg == '--target':
377 i += 1
378 self.target = args[i]
379 elif arg == '--cfg':
380 i += 1
381 if args[i].startswith('\'feature='):
382 self.features.append(unquote(args[i].replace('\'feature=', '')[:-1]))
383 else:
384 self.cfgs.append(args[i])
385 elif arg == '--extern':
386 i += 1
387 extern_names = re.sub('=/[^ ]*/deps/', ' = ', args[i])
388 self.externs.append(extern_names)
389 self.core_externs.append(re.sub(' = .*', '', extern_names))
390 elif arg == '-C': # codegen options
391 i += 1
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700392 self.add_codegens_flag(args[i])
393 elif arg.startswith('-C'):
394 # cargo has been passing "-C <xyz>" flag to rustc,
395 # but newer cargo could pass '-Cembed-bitcode=no' to rustc.
396 self.add_codegens_flag(arg[2:])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800397 elif arg == '--cap-lints':
398 i += 1
399 self.cap_lints = args[i]
400 elif arg == '-L':
401 i += 1
402 if args[i].startswith('dependency=') and args[i].endswith('/deps'):
403 if '/' + TARGET_TMP + '/' in args[i]:
404 self.root_pkg = re.sub(
405 '^.*/', '', re.sub('/' + TARGET_TMP + '/.*/deps$', '', args[i]))
406 else:
407 self.root_pkg = re.sub('^.*/', '',
408 re.sub('/[^/]+/[^/]+/deps$', '', args[i]))
409 self.root_pkg = remove_version_suffix(self.root_pkg)
410 elif arg == '-l':
411 i += 1
412 if args[i].startswith('static='):
413 self.static_libs.append(re.sub('static=', '', args[i]))
414 elif args[i].startswith('dylib='):
415 self.shared_libs.append(re.sub('dylib=', '', args[i]))
416 else:
417 self.shared_libs.append(args[i])
418 elif arg == '--out-dir' or arg == '--color': # ignored
419 i += 1
420 elif arg.startswith('--error-format=') or arg.startswith('--json='):
421 _ = arg # ignored
422 elif arg.startswith('--emit='):
423 self.emit_list = arg.replace('--emit=', '')
424 elif arg.startswith('--edition='):
425 self.edition = arg.replace('--edition=', '')
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700426 elif not arg.startswith('-'):
427 # shorten imported crate main source paths like $HOME/.cargo/
428 # registry/src/github.com-1ecc6299db9ec823/memchr-2.3.3/src/lib.rs
429 self.main_src = re.sub(r'^/[^ ]*/registry/src/', '.../', args[i])
430 self.main_src = re.sub(r'^\.\.\./github.com-[0-9a-f]*/', '.../',
431 self.main_src)
432 self.find_cargo_dir()
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700433 if self.cargo_dir: # for a subdirectory
434 if self.runner.args.no_subdir: # all .bp content to /dev/null
435 self.outf_name = '/dev/null'
436 elif not self.runner.args.onefile:
437 # Write to Android.bp in the subdirectory with Cargo.toml.
438 self.outf_name = self.cargo_dir + '/Android.bp'
439 self.main_src = self.main_src[len(self.cargo_dir) + 1:]
Ivan Lozanocc660f12021-08-11 16:49:46 -0400440
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800441 else:
442 self.errors += 'ERROR: unknown ' + arg + '\n'
443 i += 1
444 if not self.crate_name:
445 self.errors += 'ERROR: missing --crate-name\n'
446 if not self.main_src:
447 self.errors += 'ERROR: missing main source file\n'
448 else:
449 self.srcs.append(self.main_src)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700450 if not self.crate_types:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800451 # Treat "--cfg test" as "--test"
452 if 'test' in self.cfgs:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700453 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800454 else:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700455 self.errors += 'ERROR: missing --crate-type or --test\n'
456 elif len(self.crate_types) > 1:
457 if 'test' in self.crate_types:
458 self.errors += 'ERROR: cannot handle both --crate-type and --test\n'
459 if 'lib' in self.crate_types and 'rlib' in self.crate_types:
460 self.errors += 'ERROR: cannot generate both lib and rlib crate types\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800461 if not self.root_pkg:
462 self.root_pkg = self.crate_name
Ivan Lozano26aa1c32021-08-16 11:20:32 -0400463
464 # get the package version from running cargo metadata
Joel Galenson69ba8072021-08-16 11:31:29 -0700465 if not self.runner.args.no_pkg_vers and not self.skip_crate():
Ivan Lozano26aa1c32021-08-16 11:20:32 -0400466 self.get_pkg_version()
467
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700468 self.device_supported = self.runner.args.device
469 self.host_supported = not self.runner.args.no_host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800470 self.cfgs = sorted(set(self.cfgs))
471 self.features = sorted(set(self.features))
472 self.codegens = sorted(set(self.codegens))
473 self.externs = sorted(set(self.externs))
474 self.core_externs = sorted(set(self.core_externs))
475 self.static_libs = sorted(set(self.static_libs))
476 self.shared_libs = sorted(set(self.shared_libs))
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700477 self.crate_types = sorted(set(self.crate_types))
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800478 self.decide_module_type()
479 self.module_name = altered_name(self.stem)
480 return self
481
Ivan Lozano26aa1c32021-08-16 11:20:32 -0400482 def get_pkg_version(self):
483 """Attempt to retrieve the package version from the Cargo.toml
484
485 If there is only one package, use its version. Otherwise, try to
486 match the emitted `--crate_name` arg against the package name.
487
488 This may fail in cases where multiple packages are defined (workspaces)
489 and where the package name does not match the emitted crate_name
490 (e.g. [lib.name] is set).
491 """
Joel Galenson69ba8072021-08-16 11:31:29 -0700492 cargo_metadata = subprocess.run([self.runner.cargo_path, 'metadata', '--no-deps',
493 '--format-version', '1'],
Joel Galensonc5186502021-08-16 11:22:47 -0700494 cwd=os.path.abspath(self.cargo_dir),
495 stdout=subprocess.PIPE)
Ivan Lozano26aa1c32021-08-16 11:20:32 -0400496 if cargo_metadata.returncode:
497 self.errors += ('ERROR: unable to get cargo metadata for package version; ' +
498 'return code ' + cargo_metadata.returncode + '\n')
499 else:
500 metadata_json = json.loads(cargo_metadata.stdout)
501 if len(metadata_json['packages']) > 1:
502 for package in metadata_json['packages']:
503 # package names may contain '-', but is changed to '_' in the crate_name
504 if package['name'].replace('-','_') == self.crate_name:
505 self.cargo_pkg_version = package['version']
506 break
507 else:
508 self.cargo_pkg_version = metadata_json['packages'][0]['version']
509
510 if not self.cargo_pkg_version:
511 self.errors += ('ERROR: Unable to retrieve package version; ' +
512 'to disable, run with arg "--no-pkg-vers"\n')
513
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800514 def dump_line(self):
515 self.write('\n// Line ' + str(self.line_num) + ' ' + self.line)
516
517 def feature_list(self):
518 """Return a string of main_src + "feature_list"."""
519 pkg = self.main_src
520 if pkg.startswith('.../'): # keep only the main package name
521 pkg = re.sub('/.*', '', pkg[4:])
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700522 elif pkg.startswith('/'): # use relative path for a local package
523 pkg = os.path.relpath(pkg)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800524 if not self.features:
525 return pkg
526 return pkg + ' "' + ','.join(self.features) + '"'
527
528 def dump_skip_crate(self, kind):
529 if self.debug:
530 self.write('\n// IGNORED: ' + kind + ' ' + self.main_src)
531 return self
532
533 def skip_crate(self):
534 """Return crate_name or a message if this crate should be skipped."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700535 if (is_build_crate_name(self.crate_name) or
536 self.crate_name in EXCLUDED_CRATES):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800537 return self.crate_name
538 if is_dependent_file_path(self.main_src):
539 return 'dependent crate'
540 return ''
541
542 def dump(self):
543 """Dump all error/debug/module code to the output .bp file."""
544 self.runner.init_bp_file(self.outf_name)
545 with open(self.outf_name, 'a') as outf:
546 self.outf = outf
547 if self.errors:
548 self.dump_line()
549 self.write(self.errors)
550 elif self.skip_crate():
551 self.dump_skip_crate(self.skip_crate())
552 else:
553 if self.debug:
554 self.dump_debug_info()
555 self.dump_android_module()
556
557 def dump_debug_info(self):
558 """Dump parsed data, when cargo2android is called with --debug."""
559
560 def dump(name, value):
561 self.write('//%12s = %s' % (name, value))
562
563 def opt_dump(name, value):
564 if value:
565 dump(name, value)
566
567 def dump_list(fmt, values):
568 for v in values:
569 self.write(fmt % v)
570
571 self.dump_line()
572 dump('module_name', self.module_name)
573 dump('crate_name', self.crate_name)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700574 dump('crate_types', self.crate_types)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800575 dump('main_src', self.main_src)
576 dump('has_warning', self.has_warning)
577 dump('for_host', self.host_supported)
578 dump('for_device', self.device_supported)
579 dump('module_type', self.module_type)
580 opt_dump('target', self.target)
581 opt_dump('edition', self.edition)
582 opt_dump('emit_list', self.emit_list)
583 opt_dump('cap_lints', self.cap_lints)
584 dump_list('// cfg = %s', self.cfgs)
585 dump_list('// cfg = \'feature "%s"\'', self.features)
586 # TODO(chh): escape quotes in self.features, but not in other dump_list
587 dump_list('// codegen = %s', self.codegens)
588 dump_list('// externs = %s', self.externs)
589 dump_list('// -l static = %s', self.static_libs)
590 dump_list('// -l (dylib) = %s', self.shared_libs)
591
592 def dump_android_module(self):
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700593 """Dump one or more Android module definition, depending on crate_types."""
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700594 if len(self.crate_types) == 1:
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700595 self.dump_single_type_android_module()
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700596 return
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700597 if 'test' in self.crate_types:
598 self.write('\nERROR: multiple crate types cannot include test type')
599 return
600 # Dump one Android module per crate_type.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700601 for crate_type in self.crate_types:
602 self.decide_one_module_type(crate_type)
603 self.dump_one_android_module(crate_type)
604
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700605 def build_default_name(self):
606 """Return a short and readable name for the rust_defaults module."""
Joel Galensond37d7e62021-07-13 09:03:01 -0700607 # Choices: (1) root_pkg + '_test'? + '_defaults',
608 # (2) root_pkg + '_test'? + '_defaults_' + crate_name
609 # (3) root_pkg + '_test'? + '_defaults_' + main_src_basename_path
610 # (4) root_pkg + '_test'? + '_defaults_' + a_positive_sequence_number
611 test = "_test" if self.crate_types == ['test'] else ""
612 name1 = altered_defaults(self.root_pkg) + test + '_defaults'
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700613 if self.runner.try_claim_module_name(name1, self):
614 return name1
615 name2 = name1 + '_' + self.crate_name
616 if self.runner.try_claim_module_name(name2, self):
617 return name2
618 name3 = name1 + '_' + self.main_src_basename_path()
619 if self.runner.try_claim_module_name(name3, self):
620 return name3
621 return self.runner.claim_module_name(name1, self, 0)
622
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700623 def dump_srcs_list(self):
624 """Dump the srcs list, for defaults or regular modules."""
625 if len(self.srcs) > 1:
626 srcs = sorted(set(self.srcs)) # make a copy and dedup
627 else:
628 srcs = [self.main_src]
629 copy_out = self.runner.copy_out_module_name()
630 if copy_out:
631 srcs.append(':' + copy_out)
632 self.dump_android_property_list('srcs', '"%s"', srcs)
633
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700634 def dump_defaults_module(self):
635 """Dump a rust_defaults module to be shared by other modules."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700636 name = self.build_default_name()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700637 self.defaults = name
638 self.write('\nrust_defaults {')
639 self.write(' name: "' + name + '",')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700640 if self.runner.args.global_defaults:
641 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700642 self.write(' crate_name: "' + self.crate_name + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700643 if len(self.srcs) == 1: # only one source file; share it in defaults
644 self.default_srcs = True
645 if self.has_warning and not self.cap_lints:
646 self.write(' // has rustc warnings')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700647 self.dump_srcs_list()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700648 if 'test' in self.crate_types:
649 self.write(' test_suites: ["general-tests"],')
650 self.write(' auto_gen_config: true,')
651 self.dump_edition_flags_libs()
Joel Galensone4f53882021-07-19 11:14:55 -0700652 if 'test' in self.crate_types and len(self.srcs) == 1:
653 self.dump_test_data()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700654 self.write('}')
655
656 def dump_single_type_android_module(self):
657 """Dump one simple Android module, which has only one crate_type."""
658 crate_type = self.crate_types[0]
659 if crate_type != 'test':
660 # do not change self.stem or self.module_name
661 self.dump_one_android_module(crate_type)
662 return
663 # Dump one test module per source file, and separate host and device tests.
664 # crate_type == 'test'
Joel Galensonf6b3c912021-06-03 16:00:54 -0700665 self.srcs = [src for src in self.srcs if not src in self.runner.args.test_blocklist]
666 if ((self.host_supported and self.device_supported and len(self.srcs) > 0) or
667 len(self.srcs) > 1):
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700668 self.srcs = sorted(set(self.srcs))
669 self.dump_defaults_module()
670 saved_srcs = self.srcs
671 for src in saved_srcs:
672 self.srcs = [src]
673 saved_device_supported = self.device_supported
674 saved_host_supported = self.host_supported
675 saved_main_src = self.main_src
676 self.main_src = src
677 if saved_host_supported:
678 self.device_supported = False
679 self.host_supported = True
680 self.module_name = self.test_module_name()
681 self.decide_one_module_type(crate_type)
682 self.dump_one_android_module(crate_type)
683 if saved_device_supported:
684 self.device_supported = True
685 self.host_supported = False
686 self.module_name = self.test_module_name()
687 self.decide_one_module_type(crate_type)
688 self.dump_one_android_module(crate_type)
689 self.host_supported = saved_host_supported
690 self.device_supported = saved_device_supported
691 self.main_src = saved_main_src
692 self.srcs = saved_srcs
693
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700694 def dump_one_android_module(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800695 """Dump one Android module definition."""
696 if not self.module_type:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700697 self.write('\nERROR: unknown crate_type ' + crate_type)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800698 return
699 self.write('\n' + self.module_type + ' {')
700 self.dump_android_core_properties()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700701 if not self.defaults:
702 self.dump_edition_flags_libs()
703 if self.runner.args.host_first_multilib and self.host_supported and crate_type != 'test':
704 self.write(' compile_multilib: "first",')
Matthew Maurer062709c2021-08-17 11:27:36 -0700705 if self.runner.args.exported_c_header_dir and crate_type in C_LIBRARY_CRATE_TYPES:
706 self.write(' include_dirs: [')
707 for header_dir in self.runner.args.exported_c_header_dir:
708 self.write(' "%s",' % header_dir)
709 self.write(' ],')
Matthew Maurer9e4b7812021-08-16 14:21:01 -0700710 if self.runner.args.apex_available and crate_type in LIBRARY_CRATE_TYPES:
Joel Galensond9c4de62021-04-23 10:26:40 -0700711 self.write(' apex_available: [')
712 for apex in self.runner.args.apex_available:
713 self.write(' "%s",' % apex)
714 self.write(' ],')
Matthew Maurerac677252021-08-13 15:52:52 -0700715 if self.runner.args.native_bridge_supported:
716 self.write(' native_bridge_supported: true,')
717 if self.runner.args.product_available:
718 self.write(' product_available: true,')
719 if self.runner.args.recovery_available:
720 self.write(' recovery_available: true,')
Ivan Lozano91920862021-07-19 10:49:08 -0400721 if self.runner.args.vendor_available:
722 self.write(' vendor_available: true,')
723 if self.runner.args.vendor_ramdisk_available:
724 self.write(' vendor_ramdisk_available: true,')
Matthew Maurerac677252021-08-13 15:52:52 -0700725 if self.runner.args.ramdisk_available:
726 self.write(' ramdisk_available: true,')
Matthew Maurer9e4b7812021-08-16 14:21:01 -0700727 if self.runner.args.min_sdk_version and crate_type in LIBRARY_CRATE_TYPES:
Joel Galensond9c4de62021-04-23 10:26:40 -0700728 self.write(' min_sdk_version: "%s",' % self.runner.args.min_sdk_version)
Joel Galensone4f53882021-07-19 11:14:55 -0700729 if crate_type == 'test' and not self.default_srcs:
730 self.dump_test_data()
Joel Galenson5664f2a2021-06-10 10:13:49 -0700731 if self.runner.args.add_module_block:
732 with open(self.runner.args.add_module_block, 'r') as f:
733 self.write(' %s,' % f.read().replace('\n', '\n '))
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700734 self.write('}')
735
736 def dump_android_flags(self):
737 """Dump Android module flags property."""
ThiƩbaud Weksteena5a728b2021-04-08 14:23:49 +0200738 if not self.codegens and not self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700739 return
740 self.write(' flags: [')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800741 if self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700742 self.write(' "--cap-lints ' + self.cap_lints + '",')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700743 codegens_fmt = '"-C %s"'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700744 self.dump_android_property_list_items(codegens_fmt, self.codegens)
745 self.write(' ],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700746
747 def dump_edition_flags_libs(self):
748 if self.edition:
749 self.write(' edition: "' + self.edition + '",')
750 self.dump_android_property_list('features', '"%s"', self.features)
Joel Galenson3d6d1e72021-06-07 15:00:24 -0700751 cfgs = [cfg for cfg in self.cfgs if not cfg in self.runner.args.cfg_blocklist]
752 self.dump_android_property_list('cfgs', '"%s"', cfgs)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700753 self.dump_android_flags()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800754 if self.externs:
755 self.dump_android_externs()
Joel Galenson12467e52021-07-12 14:33:28 -0700756 all_static_libs = [lib for lib in self.static_libs if not lib in self.runner.args.lib_blocklist]
757 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 -0700758 self.dump_android_property_list('static_libs', '"lib%s"', static_libs)
Joel Galenson12467e52021-07-12 14:33:28 -0700759 whole_static_libs = [lib for lib in all_static_libs if lib in self.runner.args.whole_static_libs]
760 self.dump_android_property_list('whole_static_libs', '"lib%s"', whole_static_libs)
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700761 shared_libs = [lib for lib in self.shared_libs if not lib in self.runner.args.lib_blocklist]
762 self.dump_android_property_list('shared_libs', '"lib%s"', shared_libs)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800763
Joel Galensone4f53882021-07-19 11:14:55 -0700764 def dump_test_data(self):
765 data = [data for (name, data) in map(lambda kv: kv.split('=', 1), self.runner.args.test_data)
766 if self.srcs == [name]]
767 if data:
768 self.dump_android_property_list('data', '"%s"', data)
769
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700770 def main_src_basename_path(self):
771 return re.sub('/', '_', re.sub('.rs$', '', self.main_src))
772
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800773 def test_module_name(self):
774 """Return a unique name for a test module."""
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700775 # root_pkg+(_host|_device) + '_test_'+source_file_name
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700776 suffix = self.main_src_basename_path()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700777 host_device = '_host'
778 if self.device_supported:
779 host_device = '_device'
780 return self.root_pkg + host_device + '_test_' + suffix
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800781
782 def decide_module_type(self):
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700783 # Use the first crate type for the default/first module.
784 crate_type = self.crate_types[0] if self.crate_types else ''
785 self.decide_one_module_type(crate_type)
786
787 def decide_one_module_type(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800788 """Decide which Android module type to use."""
789 host = '' if self.device_supported else '_host'
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700790 rlib = '_rlib' if self.runner.args.force_rlib else ''
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700791 if crate_type == 'bin': # rust_binary[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800792 self.module_type = 'rust_binary' + host
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700793 # In rare cases like protobuf-codegen, the output binary name must
794 # be renamed to use as a plugin for protoc.
795 self.stem = altered_stem(self.crate_name)
796 self.module_name = altered_name(self.crate_name)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700797 elif crate_type == 'lib': # rust_library[_host]
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700798 # TODO(chh): should this be rust_library[_host]?
799 # Assuming that Cargo.toml do not use both 'lib' and 'rlib',
800 # because we map them both to rlib.
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700801 self.module_type = 'rust_library' + rlib + host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800802 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700803 self.module_name = altered_name(self.stem)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700804 elif crate_type == 'rlib': # rust_library[_host]
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700805 self.module_type = 'rust_library' + rlib + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700806 self.stem = 'lib' + self.crate_name
807 self.module_name = altered_name(self.stem)
808 elif crate_type == 'dylib': # rust_library[_host]_dylib
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800809 self.module_type = 'rust_library' + host + '_dylib'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700810 self.stem = 'lib' + self.crate_name
811 self.module_name = altered_name(self.stem) + '_dylib'
812 elif crate_type == 'cdylib': # rust_library[_host]_shared
Ivan Lozano0c057ad2020-12-15 10:41:26 -0500813 self.module_type = 'rust_ffi' + host + '_shared'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700814 self.stem = 'lib' + self.crate_name
815 self.module_name = altered_name(self.stem) + '_shared'
816 elif crate_type == 'staticlib': # rust_library[_host]_static
Ivan Lozano0c057ad2020-12-15 10:41:26 -0500817 self.module_type = 'rust_ffi' + host + '_static'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700818 self.stem = 'lib' + self.crate_name
819 self.module_name = altered_name(self.stem) + '_static'
820 elif crate_type == 'test': # rust_test[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800821 self.module_type = 'rust_test' + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700822 # Before do_merge, stem name is based on the --crate-name parameter.
823 # and test module name is based on stem.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800824 self.stem = self.test_module_name()
825 # self.stem will be changed after merging with other tests.
826 # self.stem is NOT used for final test binary name.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700827 # rust_test uses each source file base name as part of output file name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700828 # In do_merge, this function is called again, with a module_name.
829 # We make sure that the module name is unique in each package.
830 if self.module_name:
831 # Cargo uses "-C extra-filename=..." and "-C metadata=..." to add
832 # different suffixes and distinguish multiple tests of the same
833 # crate name. We ignore -C and use claim_module_name to get
834 # unique sequential suffix.
835 self.module_name = self.runner.claim_module_name(
836 self.module_name, self, 0)
837 # Now the module name is unique, stem should also match and unique.
838 self.stem = self.module_name
839 elif crate_type == 'proc-macro': # rust_proc_macro
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800840 self.module_type = 'rust_proc_macro'
841 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700842 self.module_name = altered_name(self.stem)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800843 else: # unknown module type, rust_prebuilt_dylib? rust_library[_host]?
844 self.module_type = ''
845 self.stem = ''
846
847 def dump_android_property_list_items(self, fmt, values):
848 for v in values:
849 # fmt has quotes, so we need escape_quotes(v)
850 self.write(' ' + (fmt % escape_quotes(v)) + ',')
851
852 def dump_android_property_list(self, name, fmt, values):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700853 if not values:
854 return
855 if len(values) > 1:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800856 self.write(' ' + name + ': [')
857 self.dump_android_property_list_items(fmt, values)
858 self.write(' ],')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700859 else:
860 self.write(' ' + name + ': [' +
861 (fmt % escape_quotes(values[0])) + '],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800862
863 def dump_android_core_properties(self):
864 """Dump the module header, name, stem, etc."""
865 self.write(' name: "' + self.module_name + '",')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700866 # see properties shared by dump_defaults_module
867 if self.defaults:
868 self.write(' defaults: ["' + self.defaults + '"],')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700869 elif self.runner.args.global_defaults:
870 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800871 if self.stem != self.module_name:
872 self.write(' stem: "' + self.stem + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700873 if self.has_warning and not self.cap_lints and not self.default_srcs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700874 self.write(' // has rustc warnings')
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700875 if self.host_supported and self.device_supported and self.module_type != 'rust_proc_macro':
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800876 self.write(' host_supported: true,')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700877 if not self.defaults:
878 self.write(' crate_name: "' + self.crate_name + '",')
Ivan Lozanocc660f12021-08-11 16:49:46 -0400879 if not self.defaults and self.cargo_env_compat:
880 self.write(' cargo_env_compat: true,')
881 self.write(' cargo_pkg_version: "' + self.cargo_pkg_version + '",')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700882 if not self.default_srcs:
883 self.dump_srcs_list()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700884 if 'test' in self.crate_types and not self.defaults:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800885 # self.root_pkg can have multiple test modules, with different *_tests[n]
886 # names, but their executables can all be installed under the same _tests
887 # directory. When built from Cargo.toml, all tests should have different
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700888 # file or crate names. So we used (root_pkg + '_tests') name as the
889 # relative_install_path.
890 # However, some package like 'slab' can have non-mergeable tests that
891 # must be separated by different module names. So, here we no longer
892 # emit relative_install_path.
893 # self.write(' relative_install_path: "' + self.root_pkg + '_tests",')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800894 self.write(' test_suites: ["general-tests"],')
895 self.write(' auto_gen_config: true,')
Joel Galensone261a152021-01-12 11:31:53 -0800896 if 'test' in self.crate_types and self.host_supported:
897 self.write(' test_options: {')
898 self.write(' unit_test: true,')
899 self.write(' },')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800900
901 def dump_android_externs(self):
902 """Dump the dependent rlibs and dylibs property."""
903 so_libs = list()
904 rust_libs = ''
905 deps_libname = re.compile('^.* = lib(.*)-[0-9a-f]*.(rlib|so|rmeta)$')
906 for lib in self.externs:
907 # normal value of lib: "libc = liblibc-*.rlib"
908 # strange case in rand crate: "getrandom_package = libgetrandom-*.rlib"
909 # we should use "libgetrandom", not "lib" + "getrandom_package"
910 groups = deps_libname.match(lib)
911 if groups is not None:
912 lib_name = groups.group(1)
913 else:
914 lib_name = re.sub(' .*$', '', lib)
Joel Galenson97e414a2021-05-27 09:42:32 -0700915 if lib_name in self.runner.args.dependency_blocklist:
916 continue
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800917 if lib.endswith('.rlib') or lib.endswith('.rmeta'):
918 # On MacOS .rmeta is used when Linux uses .rlib or .rmeta.
919 rust_libs += ' "' + altered_name('lib' + lib_name) + '",\n'
920 elif lib.endswith('.so'):
921 so_libs.append(lib_name)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700922 elif lib != 'proc_macro': # --extern proc_macro is special and ignored
923 rust_libs += ' // ERROR: unknown type of lib ' + lib + '\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800924 if rust_libs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700925 self.write(' rustlibs: [\n' + rust_libs + ' ],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800926 # Are all dependent .so files proc_macros?
927 # TODO(chh): Separate proc_macros and dylib.
928 self.dump_android_property_list('proc_macros', '"lib%s"', so_libs)
929
930
931class ARObject(object):
932 """Information of an "ar" link command."""
933
934 def __init__(self, runner, outf_name):
935 # Remembered global runner and its members.
936 self.runner = runner
937 self.pkg = ''
938 self.outf_name = outf_name # path to Android.bp
939 # "ar" arguments
940 self.line_num = 1
941 self.line = ''
942 self.flags = '' # e.g. "crs"
943 self.lib = '' # e.g. "/.../out/lib*.a"
944 self.objs = list() # e.g. "/.../out/.../*.o"
945
946 def parse(self, pkg, line_num, args_line):
947 """Collect ar obj/lib file names."""
948 self.pkg = pkg
949 self.line_num = line_num
950 self.line = args_line
951 args = args_line.split()
952 num_args = len(args)
953 if num_args < 3:
954 print('ERROR: "ar" command has too few arguments', args_line)
955 else:
956 self.flags = unquote(args[0])
957 self.lib = unquote(args[1])
958 self.objs = sorted(set(map(unquote, args[2:])))
959 return self
960
961 def write(self, s):
962 self.outf.write(s + '\n')
963
964 def dump_debug_info(self):
965 self.write('\n// Line ' + str(self.line_num) + ' "ar" ' + self.line)
966 self.write('// ar_object for %12s' % self.pkg)
967 self.write('// flags = %s' % self.flags)
968 self.write('// lib = %s' % short_out_name(self.pkg, self.lib))
969 for o in self.objs:
970 self.write('// obj = %s' % short_out_name(self.pkg, o))
971
972 def dump_android_lib(self):
973 """Write cc_library_static into Android.bp."""
974 self.write('\ncc_library_static {')
975 self.write(' name: "' + file_base_name(self.lib) + '",')
976 self.write(' host_supported: true,')
977 if self.flags != 'crs':
978 self.write(' // ar flags = %s' % self.flags)
979 if self.pkg not in self.runner.pkg_obj2cc:
980 self.write(' ERROR: cannot find source files.\n}')
981 return
982 self.write(' srcs: [')
983 obj2cc = self.runner.pkg_obj2cc[self.pkg]
984 # Note: wflags are ignored.
985 dflags = list()
986 fflags = list()
987 for obj in self.objs:
988 self.write(' "' + short_out_name(self.pkg, obj2cc[obj].src) + '",')
989 # TODO(chh): union of dflags and flags of all obj
990 # Now, just a temporary hack that uses the last obj's flags
991 dflags = obj2cc[obj].dflags
992 fflags = obj2cc[obj].fflags
993 self.write(' ],')
994 self.write(' cflags: [')
995 self.write(' "-O3",') # TODO(chh): is this default correct?
996 self.write(' "-Wno-error",')
997 for x in fflags:
998 self.write(' "-f' + x + '",')
999 for x in dflags:
1000 self.write(' "-D' + x + '",')
1001 self.write(' ],')
1002 self.write('}')
1003
1004 def dump(self):
1005 """Dump error/debug/module info to the output .bp file."""
1006 self.runner.init_bp_file(self.outf_name)
1007 with open(self.outf_name, 'a') as outf:
1008 self.outf = outf
1009 if self.runner.args.debug:
1010 self.dump_debug_info()
1011 self.dump_android_lib()
1012
1013
1014class CCObject(object):
1015 """Information of a "cc" compilation command."""
1016
1017 def __init__(self, runner, outf_name):
1018 # Remembered global runner and its members.
1019 self.runner = runner
1020 self.pkg = ''
1021 self.outf_name = outf_name # path to Android.bp
1022 # "cc" arguments
1023 self.line_num = 1
1024 self.line = ''
1025 self.src = ''
1026 self.obj = ''
1027 self.dflags = list() # -D flags
1028 self.fflags = list() # -f flags
1029 self.iflags = list() # -I flags
1030 self.wflags = list() # -W flags
1031 self.other_args = list()
1032
1033 def parse(self, pkg, line_num, args_line):
1034 """Collect cc compilation flags and src/out file names."""
1035 self.pkg = pkg
1036 self.line_num = line_num
1037 self.line = args_line
1038 args = args_line.split()
1039 i = 0
1040 while i < len(args):
1041 arg = args[i]
1042 if arg == '"-c"':
1043 i += 1
1044 if args[i].startswith('"-o'):
1045 # ring-0.13.5 dumps: ... "-c" "-o/.../*.o" ".../*.c"
1046 self.obj = unquote(args[i])[2:]
1047 i += 1
1048 self.src = unquote(args[i])
1049 else:
1050 self.src = unquote(args[i])
1051 elif arg == '"-o"':
1052 i += 1
1053 self.obj = unquote(args[i])
1054 elif arg == '"-I"':
1055 i += 1
1056 self.iflags.append(unquote(args[i]))
1057 elif arg.startswith('"-D'):
1058 self.dflags.append(unquote(args[i])[2:])
1059 elif arg.startswith('"-f'):
1060 self.fflags.append(unquote(args[i])[2:])
1061 elif arg.startswith('"-W'):
1062 self.wflags.append(unquote(args[i])[2:])
1063 elif not (arg.startswith('"-O') or arg == '"-m64"' or arg == '"-g"' or
1064 arg == '"-g3"'):
1065 # ignore -O -m64 -g
1066 self.other_args.append(unquote(args[i]))
1067 i += 1
1068 self.dflags = sorted(set(self.dflags))
1069 self.fflags = sorted(set(self.fflags))
1070 # self.wflags is not sorted because some are order sensitive
1071 # and we ignore them anyway.
1072 if self.pkg not in self.runner.pkg_obj2cc:
1073 self.runner.pkg_obj2cc[self.pkg] = {}
1074 self.runner.pkg_obj2cc[self.pkg][self.obj] = self
1075 return self
1076
1077 def write(self, s):
1078 self.outf.write(s + '\n')
1079
1080 def dump_debug_flags(self, name, flags):
1081 self.write('// ' + name + ':')
1082 for f in flags:
1083 self.write('// %s' % f)
1084
1085 def dump(self):
1086 """Dump only error/debug info to the output .bp file."""
1087 if not self.runner.args.debug:
1088 return
1089 self.runner.init_bp_file(self.outf_name)
1090 with open(self.outf_name, 'a') as outf:
1091 self.outf = outf
1092 self.write('\n// Line ' + str(self.line_num) + ' "cc" ' + self.line)
1093 self.write('// cc_object for %12s' % self.pkg)
1094 self.write('// src = %s' % short_out_name(self.pkg, self.src))
1095 self.write('// obj = %s' % short_out_name(self.pkg, self.obj))
1096 self.dump_debug_flags('-I flags', self.iflags)
1097 self.dump_debug_flags('-D flags', self.dflags)
1098 self.dump_debug_flags('-f flags', self.fflags)
1099 self.dump_debug_flags('-W flags', self.wflags)
1100 if self.other_args:
1101 self.dump_debug_flags('other args', self.other_args)
1102
1103
1104class Runner(object):
1105 """Main class to parse cargo -v output and print Android module definitions."""
1106
1107 def __init__(self, args):
1108 self.bp_files = set() # Remember all output Android.bp files.
1109 self.root_pkg = '' # name of package in ./Cargo.toml
1110 # Saved flags, modes, and data.
1111 self.args = args
1112 self.dry_run = not args.run
1113 self.skip_cargo = args.skipcargo
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001114 self.cargo_path = './cargo' # path to cargo, will be set later
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001115 self.checked_out_files = False # to check only once
1116 self.build_out_files = [] # output files generated by build.rs
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001117 # All cc/ar objects, crates, dependencies, and warning files
1118 self.cc_objects = list()
1119 self.pkg_obj2cc = {}
1120 # pkg_obj2cc[cc_object[i].pkg][cc_objects[i].obj] = cc_objects[i]
1121 self.ar_objects = list()
1122 self.crates = list()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001123 self.warning_files = set()
1124 # Keep a unique mapping from (module name) to crate
1125 self.name_owners = {}
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001126 # Save and dump all errors from cargo to Android.bp.
1127 self.errors = ''
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001128 self.setup_cargo_path()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001129 # Default action is cargo clean, followed by build or user given actions.
1130 if args.cargo:
1131 self.cargo = ['clean'] + args.cargo
1132 else:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001133 default_target = '--target x86_64-unknown-linux-gnu'
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -07001134 # Use the same target for both host and default device builds.
1135 # Same target is used as default in host x86_64 Android compilation.
1136 # Note: b/169872957, prebuilt cargo failed to build vsock
1137 # on x86_64-unknown-linux-musl systems.
1138 self.cargo = ['clean', 'build ' + default_target]
1139 if args.tests:
1140 self.cargo.append('build --tests ' + default_target)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001141
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001142 def setup_cargo_path(self):
1143 """Find cargo in the --cargo_bin or prebuilt rust bin directory."""
1144 if self.args.cargo_bin:
1145 self.cargo_path = os.path.join(self.args.cargo_bin, 'cargo')
1146 if not os.path.isfile(self.cargo_path):
1147 sys.exit('ERROR: cannot find cargo in ' + self.args.cargo_bin)
1148 print('WARNING: using cargo in ' + self.args.cargo_bin)
1149 return
1150 # We have only tested this on Linux.
1151 if platform.system() != 'Linux':
1152 sys.exit('ERROR: this script has only been tested on Linux with cargo.')
1153 # Assuming that this script is in development/scripts.
1154 my_dir = os.path.dirname(os.path.abspath(__file__))
1155 linux_dir = os.path.join(my_dir, '..', '..',
1156 'prebuilts', 'rust', 'linux-x86')
1157 if not os.path.isdir(linux_dir):
1158 sys.exit('ERROR: cannot find directory ' + linux_dir)
1159 rust_version = self.find_rust_version(my_dir, linux_dir)
1160 cargo_bin = os.path.join(linux_dir, rust_version, 'bin')
1161 self.cargo_path = os.path.join(cargo_bin, 'cargo')
1162 if not os.path.isfile(self.cargo_path):
1163 sys.exit('ERROR: cannot find cargo in ' + cargo_bin
1164 + '; please try --cargo_bin= flag.')
1165 return
1166
1167 def find_rust_version(self, my_dir, linux_dir):
1168 """Use my script directory, find prebuilt rust version."""
1169 # First look up build/soong/rust/config/global.go.
1170 path2global = os.path.join(my_dir, '..', '..',
1171 'build', 'soong', 'rust', 'config', 'global.go')
1172 if os.path.isfile(path2global):
1173 # try to find: RustDefaultVersion = "1.44.0"
1174 version_pat = re.compile(
1175 r'\s*RustDefaultVersion\s*=\s*"([0-9]+\.[0-9]+\.[0-9]+)".*$')
1176 with open(path2global, 'r') as inf:
1177 for line in inf:
1178 result = version_pat.match(line)
1179 if result:
1180 return result.group(1)
1181 print('WARNING: cannot find RustDefaultVersion in ' + path2global)
1182 # Otherwise, find the newest (largest) version number in linux_dir.
1183 rust_version = (0, 0, 0) # the prebuilt version to use
1184 version_pat = re.compile(r'([0-9]+)\.([0-9]+)\.([0-9]+)$')
1185 for dir_name in os.listdir(linux_dir):
1186 result = version_pat.match(dir_name)
1187 if not result:
1188 continue
1189 version = (result.group(1), result.group(2), result.group(3))
1190 if version > rust_version:
1191 rust_version = version
1192 return '.'.join(rust_version)
1193
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001194 def find_out_files(self):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001195 # list1 has build.rs output for normal crates
1196 list1 = glob.glob(TARGET_TMP + '/*/*/build/' + self.root_pkg + '-*/out/*')
1197 # list2 has build.rs output for proc-macro crates
1198 list2 = glob.glob(TARGET_TMP + '/*/build/' + self.root_pkg + '-*/out/*')
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001199 return list1 + list2
1200
1201 def copy_out_files(self):
1202 """Copy build.rs output files to ./out and set up build_out_files."""
1203 if self.checked_out_files:
1204 return
1205 self.checked_out_files = True
1206 cargo_out_files = self.find_out_files()
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001207 out_files = set()
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001208 if cargo_out_files:
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001209 os.makedirs('out', exist_ok=True)
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001210 for path in cargo_out_files:
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001211 file_name = path.split('/')[-1]
1212 out_files.add(file_name)
1213 shutil.copy(path, 'out/' + file_name)
1214 self.build_out_files = sorted(out_files)
1215
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001216 def has_used_out_dir(self):
1217 """Returns true if env!("OUT_DIR") is found."""
1218 return 0 == os.system('grep -rl --exclude build.rs --include \\*.rs' +
1219 ' \'env!("OUT_DIR")\' * > /dev/null')
1220
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001221 def copy_out_module_name(self):
1222 if self.args.copy_out and self.build_out_files:
1223 return 'copy_' + self.root_pkg + '_build_out'
1224 else:
1225 return ''
1226
Haibo Huang0f72c952021-03-19 11:34:15 -07001227 def read_license(self, name):
1228 if not os.path.isfile(name):
1229 return ''
1230 license = ''
1231 with open(name, 'r') as intf:
1232 line = intf.readline()
1233 # Firstly skip ANDROID_BP_HEADER
1234 while line.startswith('//'):
1235 line = intf.readline()
Joel Galensond9d13b82021-04-05 11:27:55 -07001236 # Read all lines until we see a rust_* or genrule rule.
1237 while line != '' and not (line.startswith('rust_') or line.startswith('genrule {')):
Haibo Huang0f72c952021-03-19 11:34:15 -07001238 license += line
1239 line = intf.readline()
1240 return license.strip()
1241
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001242 def dump_copy_out_module(self, outf):
1243 """Output the genrule module to copy out/* to $(genDir)."""
1244 copy_out = self.copy_out_module_name()
1245 if not copy_out:
1246 return
1247 outf.write('\ngenrule {\n')
1248 outf.write(' name: "' + copy_out + '",\n')
1249 outf.write(' srcs: ["out/*"],\n')
1250 outf.write(' cmd: "cp $(in) $(genDir)",\n')
1251 if len(self.build_out_files) > 1:
1252 outf.write(' out: [\n')
1253 for f in self.build_out_files:
1254 outf.write(' "' + f + '",\n')
1255 outf.write(' ],\n')
1256 else:
1257 outf.write(' out: ["' + self.build_out_files[0] + '"],\n')
1258 outf.write('}\n')
1259
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001260 def init_bp_file(self, name):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001261 # name could be Android.bp or sub_dir_path/Android.bp
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001262 if name not in self.bp_files:
1263 self.bp_files.add(name)
Haibo Huang0f72c952021-03-19 11:34:15 -07001264 license_section = self.read_license(name)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001265 with open(name, 'w') as outf:
Joel Galenson367360c2021-04-29 14:31:43 -07001266 print_args = filter(lambda x: x != "--no-test-mapping", sys.argv[1:])
1267 outf.write(ANDROID_BP_HEADER.format(args=' '.join(print_args)))
Haibo Huang0f72c952021-03-19 11:34:15 -07001268 outf.write('\n')
1269 outf.write(license_section)
1270 outf.write('\n')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001271 # at most one copy_out module per .bp file
1272 self.dump_copy_out_module(outf)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001273
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001274 def try_claim_module_name(self, name, owner):
1275 """Reserve and return True if it has not been reserved yet."""
1276 if name not in self.name_owners or owner == self.name_owners[name]:
1277 self.name_owners[name] = owner
1278 return True
1279 return False
1280
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001281 def claim_module_name(self, prefix, owner, counter):
1282 """Return prefix if not owned yet, otherwise, prefix+str(counter)."""
1283 while True:
1284 name = prefix
1285 if counter > 0:
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001286 name += '_' + str(counter)
1287 if self.try_claim_module_name(name, owner):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001288 return name
1289 counter += 1
1290
1291 def find_root_pkg(self):
1292 """Read name of [package] in ./Cargo.toml."""
1293 if not os.path.exists('./Cargo.toml'):
1294 return
1295 with open('./Cargo.toml', 'r') as inf:
1296 pkg_section = re.compile(r'^ *\[package\]')
1297 name = re.compile('^ *name *= * "([^"]*)"')
1298 in_pkg = False
1299 for line in inf:
1300 if in_pkg:
1301 if name.match(line):
1302 self.root_pkg = name.match(line).group(1)
1303 break
1304 else:
1305 in_pkg = pkg_section.match(line) is not None
1306
1307 def run_cargo(self):
1308 """Calls cargo -v and save its output to ./cargo.out."""
1309 if self.skip_cargo:
1310 return self
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001311 cargo_toml = './Cargo.toml'
1312 cargo_out = './cargo.out'
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001313 # Do not use Cargo.lock, because .bp rules are designed to
1314 # run with "latest" crates avaialable on Android.
1315 cargo_lock = './Cargo.lock'
1316 cargo_lock_saved = './cargo.lock.saved'
1317 had_cargo_lock = os.path.exists(cargo_lock)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001318 if not os.access(cargo_toml, os.R_OK):
1319 print('ERROR: Cannot find or read', cargo_toml)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001320 return self
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001321 if not self.dry_run:
1322 if os.path.exists(cargo_out):
1323 os.remove(cargo_out)
1324 if not self.args.use_cargo_lock and had_cargo_lock: # save it
1325 os.rename(cargo_lock, cargo_lock_saved)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001326 cmd_tail = ' --target-dir ' + TARGET_TMP + ' >> ' + cargo_out + ' 2>&1'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001327 # set up search PATH for cargo to find the correct rustc
1328 saved_path = os.environ['PATH']
1329 os.environ['PATH'] = os.path.dirname(self.cargo_path) + ':' + saved_path
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001330 # Add [workspace] to Cargo.toml if it is not there.
1331 added_workspace = False
1332 if self.args.add_workspace:
1333 with open(cargo_toml, 'r') as in_file:
1334 cargo_toml_lines = in_file.readlines()
1335 found_workspace = '[workspace]\n' in cargo_toml_lines
1336 if found_workspace:
1337 print('### WARNING: found [workspace] in Cargo.toml')
1338 else:
1339 with open(cargo_toml, 'a') as out_file:
1340 out_file.write('[workspace]\n')
1341 added_workspace = True
1342 if self.args.verbose:
1343 print('### INFO: added [workspace] to Cargo.toml')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001344 for c in self.cargo:
1345 features = ''
Chih-Hung Hsieh6c8d52f2020-03-30 18:28:52 -07001346 if c != 'clean':
1347 if self.args.features is not None:
1348 features = ' --no-default-features'
1349 if self.args.features:
1350 features += ' --features ' + self.args.features
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001351 cmd_v_flag = ' -vv ' if self.args.vv else ' -v '
1352 cmd = self.cargo_path + cmd_v_flag
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001353 cmd += c + features + cmd_tail
1354 if self.args.rustflags and c != 'clean':
1355 cmd = 'RUSTFLAGS="' + self.args.rustflags + '" ' + cmd
1356 if self.dry_run:
1357 print('Dry-run skip:', cmd)
1358 else:
1359 if self.args.verbose:
1360 print('Running:', cmd)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001361 with open(cargo_out, 'a') as out_file:
1362 out_file.write('### Running: ' + cmd + '\n')
Joel Galenson6bf54e32021-05-17 10:54:50 -07001363 ret = os.system(cmd)
1364 if ret != 0:
1365 print('*** There was an error while running cargo. ' +
1366 'See the cargo.out file for details.')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001367 if added_workspace: # restore original Cargo.toml
1368 with open(cargo_toml, 'w') as out_file:
1369 out_file.writelines(cargo_toml_lines)
1370 if self.args.verbose:
1371 print('### INFO: restored original Cargo.toml')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001372 os.environ['PATH'] = saved_path
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001373 if not self.dry_run:
1374 if not had_cargo_lock: # restore to no Cargo.lock state
1375 os.remove(cargo_lock)
1376 elif not self.args.use_cargo_lock: # restore saved Cargo.lock
1377 os.rename(cargo_lock_saved, cargo_lock)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001378 return self
1379
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001380 def dump_pkg_obj2cc(self):
1381 """Dump debug info of the pkg_obj2cc map."""
1382 if not self.args.debug:
1383 return
1384 self.init_bp_file('Android.bp')
1385 with open('Android.bp', 'a') as outf:
1386 sorted_pkgs = sorted(self.pkg_obj2cc.keys())
1387 for pkg in sorted_pkgs:
1388 if not self.pkg_obj2cc[pkg]:
1389 continue
1390 outf.write('\n// obj => src for %s\n' % pkg)
1391 obj2cc = self.pkg_obj2cc[pkg]
1392 for obj in sorted(obj2cc.keys()):
1393 outf.write('// ' + short_out_name(pkg, obj) + ' => ' +
1394 short_out_name(pkg, obj2cc[obj].src) + '\n')
1395
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001396 def apply_patch(self):
1397 """Apply local patch file if it is given."""
1398 if self.args.patch:
1399 if self.dry_run:
1400 print('Dry-run skip patch file:', self.args.patch)
1401 else:
1402 if not os.path.exists(self.args.patch):
1403 self.append_to_bp('ERROR cannot find patch file: ' + self.args.patch)
1404 return self
1405 if self.args.verbose:
1406 print('### INFO: applying local patch file:', self.args.patch)
Joel Galenson7e8247e2021-05-20 18:51:42 -07001407 subprocess.run(['patch', '-s', '--no-backup-if-mismatch', './Android.bp',
1408 self.args.patch], check=True)
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001409 return self
1410
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001411 def gen_bp(self):
1412 """Parse cargo.out and generate Android.bp files."""
1413 if self.dry_run:
1414 print('Dry-run skip: read', CARGO_OUT, 'write Android.bp')
1415 elif os.path.exists(CARGO_OUT):
1416 self.find_root_pkg()
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001417 if self.args.copy_out:
1418 self.copy_out_files()
1419 elif self.find_out_files() and self.has_used_out_dir():
1420 print('WARNING: ' + self.root_pkg + ' has cargo output files; ' +
1421 'please rerun with the --copy-out flag.')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001422 with open(CARGO_OUT, 'r') as cargo_out:
1423 self.parse(cargo_out, 'Android.bp')
1424 self.crates.sort(key=get_module_name)
1425 for obj in self.cc_objects:
1426 obj.dump()
1427 self.dump_pkg_obj2cc()
1428 for crate in self.crates:
1429 crate.dump()
1430 dumped_libs = set()
1431 for lib in self.ar_objects:
1432 if lib.pkg == self.root_pkg:
1433 lib_name = file_base_name(lib.lib)
1434 if lib_name not in dumped_libs:
1435 dumped_libs.add(lib_name)
1436 lib.dump()
Joel Galenson5664f2a2021-06-10 10:13:49 -07001437 if self.args.add_toplevel_block:
1438 with open(self.args.add_toplevel_block, 'r') as f:
1439 self.append_to_bp('\n' + f.read() + '\n')
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001440 if self.errors:
Joel Galenson3f42f802021-04-07 12:42:17 -07001441 self.append_to_bp('\n' + ERRORS_LINE + '\n' + self.errors)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001442 return self
1443
1444 def add_ar_object(self, obj):
1445 self.ar_objects.append(obj)
1446
1447 def add_cc_object(self, obj):
1448 self.cc_objects.append(obj)
1449
1450 def add_crate(self, crate):
1451 """Merge crate with someone in crates, or append to it. Return crates."""
1452 if crate.skip_crate():
1453 if self.args.debug: # include debug info of all crates
1454 self.crates.append(crate)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001455 else:
1456 for c in self.crates:
1457 if c.merge(crate, 'Android.bp'):
1458 return
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001459 # If not merged, decide module type and name now.
1460 crate.decide_module_type()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001461 self.crates.append(crate)
1462
1463 def find_warning_owners(self):
1464 """For each warning file, find its owner crate."""
1465 missing_owner = False
1466 for f in self.warning_files:
1467 cargo_dir = '' # find lowest crate, with longest path
1468 owner = None # owner crate of this warning
1469 for c in self.crates:
1470 if (f.startswith(c.cargo_dir + '/') and
1471 len(cargo_dir) < len(c.cargo_dir)):
1472 cargo_dir = c.cargo_dir
1473 owner = c
1474 if owner:
1475 owner.has_warning = True
1476 else:
1477 missing_owner = True
1478 if missing_owner and os.path.exists('Cargo.toml'):
1479 # owner is the root cargo, with empty cargo_dir
1480 for c in self.crates:
1481 if not c.cargo_dir:
1482 c.has_warning = True
1483
1484 def rustc_command(self, n, rustc_line, line, outf_name):
1485 """Process a rustc command line from cargo -vv output."""
1486 # cargo build -vv output can have multiple lines for a rustc command
1487 # due to '\n' in strings for environment variables.
1488 # strip removes leading spaces and '\n' at the end
1489 new_rustc = (rustc_line.strip() + line) if rustc_line else line
1490 # Use an heuristic to detect the completions of a multi-line command.
1491 # This might fail for some very rare case, but easy to fix manually.
1492 if not line.endswith('`\n') or (new_rustc.count('`') % 2) != 0:
1493 return new_rustc
1494 if RUSTC_VV_CMD_ARGS.match(new_rustc):
1495 args = RUSTC_VV_CMD_ARGS.match(new_rustc).group(1)
1496 self.add_crate(Crate(self, outf_name).parse(n, args))
1497 else:
1498 self.assert_empty_vv_line(new_rustc)
1499 return ''
1500
1501 def cc_ar_command(self, n, groups, outf_name):
1502 pkg = groups.group(1)
1503 line = groups.group(3)
1504 if groups.group(2) == 'cc':
1505 self.add_cc_object(CCObject(self, outf_name).parse(pkg, n, line))
1506 else:
1507 self.add_ar_object(ARObject(self, outf_name).parse(pkg, n, line))
1508
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001509 def append_to_bp(self, line):
1510 self.init_bp_file('Android.bp')
1511 with open('Android.bp', 'a') as outf:
1512 outf.write(line)
1513
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001514 def assert_empty_vv_line(self, line):
1515 if line: # report error if line is not empty
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001516 self.append_to_bp('ERROR -vv line: ' + line)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001517 return ''
1518
1519 def parse(self, inf, outf_name):
1520 """Parse rustc and warning messages in inf, return a list of Crates."""
1521 n = 0 # line number
1522 prev_warning = False # true if the previous line was warning: ...
1523 rustc_line = '' # previous line(s) matching RUSTC_VV_PAT
1524 for line in inf:
1525 n += 1
1526 if line.startswith('warning: '):
1527 prev_warning = True
1528 rustc_line = self.assert_empty_vv_line(rustc_line)
1529 continue
1530 new_rustc = ''
1531 if RUSTC_PAT.match(line):
1532 args_line = RUSTC_PAT.match(line).group(1)
1533 self.add_crate(Crate(self, outf_name).parse(n, args_line))
1534 self.assert_empty_vv_line(rustc_line)
1535 elif rustc_line or RUSTC_VV_PAT.match(line):
1536 new_rustc = self.rustc_command(n, rustc_line, line, outf_name)
1537 elif CC_AR_VV_PAT.match(line):
1538 self.cc_ar_command(n, CC_AR_VV_PAT.match(line), outf_name)
1539 elif prev_warning and WARNING_FILE_PAT.match(line):
1540 self.assert_empty_vv_line(rustc_line)
1541 fpath = WARNING_FILE_PAT.match(line).group(1)
1542 if fpath[0] != '/': # ignore absolute path
1543 self.warning_files.add(fpath)
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001544 elif line.startswith('error: ') or line.startswith('error[E'):
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001545 if not self.args.ignore_cargo_errors:
1546 self.errors += line
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001547 prev_warning = False
1548 rustc_line = new_rustc
1549 self.find_warning_owners()
1550
1551
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001552def get_parser():
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001553 """Parse main arguments."""
1554 parser = argparse.ArgumentParser('cargo2android')
1555 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001556 '--add_workspace',
1557 action='store_true',
1558 default=False,
1559 help=('append [workspace] to Cargo.toml before calling cargo,' +
1560 ' to treat current directory as root of package source;' +
1561 ' otherwise the relative source file path in generated' +
1562 ' .bp file will be from the parent directory.'))
1563 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001564 '--cargo',
1565 action='append',
1566 metavar='args_string',
1567 help=('extra cargo build -v args in a string, ' +
1568 'each --cargo flag calls cargo build -v once'))
1569 parser.add_argument(
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001570 '--cargo_bin',
1571 type=str,
1572 help='use cargo in the cargo_bin directory instead of the prebuilt one')
1573 parser.add_argument(
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001574 '--copy-out',
1575 action='store_true',
1576 default=False,
1577 help=('only for root directory, ' +
1578 'copy build.rs output to ./out/* and add a genrule to copy ' +
1579 './out/* to genrule output; for crates with code pattern: ' +
1580 'include!(concat!(env!("OUT_DIR"), "/<some_file>.rs"))'))
1581 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001582 '--debug',
1583 action='store_true',
1584 default=False,
1585 help='dump debug info into Android.bp')
1586 parser.add_argument(
1587 '--dependencies',
1588 action='store_true',
1589 default=False,
Joel Galenson833848c2021-08-17 10:50:42 -07001590 help='Deprecated. Has no effect.')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001591 parser.add_argument(
1592 '--device',
1593 action='store_true',
1594 default=False,
1595 help='run cargo also for a default device target')
1596 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001597 '--features',
1598 type=str,
1599 help=('pass features to cargo build, ' +
1600 'empty string means no default features'))
1601 parser.add_argument(
1602 '--global_defaults',
1603 type=str,
1604 help='add a defaults name to every module')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001605 parser.add_argument(
1606 '--host-first-multilib',
1607 action='store_true',
1608 default=False,
1609 help=('add a compile_multilib:"first" property ' +
1610 'to Android.bp host modules.'))
1611 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001612 '--ignore-cargo-errors',
1613 action='store_true',
1614 default=False,
1615 help='do not append cargo/rustc error messages to Android.bp')
1616 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001617 '--no-host',
1618 action='store_true',
1619 default=False,
1620 help='do not run cargo for the host; only for the device target')
1621 parser.add_argument(
1622 '--no-subdir',
1623 action='store_true',
1624 default=False,
1625 help='do not output anything for sub-directories')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001626 parser.add_argument(
1627 '--onefile',
1628 action='store_true',
1629 default=False,
1630 help=('output all into one ./Android.bp, default will generate ' +
1631 'one Android.bp per Cargo.toml in subdirectories'))
1632 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001633 '--patch',
1634 type=str,
1635 help='apply the given patch file to generated ./Android.bp')
1636 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001637 '--run',
1638 action='store_true',
1639 default=False,
1640 help='run it, default is dry-run')
1641 parser.add_argument('--rustflags', type=str, help='passing flags to rustc')
1642 parser.add_argument(
1643 '--skipcargo',
1644 action='store_true',
1645 default=False,
1646 help='skip cargo command, parse cargo.out, and generate Android.bp')
1647 parser.add_argument(
1648 '--tests',
1649 action='store_true',
1650 default=False,
1651 help='run cargo build --tests after normal build')
1652 parser.add_argument(
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001653 '--use-cargo-lock',
1654 action='store_true',
1655 default=False,
1656 help=('run cargo build with existing Cargo.lock ' +
1657 '(used when some latest dependent crates failed)'))
1658 parser.add_argument(
Matthew Maurer062709c2021-08-17 11:27:36 -07001659 '--exported_c_header_dir',
1660 nargs='*',
1661 help='Directories with headers to export for C usage'
1662 )
1663 parser.add_argument(
Joel Galensond9c4de62021-04-23 10:26:40 -07001664 '--min-sdk-version',
1665 type=str,
1666 help='Minimum SDK version')
1667 parser.add_argument(
1668 '--apex-available',
1669 nargs='*',
1670 help='Mark the main library as apex_available with the given apexes.')
1671 parser.add_argument(
Matthew Maurerac677252021-08-13 15:52:52 -07001672 '--native-bridge-supported',
1673 action='store_true',
1674 default=False,
1675 help='Mark the main library as native_bridge_supported.')
1676 parser.add_argument(
1677 '--product-available',
1678 action='store_true',
1679 default=False,
1680 help='Mark the main library as product_available.')
1681 parser.add_argument(
1682 '--recovery-available',
1683 action='store_true',
1684 default=False,
1685 help='Mark the main library as recovery_available.')
1686 parser.add_argument(
Ivan Lozano91920862021-07-19 10:49:08 -04001687 '--vendor-available',
1688 action='store_true',
1689 default=False,
1690 help='Mark the main library as vendor_available.')
1691 parser.add_argument(
1692 '--vendor-ramdisk-available',
1693 action='store_true',
1694 default=False,
1695 help='Mark the main library as vendor_ramdisk_available.')
1696 parser.add_argument(
Matthew Maurerac677252021-08-13 15:52:52 -07001697 '--ramdisk-available',
1698 action='store_true',
1699 default=False,
1700 help='Mark the main library as ramdisk_available.')
1701 parser.add_argument(
Joel Galensoncb5f2f02021-06-08 14:47:55 -07001702 '--force-rlib',
1703 action='store_true',
1704 default=False,
1705 help='Make the main library an rlib.')
1706 parser.add_argument(
Joel Galenson12467e52021-07-12 14:33:28 -07001707 '--whole-static-libs',
1708 nargs='*',
1709 default=[],
1710 help='Make the given libraries (without lib prefixes) whole_static_libs.')
1711 parser.add_argument(
Ivan Lozano26aa1c32021-08-16 11:20:32 -04001712 '--no-pkg-vers',
1713 action='store_true',
1714 default=False,
1715 help='Do not attempt to determine the package version automatically.')
1716 parser.add_argument(
Joel Galensone4f53882021-07-19 11:14:55 -07001717 '--test-data',
1718 nargs='*',
1719 default=[],
1720 help=('Add the given file to the given test\'s data property. ' +
1721 'Usage: test-path=data-path'))
1722 parser.add_argument(
Joel Galenson97e414a2021-05-27 09:42:32 -07001723 '--dependency-blocklist',
1724 nargs='*',
1725 default=[],
Joel Galenson12467e52021-07-12 14:33:28 -07001726 help='Do not emit the given dependencies (without lib prefixes).')
Joel Galenson97e414a2021-05-27 09:42:32 -07001727 parser.add_argument(
Joel Galensoncb5f2f02021-06-08 14:47:55 -07001728 '--lib-blocklist',
1729 nargs='*',
1730 default=[],
Joel Galenson12467e52021-07-12 14:33:28 -07001731 help='Do not emit the given C libraries as dependencies (without lib prefixes).')
Joel Galensoncb5f2f02021-06-08 14:47:55 -07001732 parser.add_argument(
Joel Galensonf6b3c912021-06-03 16:00:54 -07001733 '--test-blocklist',
1734 nargs='*',
1735 default=[],
1736 help=('Do not emit the given tests. ' +
1737 'Pass the path to the test file to exclude.'))
1738 parser.add_argument(
Joel Galenson3d6d1e72021-06-07 15:00:24 -07001739 '--cfg-blocklist',
1740 nargs='*',
1741 default=[],
1742 help='Do not emit the given cfg.')
1743 parser.add_argument(
Joel Galenson5664f2a2021-06-10 10:13:49 -07001744 '--add-toplevel-block',
1745 type=str,
1746 help='Add the contents of the given file to the top level of the Android.bp.')
1747 parser.add_argument(
1748 '--add-module-block',
1749 type=str,
1750 help='Add the contents of the given file to the main module.')
1751 parser.add_argument(
Joel Galensoncf4ebb02021-04-07 08:39:51 -07001752 '--no-test-mapping',
1753 action='store_true',
1754 default=False,
ThiƩbaud Weksteen198e93f2021-07-02 14:49:19 +02001755 help='Deprecated. Has no effect.')
Joel Galensoncf4ebb02021-04-07 08:39:51 -07001756 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001757 '--verbose',
1758 action='store_true',
1759 default=False,
1760 help='echo executed commands')
1761 parser.add_argument(
1762 '--vv',
1763 action='store_true',
1764 default=False,
1765 help='run cargo with -vv instead of default -v')
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001766 parser.add_argument(
1767 '--dump-config-and-exit',
1768 type=str,
1769 help=('Dump command-line arguments (minus this flag) to a config file and exit. ' +
1770 'This is intended to help migrate from command line options to config files.'))
1771 parser.add_argument(
1772 '--config',
1773 type=str,
1774 help=('Load command-line options from the given config file. ' +
1775 'Options in this file will override those passed on the command line.'))
1776 return parser
1777
1778
1779def parse_args(parser):
1780 """Parses command-line options."""
1781 args = parser.parse_args()
1782 # Use the values specified in a config file if one was found.
1783 if args.config:
1784 with open(args.config, 'r') as f:
1785 config = json.load(f)
1786 args_dict = vars(args)
1787 for arg in config:
1788 args_dict[arg.replace('-', '_')] = config[arg]
1789 return args
1790
1791
1792def dump_config(parser, args):
1793 """Writes the non-default command-line options to the specified file."""
1794 args_dict = vars(args)
1795 # Filter out the arguments that have their default value.
Joel Galenson367360c2021-04-29 14:31:43 -07001796 # Also filter certain "temporary" arguments.
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001797 non_default_args = {}
1798 for arg in args_dict:
Joel Galenson367360c2021-04-29 14:31:43 -07001799 if args_dict[arg] != parser.get_default(
1800 arg) and arg != 'dump_config_and_exit' and arg != 'no_test_mapping':
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001801 non_default_args[arg.replace('_', '-')] = args_dict[arg]
1802 # Write to the specified file.
1803 with open(args.dump_config_and_exit, 'w') as f:
1804 json.dump(non_default_args, f, indent=2, sort_keys=True)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001805
1806
1807def main():
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001808 parser = get_parser()
1809 args = parse_args(parser)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001810 if not args.run: # default is dry-run
1811 print(DRY_RUN_NOTE)
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001812 if args.dump_config_and_exit:
1813 dump_config(parser, args)
1814 else:
ThiƩbaud Weksteen198e93f2021-07-02 14:49:19 +02001815 Runner(args).run_cargo().gen_bp().apply_patch()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001816
1817
1818if __name__ == '__main__':
1819 main()