blob: eb93c46c852fdff146ba3042213904f13adcb09a [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 Maurer9e4b7812021-08-16 14:21:01 -0700130# Rust crate_type values that correspond to a library
131LIBRARY_CRATE_TYPES = ['lib', 'rlib', 'dylib', 'staticlib', 'cdylib']
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800132
133def altered_name(name):
134 return RENAME_MAP[name] if (name in RENAME_MAP) else name
135
136
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700137def altered_stem(name):
138 return RENAME_STEM_MAP[name] if (name in RENAME_STEM_MAP) else name
139
140
141def altered_defaults(name):
142 return RENAME_DEFAULTS_MAP[name] if (name in RENAME_DEFAULTS_MAP) else name
143
144
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800145def is_build_crate_name(name):
146 # We added special prefix to build script crate names.
147 return name.startswith('build_script_')
148
149
150def is_dependent_file_path(path):
151 # Absolute or dependent '.../' paths are not main files of this crate.
152 return path.startswith('/') or path.startswith('.../')
153
154
155def get_module_name(crate): # to sort crates in a list
156 return crate.module_name
157
158
159def pkg2crate_name(s):
160 return s.replace('-', '_').replace('.', '_')
161
162
163def file_base_name(path):
164 return os.path.splitext(os.path.basename(path))[0]
165
166
167def test_base_name(path):
168 return pkg2crate_name(file_base_name(path))
169
170
171def unquote(s): # remove quotes around str
172 if s and len(s) > 1 and s[0] == '"' and s[-1] == '"':
173 return s[1:-1]
174 return s
175
176
177def remove_version_suffix(s): # remove -d1.d2.d3 suffix
178 if VERSION_SUFFIX_PAT.match(s):
179 return VERSION_SUFFIX_PAT.match(s).group(1)
180 return s
181
182
183def short_out_name(pkg, s): # replace /.../pkg-*/out/* with .../out/*
184 return re.sub('^/.*/' + pkg + '-[0-9a-f]*/out/', '.../out/', s)
185
186
187def escape_quotes(s): # replace '"' with '\\"'
188 return s.replace('"', '\\"')
189
190
191class Crate(object):
192 """Information of a Rust crate to collect/emit for an Android.bp module."""
193
194 def __init__(self, runner, outf_name):
195 # Remembered global runner and its members.
196 self.runner = runner
197 self.debug = runner.args.debug
198 self.cargo_dir = '' # directory of my Cargo.toml
199 self.outf_name = outf_name # path to Android.bp
200 self.outf = None # open file handle of outf_name during dump*
201 # Variants/results that could be merged from multiple rustc lines.
202 self.host_supported = False
203 self.device_supported = False
204 self.has_warning = False
205 # Android module properties derived from rustc parameters.
206 self.module_name = '' # unique in Android build system
207 self.module_type = '' # rust_{binary,library,test}[_host] etc.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700208 self.defaults = '' # rust_defaults used by rust_test* modules
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700209 self.default_srcs = False # use 'srcs' defined in self.defaults
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800210 self.root_pkg = '' # parent package name of a sub/test packge, from -L
211 self.srcs = list() # main_src or merged multiple source files
212 self.stem = '' # real base name of output file
213 # Kept parsed status
214 self.errors = '' # all errors found during parsing
215 self.line_num = 1 # runner told input source line number
216 self.line = '' # original rustc command line parameters
217 # Parameters collected from rustc command line.
218 self.crate_name = '' # follows --crate-name
219 self.main_src = '' # follows crate_name parameter, shortened
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700220 self.crate_types = list() # follows --crate-type
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800221 self.cfgs = list() # follows --cfg, without feature= prefix
222 self.features = list() # follows --cfg, name in 'feature="..."'
223 self.codegens = list() # follows -C, some ignored
224 self.externs = list() # follows --extern
225 self.core_externs = list() # first part of self.externs elements
226 self.static_libs = list() # e.g. -l static=host_cpuid
227 self.shared_libs = list() # e.g. -l dylib=wayland-client, -l z
228 self.cap_lints = '' # follows --cap-lints
229 self.emit_list = '' # e.g., --emit=dep-info,metadata,link
230 self.edition = '2015' # rustc default, e.g., --edition=2018
231 self.target = '' # follows --target
Ivan Lozanocc660f12021-08-11 16:49:46 -0400232 self.cargo_env_compat = True
233 self.cargo_pkg_version = '' # value extracted from Cargo.toml version field
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800234
235 def write(self, s):
236 # convenient way to output one line at a time with EOL.
237 self.outf.write(s + '\n')
238
239 def same_flags(self, other):
240 # host_supported, device_supported, has_warning are not compared but merged
241 # target is not compared, to merge different target/host modules
242 # externs is not compared; only core_externs is compared
243 return (not self.errors and not other.errors and
244 self.edition == other.edition and
245 self.cap_lints == other.cap_lints and
246 self.emit_list == other.emit_list and
247 self.core_externs == other.core_externs and
248 self.codegens == other.codegens and
249 self.features == other.features and
250 self.static_libs == other.static_libs and
251 self.shared_libs == other.shared_libs and self.cfgs == other.cfgs)
252
253 def merge_host_device(self, other):
254 """Returns true if attributes are the same except host/device support."""
255 return (self.crate_name == other.crate_name and
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700256 self.crate_types == other.crate_types and
257 self.main_src == other.main_src and
258 # before merge, each test module has an unique module name and stem
259 (self.stem == other.stem or self.crate_types == ['test']) and
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800260 self.root_pkg == other.root_pkg and not self.skip_crate() and
261 self.same_flags(other))
262
263 def merge_test(self, other):
264 """Returns true if self and other are tests of same root_pkg."""
265 # Before merger, each test has its own crate_name.
266 # A merged test uses its source file base name as output file name,
267 # so a test is mergeable only if its base name equals to its crate name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700268 return (self.crate_types == other.crate_types and
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700269 self.crate_types == ['test'] and self.root_pkg == other.root_pkg and
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800270 not self.skip_crate() and
271 other.crate_name == test_base_name(other.main_src) and
272 (len(self.srcs) > 1 or
273 (self.crate_name == test_base_name(self.main_src)) and
274 self.host_supported == other.host_supported and
275 self.device_supported == other.device_supported) and
276 self.same_flags(other))
277
278 def merge(self, other, outf_name):
279 """Try to merge crate into self."""
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700280 # Cargo build --tests could recompile a library for tests.
281 # We need to merge such duplicated calls to rustc, with
282 # the algorithm in merge_host_device.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800283 should_merge_host_device = self.merge_host_device(other)
284 should_merge_test = False
285 if not should_merge_host_device:
286 should_merge_test = self.merge_test(other)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800287 if should_merge_host_device or should_merge_test:
288 self.runner.init_bp_file(outf_name)
289 with open(outf_name, 'a') as outf: # to write debug info
290 self.outf = outf
291 other.outf = outf
292 self.do_merge(other, should_merge_test)
293 return True
294 return False
295
296 def do_merge(self, other, should_merge_test):
297 """Merge attributes of other to self."""
298 if self.debug:
299 self.write('\n// Before merge definition (1):')
300 self.dump_debug_info()
301 self.write('\n// Before merge definition (2):')
302 other.dump_debug_info()
303 # Merge properties of other to self.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800304 self.has_warning = self.has_warning or other.has_warning
305 if not self.target: # okay to keep only the first target triple
306 self.target = other.target
307 # decide_module_type sets up default self.stem,
308 # which can be changed if self is a merged test module.
309 self.decide_module_type()
310 if should_merge_test:
Joel Galenson57fa23a2021-07-15 10:47:35 -0700311 if (self.main_src in self.runner.args.test_blocklist and
312 not other.main_src in self.runner.args.test_blocklist):
313 self.main_src = other.main_src
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800314 self.srcs.append(other.main_src)
315 # use a short unique name as the merged module name.
316 prefix = self.root_pkg + '_tests'
317 self.module_name = self.runner.claim_module_name(prefix, self, 0)
318 self.stem = self.module_name
319 # This normalized root_pkg name although might be the same
320 # as other module's crate_name, it is not actually used for
321 # output file name. A merged test module always have multiple
322 # source files and each source file base name is used as
323 # its output file name.
324 self.crate_name = pkg2crate_name(self.root_pkg)
325 if self.debug:
326 self.write('\n// After merge definition (1):')
327 self.dump_debug_info()
328
329 def find_cargo_dir(self):
330 """Deepest directory with Cargo.toml and contains the main_src."""
331 if not is_dependent_file_path(self.main_src):
332 dir_name = os.path.dirname(self.main_src)
333 while dir_name:
334 if os.path.exists(dir_name + '/Cargo.toml'):
335 self.cargo_dir = dir_name
336 return
337 dir_name = os.path.dirname(dir_name)
338
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700339 def add_codegens_flag(self, flag):
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700340 """Ignore options not used in Android."""
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700341 # 'prefer-dynamic' does not work with common flag -C lto
Chih-Hung Hsieh63459ed2020-08-26 11:51:15 -0700342 # 'embed-bitcode' is ignored; we might control LTO with other .bp flag
Chih-Hung Hsieh6c13b722020-09-11 21:24:03 -0700343 # 'codegen-units' is set in Android global config or by default
344 if not (flag.startswith('codegen-units=') or
345 flag.startswith('debuginfo=') or
Chih-Hung Hsieh63459ed2020-08-26 11:51:15 -0700346 flag.startswith('embed-bitcode=') or
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700347 flag.startswith('extra-filename=') or
348 flag.startswith('incremental=') or
349 flag.startswith('metadata=') or
350 flag == 'prefer-dynamic'):
351 self.codegens.append(flag)
352
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800353 def parse(self, line_num, line):
354 """Find important rustc arguments to convert to Android.bp properties."""
355 self.line_num = line_num
356 self.line = line
357 args = line.split() # Loop through every argument of rustc.
358 i = 0
359 while i < len(args):
360 arg = args[i]
361 if arg == '--crate-name':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700362 i += 1
363 self.crate_name = args[i]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800364 elif arg == '--crate-type':
365 i += 1
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700366 # cargo calls rustc with multiple --crate-type flags.
367 # rustc can accept:
368 # --crate-type [bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]
369 self.crate_types.append(args[i])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800370 elif arg == '--test':
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700371 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800372 elif arg == '--target':
373 i += 1
374 self.target = args[i]
375 elif arg == '--cfg':
376 i += 1
377 if args[i].startswith('\'feature='):
378 self.features.append(unquote(args[i].replace('\'feature=', '')[:-1]))
379 else:
380 self.cfgs.append(args[i])
381 elif arg == '--extern':
382 i += 1
383 extern_names = re.sub('=/[^ ]*/deps/', ' = ', args[i])
384 self.externs.append(extern_names)
385 self.core_externs.append(re.sub(' = .*', '', extern_names))
386 elif arg == '-C': # codegen options
387 i += 1
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700388 self.add_codegens_flag(args[i])
389 elif arg.startswith('-C'):
390 # cargo has been passing "-C <xyz>" flag to rustc,
391 # but newer cargo could pass '-Cembed-bitcode=no' to rustc.
392 self.add_codegens_flag(arg[2:])
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800393 elif arg == '--cap-lints':
394 i += 1
395 self.cap_lints = args[i]
396 elif arg == '-L':
397 i += 1
398 if args[i].startswith('dependency=') and args[i].endswith('/deps'):
399 if '/' + TARGET_TMP + '/' in args[i]:
400 self.root_pkg = re.sub(
401 '^.*/', '', re.sub('/' + TARGET_TMP + '/.*/deps$', '', args[i]))
402 else:
403 self.root_pkg = re.sub('^.*/', '',
404 re.sub('/[^/]+/[^/]+/deps$', '', args[i]))
405 self.root_pkg = remove_version_suffix(self.root_pkg)
406 elif arg == '-l':
407 i += 1
408 if args[i].startswith('static='):
409 self.static_libs.append(re.sub('static=', '', args[i]))
410 elif args[i].startswith('dylib='):
411 self.shared_libs.append(re.sub('dylib=', '', args[i]))
412 else:
413 self.shared_libs.append(args[i])
414 elif arg == '--out-dir' or arg == '--color': # ignored
415 i += 1
416 elif arg.startswith('--error-format=') or arg.startswith('--json='):
417 _ = arg # ignored
418 elif arg.startswith('--emit='):
419 self.emit_list = arg.replace('--emit=', '')
420 elif arg.startswith('--edition='):
421 self.edition = arg.replace('--edition=', '')
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700422 elif not arg.startswith('-'):
423 # shorten imported crate main source paths like $HOME/.cargo/
424 # registry/src/github.com-1ecc6299db9ec823/memchr-2.3.3/src/lib.rs
425 self.main_src = re.sub(r'^/[^ ]*/registry/src/', '.../', args[i])
426 self.main_src = re.sub(r'^\.\.\./github.com-[0-9a-f]*/', '.../',
427 self.main_src)
428 self.find_cargo_dir()
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700429 if self.cargo_dir: # for a subdirectory
430 if self.runner.args.no_subdir: # all .bp content to /dev/null
431 self.outf_name = '/dev/null'
432 elif not self.runner.args.onefile:
433 # Write to Android.bp in the subdirectory with Cargo.toml.
434 self.outf_name = self.cargo_dir + '/Android.bp'
435 self.main_src = self.main_src[len(self.cargo_dir) + 1:]
Ivan Lozanocc660f12021-08-11 16:49:46 -0400436
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800437 else:
438 self.errors += 'ERROR: unknown ' + arg + '\n'
439 i += 1
440 if not self.crate_name:
441 self.errors += 'ERROR: missing --crate-name\n'
442 if not self.main_src:
443 self.errors += 'ERROR: missing main source file\n'
444 else:
445 self.srcs.append(self.main_src)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700446 if not self.crate_types:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800447 # Treat "--cfg test" as "--test"
448 if 'test' in self.cfgs:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700449 self.crate_types.append('test')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800450 else:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700451 self.errors += 'ERROR: missing --crate-type or --test\n'
452 elif len(self.crate_types) > 1:
453 if 'test' in self.crate_types:
454 self.errors += 'ERROR: cannot handle both --crate-type and --test\n'
455 if 'lib' in self.crate_types and 'rlib' in self.crate_types:
456 self.errors += 'ERROR: cannot generate both lib and rlib crate types\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800457 if not self.root_pkg:
458 self.root_pkg = self.crate_name
Ivan Lozano26aa1c32021-08-16 11:20:32 -0400459
460 # get the package version from running cargo metadata
Joel Galenson69ba8072021-08-16 11:31:29 -0700461 if not self.runner.args.no_pkg_vers and not self.skip_crate():
Ivan Lozano26aa1c32021-08-16 11:20:32 -0400462 self.get_pkg_version()
463
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700464 self.device_supported = self.runner.args.device
465 self.host_supported = not self.runner.args.no_host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800466 self.cfgs = sorted(set(self.cfgs))
467 self.features = sorted(set(self.features))
468 self.codegens = sorted(set(self.codegens))
469 self.externs = sorted(set(self.externs))
470 self.core_externs = sorted(set(self.core_externs))
471 self.static_libs = sorted(set(self.static_libs))
472 self.shared_libs = sorted(set(self.shared_libs))
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700473 self.crate_types = sorted(set(self.crate_types))
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800474 self.decide_module_type()
475 self.module_name = altered_name(self.stem)
476 return self
477
Ivan Lozano26aa1c32021-08-16 11:20:32 -0400478 def get_pkg_version(self):
479 """Attempt to retrieve the package version from the Cargo.toml
480
481 If there is only one package, use its version. Otherwise, try to
482 match the emitted `--crate_name` arg against the package name.
483
484 This may fail in cases where multiple packages are defined (workspaces)
485 and where the package name does not match the emitted crate_name
486 (e.g. [lib.name] is set).
487 """
Joel Galenson69ba8072021-08-16 11:31:29 -0700488 cargo_metadata = subprocess.run([self.runner.cargo_path, 'metadata', '--no-deps',
489 '--format-version', '1'],
Joel Galensonc5186502021-08-16 11:22:47 -0700490 cwd=os.path.abspath(self.cargo_dir),
491 stdout=subprocess.PIPE)
Ivan Lozano26aa1c32021-08-16 11:20:32 -0400492 if cargo_metadata.returncode:
493 self.errors += ('ERROR: unable to get cargo metadata for package version; ' +
494 'return code ' + cargo_metadata.returncode + '\n')
495 else:
496 metadata_json = json.loads(cargo_metadata.stdout)
497 if len(metadata_json['packages']) > 1:
498 for package in metadata_json['packages']:
499 # package names may contain '-', but is changed to '_' in the crate_name
500 if package['name'].replace('-','_') == self.crate_name:
501 self.cargo_pkg_version = package['version']
502 break
503 else:
504 self.cargo_pkg_version = metadata_json['packages'][0]['version']
505
506 if not self.cargo_pkg_version:
507 self.errors += ('ERROR: Unable to retrieve package version; ' +
508 'to disable, run with arg "--no-pkg-vers"\n')
509
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800510 def dump_line(self):
511 self.write('\n// Line ' + str(self.line_num) + ' ' + self.line)
512
513 def feature_list(self):
514 """Return a string of main_src + "feature_list"."""
515 pkg = self.main_src
516 if pkg.startswith('.../'): # keep only the main package name
517 pkg = re.sub('/.*', '', pkg[4:])
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700518 elif pkg.startswith('/'): # use relative path for a local package
519 pkg = os.path.relpath(pkg)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800520 if not self.features:
521 return pkg
522 return pkg + ' "' + ','.join(self.features) + '"'
523
524 def dump_skip_crate(self, kind):
525 if self.debug:
526 self.write('\n// IGNORED: ' + kind + ' ' + self.main_src)
527 return self
528
529 def skip_crate(self):
530 """Return crate_name or a message if this crate should be skipped."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700531 if (is_build_crate_name(self.crate_name) or
532 self.crate_name in EXCLUDED_CRATES):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800533 return self.crate_name
534 if is_dependent_file_path(self.main_src):
535 return 'dependent crate'
536 return ''
537
538 def dump(self):
539 """Dump all error/debug/module code to the output .bp file."""
540 self.runner.init_bp_file(self.outf_name)
541 with open(self.outf_name, 'a') as outf:
542 self.outf = outf
543 if self.errors:
544 self.dump_line()
545 self.write(self.errors)
546 elif self.skip_crate():
547 self.dump_skip_crate(self.skip_crate())
548 else:
549 if self.debug:
550 self.dump_debug_info()
551 self.dump_android_module()
552
553 def dump_debug_info(self):
554 """Dump parsed data, when cargo2android is called with --debug."""
555
556 def dump(name, value):
557 self.write('//%12s = %s' % (name, value))
558
559 def opt_dump(name, value):
560 if value:
561 dump(name, value)
562
563 def dump_list(fmt, values):
564 for v in values:
565 self.write(fmt % v)
566
567 self.dump_line()
568 dump('module_name', self.module_name)
569 dump('crate_name', self.crate_name)
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700570 dump('crate_types', self.crate_types)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800571 dump('main_src', self.main_src)
572 dump('has_warning', self.has_warning)
573 dump('for_host', self.host_supported)
574 dump('for_device', self.device_supported)
575 dump('module_type', self.module_type)
576 opt_dump('target', self.target)
577 opt_dump('edition', self.edition)
578 opt_dump('emit_list', self.emit_list)
579 opt_dump('cap_lints', self.cap_lints)
580 dump_list('// cfg = %s', self.cfgs)
581 dump_list('// cfg = \'feature "%s"\'', self.features)
582 # TODO(chh): escape quotes in self.features, but not in other dump_list
583 dump_list('// codegen = %s', self.codegens)
584 dump_list('// externs = %s', self.externs)
585 dump_list('// -l static = %s', self.static_libs)
586 dump_list('// -l (dylib) = %s', self.shared_libs)
587
588 def dump_android_module(self):
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700589 """Dump one or more Android module definition, depending on crate_types."""
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700590 if len(self.crate_types) == 1:
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700591 self.dump_single_type_android_module()
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700592 return
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700593 if 'test' in self.crate_types:
594 self.write('\nERROR: multiple crate types cannot include test type')
595 return
596 # Dump one Android module per crate_type.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700597 for crate_type in self.crate_types:
598 self.decide_one_module_type(crate_type)
599 self.dump_one_android_module(crate_type)
600
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700601 def build_default_name(self):
602 """Return a short and readable name for the rust_defaults module."""
Joel Galensond37d7e62021-07-13 09:03:01 -0700603 # Choices: (1) root_pkg + '_test'? + '_defaults',
604 # (2) root_pkg + '_test'? + '_defaults_' + crate_name
605 # (3) root_pkg + '_test'? + '_defaults_' + main_src_basename_path
606 # (4) root_pkg + '_test'? + '_defaults_' + a_positive_sequence_number
607 test = "_test" if self.crate_types == ['test'] else ""
608 name1 = altered_defaults(self.root_pkg) + test + '_defaults'
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700609 if self.runner.try_claim_module_name(name1, self):
610 return name1
611 name2 = name1 + '_' + self.crate_name
612 if self.runner.try_claim_module_name(name2, self):
613 return name2
614 name3 = name1 + '_' + self.main_src_basename_path()
615 if self.runner.try_claim_module_name(name3, self):
616 return name3
617 return self.runner.claim_module_name(name1, self, 0)
618
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700619 def dump_srcs_list(self):
620 """Dump the srcs list, for defaults or regular modules."""
621 if len(self.srcs) > 1:
622 srcs = sorted(set(self.srcs)) # make a copy and dedup
623 else:
624 srcs = [self.main_src]
625 copy_out = self.runner.copy_out_module_name()
626 if copy_out:
627 srcs.append(':' + copy_out)
628 self.dump_android_property_list('srcs', '"%s"', srcs)
629
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700630 def dump_defaults_module(self):
631 """Dump a rust_defaults module to be shared by other modules."""
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700632 name = self.build_default_name()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700633 self.defaults = name
634 self.write('\nrust_defaults {')
635 self.write(' name: "' + name + '",')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700636 if self.runner.args.global_defaults:
637 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700638 self.write(' crate_name: "' + self.crate_name + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700639 if len(self.srcs) == 1: # only one source file; share it in defaults
640 self.default_srcs = True
641 if self.has_warning and not self.cap_lints:
642 self.write(' // has rustc warnings')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700643 self.dump_srcs_list()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700644 if 'test' in self.crate_types:
645 self.write(' test_suites: ["general-tests"],')
646 self.write(' auto_gen_config: true,')
647 self.dump_edition_flags_libs()
Joel Galensone4f53882021-07-19 11:14:55 -0700648 if 'test' in self.crate_types and len(self.srcs) == 1:
649 self.dump_test_data()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700650 self.write('}')
651
652 def dump_single_type_android_module(self):
653 """Dump one simple Android module, which has only one crate_type."""
654 crate_type = self.crate_types[0]
655 if crate_type != 'test':
656 # do not change self.stem or self.module_name
657 self.dump_one_android_module(crate_type)
658 return
659 # Dump one test module per source file, and separate host and device tests.
660 # crate_type == 'test'
Joel Galensonf6b3c912021-06-03 16:00:54 -0700661 self.srcs = [src for src in self.srcs if not src in self.runner.args.test_blocklist]
662 if ((self.host_supported and self.device_supported and len(self.srcs) > 0) or
663 len(self.srcs) > 1):
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700664 self.srcs = sorted(set(self.srcs))
665 self.dump_defaults_module()
666 saved_srcs = self.srcs
667 for src in saved_srcs:
668 self.srcs = [src]
669 saved_device_supported = self.device_supported
670 saved_host_supported = self.host_supported
671 saved_main_src = self.main_src
672 self.main_src = src
673 if saved_host_supported:
674 self.device_supported = False
675 self.host_supported = True
676 self.module_name = self.test_module_name()
677 self.decide_one_module_type(crate_type)
678 self.dump_one_android_module(crate_type)
679 if saved_device_supported:
680 self.device_supported = True
681 self.host_supported = False
682 self.module_name = self.test_module_name()
683 self.decide_one_module_type(crate_type)
684 self.dump_one_android_module(crate_type)
685 self.host_supported = saved_host_supported
686 self.device_supported = saved_device_supported
687 self.main_src = saved_main_src
688 self.srcs = saved_srcs
689
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700690 def dump_one_android_module(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800691 """Dump one Android module definition."""
692 if not self.module_type:
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700693 self.write('\nERROR: unknown crate_type ' + crate_type)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800694 return
695 self.write('\n' + self.module_type + ' {')
696 self.dump_android_core_properties()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700697 if not self.defaults:
698 self.dump_edition_flags_libs()
699 if self.runner.args.host_first_multilib and self.host_supported and crate_type != 'test':
700 self.write(' compile_multilib: "first",')
Matthew Maurer9e4b7812021-08-16 14:21:01 -0700701 if self.runner.args.apex_available and crate_type in LIBRARY_CRATE_TYPES:
Joel Galensond9c4de62021-04-23 10:26:40 -0700702 self.write(' apex_available: [')
703 for apex in self.runner.args.apex_available:
704 self.write(' "%s",' % apex)
705 self.write(' ],')
Matthew Maurerac677252021-08-13 15:52:52 -0700706 if self.runner.args.native_bridge_supported:
707 self.write(' native_bridge_supported: true,')
708 if self.runner.args.product_available:
709 self.write(' product_available: true,')
710 if self.runner.args.recovery_available:
711 self.write(' recovery_available: true,')
Ivan Lozano91920862021-07-19 10:49:08 -0400712 if self.runner.args.vendor_available:
713 self.write(' vendor_available: true,')
714 if self.runner.args.vendor_ramdisk_available:
715 self.write(' vendor_ramdisk_available: true,')
Matthew Maurerac677252021-08-13 15:52:52 -0700716 if self.runner.args.ramdisk_available:
717 self.write(' ramdisk_available: true,')
Matthew Maurer9e4b7812021-08-16 14:21:01 -0700718 if self.runner.args.min_sdk_version and crate_type in LIBRARY_CRATE_TYPES:
Joel Galensond9c4de62021-04-23 10:26:40 -0700719 self.write(' min_sdk_version: "%s",' % self.runner.args.min_sdk_version)
Joel Galensone4f53882021-07-19 11:14:55 -0700720 if crate_type == 'test' and not self.default_srcs:
721 self.dump_test_data()
Joel Galenson5664f2a2021-06-10 10:13:49 -0700722 if self.runner.args.add_module_block:
723 with open(self.runner.args.add_module_block, 'r') as f:
724 self.write(' %s,' % f.read().replace('\n', '\n '))
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700725 self.write('}')
726
727 def dump_android_flags(self):
728 """Dump Android module flags property."""
ThiƩbaud Weksteena5a728b2021-04-08 14:23:49 +0200729 if not self.codegens and not self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700730 return
731 self.write(' flags: [')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800732 if self.cap_lints:
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700733 self.write(' "--cap-lints ' + self.cap_lints + '",')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700734 codegens_fmt = '"-C %s"'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -0700735 self.dump_android_property_list_items(codegens_fmt, self.codegens)
736 self.write(' ],')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700737
738 def dump_edition_flags_libs(self):
739 if self.edition:
740 self.write(' edition: "' + self.edition + '",')
741 self.dump_android_property_list('features', '"%s"', self.features)
Joel Galenson3d6d1e72021-06-07 15:00:24 -0700742 cfgs = [cfg for cfg in self.cfgs if not cfg in self.runner.args.cfg_blocklist]
743 self.dump_android_property_list('cfgs', '"%s"', cfgs)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700744 self.dump_android_flags()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800745 if self.externs:
746 self.dump_android_externs()
Joel Galenson12467e52021-07-12 14:33:28 -0700747 all_static_libs = [lib for lib in self.static_libs if not lib in self.runner.args.lib_blocklist]
748 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 -0700749 self.dump_android_property_list('static_libs', '"lib%s"', static_libs)
Joel Galenson12467e52021-07-12 14:33:28 -0700750 whole_static_libs = [lib for lib in all_static_libs if lib in self.runner.args.whole_static_libs]
751 self.dump_android_property_list('whole_static_libs', '"lib%s"', whole_static_libs)
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700752 shared_libs = [lib for lib in self.shared_libs if not lib in self.runner.args.lib_blocklist]
753 self.dump_android_property_list('shared_libs', '"lib%s"', shared_libs)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800754
Joel Galensone4f53882021-07-19 11:14:55 -0700755 def dump_test_data(self):
756 data = [data for (name, data) in map(lambda kv: kv.split('=', 1), self.runner.args.test_data)
757 if self.srcs == [name]]
758 if data:
759 self.dump_android_property_list('data', '"%s"', data)
760
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700761 def main_src_basename_path(self):
762 return re.sub('/', '_', re.sub('.rs$', '', self.main_src))
763
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800764 def test_module_name(self):
765 """Return a unique name for a test module."""
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700766 # root_pkg+(_host|_device) + '_test_'+source_file_name
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -0700767 suffix = self.main_src_basename_path()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700768 host_device = '_host'
769 if self.device_supported:
770 host_device = '_device'
771 return self.root_pkg + host_device + '_test_' + suffix
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800772
773 def decide_module_type(self):
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700774 # Use the first crate type for the default/first module.
775 crate_type = self.crate_types[0] if self.crate_types else ''
776 self.decide_one_module_type(crate_type)
777
778 def decide_one_module_type(self, crate_type):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800779 """Decide which Android module type to use."""
780 host = '' if self.device_supported else '_host'
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700781 rlib = '_rlib' if self.runner.args.force_rlib else ''
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700782 if crate_type == 'bin': # rust_binary[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800783 self.module_type = 'rust_binary' + host
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700784 # In rare cases like protobuf-codegen, the output binary name must
785 # be renamed to use as a plugin for protoc.
786 self.stem = altered_stem(self.crate_name)
787 self.module_name = altered_name(self.crate_name)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700788 elif crate_type == 'lib': # rust_library[_host]
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700789 # TODO(chh): should this be rust_library[_host]?
790 # Assuming that Cargo.toml do not use both 'lib' and 'rlib',
791 # because we map them both to rlib.
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700792 self.module_type = 'rust_library' + rlib + host
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800793 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700794 self.module_name = altered_name(self.stem)
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700795 elif crate_type == 'rlib': # rust_library[_host]
Joel Galensoncb5f2f02021-06-08 14:47:55 -0700796 self.module_type = 'rust_library' + rlib + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700797 self.stem = 'lib' + self.crate_name
798 self.module_name = altered_name(self.stem)
799 elif crate_type == 'dylib': # rust_library[_host]_dylib
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800800 self.module_type = 'rust_library' + host + '_dylib'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700801 self.stem = 'lib' + self.crate_name
802 self.module_name = altered_name(self.stem) + '_dylib'
803 elif crate_type == 'cdylib': # rust_library[_host]_shared
Ivan Lozano0c057ad2020-12-15 10:41:26 -0500804 self.module_type = 'rust_ffi' + host + '_shared'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700805 self.stem = 'lib' + self.crate_name
806 self.module_name = altered_name(self.stem) + '_shared'
807 elif crate_type == 'staticlib': # rust_library[_host]_static
Ivan Lozano0c057ad2020-12-15 10:41:26 -0500808 self.module_type = 'rust_ffi' + host + '_static'
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700809 self.stem = 'lib' + self.crate_name
810 self.module_name = altered_name(self.stem) + '_static'
811 elif crate_type == 'test': # rust_test[_host]
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800812 self.module_type = 'rust_test' + host
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700813 # Before do_merge, stem name is based on the --crate-name parameter.
814 # and test module name is based on stem.
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800815 self.stem = self.test_module_name()
816 # self.stem will be changed after merging with other tests.
817 # self.stem is NOT used for final test binary name.
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700818 # rust_test uses each source file base name as part of output file name.
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700819 # In do_merge, this function is called again, with a module_name.
820 # We make sure that the module name is unique in each package.
821 if self.module_name:
822 # Cargo uses "-C extra-filename=..." and "-C metadata=..." to add
823 # different suffixes and distinguish multiple tests of the same
824 # crate name. We ignore -C and use claim_module_name to get
825 # unique sequential suffix.
826 self.module_name = self.runner.claim_module_name(
827 self.module_name, self, 0)
828 # Now the module name is unique, stem should also match and unique.
829 self.stem = self.module_name
830 elif crate_type == 'proc-macro': # rust_proc_macro
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800831 self.module_type = 'rust_proc_macro'
832 self.stem = 'lib' + self.crate_name
Chih-Hung Hsieh8a1a2302020-04-03 14:33:33 -0700833 self.module_name = altered_name(self.stem)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800834 else: # unknown module type, rust_prebuilt_dylib? rust_library[_host]?
835 self.module_type = ''
836 self.stem = ''
837
838 def dump_android_property_list_items(self, fmt, values):
839 for v in values:
840 # fmt has quotes, so we need escape_quotes(v)
841 self.write(' ' + (fmt % escape_quotes(v)) + ',')
842
843 def dump_android_property_list(self, name, fmt, values):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700844 if not values:
845 return
846 if len(values) > 1:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800847 self.write(' ' + name + ': [')
848 self.dump_android_property_list_items(fmt, values)
849 self.write(' ],')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700850 else:
851 self.write(' ' + name + ': [' +
852 (fmt % escape_quotes(values[0])) + '],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800853
854 def dump_android_core_properties(self):
855 """Dump the module header, name, stem, etc."""
856 self.write(' name: "' + self.module_name + '",')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700857 # see properties shared by dump_defaults_module
858 if self.defaults:
859 self.write(' defaults: ["' + self.defaults + '"],')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -0700860 elif self.runner.args.global_defaults:
861 self.write(' defaults: ["' + self.runner.args.global_defaults + '"],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800862 if self.stem != self.module_name:
863 self.write(' stem: "' + self.stem + '",')
Chih-Hung Hsiehf7eff152020-07-16 15:36:22 -0700864 if self.has_warning and not self.cap_lints and not self.default_srcs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700865 self.write(' // has rustc warnings')
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -0700866 if self.host_supported and self.device_supported and self.module_type != 'rust_proc_macro':
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800867 self.write(' host_supported: true,')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700868 if not self.defaults:
869 self.write(' crate_name: "' + self.crate_name + '",')
Ivan Lozanocc660f12021-08-11 16:49:46 -0400870 if not self.defaults and self.cargo_env_compat:
871 self.write(' cargo_env_compat: true,')
872 self.write(' cargo_pkg_version: "' + self.cargo_pkg_version + '",')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -0700873 if not self.default_srcs:
874 self.dump_srcs_list()
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700875 if 'test' in self.crate_types and not self.defaults:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800876 # self.root_pkg can have multiple test modules, with different *_tests[n]
877 # names, but their executables can all be installed under the same _tests
878 # directory. When built from Cargo.toml, all tests should have different
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -0700879 # file or crate names. So we used (root_pkg + '_tests') name as the
880 # relative_install_path.
881 # However, some package like 'slab' can have non-mergeable tests that
882 # must be separated by different module names. So, here we no longer
883 # emit relative_install_path.
884 # self.write(' relative_install_path: "' + self.root_pkg + '_tests",')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800885 self.write(' test_suites: ["general-tests"],')
886 self.write(' auto_gen_config: true,')
Joel Galensone261a152021-01-12 11:31:53 -0800887 if 'test' in self.crate_types and self.host_supported:
888 self.write(' test_options: {')
889 self.write(' unit_test: true,')
890 self.write(' },')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800891
892 def dump_android_externs(self):
893 """Dump the dependent rlibs and dylibs property."""
894 so_libs = list()
895 rust_libs = ''
896 deps_libname = re.compile('^.* = lib(.*)-[0-9a-f]*.(rlib|so|rmeta)$')
897 for lib in self.externs:
898 # normal value of lib: "libc = liblibc-*.rlib"
899 # strange case in rand crate: "getrandom_package = libgetrandom-*.rlib"
900 # we should use "libgetrandom", not "lib" + "getrandom_package"
901 groups = deps_libname.match(lib)
902 if groups is not None:
903 lib_name = groups.group(1)
904 else:
905 lib_name = re.sub(' .*$', '', lib)
Joel Galenson97e414a2021-05-27 09:42:32 -0700906 if lib_name in self.runner.args.dependency_blocklist:
907 continue
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800908 if lib.endswith('.rlib') or lib.endswith('.rmeta'):
909 # On MacOS .rmeta is used when Linux uses .rlib or .rmeta.
910 rust_libs += ' "' + altered_name('lib' + lib_name) + '",\n'
911 elif lib.endswith('.so'):
912 so_libs.append(lib_name)
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -0700913 elif lib != 'proc_macro': # --extern proc_macro is special and ignored
914 rust_libs += ' // ERROR: unknown type of lib ' + lib + '\n'
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800915 if rust_libs:
Chih-Hung Hsieh35ca4bc2020-07-10 16:49:51 -0700916 self.write(' rustlibs: [\n' + rust_libs + ' ],')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -0800917 # Are all dependent .so files proc_macros?
918 # TODO(chh): Separate proc_macros and dylib.
919 self.dump_android_property_list('proc_macros', '"lib%s"', so_libs)
920
921
922class ARObject(object):
923 """Information of an "ar" link command."""
924
925 def __init__(self, runner, outf_name):
926 # Remembered global runner and its members.
927 self.runner = runner
928 self.pkg = ''
929 self.outf_name = outf_name # path to Android.bp
930 # "ar" arguments
931 self.line_num = 1
932 self.line = ''
933 self.flags = '' # e.g. "crs"
934 self.lib = '' # e.g. "/.../out/lib*.a"
935 self.objs = list() # e.g. "/.../out/.../*.o"
936
937 def parse(self, pkg, line_num, args_line):
938 """Collect ar obj/lib file names."""
939 self.pkg = pkg
940 self.line_num = line_num
941 self.line = args_line
942 args = args_line.split()
943 num_args = len(args)
944 if num_args < 3:
945 print('ERROR: "ar" command has too few arguments', args_line)
946 else:
947 self.flags = unquote(args[0])
948 self.lib = unquote(args[1])
949 self.objs = sorted(set(map(unquote, args[2:])))
950 return self
951
952 def write(self, s):
953 self.outf.write(s + '\n')
954
955 def dump_debug_info(self):
956 self.write('\n// Line ' + str(self.line_num) + ' "ar" ' + self.line)
957 self.write('// ar_object for %12s' % self.pkg)
958 self.write('// flags = %s' % self.flags)
959 self.write('// lib = %s' % short_out_name(self.pkg, self.lib))
960 for o in self.objs:
961 self.write('// obj = %s' % short_out_name(self.pkg, o))
962
963 def dump_android_lib(self):
964 """Write cc_library_static into Android.bp."""
965 self.write('\ncc_library_static {')
966 self.write(' name: "' + file_base_name(self.lib) + '",')
967 self.write(' host_supported: true,')
968 if self.flags != 'crs':
969 self.write(' // ar flags = %s' % self.flags)
970 if self.pkg not in self.runner.pkg_obj2cc:
971 self.write(' ERROR: cannot find source files.\n}')
972 return
973 self.write(' srcs: [')
974 obj2cc = self.runner.pkg_obj2cc[self.pkg]
975 # Note: wflags are ignored.
976 dflags = list()
977 fflags = list()
978 for obj in self.objs:
979 self.write(' "' + short_out_name(self.pkg, obj2cc[obj].src) + '",')
980 # TODO(chh): union of dflags and flags of all obj
981 # Now, just a temporary hack that uses the last obj's flags
982 dflags = obj2cc[obj].dflags
983 fflags = obj2cc[obj].fflags
984 self.write(' ],')
985 self.write(' cflags: [')
986 self.write(' "-O3",') # TODO(chh): is this default correct?
987 self.write(' "-Wno-error",')
988 for x in fflags:
989 self.write(' "-f' + x + '",')
990 for x in dflags:
991 self.write(' "-D' + x + '",')
992 self.write(' ],')
993 self.write('}')
994
995 def dump(self):
996 """Dump error/debug/module info to the output .bp file."""
997 self.runner.init_bp_file(self.outf_name)
998 with open(self.outf_name, 'a') as outf:
999 self.outf = outf
1000 if self.runner.args.debug:
1001 self.dump_debug_info()
1002 self.dump_android_lib()
1003
1004
1005class CCObject(object):
1006 """Information of a "cc" compilation command."""
1007
1008 def __init__(self, runner, outf_name):
1009 # Remembered global runner and its members.
1010 self.runner = runner
1011 self.pkg = ''
1012 self.outf_name = outf_name # path to Android.bp
1013 # "cc" arguments
1014 self.line_num = 1
1015 self.line = ''
1016 self.src = ''
1017 self.obj = ''
1018 self.dflags = list() # -D flags
1019 self.fflags = list() # -f flags
1020 self.iflags = list() # -I flags
1021 self.wflags = list() # -W flags
1022 self.other_args = list()
1023
1024 def parse(self, pkg, line_num, args_line):
1025 """Collect cc compilation flags and src/out file names."""
1026 self.pkg = pkg
1027 self.line_num = line_num
1028 self.line = args_line
1029 args = args_line.split()
1030 i = 0
1031 while i < len(args):
1032 arg = args[i]
1033 if arg == '"-c"':
1034 i += 1
1035 if args[i].startswith('"-o'):
1036 # ring-0.13.5 dumps: ... "-c" "-o/.../*.o" ".../*.c"
1037 self.obj = unquote(args[i])[2:]
1038 i += 1
1039 self.src = unquote(args[i])
1040 else:
1041 self.src = unquote(args[i])
1042 elif arg == '"-o"':
1043 i += 1
1044 self.obj = unquote(args[i])
1045 elif arg == '"-I"':
1046 i += 1
1047 self.iflags.append(unquote(args[i]))
1048 elif arg.startswith('"-D'):
1049 self.dflags.append(unquote(args[i])[2:])
1050 elif arg.startswith('"-f'):
1051 self.fflags.append(unquote(args[i])[2:])
1052 elif arg.startswith('"-W'):
1053 self.wflags.append(unquote(args[i])[2:])
1054 elif not (arg.startswith('"-O') or arg == '"-m64"' or arg == '"-g"' or
1055 arg == '"-g3"'):
1056 # ignore -O -m64 -g
1057 self.other_args.append(unquote(args[i]))
1058 i += 1
1059 self.dflags = sorted(set(self.dflags))
1060 self.fflags = sorted(set(self.fflags))
1061 # self.wflags is not sorted because some are order sensitive
1062 # and we ignore them anyway.
1063 if self.pkg not in self.runner.pkg_obj2cc:
1064 self.runner.pkg_obj2cc[self.pkg] = {}
1065 self.runner.pkg_obj2cc[self.pkg][self.obj] = self
1066 return self
1067
1068 def write(self, s):
1069 self.outf.write(s + '\n')
1070
1071 def dump_debug_flags(self, name, flags):
1072 self.write('// ' + name + ':')
1073 for f in flags:
1074 self.write('// %s' % f)
1075
1076 def dump(self):
1077 """Dump only error/debug info to the output .bp file."""
1078 if not self.runner.args.debug:
1079 return
1080 self.runner.init_bp_file(self.outf_name)
1081 with open(self.outf_name, 'a') as outf:
1082 self.outf = outf
1083 self.write('\n// Line ' + str(self.line_num) + ' "cc" ' + self.line)
1084 self.write('// cc_object for %12s' % self.pkg)
1085 self.write('// src = %s' % short_out_name(self.pkg, self.src))
1086 self.write('// obj = %s' % short_out_name(self.pkg, self.obj))
1087 self.dump_debug_flags('-I flags', self.iflags)
1088 self.dump_debug_flags('-D flags', self.dflags)
1089 self.dump_debug_flags('-f flags', self.fflags)
1090 self.dump_debug_flags('-W flags', self.wflags)
1091 if self.other_args:
1092 self.dump_debug_flags('other args', self.other_args)
1093
1094
1095class Runner(object):
1096 """Main class to parse cargo -v output and print Android module definitions."""
1097
1098 def __init__(self, args):
1099 self.bp_files = set() # Remember all output Android.bp files.
1100 self.root_pkg = '' # name of package in ./Cargo.toml
1101 # Saved flags, modes, and data.
1102 self.args = args
1103 self.dry_run = not args.run
1104 self.skip_cargo = args.skipcargo
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001105 self.cargo_path = './cargo' # path to cargo, will be set later
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001106 self.checked_out_files = False # to check only once
1107 self.build_out_files = [] # output files generated by build.rs
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001108 # All cc/ar objects, crates, dependencies, and warning files
1109 self.cc_objects = list()
1110 self.pkg_obj2cc = {}
1111 # pkg_obj2cc[cc_object[i].pkg][cc_objects[i].obj] = cc_objects[i]
1112 self.ar_objects = list()
1113 self.crates = list()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001114 self.warning_files = set()
1115 # Keep a unique mapping from (module name) to crate
1116 self.name_owners = {}
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001117 # Save and dump all errors from cargo to Android.bp.
1118 self.errors = ''
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001119 self.setup_cargo_path()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001120 # Default action is cargo clean, followed by build or user given actions.
1121 if args.cargo:
1122 self.cargo = ['clean'] + args.cargo
1123 else:
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001124 default_target = '--target x86_64-unknown-linux-gnu'
Chih-Hung Hsiehe1b7bb62020-09-30 13:09:30 -07001125 # Use the same target for both host and default device builds.
1126 # Same target is used as default in host x86_64 Android compilation.
1127 # Note: b/169872957, prebuilt cargo failed to build vsock
1128 # on x86_64-unknown-linux-musl systems.
1129 self.cargo = ['clean', 'build ' + default_target]
1130 if args.tests:
1131 self.cargo.append('build --tests ' + default_target)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001132
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001133 def setup_cargo_path(self):
1134 """Find cargo in the --cargo_bin or prebuilt rust bin directory."""
1135 if self.args.cargo_bin:
1136 self.cargo_path = os.path.join(self.args.cargo_bin, 'cargo')
1137 if not os.path.isfile(self.cargo_path):
1138 sys.exit('ERROR: cannot find cargo in ' + self.args.cargo_bin)
1139 print('WARNING: using cargo in ' + self.args.cargo_bin)
1140 return
1141 # We have only tested this on Linux.
1142 if platform.system() != 'Linux':
1143 sys.exit('ERROR: this script has only been tested on Linux with cargo.')
1144 # Assuming that this script is in development/scripts.
1145 my_dir = os.path.dirname(os.path.abspath(__file__))
1146 linux_dir = os.path.join(my_dir, '..', '..',
1147 'prebuilts', 'rust', 'linux-x86')
1148 if not os.path.isdir(linux_dir):
1149 sys.exit('ERROR: cannot find directory ' + linux_dir)
1150 rust_version = self.find_rust_version(my_dir, linux_dir)
1151 cargo_bin = os.path.join(linux_dir, rust_version, 'bin')
1152 self.cargo_path = os.path.join(cargo_bin, 'cargo')
1153 if not os.path.isfile(self.cargo_path):
1154 sys.exit('ERROR: cannot find cargo in ' + cargo_bin
1155 + '; please try --cargo_bin= flag.')
1156 return
1157
1158 def find_rust_version(self, my_dir, linux_dir):
1159 """Use my script directory, find prebuilt rust version."""
1160 # First look up build/soong/rust/config/global.go.
1161 path2global = os.path.join(my_dir, '..', '..',
1162 'build', 'soong', 'rust', 'config', 'global.go')
1163 if os.path.isfile(path2global):
1164 # try to find: RustDefaultVersion = "1.44.0"
1165 version_pat = re.compile(
1166 r'\s*RustDefaultVersion\s*=\s*"([0-9]+\.[0-9]+\.[0-9]+)".*$')
1167 with open(path2global, 'r') as inf:
1168 for line in inf:
1169 result = version_pat.match(line)
1170 if result:
1171 return result.group(1)
1172 print('WARNING: cannot find RustDefaultVersion in ' + path2global)
1173 # Otherwise, find the newest (largest) version number in linux_dir.
1174 rust_version = (0, 0, 0) # the prebuilt version to use
1175 version_pat = re.compile(r'([0-9]+)\.([0-9]+)\.([0-9]+)$')
1176 for dir_name in os.listdir(linux_dir):
1177 result = version_pat.match(dir_name)
1178 if not result:
1179 continue
1180 version = (result.group(1), result.group(2), result.group(3))
1181 if version > rust_version:
1182 rust_version = version
1183 return '.'.join(rust_version)
1184
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001185 def find_out_files(self):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001186 # list1 has build.rs output for normal crates
1187 list1 = glob.glob(TARGET_TMP + '/*/*/build/' + self.root_pkg + '-*/out/*')
1188 # list2 has build.rs output for proc-macro crates
1189 list2 = glob.glob(TARGET_TMP + '/*/build/' + self.root_pkg + '-*/out/*')
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001190 return list1 + list2
1191
1192 def copy_out_files(self):
1193 """Copy build.rs output files to ./out and set up build_out_files."""
1194 if self.checked_out_files:
1195 return
1196 self.checked_out_files = True
1197 cargo_out_files = self.find_out_files()
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001198 out_files = set()
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001199 if cargo_out_files:
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001200 os.makedirs('out', exist_ok=True)
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001201 for path in cargo_out_files:
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001202 file_name = path.split('/')[-1]
1203 out_files.add(file_name)
1204 shutil.copy(path, 'out/' + file_name)
1205 self.build_out_files = sorted(out_files)
1206
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001207 def has_used_out_dir(self):
1208 """Returns true if env!("OUT_DIR") is found."""
1209 return 0 == os.system('grep -rl --exclude build.rs --include \\*.rs' +
1210 ' \'env!("OUT_DIR")\' * > /dev/null')
1211
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001212 def copy_out_module_name(self):
1213 if self.args.copy_out and self.build_out_files:
1214 return 'copy_' + self.root_pkg + '_build_out'
1215 else:
1216 return ''
1217
Haibo Huang0f72c952021-03-19 11:34:15 -07001218 def read_license(self, name):
1219 if not os.path.isfile(name):
1220 return ''
1221 license = ''
1222 with open(name, 'r') as intf:
1223 line = intf.readline()
1224 # Firstly skip ANDROID_BP_HEADER
1225 while line.startswith('//'):
1226 line = intf.readline()
Joel Galensond9d13b82021-04-05 11:27:55 -07001227 # Read all lines until we see a rust_* or genrule rule.
1228 while line != '' and not (line.startswith('rust_') or line.startswith('genrule {')):
Haibo Huang0f72c952021-03-19 11:34:15 -07001229 license += line
1230 line = intf.readline()
1231 return license.strip()
1232
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001233 def dump_copy_out_module(self, outf):
1234 """Output the genrule module to copy out/* to $(genDir)."""
1235 copy_out = self.copy_out_module_name()
1236 if not copy_out:
1237 return
1238 outf.write('\ngenrule {\n')
1239 outf.write(' name: "' + copy_out + '",\n')
1240 outf.write(' srcs: ["out/*"],\n')
1241 outf.write(' cmd: "cp $(in) $(genDir)",\n')
1242 if len(self.build_out_files) > 1:
1243 outf.write(' out: [\n')
1244 for f in self.build_out_files:
1245 outf.write(' "' + f + '",\n')
1246 outf.write(' ],\n')
1247 else:
1248 outf.write(' out: ["' + self.build_out_files[0] + '"],\n')
1249 outf.write('}\n')
1250
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001251 def init_bp_file(self, name):
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001252 # name could be Android.bp or sub_dir_path/Android.bp
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001253 if name not in self.bp_files:
1254 self.bp_files.add(name)
Haibo Huang0f72c952021-03-19 11:34:15 -07001255 license_section = self.read_license(name)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001256 with open(name, 'w') as outf:
Joel Galenson367360c2021-04-29 14:31:43 -07001257 print_args = filter(lambda x: x != "--no-test-mapping", sys.argv[1:])
1258 outf.write(ANDROID_BP_HEADER.format(args=' '.join(print_args)))
Haibo Huang0f72c952021-03-19 11:34:15 -07001259 outf.write('\n')
1260 outf.write(license_section)
1261 outf.write('\n')
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001262 # at most one copy_out module per .bp file
1263 self.dump_copy_out_module(outf)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001264
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001265 def try_claim_module_name(self, name, owner):
1266 """Reserve and return True if it has not been reserved yet."""
1267 if name not in self.name_owners or owner == self.name_owners[name]:
1268 self.name_owners[name] = owner
1269 return True
1270 return False
1271
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001272 def claim_module_name(self, prefix, owner, counter):
1273 """Return prefix if not owned yet, otherwise, prefix+str(counter)."""
1274 while True:
1275 name = prefix
1276 if counter > 0:
Chih-Hung Hsiehe02dce12020-07-14 16:05:21 -07001277 name += '_' + str(counter)
1278 if self.try_claim_module_name(name, owner):
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001279 return name
1280 counter += 1
1281
1282 def find_root_pkg(self):
1283 """Read name of [package] in ./Cargo.toml."""
1284 if not os.path.exists('./Cargo.toml'):
1285 return
1286 with open('./Cargo.toml', 'r') as inf:
1287 pkg_section = re.compile(r'^ *\[package\]')
1288 name = re.compile('^ *name *= * "([^"]*)"')
1289 in_pkg = False
1290 for line in inf:
1291 if in_pkg:
1292 if name.match(line):
1293 self.root_pkg = name.match(line).group(1)
1294 break
1295 else:
1296 in_pkg = pkg_section.match(line) is not None
1297
1298 def run_cargo(self):
1299 """Calls cargo -v and save its output to ./cargo.out."""
1300 if self.skip_cargo:
1301 return self
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001302 cargo_toml = './Cargo.toml'
1303 cargo_out = './cargo.out'
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001304 # Do not use Cargo.lock, because .bp rules are designed to
1305 # run with "latest" crates avaialable on Android.
1306 cargo_lock = './Cargo.lock'
1307 cargo_lock_saved = './cargo.lock.saved'
1308 had_cargo_lock = os.path.exists(cargo_lock)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001309 if not os.access(cargo_toml, os.R_OK):
1310 print('ERROR: Cannot find or read', cargo_toml)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001311 return self
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001312 if not self.dry_run:
1313 if os.path.exists(cargo_out):
1314 os.remove(cargo_out)
1315 if not self.args.use_cargo_lock and had_cargo_lock: # save it
1316 os.rename(cargo_lock, cargo_lock_saved)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001317 cmd_tail = ' --target-dir ' + TARGET_TMP + ' >> ' + cargo_out + ' 2>&1'
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001318 # set up search PATH for cargo to find the correct rustc
1319 saved_path = os.environ['PATH']
1320 os.environ['PATH'] = os.path.dirname(self.cargo_path) + ':' + saved_path
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001321 # Add [workspace] to Cargo.toml if it is not there.
1322 added_workspace = False
1323 if self.args.add_workspace:
1324 with open(cargo_toml, 'r') as in_file:
1325 cargo_toml_lines = in_file.readlines()
1326 found_workspace = '[workspace]\n' in cargo_toml_lines
1327 if found_workspace:
1328 print('### WARNING: found [workspace] in Cargo.toml')
1329 else:
1330 with open(cargo_toml, 'a') as out_file:
1331 out_file.write('[workspace]\n')
1332 added_workspace = True
1333 if self.args.verbose:
1334 print('### INFO: added [workspace] to Cargo.toml')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001335 for c in self.cargo:
1336 features = ''
Chih-Hung Hsieh6c8d52f2020-03-30 18:28:52 -07001337 if c != 'clean':
1338 if self.args.features is not None:
1339 features = ' --no-default-features'
1340 if self.args.features:
1341 features += ' --features ' + self.args.features
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001342 cmd_v_flag = ' -vv ' if self.args.vv else ' -v '
1343 cmd = self.cargo_path + cmd_v_flag
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001344 cmd += c + features + cmd_tail
1345 if self.args.rustflags and c != 'clean':
1346 cmd = 'RUSTFLAGS="' + self.args.rustflags + '" ' + cmd
1347 if self.dry_run:
1348 print('Dry-run skip:', cmd)
1349 else:
1350 if self.args.verbose:
1351 print('Running:', cmd)
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001352 with open(cargo_out, 'a') as out_file:
1353 out_file.write('### Running: ' + cmd + '\n')
Joel Galenson6bf54e32021-05-17 10:54:50 -07001354 ret = os.system(cmd)
1355 if ret != 0:
1356 print('*** There was an error while running cargo. ' +
1357 'See the cargo.out file for details.')
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001358 if added_workspace: # restore original Cargo.toml
1359 with open(cargo_toml, 'w') as out_file:
1360 out_file.writelines(cargo_toml_lines)
1361 if self.args.verbose:
1362 print('### INFO: restored original Cargo.toml')
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001363 os.environ['PATH'] = saved_path
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001364 if not self.dry_run:
1365 if not had_cargo_lock: # restore to no Cargo.lock state
1366 os.remove(cargo_lock)
1367 elif not self.args.use_cargo_lock: # restore saved Cargo.lock
1368 os.rename(cargo_lock_saved, cargo_lock)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001369 return self
1370
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001371 def dump_pkg_obj2cc(self):
1372 """Dump debug info of the pkg_obj2cc map."""
1373 if not self.args.debug:
1374 return
1375 self.init_bp_file('Android.bp')
1376 with open('Android.bp', 'a') as outf:
1377 sorted_pkgs = sorted(self.pkg_obj2cc.keys())
1378 for pkg in sorted_pkgs:
1379 if not self.pkg_obj2cc[pkg]:
1380 continue
1381 outf.write('\n// obj => src for %s\n' % pkg)
1382 obj2cc = self.pkg_obj2cc[pkg]
1383 for obj in sorted(obj2cc.keys()):
1384 outf.write('// ' + short_out_name(pkg, obj) + ' => ' +
1385 short_out_name(pkg, obj2cc[obj].src) + '\n')
1386
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001387 def apply_patch(self):
1388 """Apply local patch file if it is given."""
1389 if self.args.patch:
1390 if self.dry_run:
1391 print('Dry-run skip patch file:', self.args.patch)
1392 else:
1393 if not os.path.exists(self.args.patch):
1394 self.append_to_bp('ERROR cannot find patch file: ' + self.args.patch)
1395 return self
1396 if self.args.verbose:
1397 print('### INFO: applying local patch file:', self.args.patch)
Joel Galenson7e8247e2021-05-20 18:51:42 -07001398 subprocess.run(['patch', '-s', '--no-backup-if-mismatch', './Android.bp',
1399 self.args.patch], check=True)
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001400 return self
1401
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001402 def gen_bp(self):
1403 """Parse cargo.out and generate Android.bp files."""
1404 if self.dry_run:
1405 print('Dry-run skip: read', CARGO_OUT, 'write Android.bp')
1406 elif os.path.exists(CARGO_OUT):
1407 self.find_root_pkg()
Chih-Hung Hsieh60140752020-11-03 15:05:58 -08001408 if self.args.copy_out:
1409 self.copy_out_files()
1410 elif self.find_out_files() and self.has_used_out_dir():
1411 print('WARNING: ' + self.root_pkg + ' has cargo output files; ' +
1412 'please rerun with the --copy-out flag.')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001413 with open(CARGO_OUT, 'r') as cargo_out:
1414 self.parse(cargo_out, 'Android.bp')
1415 self.crates.sort(key=get_module_name)
1416 for obj in self.cc_objects:
1417 obj.dump()
1418 self.dump_pkg_obj2cc()
1419 for crate in self.crates:
1420 crate.dump()
1421 dumped_libs = set()
1422 for lib in self.ar_objects:
1423 if lib.pkg == self.root_pkg:
1424 lib_name = file_base_name(lib.lib)
1425 if lib_name not in dumped_libs:
1426 dumped_libs.add(lib_name)
1427 lib.dump()
Joel Galenson5664f2a2021-06-10 10:13:49 -07001428 if self.args.add_toplevel_block:
1429 with open(self.args.add_toplevel_block, 'r') as f:
1430 self.append_to_bp('\n' + f.read() + '\n')
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001431 if self.errors:
Joel Galenson3f42f802021-04-07 12:42:17 -07001432 self.append_to_bp('\n' + ERRORS_LINE + '\n' + self.errors)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001433 return self
1434
1435 def add_ar_object(self, obj):
1436 self.ar_objects.append(obj)
1437
1438 def add_cc_object(self, obj):
1439 self.cc_objects.append(obj)
1440
1441 def add_crate(self, crate):
1442 """Merge crate with someone in crates, or append to it. Return crates."""
1443 if crate.skip_crate():
1444 if self.args.debug: # include debug info of all crates
1445 self.crates.append(crate)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001446 else:
1447 for c in self.crates:
1448 if c.merge(crate, 'Android.bp'):
1449 return
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001450 # If not merged, decide module type and name now.
1451 crate.decide_module_type()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001452 self.crates.append(crate)
1453
1454 def find_warning_owners(self):
1455 """For each warning file, find its owner crate."""
1456 missing_owner = False
1457 for f in self.warning_files:
1458 cargo_dir = '' # find lowest crate, with longest path
1459 owner = None # owner crate of this warning
1460 for c in self.crates:
1461 if (f.startswith(c.cargo_dir + '/') and
1462 len(cargo_dir) < len(c.cargo_dir)):
1463 cargo_dir = c.cargo_dir
1464 owner = c
1465 if owner:
1466 owner.has_warning = True
1467 else:
1468 missing_owner = True
1469 if missing_owner and os.path.exists('Cargo.toml'):
1470 # owner is the root cargo, with empty cargo_dir
1471 for c in self.crates:
1472 if not c.cargo_dir:
1473 c.has_warning = True
1474
1475 def rustc_command(self, n, rustc_line, line, outf_name):
1476 """Process a rustc command line from cargo -vv output."""
1477 # cargo build -vv output can have multiple lines for a rustc command
1478 # due to '\n' in strings for environment variables.
1479 # strip removes leading spaces and '\n' at the end
1480 new_rustc = (rustc_line.strip() + line) if rustc_line else line
1481 # Use an heuristic to detect the completions of a multi-line command.
1482 # This might fail for some very rare case, but easy to fix manually.
1483 if not line.endswith('`\n') or (new_rustc.count('`') % 2) != 0:
1484 return new_rustc
1485 if RUSTC_VV_CMD_ARGS.match(new_rustc):
1486 args = RUSTC_VV_CMD_ARGS.match(new_rustc).group(1)
1487 self.add_crate(Crate(self, outf_name).parse(n, args))
1488 else:
1489 self.assert_empty_vv_line(new_rustc)
1490 return ''
1491
1492 def cc_ar_command(self, n, groups, outf_name):
1493 pkg = groups.group(1)
1494 line = groups.group(3)
1495 if groups.group(2) == 'cc':
1496 self.add_cc_object(CCObject(self, outf_name).parse(pkg, n, line))
1497 else:
1498 self.add_ar_object(ARObject(self, outf_name).parse(pkg, n, line))
1499
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001500 def append_to_bp(self, line):
1501 self.init_bp_file('Android.bp')
1502 with open('Android.bp', 'a') as outf:
1503 outf.write(line)
1504
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001505 def assert_empty_vv_line(self, line):
1506 if line: # report error if line is not empty
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001507 self.append_to_bp('ERROR -vv line: ' + line)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001508 return ''
1509
1510 def parse(self, inf, outf_name):
1511 """Parse rustc and warning messages in inf, return a list of Crates."""
1512 n = 0 # line number
1513 prev_warning = False # true if the previous line was warning: ...
1514 rustc_line = '' # previous line(s) matching RUSTC_VV_PAT
1515 for line in inf:
1516 n += 1
1517 if line.startswith('warning: '):
1518 prev_warning = True
1519 rustc_line = self.assert_empty_vv_line(rustc_line)
1520 continue
1521 new_rustc = ''
1522 if RUSTC_PAT.match(line):
1523 args_line = RUSTC_PAT.match(line).group(1)
1524 self.add_crate(Crate(self, outf_name).parse(n, args_line))
1525 self.assert_empty_vv_line(rustc_line)
1526 elif rustc_line or RUSTC_VV_PAT.match(line):
1527 new_rustc = self.rustc_command(n, rustc_line, line, outf_name)
1528 elif CC_AR_VV_PAT.match(line):
1529 self.cc_ar_command(n, CC_AR_VV_PAT.match(line), outf_name)
1530 elif prev_warning and WARNING_FILE_PAT.match(line):
1531 self.assert_empty_vv_line(rustc_line)
1532 fpath = WARNING_FILE_PAT.match(line).group(1)
1533 if fpath[0] != '/': # ignore absolute path
1534 self.warning_files.add(fpath)
Chih-Hung Hsieh185052a2020-05-07 14:48:57 -07001535 elif line.startswith('error: ') or line.startswith('error[E'):
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001536 if not self.args.ignore_cargo_errors:
1537 self.errors += line
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001538 prev_warning = False
1539 rustc_line = new_rustc
1540 self.find_warning_owners()
1541
1542
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001543def get_parser():
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001544 """Parse main arguments."""
1545 parser = argparse.ArgumentParser('cargo2android')
1546 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001547 '--add_workspace',
1548 action='store_true',
1549 default=False,
1550 help=('append [workspace] to Cargo.toml before calling cargo,' +
1551 ' to treat current directory as root of package source;' +
1552 ' otherwise the relative source file path in generated' +
1553 ' .bp file will be from the parent directory.'))
1554 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001555 '--cargo',
1556 action='append',
1557 metavar='args_string',
1558 help=('extra cargo build -v args in a string, ' +
1559 'each --cargo flag calls cargo build -v once'))
1560 parser.add_argument(
Chih-Hung Hsieh776f6a12020-07-22 14:16:54 -07001561 '--cargo_bin',
1562 type=str,
1563 help='use cargo in the cargo_bin directory instead of the prebuilt one')
1564 parser.add_argument(
Chih-Hung Hsiehe2342ba2020-10-25 03:51:24 -07001565 '--copy-out',
1566 action='store_true',
1567 default=False,
1568 help=('only for root directory, ' +
1569 'copy build.rs output to ./out/* and add a genrule to copy ' +
1570 './out/* to genrule output; for crates with code pattern: ' +
1571 'include!(concat!(env!("OUT_DIR"), "/<some_file>.rs"))'))
1572 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001573 '--debug',
1574 action='store_true',
1575 default=False,
1576 help='dump debug info into Android.bp')
1577 parser.add_argument(
1578 '--dependencies',
1579 action='store_true',
1580 default=False,
Joel Galenson833848c2021-08-17 10:50:42 -07001581 help='Deprecated. Has no effect.')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001582 parser.add_argument(
1583 '--device',
1584 action='store_true',
1585 default=False,
1586 help='run cargo also for a default device target')
1587 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001588 '--features',
1589 type=str,
1590 help=('pass features to cargo build, ' +
1591 'empty string means no default features'))
1592 parser.add_argument(
1593 '--global_defaults',
1594 type=str,
1595 help='add a defaults name to every module')
Chih-Hung Hsieh3725e082020-07-12 00:51:20 -07001596 parser.add_argument(
1597 '--host-first-multilib',
1598 action='store_true',
1599 default=False,
1600 help=('add a compile_multilib:"first" property ' +
1601 'to Android.bp host modules.'))
1602 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001603 '--ignore-cargo-errors',
1604 action='store_true',
1605 default=False,
1606 help='do not append cargo/rustc error messages to Android.bp')
1607 parser.add_argument(
Chih-Hung Hsieh07119862020-07-24 15:34:06 -07001608 '--no-host',
1609 action='store_true',
1610 default=False,
1611 help='do not run cargo for the host; only for the device target')
1612 parser.add_argument(
1613 '--no-subdir',
1614 action='store_true',
1615 default=False,
1616 help='do not output anything for sub-directories')
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001617 parser.add_argument(
1618 '--onefile',
1619 action='store_true',
1620 default=False,
1621 help=('output all into one ./Android.bp, default will generate ' +
1622 'one Android.bp per Cargo.toml in subdirectories'))
1623 parser.add_argument(
Chih-Hung Hsiehec8846b2020-10-30 17:03:47 -07001624 '--patch',
1625 type=str,
1626 help='apply the given patch file to generated ./Android.bp')
1627 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001628 '--run',
1629 action='store_true',
1630 default=False,
1631 help='run it, default is dry-run')
1632 parser.add_argument('--rustflags', type=str, help='passing flags to rustc')
1633 parser.add_argument(
1634 '--skipcargo',
1635 action='store_true',
1636 default=False,
1637 help='skip cargo command, parse cargo.out, and generate Android.bp')
1638 parser.add_argument(
1639 '--tests',
1640 action='store_true',
1641 default=False,
1642 help='run cargo build --tests after normal build')
1643 parser.add_argument(
Chih-Hung Hsieh610a8942020-10-29 17:21:35 -07001644 '--use-cargo-lock',
1645 action='store_true',
1646 default=False,
1647 help=('run cargo build with existing Cargo.lock ' +
1648 '(used when some latest dependent crates failed)'))
1649 parser.add_argument(
Joel Galensond9c4de62021-04-23 10:26:40 -07001650 '--min-sdk-version',
1651 type=str,
1652 help='Minimum SDK version')
1653 parser.add_argument(
1654 '--apex-available',
1655 nargs='*',
1656 help='Mark the main library as apex_available with the given apexes.')
1657 parser.add_argument(
Matthew Maurerac677252021-08-13 15:52:52 -07001658 '--native-bridge-supported',
1659 action='store_true',
1660 default=False,
1661 help='Mark the main library as native_bridge_supported.')
1662 parser.add_argument(
1663 '--product-available',
1664 action='store_true',
1665 default=False,
1666 help='Mark the main library as product_available.')
1667 parser.add_argument(
1668 '--recovery-available',
1669 action='store_true',
1670 default=False,
1671 help='Mark the main library as recovery_available.')
1672 parser.add_argument(
Ivan Lozano91920862021-07-19 10:49:08 -04001673 '--vendor-available',
1674 action='store_true',
1675 default=False,
1676 help='Mark the main library as vendor_available.')
1677 parser.add_argument(
1678 '--vendor-ramdisk-available',
1679 action='store_true',
1680 default=False,
1681 help='Mark the main library as vendor_ramdisk_available.')
1682 parser.add_argument(
Matthew Maurerac677252021-08-13 15:52:52 -07001683 '--ramdisk-available',
1684 action='store_true',
1685 default=False,
1686 help='Mark the main library as ramdisk_available.')
1687 parser.add_argument(
Joel Galensoncb5f2f02021-06-08 14:47:55 -07001688 '--force-rlib',
1689 action='store_true',
1690 default=False,
1691 help='Make the main library an rlib.')
1692 parser.add_argument(
Joel Galenson12467e52021-07-12 14:33:28 -07001693 '--whole-static-libs',
1694 nargs='*',
1695 default=[],
1696 help='Make the given libraries (without lib prefixes) whole_static_libs.')
1697 parser.add_argument(
Ivan Lozano26aa1c32021-08-16 11:20:32 -04001698 '--no-pkg-vers',
1699 action='store_true',
1700 default=False,
1701 help='Do not attempt to determine the package version automatically.')
1702 parser.add_argument(
Joel Galensone4f53882021-07-19 11:14:55 -07001703 '--test-data',
1704 nargs='*',
1705 default=[],
1706 help=('Add the given file to the given test\'s data property. ' +
1707 'Usage: test-path=data-path'))
1708 parser.add_argument(
Joel Galenson97e414a2021-05-27 09:42:32 -07001709 '--dependency-blocklist',
1710 nargs='*',
1711 default=[],
Joel Galenson12467e52021-07-12 14:33:28 -07001712 help='Do not emit the given dependencies (without lib prefixes).')
Joel Galenson97e414a2021-05-27 09:42:32 -07001713 parser.add_argument(
Joel Galensoncb5f2f02021-06-08 14:47:55 -07001714 '--lib-blocklist',
1715 nargs='*',
1716 default=[],
Joel Galenson12467e52021-07-12 14:33:28 -07001717 help='Do not emit the given C libraries as dependencies (without lib prefixes).')
Joel Galensoncb5f2f02021-06-08 14:47:55 -07001718 parser.add_argument(
Joel Galensonf6b3c912021-06-03 16:00:54 -07001719 '--test-blocklist',
1720 nargs='*',
1721 default=[],
1722 help=('Do not emit the given tests. ' +
1723 'Pass the path to the test file to exclude.'))
1724 parser.add_argument(
Joel Galenson3d6d1e72021-06-07 15:00:24 -07001725 '--cfg-blocklist',
1726 nargs='*',
1727 default=[],
1728 help='Do not emit the given cfg.')
1729 parser.add_argument(
Joel Galenson5664f2a2021-06-10 10:13:49 -07001730 '--add-toplevel-block',
1731 type=str,
1732 help='Add the contents of the given file to the top level of the Android.bp.')
1733 parser.add_argument(
1734 '--add-module-block',
1735 type=str,
1736 help='Add the contents of the given file to the main module.')
1737 parser.add_argument(
Joel Galensoncf4ebb02021-04-07 08:39:51 -07001738 '--no-test-mapping',
1739 action='store_true',
1740 default=False,
ThiƩbaud Weksteen198e93f2021-07-02 14:49:19 +02001741 help='Deprecated. Has no effect.')
Joel Galensoncf4ebb02021-04-07 08:39:51 -07001742 parser.add_argument(
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001743 '--verbose',
1744 action='store_true',
1745 default=False,
1746 help='echo executed commands')
1747 parser.add_argument(
1748 '--vv',
1749 action='store_true',
1750 default=False,
1751 help='run cargo with -vv instead of default -v')
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001752 parser.add_argument(
1753 '--dump-config-and-exit',
1754 type=str,
1755 help=('Dump command-line arguments (minus this flag) to a config file and exit. ' +
1756 'This is intended to help migrate from command line options to config files.'))
1757 parser.add_argument(
1758 '--config',
1759 type=str,
1760 help=('Load command-line options from the given config file. ' +
1761 'Options in this file will override those passed on the command line.'))
1762 return parser
1763
1764
1765def parse_args(parser):
1766 """Parses command-line options."""
1767 args = parser.parse_args()
1768 # Use the values specified in a config file if one was found.
1769 if args.config:
1770 with open(args.config, 'r') as f:
1771 config = json.load(f)
1772 args_dict = vars(args)
1773 for arg in config:
1774 args_dict[arg.replace('-', '_')] = config[arg]
1775 return args
1776
1777
1778def dump_config(parser, args):
1779 """Writes the non-default command-line options to the specified file."""
1780 args_dict = vars(args)
1781 # Filter out the arguments that have their default value.
Joel Galenson367360c2021-04-29 14:31:43 -07001782 # Also filter certain "temporary" arguments.
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001783 non_default_args = {}
1784 for arg in args_dict:
Joel Galenson367360c2021-04-29 14:31:43 -07001785 if args_dict[arg] != parser.get_default(
1786 arg) and arg != 'dump_config_and_exit' and arg != 'no_test_mapping':
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001787 non_default_args[arg.replace('_', '-')] = args_dict[arg]
1788 # Write to the specified file.
1789 with open(args.dump_config_and_exit, 'w') as f:
1790 json.dump(non_default_args, f, indent=2, sort_keys=True)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001791
1792
1793def main():
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001794 parser = get_parser()
1795 args = parse_args(parser)
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001796 if not args.run: # default is dry-run
1797 print(DRY_RUN_NOTE)
Joel Galenson0fbdafe2021-04-21 16:33:33 -07001798 if args.dump_config_and_exit:
1799 dump_config(parser, args)
1800 else:
ThiƩbaud Weksteen198e93f2021-07-02 14:49:19 +02001801 Runner(args).run_cargo().gen_bp().apply_patch()
Chih-Hung Hsiehe8887372019-11-05 10:34:17 -08001802
1803
1804if __name__ == '__main__':
1805 main()