blob: 937d7775486fbead0da78955d72e9f78e8787eb9 [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.
Jason Macnak051340d2021-09-04 11:04:26 -070066 'libash': 'libash_rust',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080067 'libbacktrace': 'libbacktrace_rust',
Andrew Walbrane51f1042020-08-11 16:42:48 +010068 'libbase': 'libbase_rust',
Luke Huanga1371af2021-06-29 18:04:40 +080069 'libbase64': 'libbase64_rust',
Victor Hsieh21bea792020-12-04 10:59:16 -080070 'libfuse': 'libfuse_rust',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080071 'libgcc': 'libgcc_rust',
72 'liblog': 'liblog_rust',
Chih-Hung Hsieh07119862020-07-24 15:34:06 -070073 'libminijail': 'libminijail_rust',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080074 'libsync': 'libsync_rust',
75 'libx86_64': 'libx86_64_rust',
Jooyung Hana427c9b2021-07-16 08:53:14 +090076 'libxml': 'libxml_rust',
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -070077 'protoc_gen_rust': 'protoc-gen-rust',
78}
79
80RENAME_STEM_MAP = {
81 # This map includes all changes to the default rust module stem names,
82 # which is used for output files when different from the module name.
83 'protoc_gen_rust': 'protoc-gen-rust',
84}
85
86RENAME_DEFAULTS_MAP = {
87 # This map includes all changes to the default prefix of rust_default
88 # module names, to avoid conflict with existing Android modules.
89 'libc': 'rust_libc',
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080090}
91
92# Header added to all generated Android.bp files.
Joel Galenson56446742021-02-18 08:27:48 -080093ANDROID_BP_HEADER = (
94 '// This file is generated by cargo2android.py {args}.\n' +
95 '// Do not modify this file as changes will be overridden on upgrade.\n')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -080096
97CARGO_OUT = 'cargo.out' # Name of file to keep cargo build -v output.
98
Joel Galenson3f42f802021-04-07 12:42:17 -070099# This should be kept in sync with tools/external_updater/crates_updater.py.
100ERRORS_LINE = 'Errors in ' + CARGO_OUT + ':'
101
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800102TARGET_TMP = 'target.tmp' # Name of temporary output directory.
103
104# Message to be displayed when this script is called without the --run flag.
105DRY_RUN_NOTE = (
106 'Dry-run: This script uses ./' + TARGET_TMP + ' for output directory,\n' +
107 'runs cargo clean, runs cargo build -v, saves output to ./cargo.out,\n' +
108 'and writes to Android.bp in the current and subdirectories.\n\n' +
109 'To do do all of the above, use the --run flag.\n' +
110 'See --help for other flags, and more usage notes in this script.\n')
111
112# Cargo -v output of a call to rustc.
113RUSTC_PAT = re.compile('^ +Running `rustc (.*)`$')
114
115# Cargo -vv output of a call to rustc could be split into multiple lines.
116# Assume that the first line will contain some CARGO_* env definition.
117RUSTC_VV_PAT = re.compile('^ +Running `.*CARGO_.*=.*$')
118# The combined -vv output rustc command line pattern.
119RUSTC_VV_CMD_ARGS = re.compile('^ *Running `.*CARGO_.*=.* rustc (.*)`$')
120
121# Cargo -vv output of a "cc" or "ar" command; all in one line.
122CC_AR_VV_PAT = re.compile(r'^\[([^ ]*)[^\]]*\] running:? "(cc|ar)" (.*)$')
123# Some package, such as ring-0.13.5, has pattern '... running "cc"'.
124
125# Rustc output of file location path pattern for a warning message.
126WARNING_FILE_PAT = re.compile('^ *--> ([^:]*):[0-9]+')
127
128# Rust package name with suffix -d1.d2.d3.
129VERSION_SUFFIX_PAT = re.compile(r'^(.*)-[0-9]+\.[0-9]+\.[0-9]+$')
130
Matthew Maurer062709c2021-08-17 11:27:36 -0700131# Crate types corresponding to a C ABI library
132C_LIBRARY_CRATE_TYPES = ['staticlib', 'cdylib']
133# Crate types corresponding to a Rust ABI library
134RUST_LIBRARY_CRATE_TYPES = ['lib', 'rlib', 'dylib']
135# Crate types corresponding to a library
136LIBRARY_CRATE_TYPES = C_LIBRARY_CRATE_TYPES + RUST_LIBRARY_CRATE_TYPES
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800137
138def altered_name(name):
139 return RENAME_MAP[name] if (name in RENAME_MAP) else name
140
141
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700142def altered_stem(name):
143 return RENAME_STEM_MAP[name] if (name in RENAME_STEM_MAP) else name
144
145
146def altered_defaults(name):
147 return RENAME_DEFAULTS_MAP[name] if (name in RENAME_DEFAULTS_MAP) else name
148
149
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800150def is_build_crate_name(name):
151 # We added special prefix to build script crate names.
152 return name.startswith('build_script_')
153
154
155def is_dependent_file_path(path):
156 # Absolute or dependent '.../' paths are not main files of this crate.
157 return path.startswith('/') or path.startswith('.../')
158
159
160def get_module_name(crate): # to sort crates in a list
161 return crate.module_name
162
163
164def pkg2crate_name(s):
165 return s.replace('-', '_').replace('.', '_')
166
167
168def file_base_name(path):
169 return os.path.splitext(os.path.basename(path))[0]
170
171
172def test_base_name(path):
173 return pkg2crate_name(file_base_name(path))
174
175
176def unquote(s): # remove quotes around str
177 if s and len(s) > 1 and s[0] == '"' and s[-1] == '"':
178 return s[1:-1]
179 return s
180
181
182def remove_version_suffix(s): # remove -d1.d2.d3 suffix
183 if VERSION_SUFFIX_PAT.match(s):
184 return VERSION_SUFFIX_PAT.match(s).group(1)
185 return s
186
187
188def short_out_name(pkg, s): # replace /.../pkg-*/out/* with .../out/*
189 return re.sub('^/.*/' + pkg + '-[0-9a-f]*/out/', '.../out/', s)
190
191
192def escape_quotes(s): # replace '"' with '\\"'
193 return s.replace('"', '\\"')
194
195
196class Crate(object):
197 """Information of a Rust crate to collect/emit for an Android.bp module."""
198
199 def __init__(self, runner, outf_name):
200 # Remembered global runner and its members.
201 self.runner = runner
202 self.debug = runner.args.debug
203 self.cargo_dir = '' # directory of my Cargo.toml
204 self.outf_name = outf_name # path to Android.bp
205 self.outf = None # open file handle of outf_name during dump*
206 # Variants/results that could be merged from multiple rustc lines.
207 self.host_supported = False
208 self.device_supported = False
209 self.has_warning = False
210 # Android module properties derived from rustc parameters.
211 self.module_name = '' # unique in Android build system
212 self.module_type = '' # rust_{binary,library,test}[_host] etc.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700213 self.defaults = '' # rust_defaults used by rust_test* modules
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700214 self.default_srcs = False # use 'srcs' defined in self.defaults
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800215 self.root_pkg = '' # parent package name of a sub/test packge, from -L
216 self.srcs = list() # main_src or merged multiple source files
217 self.stem = '' # real base name of output file
218 # Kept parsed status
219 self.errors = '' # all errors found during parsing
220 self.line_num = 1 # runner told input source line number
221 self.line = '' # original rustc command line parameters
222 # Parameters collected from rustc command line.
223 self.crate_name = '' # follows --crate-name
224 self.main_src = '' # follows crate_name parameter, shortened
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700225 self.crate_types = list() # follows --crate-type
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800226 self.cfgs = list() # follows --cfg, without feature= prefix
227 self.features = list() # follows --cfg, name in 'feature="..."'
228 self.codegens = list() # follows -C, some ignored
229 self.externs = list() # follows --extern
230 self.core_externs = list() # first part of self.externs elements
231 self.static_libs = list() # e.g. -l static=host_cpuid
232 self.shared_libs = list() # e.g. -l dylib=wayland-client, -l z
233 self.cap_lints = '' # follows --cap-lints
234 self.emit_list = '' # e.g., --emit=dep-info,metadata,link
235 self.edition = '2015' # rustc default, e.g., --edition=2018
236 self.target = '' # follows --target
Ivan Lozanocc660f12021-08-11 16:49:46 -0400237 self.cargo_env_compat = True
238 self.cargo_pkg_version = '' # value extracted from Cargo.toml version field
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800239
240 def write(self, s):
241 # convenient way to output one line at a time with EOL.
242 self.outf.write(s + '\n')
243
244 def same_flags(self, other):
245 # host_supported, device_supported, has_warning are not compared but merged
246 # target is not compared, to merge different target/host modules
247 # externs is not compared; only core_externs is compared
248 return (not self.errors and not other.errors and
249 self.edition == other.edition and
250 self.cap_lints == other.cap_lints and
251 self.emit_list == other.emit_list and
252 self.core_externs == other.core_externs and
253 self.codegens == other.codegens and
254 self.features == other.features and
255 self.static_libs == other.static_libs and
256 self.shared_libs == other.shared_libs and self.cfgs == other.cfgs)
257
258 def merge_host_device(self, other):
259 """Returns true if attributes are the same except host/device support."""
260 return (self.crate_name == other.crate_name and
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700261 self.crate_types == other.crate_types and
262 self.main_src == other.main_src and
263 # before merge, each test module has an unique module name and stem
264 (self.stem == other.stem or self.crate_types == ['test']) and
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800265 self.root_pkg == other.root_pkg and not self.skip_crate() and
266 self.same_flags(other))
267
268 def merge_test(self, other):
269 """Returns true if self and other are tests of same root_pkg."""
270 # Before merger, each test has its own crate_name.
271 # A merged test uses its source file base name as output file name,
272 # so a test is mergeable only if its base name equals to its crate name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700273 return (self.crate_types == other.crate_types and
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700274 self.crate_types == ['test'] and self.root_pkg == other.root_pkg and
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800275 not self.skip_crate() and
276 other.crate_name == test_base_name(other.main_src) and
277 (len(self.srcs) > 1 or
278 (self.crate_name == test_base_name(self.main_src)) and
279 self.host_supported == other.host_supported and
280 self.device_supported == other.device_supported) and
281 self.same_flags(other))
282
283 def merge(self, other, outf_name):
284 """Try to merge crate into self."""
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700285 # Cargo build --tests could recompile a library for tests.
286 # We need to merge such duplicated calls to rustc, with
287 # the algorithm in merge_host_device.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800288 should_merge_host_device = self.merge_host_device(other)
289 should_merge_test = False
290 if not should_merge_host_device:
291 should_merge_test = self.merge_test(other)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800292 if should_merge_host_device or should_merge_test:
293 self.runner.init_bp_file(outf_name)
294 with open(outf_name, 'a') as outf: # to write debug info
295 self.outf = outf
296 other.outf = outf
297 self.do_merge(other, should_merge_test)
298 return True
299 return False
300
301 def do_merge(self, other, should_merge_test):
302 """Merge attributes of other to self."""
303 if self.debug:
304 self.write('\n// Before merge definition (1):')
305 self.dump_debug_info()
306 self.write('\n// Before merge definition (2):')
307 other.dump_debug_info()
308 # Merge properties of other to self.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800309 self.has_warning = self.has_warning or other.has_warning
310 if not self.target: # okay to keep only the first target triple
311 self.target = other.target
312 # decide_module_type sets up default self.stem,
313 # which can be changed if self is a merged test module.
314 self.decide_module_type()
315 if should_merge_test:
Joel Galenson57fa23a2021-07-15 10:47:35 -0700316 if (self.main_src in self.runner.args.test_blocklist and
317 not other.main_src in self.runner.args.test_blocklist):
318 self.main_src = other.main_src
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800319 self.srcs.append(other.main_src)
320 # use a short unique name as the merged module name.
321 prefix = self.root_pkg + '_tests'
322 self.module_name = self.runner.claim_module_name(prefix, self, 0)
323 self.stem = self.module_name
324 # This normalized root_pkg name although might be the same
325 # as other module's crate_name, it is not actually used for
326 # output file name. A merged test module always have multiple
327 # source files and each source file base name is used as
328 # its output file name.
329 self.crate_name = pkg2crate_name(self.root_pkg)
330 if self.debug:
331 self.write('\n// After merge definition (1):')
332 self.dump_debug_info()
333
334 def find_cargo_dir(self):
335 """Deepest directory with Cargo.toml and contains the main_src."""
336 if not is_dependent_file_path(self.main_src):
337 dir_name = os.path.dirname(self.main_src)
338 while dir_name:
339 if os.path.exists(dir_name + '/Cargo.toml'):
340 self.cargo_dir = dir_name
341 return
342 dir_name = os.path.dirname(dir_name)
343
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700344 def add_codegens_flag(self, flag):
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700345 """Ignore options not used in Android."""
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700346 # 'prefer-dynamic' does not work with common flag -C lto
Chih-Hung Hsieh63459ed2020-08-26 11:51:15 -0700347 # 'embed-bitcode' is ignored; we might control LTO with other .bp flag
Chih-Hung Hsieh6c13b722020-09-11 21:24:03 -0700348 # 'codegen-units' is set in Android global config or by default
349 if not (flag.startswith('codegen-units=') or
350 flag.startswith('debuginfo=') or
Chih-Hung Hsieh63459ed2020-08-26 11:51:15 -0700351 flag.startswith('embed-bitcode=') or
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700352 flag.startswith('extra-filename=') or
353 flag.startswith('incremental=') or
354 flag.startswith('metadata=') or
355 flag == 'prefer-dynamic'):
356 self.codegens.append(flag)
357
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800358 def parse(self, line_num, line):
359 """Find important rustc arguments to convert to Android.bp properties."""
360 self.line_num = line_num
361 self.line = line
362 args = line.split() # Loop through every argument of rustc.
363 i = 0
364 while i < len(args):
365 arg = args[i]
366 if arg == '--crate-name':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700367 i += 1
368 self.crate_name = args[i]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800369 elif arg == '--crate-type':
370 i += 1
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700371 # cargo calls rustc with multiple --crate-type flags.
372 # rustc can accept:
373 # --crate-type [bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]
374 self.crate_types.append(args[i])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800375 elif arg == '--test':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700376 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800377 elif arg == '--target':
378 i += 1
379 self.target = args[i]
380 elif arg == '--cfg':
381 i += 1
382 if args[i].startswith('\'feature='):
383 self.features.append(unquote(args[i].replace('\'feature=', '')[:-1]))
384 else:
385 self.cfgs.append(args[i])
386 elif arg == '--extern':
387 i += 1
388 extern_names = re.sub('=/[^ ]*/deps/', ' = ', args[i])
389 self.externs.append(extern_names)
390 self.core_externs.append(re.sub(' = .*', '', extern_names))
391 elif arg == '-C': # codegen options
392 i += 1
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700393 self.add_codegens_flag(args[i])
394 elif arg.startswith('-C'):
395 # cargo has been passing "-C <xyz>" flag to rustc,
396 # but newer cargo could pass '-Cembed-bitcode=no' to rustc.
397 self.add_codegens_flag(arg[2:])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800398 elif arg == '--cap-lints':
399 i += 1
400 self.cap_lints = args[i]
401 elif arg == '-L':
402 i += 1
403 if args[i].startswith('dependency=') and args[i].endswith('/deps'):
404 if '/' + TARGET_TMP + '/' in args[i]:
405 self.root_pkg = re.sub(
406 '^.*/', '', re.sub('/' + TARGET_TMP + '/.*/deps$', '', args[i]))
407 else:
408 self.root_pkg = re.sub('^.*/', '',
409 re.sub('/[^/]+/[^/]+/deps$', '', args[i]))
410 self.root_pkg = remove_version_suffix(self.root_pkg)
411 elif arg == '-l':
412 i += 1
413 if args[i].startswith('static='):
414 self.static_libs.append(re.sub('static=', '', args[i]))
415 elif args[i].startswith('dylib='):
416 self.shared_libs.append(re.sub('dylib=', '', args[i]))
417 else:
418 self.shared_libs.append(args[i])
419 elif arg == '--out-dir' or arg == '--color': # ignored
420 i += 1
421 elif arg.startswith('--error-format=') or arg.startswith('--json='):
422 _ = arg # ignored
423 elif arg.startswith('--emit='):
424 self.emit_list = arg.replace('--emit=', '')
425 elif arg.startswith('--edition='):
426 self.edition = arg.replace('--edition=', '')
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700427 elif not arg.startswith('-'):
428 # shorten imported crate main source paths like $HOME/.cargo/
429 # registry/src/github.com-1ecc6299db9ec823/memchr-2.3.3/src/lib.rs
430 self.main_src = re.sub(r'^/[^ ]*/registry/src/', '.../', args[i])
431 self.main_src = re.sub(r'^\.\.\./github.com-[0-9a-f]*/', '.../',
432 self.main_src)
433 self.find_cargo_dir()
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700434 if self.cargo_dir: # for a subdirectory
435 if self.runner.args.no_subdir: # all .bp content to /dev/null
436 self.outf_name = '/dev/null'
437 elif not self.runner.args.onefile:
438 # Write to Android.bp in the subdirectory with Cargo.toml.
439 self.outf_name = self.cargo_dir + '/Android.bp'
440 self.main_src = self.main_src[len(self.cargo_dir) + 1:]
Ivan Lozanocc660f12021-08-11 16:49:46 -0400441
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800442 else:
443 self.errors += 'ERROR: unknown ' + arg + '\n'
444 i += 1
445 if not self.crate_name:
446 self.errors += 'ERROR: missing --crate-name\n'
447 if not self.main_src:
448 self.errors += 'ERROR: missing main source file\n'
449 else:
450 self.srcs.append(self.main_src)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700451 if not self.crate_types:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800452 # Treat "--cfg test" as "--test"
453 if 'test' in self.cfgs:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700454 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800455 else:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700456 self.errors += 'ERROR: missing --crate-type or --test\n'
457 elif len(self.crate_types) > 1:
458 if 'test' in self.crate_types:
459 self.errors += 'ERROR: cannot handle both --crate-type and --test\n'
460 if 'lib' in self.crate_types and 'rlib' in self.crate_types:
461 self.errors += 'ERROR: cannot generate both lib and rlib crate types\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800462 if not self.root_pkg:
463 self.root_pkg = self.crate_name
Ivan Lozano26aa1c32021-08-16 11:20:32 -0400464
465 # get the package version from running cargo metadata
Joel Galenson69ba8072021-08-16 11:31:29 -0700466 if not self.runner.args.no_pkg_vers and not self.skip_crate():
Ivan Lozano26aa1c32021-08-16 11:20:32 -0400467 self.get_pkg_version()
468
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700469 self.device_supported = self.runner.args.device
470 self.host_supported = not self.runner.args.no_host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800471 self.cfgs = sorted(set(self.cfgs))
472 self.features = sorted(set(self.features))
473 self.codegens = sorted(set(self.codegens))
474 self.externs = sorted(set(self.externs))
475 self.core_externs = sorted(set(self.core_externs))
476 self.static_libs = sorted(set(self.static_libs))
477 self.shared_libs = sorted(set(self.shared_libs))
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700478 self.crate_types = sorted(set(self.crate_types))
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800479 self.decide_module_type()
480 self.module_name = altered_name(self.stem)
481 return self
482
Ivan Lozano26aa1c32021-08-16 11:20:32 -0400483 def get_pkg_version(self):
484 """Attempt to retrieve the package version from the Cargo.toml
485
486 If there is only one package, use its version. Otherwise, try to
487 match the emitted `--crate_name` arg against the package name.
488
489 This may fail in cases where multiple packages are defined (workspaces)
490 and where the package name does not match the emitted crate_name
491 (e.g. [lib.name] is set).
492 """
Joel Galenson69ba8072021-08-16 11:31:29 -0700493 cargo_metadata = subprocess.run([self.runner.cargo_path, 'metadata', '--no-deps',
494 '--format-version', '1'],
Joel Galensonc5186502021-08-16 11:22:47 -0700495 cwd=os.path.abspath(self.cargo_dir),
496 stdout=subprocess.PIPE)
Ivan Lozano26aa1c32021-08-16 11:20:32 -0400497 if cargo_metadata.returncode:
498 self.errors += ('ERROR: unable to get cargo metadata for package version; ' +
499 'return code ' + cargo_metadata.returncode + '\n')
500 else:
501 metadata_json = json.loads(cargo_metadata.stdout)
502 if len(metadata_json['packages']) > 1:
503 for package in metadata_json['packages']:
504 # package names may contain '-', but is changed to '_' in the crate_name
505 if package['name'].replace('-','_') == self.crate_name:
506 self.cargo_pkg_version = package['version']
507 break
508 else:
509 self.cargo_pkg_version = metadata_json['packages'][0]['version']
510
511 if not self.cargo_pkg_version:
512 self.errors += ('ERROR: Unable to retrieve package version; ' +
513 'to disable, run with arg "--no-pkg-vers"\n')
514
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800515 def dump_line(self):
516 self.write('\n// Line ' + str(self.line_num) + ' ' + self.line)
517
518 def feature_list(self):
519 """Return a string of main_src + "feature_list"."""
520 pkg = self.main_src
521 if pkg.startswith('.../'): # keep only the main package name
522 pkg = re.sub('/.*', '', pkg[4:])
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700523 elif pkg.startswith('/'): # use relative path for a local package
524 pkg = os.path.relpath(pkg)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800525 if not self.features:
526 return pkg
527 return pkg + ' "' + ','.join(self.features) + '"'
528
529 def dump_skip_crate(self, kind):
530 if self.debug:
531 self.write('\n// IGNORED: ' + kind + ' ' + self.main_src)
532 return self
533
534 def skip_crate(self):
535 """Return crate_name or a message if this crate should be skipped."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700536 if (is_build_crate_name(self.crate_name) or
537 self.crate_name in EXCLUDED_CRATES):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800538 return self.crate_name
539 if is_dependent_file_path(self.main_src):
540 return 'dependent crate'
541 return ''
542
543 def dump(self):
544 """Dump all error/debug/module code to the output .bp file."""
545 self.runner.init_bp_file(self.outf_name)
546 with open(self.outf_name, 'a') as outf:
547 self.outf = outf
548 if self.errors:
549 self.dump_line()
550 self.write(self.errors)
551 elif self.skip_crate():
552 self.dump_skip_crate(self.skip_crate())
553 else:
554 if self.debug:
555 self.dump_debug_info()
556 self.dump_android_module()
557
558 def dump_debug_info(self):
559 """Dump parsed data, when cargo2android is called with --debug."""
560
561 def dump(name, value):
562 self.write('//%12s = %s' % (name, value))
563
564 def opt_dump(name, value):
565 if value:
566 dump(name, value)
567
568 def dump_list(fmt, values):
569 for v in values:
570 self.write(fmt % v)
571
572 self.dump_line()
573 dump('module_name', self.module_name)
574 dump('crate_name', self.crate_name)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700575 dump('crate_types', self.crate_types)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800576 dump('main_src', self.main_src)
577 dump('has_warning', self.has_warning)
578 dump('for_host', self.host_supported)
579 dump('for_device', self.device_supported)
580 dump('module_type', self.module_type)
581 opt_dump('target', self.target)
582 opt_dump('edition', self.edition)
583 opt_dump('emit_list', self.emit_list)
584 opt_dump('cap_lints', self.cap_lints)
585 dump_list('// cfg = %s', self.cfgs)
586 dump_list('// cfg = \'feature "%s"\'', self.features)
587 # TODO(chh): escape quotes in self.features, but not in other dump_list
588 dump_list('// codegen = %s', self.codegens)
589 dump_list('// externs = %s', self.externs)
590 dump_list('// -l static = %s', self.static_libs)
591 dump_list('// -l (dylib) = %s', self.shared_libs)
592
593 def dump_android_module(self):
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700594 """Dump one or more Android module definition, depending on crate_types."""
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700595 if len(self.crate_types) == 1:
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700596 self.dump_single_type_android_module()
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700597 return
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700598 if 'test' in self.crate_types:
599 self.write('\nERROR: multiple crate types cannot include test type')
600 return
601 # Dump one Android module per crate_type.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700602 for crate_type in self.crate_types:
603 self.decide_one_module_type(crate_type)
604 self.dump_one_android_module(crate_type)
605
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700606 def build_default_name(self):
607 """Return a short and readable name for the rust_defaults module."""
Joel Galensond37d7e62021-07-13 09:03:01 -0700608 # Choices: (1) root_pkg + '_test'? + '_defaults',
609 # (2) root_pkg + '_test'? + '_defaults_' + crate_name
610 # (3) root_pkg + '_test'? + '_defaults_' + main_src_basename_path
611 # (4) root_pkg + '_test'? + '_defaults_' + a_positive_sequence_number
612 test = "_test" if self.crate_types == ['test'] else ""
613 name1 = altered_defaults(self.root_pkg) + test + '_defaults'
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700614 if self.runner.try_claim_module_name(name1, self):
615 return name1
616 name2 = name1 + '_' + self.crate_name
617 if self.runner.try_claim_module_name(name2, self):
618 return name2
619 name3 = name1 + '_' + self.main_src_basename_path()
620 if self.runner.try_claim_module_name(name3, self):
621 return name3
622 return self.runner.claim_module_name(name1, self, 0)
623
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700624 def dump_srcs_list(self):
625 """Dump the srcs list, for defaults or regular modules."""
626 if len(self.srcs) > 1:
627 srcs = sorted(set(self.srcs)) # make a copy and dedup
628 else:
629 srcs = [self.main_src]
630 copy_out = self.runner.copy_out_module_name()
631 if copy_out:
632 srcs.append(':' + copy_out)
633 self.dump_android_property_list('srcs', '"%s"', srcs)
634
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700635 def dump_defaults_module(self):
636 """Dump a rust_defaults module to be shared by other modules."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700637 name = self.build_default_name()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700638 self.defaults = name
639 self.write('\nrust_defaults {')
640 self.write(' name: "' + name + '",')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700641 if self.runner.args.global_defaults:
642 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700643 self.write(' crate_name: "' + self.crate_name + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700644 if len(self.srcs) == 1: # only one source file; share it in defaults
645 self.default_srcs = True
646 if self.has_warning and not self.cap_lints:
647 self.write(' // has rustc warnings')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700648 self.dump_srcs_list()
Joel Galensonb06c42a2021-08-31 14:28:48 -0700649 if self.cargo_env_compat:
650 self.write(' cargo_env_compat: true,')
Joel Galenson94b8a8d2021-09-28 11:53:51 -0700651 if not self.runner.args.no_pkg_vers:
652 self.write(' cargo_pkg_version: "' + self.cargo_pkg_version + '",')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700653 if 'test' in self.crate_types:
654 self.write(' test_suites: ["general-tests"],')
655 self.write(' auto_gen_config: true,')
656 self.dump_edition_flags_libs()
Joel Galensone4f53882021-07-19 11:14:55 -0700657 if 'test' in self.crate_types and len(self.srcs) == 1:
658 self.dump_test_data()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700659 self.write('}')
660
661 def dump_single_type_android_module(self):
662 """Dump one simple Android module, which has only one crate_type."""
663 crate_type = self.crate_types[0]
664 if crate_type != 'test':
665 # do not change self.stem or self.module_name
666 self.dump_one_android_module(crate_type)
667 return
668 # Dump one test module per source file, and separate host and device tests.
669 # crate_type == 'test'
Joel Galensonf6b3c912021-06-03 16:00:54 -0700670 self.srcs = [src for src in self.srcs if not src in self.runner.args.test_blocklist]
671 if ((self.host_supported and self.device_supported and len(self.srcs) > 0) or
672 len(self.srcs) > 1):
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700673 self.srcs = sorted(set(self.srcs))
674 self.dump_defaults_module()
675 saved_srcs = self.srcs
676 for src in saved_srcs:
677 self.srcs = [src]
678 saved_device_supported = self.device_supported
679 saved_host_supported = self.host_supported
680 saved_main_src = self.main_src
681 self.main_src = src
682 if saved_host_supported:
683 self.device_supported = False
684 self.host_supported = True
685 self.module_name = self.test_module_name()
686 self.decide_one_module_type(crate_type)
687 self.dump_one_android_module(crate_type)
688 if saved_device_supported:
689 self.device_supported = True
690 self.host_supported = False
691 self.module_name = self.test_module_name()
692 self.decide_one_module_type(crate_type)
693 self.dump_one_android_module(crate_type)
694 self.host_supported = saved_host_supported
695 self.device_supported = saved_device_supported
696 self.main_src = saved_main_src
697 self.srcs = saved_srcs
698
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700699 def dump_one_android_module(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800700 """Dump one Android module definition."""
701 if not self.module_type:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700702 self.write('\nERROR: unknown crate_type ' + crate_type)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800703 return
704 self.write('\n' + self.module_type + ' {')
705 self.dump_android_core_properties()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700706 if not self.defaults:
707 self.dump_edition_flags_libs()
708 if self.runner.args.host_first_multilib and self.host_supported and crate_type != 'test':
709 self.write(' compile_multilib: "first",')
Matthew Maurer062709c2021-08-17 11:27:36 -0700710 if self.runner.args.exported_c_header_dir and crate_type in C_LIBRARY_CRATE_TYPES:
711 self.write(' include_dirs: [')
712 for header_dir in self.runner.args.exported_c_header_dir:
713 self.write(' "%s",' % header_dir)
714 self.write(' ],')
Matthew Maurer9e4b7812021-08-16 14:21:01 -0700715 if self.runner.args.apex_available and crate_type in LIBRARY_CRATE_TYPES:
Joel Galensond9c4de62021-04-23 10:26:40 -0700716 self.write(' apex_available: [')
717 for apex in self.runner.args.apex_available:
718 self.write(' "%s",' % apex)
719 self.write(' ],')
Matthew Maurer70182e42021-08-17 13:53:52 -0700720 if crate_type != 'test':
721 if self.runner.args.native_bridge_supported:
722 self.write(' native_bridge_supported: true,')
723 if self.runner.args.product_available:
724 self.write(' product_available: true,')
725 if self.runner.args.recovery_available:
726 self.write(' recovery_available: true,')
727 if self.runner.args.vendor_available:
728 self.write(' vendor_available: true,')
729 if self.runner.args.vendor_ramdisk_available:
730 self.write(' vendor_ramdisk_available: true,')
731 if self.runner.args.ramdisk_available:
732 self.write(' ramdisk_available: true,')
Matthew Maurer9e4b7812021-08-16 14:21:01 -0700733 if self.runner.args.min_sdk_version and crate_type in LIBRARY_CRATE_TYPES:
Joel Galensond9c4de62021-04-23 10:26:40 -0700734 self.write(' min_sdk_version: "%s",' % self.runner.args.min_sdk_version)
Joel Galensone4f53882021-07-19 11:14:55 -0700735 if crate_type == 'test' and not self.default_srcs:
736 self.dump_test_data()
Joel Galenson5664f2a2021-06-10 10:13:49 -0700737 if self.runner.args.add_module_block:
738 with open(self.runner.args.add_module_block, 'r') as f:
739 self.write(' %s,' % f.read().replace('\n', '\n '))
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700740 self.write('}')
741
742 def dump_android_flags(self):
743 """Dump Android module flags property."""
ThiƩbaud Weksteena5a728b2021-04-08 14:23:49 +0200744 if not self.codegens and not self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700745 return
746 self.write(' flags: [')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800747 if self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700748 self.write(' "--cap-lints ' + self.cap_lints + '",')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700749 codegens_fmt = '"-C %s"'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700750 self.dump_android_property_list_items(codegens_fmt, self.codegens)
751 self.write(' ],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700752
753 def dump_edition_flags_libs(self):
754 if self.edition:
755 self.write(' edition: "' + self.edition + '",')
756 self.dump_android_property_list('features', '"%s"', self.features)
Joel Galenson3d6d1e72021-06-07 15:00:24 -0700757 cfgs = [cfg for cfg in self.cfgs if not cfg in self.runner.args.cfg_blocklist]
758 self.dump_android_property_list('cfgs', '"%s"', cfgs)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700759 self.dump_android_flags()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800760 if self.externs:
761 self.dump_android_externs()
Joel Galenson12467e52021-07-12 14:33:28 -0700762 all_static_libs = [lib for lib in self.static_libs if not lib in self.runner.args.lib_blocklist]
763 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 -0700764 self.dump_android_property_list('static_libs', '"lib%s"', static_libs)
Joel Galenson12467e52021-07-12 14:33:28 -0700765 whole_static_libs = [lib for lib in all_static_libs if lib in self.runner.args.whole_static_libs]
766 self.dump_android_property_list('whole_static_libs', '"lib%s"', whole_static_libs)
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700767 shared_libs = [lib for lib in self.shared_libs if not lib in self.runner.args.lib_blocklist]
768 self.dump_android_property_list('shared_libs', '"lib%s"', shared_libs)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800769
Joel Galensone4f53882021-07-19 11:14:55 -0700770 def dump_test_data(self):
771 data = [data for (name, data) in map(lambda kv: kv.split('=', 1), self.runner.args.test_data)
772 if self.srcs == [name]]
773 if data:
774 self.dump_android_property_list('data', '"%s"', data)
775
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700776 def main_src_basename_path(self):
777 return re.sub('/', '_', re.sub('.rs$', '', self.main_src))
778
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800779 def test_module_name(self):
780 """Return a unique name for a test module."""
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700781 # root_pkg+(_host|_device) + '_test_'+source_file_name
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700782 suffix = self.main_src_basename_path()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700783 host_device = '_host'
784 if self.device_supported:
785 host_device = '_device'
786 return self.root_pkg + host_device + '_test_' + suffix
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800787
788 def decide_module_type(self):
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700789 # Use the first crate type for the default/first module.
790 crate_type = self.crate_types[0] if self.crate_types else ''
791 self.decide_one_module_type(crate_type)
792
793 def decide_one_module_type(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800794 """Decide which Android module type to use."""
795 host = '' if self.device_supported else '_host'
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700796 rlib = '_rlib' if self.runner.args.force_rlib else ''
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700797 if crate_type == 'bin': # rust_binary[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800798 self.module_type = 'rust_binary' + host
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700799 # In rare cases like protobuf-codegen, the output binary name must
800 # be renamed to use as a plugin for protoc.
801 self.stem = altered_stem(self.crate_name)
802 self.module_name = altered_name(self.crate_name)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700803 elif crate_type == 'lib': # rust_library[_host]
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700804 # TODO(chh): should this be rust_library[_host]?
805 # Assuming that Cargo.toml do not use both 'lib' and 'rlib',
806 # because we map them both to rlib.
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700807 self.module_type = 'rust_library' + rlib + host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800808 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700809 self.module_name = altered_name(self.stem)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700810 elif crate_type == 'rlib': # rust_library[_host]
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700811 self.module_type = 'rust_library' + rlib + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700812 self.stem = 'lib' + self.crate_name
813 self.module_name = altered_name(self.stem)
814 elif crate_type == 'dylib': # rust_library[_host]_dylib
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800815 self.module_type = 'rust_library' + host + '_dylib'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700816 self.stem = 'lib' + self.crate_name
817 self.module_name = altered_name(self.stem) + '_dylib'
818 elif crate_type == 'cdylib': # rust_library[_host]_shared
Ivan Lozano0c057ad2020-12-15 10:41:26 -0500819 self.module_type = 'rust_ffi' + host + '_shared'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700820 self.stem = 'lib' + self.crate_name
821 self.module_name = altered_name(self.stem) + '_shared'
822 elif crate_type == 'staticlib': # rust_library[_host]_static
Ivan Lozano0c057ad2020-12-15 10:41:26 -0500823 self.module_type = 'rust_ffi' + host + '_static'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700824 self.stem = 'lib' + self.crate_name
825 self.module_name = altered_name(self.stem) + '_static'
826 elif crate_type == 'test': # rust_test[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800827 self.module_type = 'rust_test' + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700828 # Before do_merge, stem name is based on the --crate-name parameter.
829 # and test module name is based on stem.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800830 self.stem = self.test_module_name()
831 # self.stem will be changed after merging with other tests.
832 # self.stem is NOT used for final test binary name.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700833 # rust_test uses each source file base name as part of output file name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700834 # In do_merge, this function is called again, with a module_name.
835 # We make sure that the module name is unique in each package.
836 if self.module_name:
837 # Cargo uses "-C extra-filename=..." and "-C metadata=..." to add
838 # different suffixes and distinguish multiple tests of the same
839 # crate name. We ignore -C and use claim_module_name to get
840 # unique sequential suffix.
841 self.module_name = self.runner.claim_module_name(
842 self.module_name, self, 0)
843 # Now the module name is unique, stem should also match and unique.
844 self.stem = self.module_name
845 elif crate_type == 'proc-macro': # rust_proc_macro
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800846 self.module_type = 'rust_proc_macro'
847 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700848 self.module_name = altered_name(self.stem)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800849 else: # unknown module type, rust_prebuilt_dylib? rust_library[_host]?
850 self.module_type = ''
851 self.stem = ''
852
853 def dump_android_property_list_items(self, fmt, values):
854 for v in values:
855 # fmt has quotes, so we need escape_quotes(v)
856 self.write(' ' + (fmt % escape_quotes(v)) + ',')
857
858 def dump_android_property_list(self, name, fmt, values):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700859 if not values:
860 return
861 if len(values) > 1:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800862 self.write(' ' + name + ': [')
863 self.dump_android_property_list_items(fmt, values)
864 self.write(' ],')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700865 else:
866 self.write(' ' + name + ': [' +
867 (fmt % escape_quotes(values[0])) + '],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800868
869 def dump_android_core_properties(self):
870 """Dump the module header, name, stem, etc."""
871 self.write(' name: "' + self.module_name + '",')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700872 # see properties shared by dump_defaults_module
873 if self.defaults:
874 self.write(' defaults: ["' + self.defaults + '"],')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700875 elif self.runner.args.global_defaults:
876 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800877 if self.stem != self.module_name:
878 self.write(' stem: "' + self.stem + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700879 if self.has_warning and not self.cap_lints and not self.default_srcs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700880 self.write(' // has rustc warnings')
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700881 if self.host_supported and self.device_supported and self.module_type != 'rust_proc_macro':
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800882 self.write(' host_supported: true,')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700883 if not self.defaults:
884 self.write(' crate_name: "' + self.crate_name + '",')
Ivan Lozanocc660f12021-08-11 16:49:46 -0400885 if not self.defaults and self.cargo_env_compat:
886 self.write(' cargo_env_compat: true,')
Joel Galenson94b8a8d2021-09-28 11:53:51 -0700887 if not self.runner.args.no_pkg_vers:
888 self.write(' cargo_pkg_version: "' + self.cargo_pkg_version + '",')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700889 if not self.default_srcs:
890 self.dump_srcs_list()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700891 if 'test' in self.crate_types and not self.defaults:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800892 # self.root_pkg can have multiple test modules, with different *_tests[n]
893 # names, but their executables can all be installed under the same _tests
894 # directory. When built from Cargo.toml, all tests should have different
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700895 # file or crate names. So we used (root_pkg + '_tests') name as the
896 # relative_install_path.
897 # However, some package like 'slab' can have non-mergeable tests that
898 # must be separated by different module names. So, here we no longer
899 # emit relative_install_path.
900 # self.write(' relative_install_path: "' + self.root_pkg + '_tests",')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800901 self.write(' test_suites: ["general-tests"],')
902 self.write(' auto_gen_config: true,')
Joel Galensone261a152021-01-12 11:31:53 -0800903 if 'test' in self.crate_types and self.host_supported:
904 self.write(' test_options: {')
905 self.write(' unit_test: true,')
906 self.write(' },')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800907
908 def dump_android_externs(self):
909 """Dump the dependent rlibs and dylibs property."""
910 so_libs = list()
911 rust_libs = ''
912 deps_libname = re.compile('^.* = lib(.*)-[0-9a-f]*.(rlib|so|rmeta)$')
913 for lib in self.externs:
914 # normal value of lib: "libc = liblibc-*.rlib"
915 # strange case in rand crate: "getrandom_package = libgetrandom-*.rlib"
916 # we should use "libgetrandom", not "lib" + "getrandom_package"
917 groups = deps_libname.match(lib)
918 if groups is not None:
919 lib_name = groups.group(1)
920 else:
921 lib_name = re.sub(' .*$', '', lib)
Joel Galenson97e414a2021-05-27 09:42:32 -0700922 if lib_name in self.runner.args.dependency_blocklist:
923 continue
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800924 if lib.endswith('.rlib') or lib.endswith('.rmeta'):
925 # On MacOS .rmeta is used when Linux uses .rlib or .rmeta.
926 rust_libs += ' "' + altered_name('lib' + lib_name) + '",\n'
927 elif lib.endswith('.so'):
928 so_libs.append(lib_name)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700929 elif lib != 'proc_macro': # --extern proc_macro is special and ignored
930 rust_libs += ' // ERROR: unknown type of lib ' + lib + '\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800931 if rust_libs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700932 self.write(' rustlibs: [\n' + rust_libs + ' ],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800933 # Are all dependent .so files proc_macros?
934 # TODO(chh): Separate proc_macros and dylib.
935 self.dump_android_property_list('proc_macros', '"lib%s"', so_libs)
936
937
938class ARObject(object):
939 """Information of an "ar" link command."""
940
941 def __init__(self, runner, outf_name):
942 # Remembered global runner and its members.
943 self.runner = runner
944 self.pkg = ''
945 self.outf_name = outf_name # path to Android.bp
946 # "ar" arguments
947 self.line_num = 1
948 self.line = ''
949 self.flags = '' # e.g. "crs"
950 self.lib = '' # e.g. "/.../out/lib*.a"
951 self.objs = list() # e.g. "/.../out/.../*.o"
952
953 def parse(self, pkg, line_num, args_line):
954 """Collect ar obj/lib file names."""
955 self.pkg = pkg
956 self.line_num = line_num
957 self.line = args_line
958 args = args_line.split()
959 num_args = len(args)
960 if num_args < 3:
961 print('ERROR: "ar" command has too few arguments', args_line)
962 else:
963 self.flags = unquote(args[0])
964 self.lib = unquote(args[1])
965 self.objs = sorted(set(map(unquote, args[2:])))
966 return self
967
968 def write(self, s):
969 self.outf.write(s + '\n')
970
971 def dump_debug_info(self):
972 self.write('\n// Line ' + str(self.line_num) + ' "ar" ' + self.line)
973 self.write('// ar_object for %12s' % self.pkg)
974 self.write('// flags = %s' % self.flags)
975 self.write('// lib = %s' % short_out_name(self.pkg, self.lib))
976 for o in self.objs:
977 self.write('// obj = %s' % short_out_name(self.pkg, o))
978
979 def dump_android_lib(self):
980 """Write cc_library_static into Android.bp."""
981 self.write('\ncc_library_static {')
982 self.write(' name: "' + file_base_name(self.lib) + '",')
983 self.write(' host_supported: true,')
984 if self.flags != 'crs':
985 self.write(' // ar flags = %s' % self.flags)
986 if self.pkg not in self.runner.pkg_obj2cc:
987 self.write(' ERROR: cannot find source files.\n}')
988 return
989 self.write(' srcs: [')
990 obj2cc = self.runner.pkg_obj2cc[self.pkg]
991 # Note: wflags are ignored.
992 dflags = list()
993 fflags = list()
994 for obj in self.objs:
995 self.write(' "' + short_out_name(self.pkg, obj2cc[obj].src) + '",')
996 # TODO(chh): union of dflags and flags of all obj
997 # Now, just a temporary hack that uses the last obj's flags
998 dflags = obj2cc[obj].dflags
999 fflags = obj2cc[obj].fflags
1000 self.write(' ],')
1001 self.write(' cflags: [')
1002 self.write(' "-O3",') # TODO(chh): is this default correct?
1003 self.write(' "-Wno-error",')
1004 for x in fflags:
1005 self.write(' "-f' + x + '",')
1006 for x in dflags:
1007 self.write(' "-D' + x + '",')
1008 self.write(' ],')
1009 self.write('}')
1010
1011 def dump(self):
1012 """Dump error/debug/module info to the output .bp file."""
1013 self.runner.init_bp_file(self.outf_name)
1014 with open(self.outf_name, 'a') as outf:
1015 self.outf = outf
1016 if self.runner.args.debug:
1017 self.dump_debug_info()
1018 self.dump_android_lib()
1019
1020
1021class CCObject(object):
1022 """Information of a "cc" compilation command."""
1023
1024 def __init__(self, runner, outf_name):
1025 # Remembered global runner and its members.
1026 self.runner = runner
1027 self.pkg = ''
1028 self.outf_name = outf_name # path to Android.bp
1029 # "cc" arguments
1030 self.line_num = 1
1031 self.line = ''
1032 self.src = ''
1033 self.obj = ''
1034 self.dflags = list() # -D flags
1035 self.fflags = list() # -f flags
1036 self.iflags = list() # -I flags
1037 self.wflags = list() # -W flags
1038 self.other_args = list()
1039
1040 def parse(self, pkg, line_num, args_line):
1041 """Collect cc compilation flags and src/out file names."""
1042 self.pkg = pkg
1043 self.line_num = line_num
1044 self.line = args_line
1045 args = args_line.split()
1046 i = 0
1047 while i < len(args):
1048 arg = args[i]
1049 if arg == '"-c"':
1050 i += 1
1051 if args[i].startswith('"-o'):
1052 # ring-0.13.5 dumps: ... "-c" "-o/.../*.o" ".../*.c"
1053 self.obj = unquote(args[i])[2:]
1054 i += 1
1055 self.src = unquote(args[i])
1056 else:
1057 self.src = unquote(args[i])
1058 elif arg == '"-o"':
1059 i += 1
1060 self.obj = unquote(args[i])
1061 elif arg == '"-I"':
1062 i += 1
1063 self.iflags.append(unquote(args[i]))
1064 elif arg.startswith('"-D'):
1065 self.dflags.append(unquote(args[i])[2:])
1066 elif arg.startswith('"-f'):
1067 self.fflags.append(unquote(args[i])[2:])
1068 elif arg.startswith('"-W'):
1069 self.wflags.append(unquote(args[i])[2:])
1070 elif not (arg.startswith('"-O') or arg == '"-m64"' or arg == '"-g"' or
1071 arg == '"-g3"'):
1072 # ignore -O -m64 -g
1073 self.other_args.append(unquote(args[i]))
1074 i += 1
1075 self.dflags = sorted(set(self.dflags))
1076 self.fflags = sorted(set(self.fflags))
1077 # self.wflags is not sorted because some are order sensitive
1078 # and we ignore them anyway.
1079 if self.pkg not in self.runner.pkg_obj2cc:
1080 self.runner.pkg_obj2cc[self.pkg] = {}
1081 self.runner.pkg_obj2cc[self.pkg][self.obj] = self
1082 return self
1083
1084 def write(self, s):
1085 self.outf.write(s + '\n')
1086
1087 def dump_debug_flags(self, name, flags):
1088 self.write('// ' + name + ':')
1089 for f in flags:
1090 self.write('// %s' % f)
1091
1092 def dump(self):
1093 """Dump only error/debug info to the output .bp file."""
1094 if not self.runner.args.debug:
1095 return
1096 self.runner.init_bp_file(self.outf_name)
1097 with open(self.outf_name, 'a') as outf:
1098 self.outf = outf
1099 self.write('\n// Line ' + str(self.line_num) + ' "cc" ' + self.line)
1100 self.write('// cc_object for %12s' % self.pkg)
1101 self.write('// src = %s' % short_out_name(self.pkg, self.src))
1102 self.write('// obj = %s' % short_out_name(self.pkg, self.obj))
1103 self.dump_debug_flags('-I flags', self.iflags)
1104 self.dump_debug_flags('-D flags', self.dflags)
1105 self.dump_debug_flags('-f flags', self.fflags)
1106 self.dump_debug_flags('-W flags', self.wflags)
1107 if self.other_args:
1108 self.dump_debug_flags('other args', self.other_args)
1109
1110
1111class Runner(object):
1112 """Main class to parse cargo -v output and print Android module definitions."""
1113
1114 def __init__(self, args):
1115 self.bp_files = set() # Remember all output Android.bp files.
1116 self.root_pkg = '' # name of package in ./Cargo.toml
1117 # Saved flags, modes, and data.
1118 self.args = args
1119 self.dry_run = not args.run
1120 self.skip_cargo = args.skipcargo
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001121 self.cargo_path = './cargo' # path to cargo, will be set later
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001122 self.checked_out_files = False # to check only once
1123 self.build_out_files = [] # output files generated by build.rs
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001124 # All cc/ar objects, crates, dependencies, and warning files
1125 self.cc_objects = list()
1126 self.pkg_obj2cc = {}
1127 # pkg_obj2cc[cc_object[i].pkg][cc_objects[i].obj] = cc_objects[i]
1128 self.ar_objects = list()
1129 self.crates = list()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001130 self.warning_files = set()
1131 # Keep a unique mapping from (module name) to crate
1132 self.name_owners = {}
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001133 # Save and dump all errors from cargo to Android.bp.
1134 self.errors = ''
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001135 self.setup_cargo_path()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001136 # Default action is cargo clean, followed by build or user given actions.
1137 if args.cargo:
1138 self.cargo = ['clean'] + args.cargo
1139 else:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001140 default_target = '--target x86_64-unknown-linux-gnu'
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -07001141 # Use the same target for both host and default device builds.
1142 # Same target is used as default in host x86_64 Android compilation.
1143 # Note: b/169872957, prebuilt cargo failed to build vsock
1144 # on x86_64-unknown-linux-musl systems.
1145 self.cargo = ['clean', 'build ' + default_target]
1146 if args.tests:
1147 self.cargo.append('build --tests ' + default_target)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001148
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001149 def setup_cargo_path(self):
1150 """Find cargo in the --cargo_bin or prebuilt rust bin directory."""
1151 if self.args.cargo_bin:
1152 self.cargo_path = os.path.join(self.args.cargo_bin, 'cargo')
1153 if not os.path.isfile(self.cargo_path):
1154 sys.exit('ERROR: cannot find cargo in ' + self.args.cargo_bin)
1155 print('WARNING: using cargo in ' + self.args.cargo_bin)
1156 return
1157 # We have only tested this on Linux.
1158 if platform.system() != 'Linux':
1159 sys.exit('ERROR: this script has only been tested on Linux with cargo.')
1160 # Assuming that this script is in development/scripts.
1161 my_dir = os.path.dirname(os.path.abspath(__file__))
1162 linux_dir = os.path.join(my_dir, '..', '..',
1163 'prebuilts', 'rust', 'linux-x86')
1164 if not os.path.isdir(linux_dir):
1165 sys.exit('ERROR: cannot find directory ' + linux_dir)
1166 rust_version = self.find_rust_version(my_dir, linux_dir)
1167 cargo_bin = os.path.join(linux_dir, rust_version, 'bin')
1168 self.cargo_path = os.path.join(cargo_bin, 'cargo')
1169 if not os.path.isfile(self.cargo_path):
1170 sys.exit('ERROR: cannot find cargo in ' + cargo_bin
1171 + '; please try --cargo_bin= flag.')
1172 return
1173
1174 def find_rust_version(self, my_dir, linux_dir):
1175 """Use my script directory, find prebuilt rust version."""
1176 # First look up build/soong/rust/config/global.go.
1177 path2global = os.path.join(my_dir, '..', '..',
1178 'build', 'soong', 'rust', 'config', 'global.go')
1179 if os.path.isfile(path2global):
1180 # try to find: RustDefaultVersion = "1.44.0"
1181 version_pat = re.compile(
1182 r'\s*RustDefaultVersion\s*=\s*"([0-9]+\.[0-9]+\.[0-9]+)".*$')
1183 with open(path2global, 'r') as inf:
1184 for line in inf:
1185 result = version_pat.match(line)
1186 if result:
1187 return result.group(1)
1188 print('WARNING: cannot find RustDefaultVersion in ' + path2global)
1189 # Otherwise, find the newest (largest) version number in linux_dir.
1190 rust_version = (0, 0, 0) # the prebuilt version to use
1191 version_pat = re.compile(r'([0-9]+)\.([0-9]+)\.([0-9]+)$')
1192 for dir_name in os.listdir(linux_dir):
1193 result = version_pat.match(dir_name)
1194 if not result:
1195 continue
1196 version = (result.group(1), result.group(2), result.group(3))
1197 if version > rust_version:
1198 rust_version = version
1199 return '.'.join(rust_version)
1200
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001201 def find_out_files(self):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001202 # list1 has build.rs output for normal crates
1203 list1 = glob.glob(TARGET_TMP + '/*/*/build/' + self.root_pkg + '-*/out/*')
1204 # list2 has build.rs output for proc-macro crates
1205 list2 = glob.glob(TARGET_TMP + '/*/build/' + self.root_pkg + '-*/out/*')
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001206 return list1 + list2
1207
1208 def copy_out_files(self):
1209 """Copy build.rs output files to ./out and set up build_out_files."""
1210 if self.checked_out_files:
1211 return
1212 self.checked_out_files = True
1213 cargo_out_files = self.find_out_files()
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001214 out_files = set()
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001215 if cargo_out_files:
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001216 os.makedirs('out', exist_ok=True)
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001217 for path in cargo_out_files:
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001218 file_name = path.split('/')[-1]
1219 out_files.add(file_name)
1220 shutil.copy(path, 'out/' + file_name)
1221 self.build_out_files = sorted(out_files)
1222
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001223 def has_used_out_dir(self):
1224 """Returns true if env!("OUT_DIR") is found."""
1225 return 0 == os.system('grep -rl --exclude build.rs --include \\*.rs' +
1226 ' \'env!("OUT_DIR")\' * > /dev/null')
1227
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001228 def copy_out_module_name(self):
1229 if self.args.copy_out and self.build_out_files:
1230 return 'copy_' + self.root_pkg + '_build_out'
1231 else:
1232 return ''
1233
Haibo Huang0f72c952021-03-19 11:34:15 -07001234 def read_license(self, name):
1235 if not os.path.isfile(name):
1236 return ''
1237 license = ''
1238 with open(name, 'r') as intf:
1239 line = intf.readline()
1240 # Firstly skip ANDROID_BP_HEADER
1241 while line.startswith('//'):
1242 line = intf.readline()
Joel Galensond9d13b82021-04-05 11:27:55 -07001243 # Read all lines until we see a rust_* or genrule rule.
1244 while line != '' and not (line.startswith('rust_') or line.startswith('genrule {')):
Haibo Huang0f72c952021-03-19 11:34:15 -07001245 license += line
1246 line = intf.readline()
1247 return license.strip()
1248
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001249 def dump_copy_out_module(self, outf):
1250 """Output the genrule module to copy out/* to $(genDir)."""
1251 copy_out = self.copy_out_module_name()
1252 if not copy_out:
1253 return
1254 outf.write('\ngenrule {\n')
1255 outf.write(' name: "' + copy_out + '",\n')
1256 outf.write(' srcs: ["out/*"],\n')
1257 outf.write(' cmd: "cp $(in) $(genDir)",\n')
1258 if len(self.build_out_files) > 1:
1259 outf.write(' out: [\n')
1260 for f in self.build_out_files:
1261 outf.write(' "' + f + '",\n')
1262 outf.write(' ],\n')
1263 else:
1264 outf.write(' out: ["' + self.build_out_files[0] + '"],\n')
1265 outf.write('}\n')
1266
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001267 def init_bp_file(self, name):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001268 # name could be Android.bp or sub_dir_path/Android.bp
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001269 if name not in self.bp_files:
1270 self.bp_files.add(name)
Haibo Huang0f72c952021-03-19 11:34:15 -07001271 license_section = self.read_license(name)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001272 with open(name, 'w') as outf:
Joel Galensonc3bfaf82021-08-18 09:39:36 -07001273 outf.write(ANDROID_BP_HEADER.format(args=' '.join(sys.argv[1:])))
Haibo Huang0f72c952021-03-19 11:34:15 -07001274 outf.write('\n')
1275 outf.write(license_section)
1276 outf.write('\n')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001277 # at most one copy_out module per .bp file
1278 self.dump_copy_out_module(outf)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001279
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001280 def try_claim_module_name(self, name, owner):
1281 """Reserve and return True if it has not been reserved yet."""
1282 if name not in self.name_owners or owner == self.name_owners[name]:
1283 self.name_owners[name] = owner
1284 return True
1285 return False
1286
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001287 def claim_module_name(self, prefix, owner, counter):
1288 """Return prefix if not owned yet, otherwise, prefix+str(counter)."""
1289 while True:
1290 name = prefix
1291 if counter > 0:
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001292 name += '_' + str(counter)
1293 if self.try_claim_module_name(name, owner):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001294 return name
1295 counter += 1
1296
1297 def find_root_pkg(self):
1298 """Read name of [package] in ./Cargo.toml."""
1299 if not os.path.exists('./Cargo.toml'):
1300 return
1301 with open('./Cargo.toml', 'r') as inf:
1302 pkg_section = re.compile(r'^ *\[package\]')
1303 name = re.compile('^ *name *= * "([^"]*)"')
1304 in_pkg = False
1305 for line in inf:
1306 if in_pkg:
1307 if name.match(line):
1308 self.root_pkg = name.match(line).group(1)
1309 break
1310 else:
1311 in_pkg = pkg_section.match(line) is not None
1312
1313 def run_cargo(self):
1314 """Calls cargo -v and save its output to ./cargo.out."""
1315 if self.skip_cargo:
1316 return self
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001317 cargo_toml = './Cargo.toml'
1318 cargo_out = './cargo.out'
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001319 # Do not use Cargo.lock, because .bp rules are designed to
1320 # run with "latest" crates avaialable on Android.
1321 cargo_lock = './Cargo.lock'
1322 cargo_lock_saved = './cargo.lock.saved'
1323 had_cargo_lock = os.path.exists(cargo_lock)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001324 if not os.access(cargo_toml, os.R_OK):
1325 print('ERROR: Cannot find or read', cargo_toml)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001326 return self
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001327 if not self.dry_run:
1328 if os.path.exists(cargo_out):
1329 os.remove(cargo_out)
1330 if not self.args.use_cargo_lock and had_cargo_lock: # save it
1331 os.rename(cargo_lock, cargo_lock_saved)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001332 cmd_tail = ' --target-dir ' + TARGET_TMP + ' >> ' + cargo_out + ' 2>&1'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001333 # set up search PATH for cargo to find the correct rustc
1334 saved_path = os.environ['PATH']
1335 os.environ['PATH'] = os.path.dirname(self.cargo_path) + ':' + saved_path
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001336 # Add [workspace] to Cargo.toml if it is not there.
1337 added_workspace = False
1338 if self.args.add_workspace:
1339 with open(cargo_toml, 'r') as in_file:
1340 cargo_toml_lines = in_file.readlines()
1341 found_workspace = '[workspace]\n' in cargo_toml_lines
1342 if found_workspace:
1343 print('### WARNING: found [workspace] in Cargo.toml')
1344 else:
1345 with open(cargo_toml, 'a') as out_file:
1346 out_file.write('[workspace]\n')
1347 added_workspace = True
1348 if self.args.verbose:
1349 print('### INFO: added [workspace] to Cargo.toml')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001350 for c in self.cargo:
1351 features = ''
Chih-Hung Hsieh6c8d52f2020-03-30 18:28:52 -07001352 if c != 'clean':
1353 if self.args.features is not None:
1354 features = ' --no-default-features'
1355 if self.args.features:
1356 features += ' --features ' + self.args.features
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001357 cmd_v_flag = ' -vv ' if self.args.vv else ' -v '
1358 cmd = self.cargo_path + cmd_v_flag
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001359 cmd += c + features + cmd_tail
1360 if self.args.rustflags and c != 'clean':
1361 cmd = 'RUSTFLAGS="' + self.args.rustflags + '" ' + cmd
1362 if self.dry_run:
1363 print('Dry-run skip:', cmd)
1364 else:
1365 if self.args.verbose:
1366 print('Running:', cmd)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001367 with open(cargo_out, 'a') as out_file:
1368 out_file.write('### Running: ' + cmd + '\n')
Joel Galenson6bf54e32021-05-17 10:54:50 -07001369 ret = os.system(cmd)
1370 if ret != 0:
1371 print('*** There was an error while running cargo. ' +
1372 'See the cargo.out file for details.')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001373 if added_workspace: # restore original Cargo.toml
1374 with open(cargo_toml, 'w') as out_file:
1375 out_file.writelines(cargo_toml_lines)
1376 if self.args.verbose:
1377 print('### INFO: restored original Cargo.toml')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001378 os.environ['PATH'] = saved_path
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001379 if not self.dry_run:
1380 if not had_cargo_lock: # restore to no Cargo.lock state
1381 os.remove(cargo_lock)
1382 elif not self.args.use_cargo_lock: # restore saved Cargo.lock
1383 os.rename(cargo_lock_saved, cargo_lock)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001384 return self
1385
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001386 def dump_pkg_obj2cc(self):
1387 """Dump debug info of the pkg_obj2cc map."""
1388 if not self.args.debug:
1389 return
1390 self.init_bp_file('Android.bp')
1391 with open('Android.bp', 'a') as outf:
1392 sorted_pkgs = sorted(self.pkg_obj2cc.keys())
1393 for pkg in sorted_pkgs:
1394 if not self.pkg_obj2cc[pkg]:
1395 continue
1396 outf.write('\n// obj => src for %s\n' % pkg)
1397 obj2cc = self.pkg_obj2cc[pkg]
1398 for obj in sorted(obj2cc.keys()):
1399 outf.write('// ' + short_out_name(pkg, obj) + ' => ' +
1400 short_out_name(pkg, obj2cc[obj].src) + '\n')
1401
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001402 def apply_patch(self):
1403 """Apply local patch file if it is given."""
1404 if self.args.patch:
1405 if self.dry_run:
1406 print('Dry-run skip patch file:', self.args.patch)
1407 else:
1408 if not os.path.exists(self.args.patch):
1409 self.append_to_bp('ERROR cannot find patch file: ' + self.args.patch)
1410 return self
1411 if self.args.verbose:
1412 print('### INFO: applying local patch file:', self.args.patch)
Joel Galenson7e8247e2021-05-20 18:51:42 -07001413 subprocess.run(['patch', '-s', '--no-backup-if-mismatch', './Android.bp',
1414 self.args.patch], check=True)
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001415 return self
1416
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001417 def gen_bp(self):
1418 """Parse cargo.out and generate Android.bp files."""
1419 if self.dry_run:
1420 print('Dry-run skip: read', CARGO_OUT, 'write Android.bp')
1421 elif os.path.exists(CARGO_OUT):
1422 self.find_root_pkg()
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001423 if self.args.copy_out:
1424 self.copy_out_files()
1425 elif self.find_out_files() and self.has_used_out_dir():
1426 print('WARNING: ' + self.root_pkg + ' has cargo output files; ' +
1427 'please rerun with the --copy-out flag.')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001428 with open(CARGO_OUT, 'r') as cargo_out:
1429 self.parse(cargo_out, 'Android.bp')
1430 self.crates.sort(key=get_module_name)
1431 for obj in self.cc_objects:
1432 obj.dump()
1433 self.dump_pkg_obj2cc()
1434 for crate in self.crates:
1435 crate.dump()
1436 dumped_libs = set()
1437 for lib in self.ar_objects:
1438 if lib.pkg == self.root_pkg:
1439 lib_name = file_base_name(lib.lib)
1440 if lib_name not in dumped_libs:
1441 dumped_libs.add(lib_name)
1442 lib.dump()
Joel Galenson5664f2a2021-06-10 10:13:49 -07001443 if self.args.add_toplevel_block:
1444 with open(self.args.add_toplevel_block, 'r') as f:
1445 self.append_to_bp('\n' + f.read() + '\n')
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001446 if self.errors:
Joel Galenson3f42f802021-04-07 12:42:17 -07001447 self.append_to_bp('\n' + ERRORS_LINE + '\n' + self.errors)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001448 return self
1449
1450 def add_ar_object(self, obj):
1451 self.ar_objects.append(obj)
1452
1453 def add_cc_object(self, obj):
1454 self.cc_objects.append(obj)
1455
1456 def add_crate(self, crate):
1457 """Merge crate with someone in crates, or append to it. Return crates."""
1458 if crate.skip_crate():
1459 if self.args.debug: # include debug info of all crates
1460 self.crates.append(crate)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001461 else:
1462 for c in self.crates:
1463 if c.merge(crate, 'Android.bp'):
1464 return
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001465 # If not merged, decide module type and name now.
1466 crate.decide_module_type()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001467 self.crates.append(crate)
1468
1469 def find_warning_owners(self):
1470 """For each warning file, find its owner crate."""
1471 missing_owner = False
1472 for f in self.warning_files:
1473 cargo_dir = '' # find lowest crate, with longest path
1474 owner = None # owner crate of this warning
1475 for c in self.crates:
1476 if (f.startswith(c.cargo_dir + '/') and
1477 len(cargo_dir) < len(c.cargo_dir)):
1478 cargo_dir = c.cargo_dir
1479 owner = c
1480 if owner:
1481 owner.has_warning = True
1482 else:
1483 missing_owner = True
1484 if missing_owner and os.path.exists('Cargo.toml'):
1485 # owner is the root cargo, with empty cargo_dir
1486 for c in self.crates:
1487 if not c.cargo_dir:
1488 c.has_warning = True
1489
1490 def rustc_command(self, n, rustc_line, line, outf_name):
1491 """Process a rustc command line from cargo -vv output."""
1492 # cargo build -vv output can have multiple lines for a rustc command
1493 # due to '\n' in strings for environment variables.
1494 # strip removes leading spaces and '\n' at the end
1495 new_rustc = (rustc_line.strip() + line) if rustc_line else line
1496 # Use an heuristic to detect the completions of a multi-line command.
1497 # This might fail for some very rare case, but easy to fix manually.
1498 if not line.endswith('`\n') or (new_rustc.count('`') % 2) != 0:
1499 return new_rustc
1500 if RUSTC_VV_CMD_ARGS.match(new_rustc):
1501 args = RUSTC_VV_CMD_ARGS.match(new_rustc).group(1)
1502 self.add_crate(Crate(self, outf_name).parse(n, args))
1503 else:
1504 self.assert_empty_vv_line(new_rustc)
1505 return ''
1506
1507 def cc_ar_command(self, n, groups, outf_name):
1508 pkg = groups.group(1)
1509 line = groups.group(3)
1510 if groups.group(2) == 'cc':
1511 self.add_cc_object(CCObject(self, outf_name).parse(pkg, n, line))
1512 else:
1513 self.add_ar_object(ARObject(self, outf_name).parse(pkg, n, line))
1514
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001515 def append_to_bp(self, line):
1516 self.init_bp_file('Android.bp')
1517 with open('Android.bp', 'a') as outf:
1518 outf.write(line)
1519
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001520 def assert_empty_vv_line(self, line):
1521 if line: # report error if line is not empty
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001522 self.append_to_bp('ERROR -vv line: ' + line)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001523 return ''
1524
1525 def parse(self, inf, outf_name):
1526 """Parse rustc and warning messages in inf, return a list of Crates."""
1527 n = 0 # line number
1528 prev_warning = False # true if the previous line was warning: ...
1529 rustc_line = '' # previous line(s) matching RUSTC_VV_PAT
1530 for line in inf:
1531 n += 1
1532 if line.startswith('warning: '):
1533 prev_warning = True
1534 rustc_line = self.assert_empty_vv_line(rustc_line)
1535 continue
1536 new_rustc = ''
1537 if RUSTC_PAT.match(line):
1538 args_line = RUSTC_PAT.match(line).group(1)
1539 self.add_crate(Crate(self, outf_name).parse(n, args_line))
1540 self.assert_empty_vv_line(rustc_line)
1541 elif rustc_line or RUSTC_VV_PAT.match(line):
1542 new_rustc = self.rustc_command(n, rustc_line, line, outf_name)
1543 elif CC_AR_VV_PAT.match(line):
1544 self.cc_ar_command(n, CC_AR_VV_PAT.match(line), outf_name)
1545 elif prev_warning and WARNING_FILE_PAT.match(line):
1546 self.assert_empty_vv_line(rustc_line)
1547 fpath = WARNING_FILE_PAT.match(line).group(1)
1548 if fpath[0] != '/': # ignore absolute path
1549 self.warning_files.add(fpath)
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001550 elif line.startswith('error: ') or line.startswith('error[E'):
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001551 if not self.args.ignore_cargo_errors:
1552 self.errors += line
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001553 prev_warning = False
1554 rustc_line = new_rustc
1555 self.find_warning_owners()
1556
1557
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001558def get_parser():
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001559 """Parse main arguments."""
1560 parser = argparse.ArgumentParser('cargo2android')
1561 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001562 '--add_workspace',
1563 action='store_true',
1564 default=False,
1565 help=('append [workspace] to Cargo.toml before calling cargo,' +
1566 ' to treat current directory as root of package source;' +
1567 ' otherwise the relative source file path in generated' +
1568 ' .bp file will be from the parent directory.'))
1569 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001570 '--cargo',
1571 action='append',
1572 metavar='args_string',
1573 help=('extra cargo build -v args in a string, ' +
1574 'each --cargo flag calls cargo build -v once'))
1575 parser.add_argument(
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001576 '--cargo_bin',
1577 type=str,
1578 help='use cargo in the cargo_bin directory instead of the prebuilt one')
1579 parser.add_argument(
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001580 '--copy-out',
1581 action='store_true',
1582 default=False,
1583 help=('only for root directory, ' +
1584 'copy build.rs output to ./out/* and add a genrule to copy ' +
1585 './out/* to genrule output; for crates with code pattern: ' +
1586 'include!(concat!(env!("OUT_DIR"), "/<some_file>.rs"))'))
1587 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001588 '--debug',
1589 action='store_true',
1590 default=False,
1591 help='dump debug info into Android.bp')
1592 parser.add_argument(
1593 '--dependencies',
1594 action='store_true',
1595 default=False,
Joel Galenson833848c2021-08-17 10:50:42 -07001596 help='Deprecated. Has no effect.')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001597 parser.add_argument(
1598 '--device',
1599 action='store_true',
1600 default=False,
1601 help='run cargo also for a default device target')
1602 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001603 '--features',
1604 type=str,
1605 help=('pass features to cargo build, ' +
1606 'empty string means no default features'))
1607 parser.add_argument(
1608 '--global_defaults',
1609 type=str,
1610 help='add a defaults name to every module')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001611 parser.add_argument(
1612 '--host-first-multilib',
1613 action='store_true',
1614 default=False,
1615 help=('add a compile_multilib:"first" property ' +
1616 'to Android.bp host modules.'))
1617 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001618 '--ignore-cargo-errors',
1619 action='store_true',
1620 default=False,
1621 help='do not append cargo/rustc error messages to Android.bp')
1622 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001623 '--no-host',
1624 action='store_true',
1625 default=False,
1626 help='do not run cargo for the host; only for the device target')
1627 parser.add_argument(
1628 '--no-subdir',
1629 action='store_true',
1630 default=False,
1631 help='do not output anything for sub-directories')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001632 parser.add_argument(
1633 '--onefile',
1634 action='store_true',
1635 default=False,
1636 help=('output all into one ./Android.bp, default will generate ' +
1637 'one Android.bp per Cargo.toml in subdirectories'))
1638 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001639 '--patch',
1640 type=str,
1641 help='apply the given patch file to generated ./Android.bp')
1642 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001643 '--run',
1644 action='store_true',
1645 default=False,
1646 help='run it, default is dry-run')
1647 parser.add_argument('--rustflags', type=str, help='passing flags to rustc')
1648 parser.add_argument(
1649 '--skipcargo',
1650 action='store_true',
1651 default=False,
1652 help='skip cargo command, parse cargo.out, and generate Android.bp')
1653 parser.add_argument(
1654 '--tests',
1655 action='store_true',
1656 default=False,
1657 help='run cargo build --tests after normal build')
1658 parser.add_argument(
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001659 '--use-cargo-lock',
1660 action='store_true',
1661 default=False,
1662 help=('run cargo build with existing Cargo.lock ' +
1663 '(used when some latest dependent crates failed)'))
1664 parser.add_argument(
Matthew Maurer062709c2021-08-17 11:27:36 -07001665 '--exported_c_header_dir',
1666 nargs='*',
1667 help='Directories with headers to export for C usage'
1668 )
1669 parser.add_argument(
Joel Galensond9c4de62021-04-23 10:26:40 -07001670 '--min-sdk-version',
1671 type=str,
1672 help='Minimum SDK version')
1673 parser.add_argument(
1674 '--apex-available',
1675 nargs='*',
1676 help='Mark the main library as apex_available with the given apexes.')
1677 parser.add_argument(
Matthew Maurerac677252021-08-13 15:52:52 -07001678 '--native-bridge-supported',
1679 action='store_true',
1680 default=False,
1681 help='Mark the main library as native_bridge_supported.')
1682 parser.add_argument(
1683 '--product-available',
1684 action='store_true',
1685 default=False,
1686 help='Mark the main library as product_available.')
1687 parser.add_argument(
1688 '--recovery-available',
1689 action='store_true',
1690 default=False,
1691 help='Mark the main library as recovery_available.')
1692 parser.add_argument(
Ivan Lozano91920862021-07-19 10:49:08 -04001693 '--vendor-available',
1694 action='store_true',
1695 default=False,
1696 help='Mark the main library as vendor_available.')
1697 parser.add_argument(
1698 '--vendor-ramdisk-available',
1699 action='store_true',
1700 default=False,
1701 help='Mark the main library as vendor_ramdisk_available.')
1702 parser.add_argument(
Matthew Maurerac677252021-08-13 15:52:52 -07001703 '--ramdisk-available',
1704 action='store_true',
1705 default=False,
1706 help='Mark the main library as ramdisk_available.')
1707 parser.add_argument(
Joel Galensoncb5f2f02021-06-08 14:47:55 -07001708 '--force-rlib',
1709 action='store_true',
1710 default=False,
1711 help='Make the main library an rlib.')
1712 parser.add_argument(
Joel Galenson12467e52021-07-12 14:33:28 -07001713 '--whole-static-libs',
1714 nargs='*',
1715 default=[],
1716 help='Make the given libraries (without lib prefixes) whole_static_libs.')
1717 parser.add_argument(
Ivan Lozano26aa1c32021-08-16 11:20:32 -04001718 '--no-pkg-vers',
1719 action='store_true',
1720 default=False,
1721 help='Do not attempt to determine the package version automatically.')
1722 parser.add_argument(
Joel Galensone4f53882021-07-19 11:14:55 -07001723 '--test-data',
1724 nargs='*',
1725 default=[],
1726 help=('Add the given file to the given test\'s data property. ' +
1727 'Usage: test-path=data-path'))
1728 parser.add_argument(
Joel Galenson97e414a2021-05-27 09:42:32 -07001729 '--dependency-blocklist',
1730 nargs='*',
1731 default=[],
Joel Galenson12467e52021-07-12 14:33:28 -07001732 help='Do not emit the given dependencies (without lib prefixes).')
Joel Galenson97e414a2021-05-27 09:42:32 -07001733 parser.add_argument(
Joel Galensoncb5f2f02021-06-08 14:47:55 -07001734 '--lib-blocklist',
1735 nargs='*',
1736 default=[],
Joel Galenson12467e52021-07-12 14:33:28 -07001737 help='Do not emit the given C libraries as dependencies (without lib prefixes).')
Joel Galensoncb5f2f02021-06-08 14:47:55 -07001738 parser.add_argument(
Joel Galensonf6b3c912021-06-03 16:00:54 -07001739 '--test-blocklist',
1740 nargs='*',
1741 default=[],
1742 help=('Do not emit the given tests. ' +
1743 'Pass the path to the test file to exclude.'))
1744 parser.add_argument(
Joel Galenson3d6d1e72021-06-07 15:00:24 -07001745 '--cfg-blocklist',
1746 nargs='*',
1747 default=[],
1748 help='Do not emit the given cfg.')
1749 parser.add_argument(
Joel Galenson5664f2a2021-06-10 10:13:49 -07001750 '--add-toplevel-block',
1751 type=str,
1752 help='Add the contents of the given file to the top level of the Android.bp.')
1753 parser.add_argument(
1754 '--add-module-block',
1755 type=str,
1756 help='Add the contents of the given file to the main module.')
1757 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001758 '--verbose',
1759 action='store_true',
1760 default=False,
1761 help='echo executed commands')
1762 parser.add_argument(
1763 '--vv',
1764 action='store_true',
1765 default=False,
1766 help='run cargo with -vv instead of default -v')
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001767 parser.add_argument(
1768 '--dump-config-and-exit',
1769 type=str,
1770 help=('Dump command-line arguments (minus this flag) to a config file and exit. ' +
1771 'This is intended to help migrate from command line options to config files.'))
1772 parser.add_argument(
1773 '--config',
1774 type=str,
1775 help=('Load command-line options from the given config file. ' +
1776 'Options in this file will override those passed on the command line.'))
1777 return parser
1778
1779
1780def parse_args(parser):
1781 """Parses command-line options."""
1782 args = parser.parse_args()
1783 # Use the values specified in a config file if one was found.
1784 if args.config:
1785 with open(args.config, 'r') as f:
1786 config = json.load(f)
1787 args_dict = vars(args)
1788 for arg in config:
1789 args_dict[arg.replace('-', '_')] = config[arg]
1790 return args
1791
1792
1793def dump_config(parser, args):
1794 """Writes the non-default command-line options to the specified file."""
1795 args_dict = vars(args)
1796 # Filter out the arguments that have their default value.
Joel Galenson367360c2021-04-29 14:31:43 -07001797 # Also filter certain "temporary" arguments.
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001798 non_default_args = {}
1799 for arg in args_dict:
Joel Galenson9a82ad92021-08-17 17:52:04 -07001800 if (args_dict[arg] != parser.get_default(arg) and arg != 'dump_config_and_exit'
Joel Galensonc3bfaf82021-08-18 09:39:36 -07001801 and arg != 'config'):
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001802 non_default_args[arg.replace('_', '-')] = args_dict[arg]
1803 # Write to the specified file.
1804 with open(args.dump_config_and_exit, 'w') as f:
1805 json.dump(non_default_args, f, indent=2, sort_keys=True)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001806
1807
1808def main():
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001809 parser = get_parser()
1810 args = parse_args(parser)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001811 if not args.run: # default is dry-run
1812 print(DRY_RUN_NOTE)
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001813 if args.dump_config_and_exit:
1814 dump_config(parser, args)
1815 else:
ThiƩbaud Weksteen198e93f2021-07-02 14:49:19 +02001816 Runner(args).run_cargo().gen_bp().apply_patch()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001817
1818
1819if __name__ == '__main__':
1820 main()