xref: /aosp_15_r20/development/scripts/cargo2rulesmk.py (revision 90c8c64db3049935a07c6143d7fd006e26f8ecca)
1*90c8c64dSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*90c8c64dSAndroid Build Coastguard Worker# Copyright (C) 2023 The Android Open Source Project
3*90c8c64dSAndroid Build Coastguard Worker#
4*90c8c64dSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
5*90c8c64dSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
6*90c8c64dSAndroid Build Coastguard Worker# You may obtain a copy of the License at
7*90c8c64dSAndroid Build Coastguard Worker#
8*90c8c64dSAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
9*90c8c64dSAndroid Build Coastguard Worker#
10*90c8c64dSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
11*90c8c64dSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
12*90c8c64dSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*90c8c64dSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
14*90c8c64dSAndroid Build Coastguard Worker# limitations under the License.
15*90c8c64dSAndroid Build Coastguard Worker#
16*90c8c64dSAndroid Build Coastguard Worker"""Call cargo -v, parse its output, and generate a Trusty build system module.
17*90c8c64dSAndroid Build Coastguard Worker
18*90c8c64dSAndroid Build Coastguard WorkerUsage: Run this script in a crate workspace root directory. The Cargo.toml file
19*90c8c64dSAndroid Build Coastguard Workershould work at least for the host platform.
20*90c8c64dSAndroid Build Coastguard Worker
21*90c8c64dSAndroid Build Coastguard WorkerWithout other flags, "cargo2rulesmk.py --run" calls cargo clean, calls cargo
22*90c8c64dSAndroid Build Coastguard Workerbuild -v, and generates makefile rules. The cargo build only generates crates
23*90c8c64dSAndroid Build Coastguard Workerfor the host without test crates.
24*90c8c64dSAndroid Build Coastguard Worker
25*90c8c64dSAndroid Build Coastguard WorkerIf there are rustc warning messages, this script will add a warning comment to
26*90c8c64dSAndroid Build Coastguard Workerthe owner crate module in rules.mk.
27*90c8c64dSAndroid Build Coastguard Worker"""
28*90c8c64dSAndroid Build Coastguard Worker
29*90c8c64dSAndroid Build Coastguard Workerimport argparse
30*90c8c64dSAndroid Build Coastguard Workerimport glob
31*90c8c64dSAndroid Build Coastguard Workerimport json
32*90c8c64dSAndroid Build Coastguard Workerimport os
33*90c8c64dSAndroid Build Coastguard Workerimport os.path
34*90c8c64dSAndroid Build Coastguard Workerimport platform
35*90c8c64dSAndroid Build Coastguard Workerimport re
36*90c8c64dSAndroid Build Coastguard Workerimport shutil
37*90c8c64dSAndroid Build Coastguard Workerimport subprocess
38*90c8c64dSAndroid Build Coastguard Workerimport sys
39*90c8c64dSAndroid Build Coastguard Worker
40*90c8c64dSAndroid Build Coastguard Workerfrom typing import List
41*90c8c64dSAndroid Build Coastguard Worker
42*90c8c64dSAndroid Build Coastguard Worker
43*90c8c64dSAndroid Build Coastguard Workerassert "/development/scripts" in os.path.dirname(__file__)
44*90c8c64dSAndroid Build Coastguard WorkerTOP_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "../.."))
45*90c8c64dSAndroid Build Coastguard Worker
46*90c8c64dSAndroid Build Coastguard Worker# Some Rust packages include extra unwanted crates.
47*90c8c64dSAndroid Build Coastguard Worker# This set contains all such excluded crate names.
48*90c8c64dSAndroid Build Coastguard WorkerEXCLUDED_CRATES = {"protobuf_bin_gen_rust_do_not_use"}
49*90c8c64dSAndroid Build Coastguard Worker
50*90c8c64dSAndroid Build Coastguard Worker
51*90c8c64dSAndroid Build Coastguard WorkerCUSTOM_MODULE_CRATES = {
52*90c8c64dSAndroid Build Coastguard Worker    # This map tracks Rust crates that have special modules that
53*90c8c64dSAndroid Build Coastguard Worker    # were not generated automatically by this script. Examples
54*90c8c64dSAndroid Build Coastguard Worker    # include compiler builtins and other foundational libraries.
55*90c8c64dSAndroid Build Coastguard Worker    # It also tracks crates tht are not under external/rust/crates.
56*90c8c64dSAndroid Build Coastguard Worker    "compiler_builtins": "trusty/user/base/lib/libcompiler_builtins-rust",
57*90c8c64dSAndroid Build Coastguard Worker    "core": "trusty/user/base/lib/libcore-rust",
58*90c8c64dSAndroid Build Coastguard Worker}
59*90c8c64dSAndroid Build Coastguard Worker
60*90c8c64dSAndroid Build Coastguard WorkerRENAME_STEM_MAP = {
61*90c8c64dSAndroid Build Coastguard Worker    # This map includes all changes to the default rust module stem names,
62*90c8c64dSAndroid Build Coastguard Worker    # which is used for output files when different from the module name.
63*90c8c64dSAndroid Build Coastguard Worker    "protoc_gen_rust": "protoc-gen-rust",
64*90c8c64dSAndroid Build Coastguard Worker}
65*90c8c64dSAndroid Build Coastguard Worker
66*90c8c64dSAndroid Build Coastguard Worker# Header added to all generated rules.mk files.
67*90c8c64dSAndroid Build Coastguard WorkerRULES_MK_HEADER = (
68*90c8c64dSAndroid Build Coastguard Worker    "# This file is generated by cargo2rulesmk.py {args}.\n"
69*90c8c64dSAndroid Build Coastguard Worker    + "# Do not modify this file as changes will be overridden on upgrade.\n\n"
70*90c8c64dSAndroid Build Coastguard Worker)
71*90c8c64dSAndroid Build Coastguard Worker
72*90c8c64dSAndroid Build Coastguard WorkerCARGO_OUT = "cargo.out"  # Name of file to keep cargo build -v output.
73*90c8c64dSAndroid Build Coastguard Worker
74*90c8c64dSAndroid Build Coastguard Worker# This should be kept in sync with tools/external_updater/crates_updater.py.
75*90c8c64dSAndroid Build Coastguard WorkerERRORS_LINE = "Errors in " + CARGO_OUT + ":"
76*90c8c64dSAndroid Build Coastguard Worker
77*90c8c64dSAndroid Build Coastguard WorkerTARGET_TMP = "target.tmp"  # Name of temporary output directory.
78*90c8c64dSAndroid Build Coastguard Worker
79*90c8c64dSAndroid Build Coastguard Worker# Message to be displayed when this script is called without the --run flag.
80*90c8c64dSAndroid Build Coastguard WorkerDRY_RUN_NOTE = (
81*90c8c64dSAndroid Build Coastguard Worker    "Dry-run: This script uses ./"
82*90c8c64dSAndroid Build Coastguard Worker    + TARGET_TMP
83*90c8c64dSAndroid Build Coastguard Worker    + " for output directory,\n"
84*90c8c64dSAndroid Build Coastguard Worker    + "runs cargo clean, runs cargo build -v, saves output to ./cargo.out,\n"
85*90c8c64dSAndroid Build Coastguard Worker    + "and writes to rules.mk in the current and subdirectories.\n\n"
86*90c8c64dSAndroid Build Coastguard Worker    + "To do do all of the above, use the --run flag.\n"
87*90c8c64dSAndroid Build Coastguard Worker    + "See --help for other flags, and more usage notes in this script.\n"
88*90c8c64dSAndroid Build Coastguard Worker)
89*90c8c64dSAndroid Build Coastguard Worker
90*90c8c64dSAndroid Build Coastguard Worker# Cargo -v output of a call to rustc.
91*90c8c64dSAndroid Build Coastguard WorkerRUSTC_PAT = re.compile("^ +Running `(.*\/)?rustc (.*)`$")
92*90c8c64dSAndroid Build Coastguard Worker
93*90c8c64dSAndroid Build Coastguard Worker# Cargo -vv output of a call to rustc could be split into multiple lines.
94*90c8c64dSAndroid Build Coastguard Worker# Assume that the first line will contain some CARGO_* env definition.
95*90c8c64dSAndroid Build Coastguard WorkerRUSTC_VV_PAT = re.compile("^ +Running `.*CARGO_.*=.*$")
96*90c8c64dSAndroid Build Coastguard Worker# The combined -vv output rustc command line pattern.
97*90c8c64dSAndroid Build Coastguard WorkerRUSTC_VV_CMD_ARGS = re.compile("^ *Running `.*CARGO_.*=.* (.*\/)?rustc (.*)`$")
98*90c8c64dSAndroid Build Coastguard Worker
99*90c8c64dSAndroid Build Coastguard Worker# Cargo -vv output of a "cc" or "ar" command; all in one line.
100*90c8c64dSAndroid Build Coastguard WorkerCC_AR_VV_PAT = re.compile(r'^\[([^ ]*)[^\]]*\] running:? "(cc|ar)" (.*)$')
101*90c8c64dSAndroid Build Coastguard Worker# Some package, such as ring-0.13.5, has pattern '... running "cc"'.
102*90c8c64dSAndroid Build Coastguard Worker
103*90c8c64dSAndroid Build Coastguard Worker# Rustc output of file location path pattern for a warning message.
104*90c8c64dSAndroid Build Coastguard WorkerWARNING_FILE_PAT = re.compile("^ *--> ([^:]*):[0-9]+")
105*90c8c64dSAndroid Build Coastguard Worker
106*90c8c64dSAndroid Build Coastguard Worker# cargo test --list output of the start of running a binary.
107*90c8c64dSAndroid Build Coastguard WorkerCARGO_TEST_LIST_START_PAT = re.compile(r"^\s*Running (.*) \(.*\)$")
108*90c8c64dSAndroid Build Coastguard Worker
109*90c8c64dSAndroid Build Coastguard Worker# cargo test --list output of the end of running a binary.
110*90c8c64dSAndroid Build Coastguard WorkerCARGO_TEST_LIST_END_PAT = re.compile(r"^(\d+) tests?, (\d+) benchmarks$")
111*90c8c64dSAndroid Build Coastguard Worker
112*90c8c64dSAndroid Build Coastguard WorkerCARGO2ANDROID_RUNNING_PAT = re.compile("^### Running: .*$")
113*90c8c64dSAndroid Build Coastguard Worker
114*90c8c64dSAndroid Build Coastguard Worker# Rust package name with suffix -d1.d2.d3(+.*)?.
115*90c8c64dSAndroid Build Coastguard WorkerVERSION_SUFFIX_PAT = re.compile(
116*90c8c64dSAndroid Build Coastguard Worker    r"^(.*)-[0-9]+\.[0-9]+\.[0-9]+(?:-(alpha|beta)\.[0-9]+)?(?:\+.*)?$"
117*90c8c64dSAndroid Build Coastguard Worker)
118*90c8c64dSAndroid Build Coastguard Worker
119*90c8c64dSAndroid Build Coastguard Worker# Crate types corresponding to a C ABI library
120*90c8c64dSAndroid Build Coastguard WorkerC_LIBRARY_CRATE_TYPES = ["staticlib", "cdylib"]
121*90c8c64dSAndroid Build Coastguard Worker# Crate types corresponding to a Rust ABI library
122*90c8c64dSAndroid Build Coastguard WorkerRUST_LIBRARY_CRATE_TYPES = ["lib", "rlib", "dylib", "proc-macro"]
123*90c8c64dSAndroid Build Coastguard Worker# Crate types corresponding to a library
124*90c8c64dSAndroid Build Coastguard WorkerLIBRARY_CRATE_TYPES = C_LIBRARY_CRATE_TYPES + RUST_LIBRARY_CRATE_TYPES
125*90c8c64dSAndroid Build Coastguard Worker
126*90c8c64dSAndroid Build Coastguard Worker
127*90c8c64dSAndroid Build Coastguard Workerdef altered_stem(name):
128*90c8c64dSAndroid Build Coastguard Worker    return RENAME_STEM_MAP[name] if (name in RENAME_STEM_MAP) else name
129*90c8c64dSAndroid Build Coastguard Worker
130*90c8c64dSAndroid Build Coastguard Worker
131*90c8c64dSAndroid Build Coastguard Workerdef is_build_crate_name(name):
132*90c8c64dSAndroid Build Coastguard Worker    # We added special prefix to build script crate names.
133*90c8c64dSAndroid Build Coastguard Worker    return name.startswith("build_script_")
134*90c8c64dSAndroid Build Coastguard Worker
135*90c8c64dSAndroid Build Coastguard Worker
136*90c8c64dSAndroid Build Coastguard Workerdef is_dependent_file_path(path):
137*90c8c64dSAndroid Build Coastguard Worker    # Absolute or dependent '.../' paths are not main files of this crate.
138*90c8c64dSAndroid Build Coastguard Worker    return path.startswith("/") or path.startswith(".../")
139*90c8c64dSAndroid Build Coastguard Worker
140*90c8c64dSAndroid Build Coastguard Worker
141*90c8c64dSAndroid Build Coastguard Workerdef get_module_name(crate):  # to sort crates in a list
142*90c8c64dSAndroid Build Coastguard Worker    return crate.module_name
143*90c8c64dSAndroid Build Coastguard Worker
144*90c8c64dSAndroid Build Coastguard Worker
145*90c8c64dSAndroid Build Coastguard Workerdef pkg2crate_name(s):
146*90c8c64dSAndroid Build Coastguard Worker    return s.replace("-", "_").replace(".", "_")
147*90c8c64dSAndroid Build Coastguard Worker
148*90c8c64dSAndroid Build Coastguard Worker
149*90c8c64dSAndroid Build Coastguard Workerdef file_base_name(path):
150*90c8c64dSAndroid Build Coastguard Worker    return os.path.splitext(os.path.basename(path))[0]
151*90c8c64dSAndroid Build Coastguard Worker
152*90c8c64dSAndroid Build Coastguard Worker
153*90c8c64dSAndroid Build Coastguard Workerdef test_base_name(path):
154*90c8c64dSAndroid Build Coastguard Worker    return pkg2crate_name(file_base_name(path))
155*90c8c64dSAndroid Build Coastguard Worker
156*90c8c64dSAndroid Build Coastguard Worker
157*90c8c64dSAndroid Build Coastguard Workerdef unquote(s):  # remove quotes around str
158*90c8c64dSAndroid Build Coastguard Worker    if s and len(s) > 1 and s[0] == s[-1] and s[0] in ('"', "'"):
159*90c8c64dSAndroid Build Coastguard Worker        return s[1:-1]
160*90c8c64dSAndroid Build Coastguard Worker    return s
161*90c8c64dSAndroid Build Coastguard Worker
162*90c8c64dSAndroid Build Coastguard Worker
163*90c8c64dSAndroid Build Coastguard Workerdef remove_version_suffix(s):  # remove -d1.d2.d3 suffix
164*90c8c64dSAndroid Build Coastguard Worker    if match := VERSION_SUFFIX_PAT.match(s):
165*90c8c64dSAndroid Build Coastguard Worker        return match.group(1)
166*90c8c64dSAndroid Build Coastguard Worker    return s
167*90c8c64dSAndroid Build Coastguard Worker
168*90c8c64dSAndroid Build Coastguard Worker
169*90c8c64dSAndroid Build Coastguard Workerdef short_out_name(pkg, s):  # replace /.../pkg-*/out/* with .../out/*
170*90c8c64dSAndroid Build Coastguard Worker    return re.sub("^/.*/" + pkg + "-[0-9a-f]*/out/", ".../out/", s)
171*90c8c64dSAndroid Build Coastguard Worker
172*90c8c64dSAndroid Build Coastguard Worker
173*90c8c64dSAndroid Build Coastguard Workerclass Crate(object):
174*90c8c64dSAndroid Build Coastguard Worker    """Information of a Rust crate to collect/emit for a rules.mk module."""
175*90c8c64dSAndroid Build Coastguard Worker
176*90c8c64dSAndroid Build Coastguard Worker    def __init__(self, runner, outf_name):
177*90c8c64dSAndroid Build Coastguard Worker        # Remembered global runner and its members.
178*90c8c64dSAndroid Build Coastguard Worker        self.runner = runner
179*90c8c64dSAndroid Build Coastguard Worker        self.debug = runner.args.debug
180*90c8c64dSAndroid Build Coastguard Worker        self.cargo_dir = ""  # directory of my Cargo.toml
181*90c8c64dSAndroid Build Coastguard Worker        self.outf_name = outf_name  # path to rules.mk
182*90c8c64dSAndroid Build Coastguard Worker        self.outf = None  # open file handle of outf_name during dump*
183*90c8c64dSAndroid Build Coastguard Worker        self.has_warning = False
184*90c8c64dSAndroid Build Coastguard Worker        # Trusty module properties derived from rustc parameters.
185*90c8c64dSAndroid Build Coastguard Worker        self.module_name = ""
186*90c8c64dSAndroid Build Coastguard Worker        self.defaults = ""  # rust_defaults used by rust_test* modules
187*90c8c64dSAndroid Build Coastguard Worker        self.default_srcs = False  # use 'srcs' defined in self.defaults
188*90c8c64dSAndroid Build Coastguard Worker        self.root_pkg = ""  # parent package name of a sub/test packge, from -L
189*90c8c64dSAndroid Build Coastguard Worker        self.srcs = []  # main_src or merged multiple source files
190*90c8c64dSAndroid Build Coastguard Worker        self.stem = ""  # real base name of output file
191*90c8c64dSAndroid Build Coastguard Worker        # Kept parsed status
192*90c8c64dSAndroid Build Coastguard Worker        self.errors = ""  # all errors found during parsing
193*90c8c64dSAndroid Build Coastguard Worker        self.line_num = 1  # runner told input source line number
194*90c8c64dSAndroid Build Coastguard Worker        self.line = ""  # original rustc command line parameters
195*90c8c64dSAndroid Build Coastguard Worker        # Parameters collected from rustc command line.
196*90c8c64dSAndroid Build Coastguard Worker        self.crate_name = ""  # follows --crate-name
197*90c8c64dSAndroid Build Coastguard Worker        self.main_src = ""  # follows crate_name parameter, shortened
198*90c8c64dSAndroid Build Coastguard Worker        self.crate_types = []  # follows --crate-type
199*90c8c64dSAndroid Build Coastguard Worker        self.cfgs = []  # follows --cfg, without feature= prefix
200*90c8c64dSAndroid Build Coastguard Worker        self.features = []  # follows --cfg, name in 'feature="..."'
201*90c8c64dSAndroid Build Coastguard Worker        self.codegens = []  # follows -C, some ignored
202*90c8c64dSAndroid Build Coastguard Worker        self.static_libs = []  # e.g.  -l static=host_cpuid
203*90c8c64dSAndroid Build Coastguard Worker        self.shared_libs = []  # e.g.  -l dylib=wayland-client, -l z
204*90c8c64dSAndroid Build Coastguard Worker        self.cap_lints = ""  # follows --cap-lints
205*90c8c64dSAndroid Build Coastguard Worker        self.emit_list = ""  # e.g., --emit=dep-info,metadata,link
206*90c8c64dSAndroid Build Coastguard Worker        self.edition = "2015"  # rustc default, e.g., --edition=2018
207*90c8c64dSAndroid Build Coastguard Worker        self.target = ""  # follows --target
208*90c8c64dSAndroid Build Coastguard Worker        self.cargo_env_compat = True
209*90c8c64dSAndroid Build Coastguard Worker        # Parameters collected from cargo metadata output
210*90c8c64dSAndroid Build Coastguard Worker        self.dependencies = []  # crate dependencies output by `cargo metadata`
211*90c8c64dSAndroid Build Coastguard Worker        self.feature_dependencies: dict[str, List[str]] = {}  # maps features to
212*90c8c64dSAndroid Build Coastguard Worker        # optional dependencies
213*90c8c64dSAndroid Build Coastguard Worker
214*90c8c64dSAndroid Build Coastguard Worker    def write(self, s):
215*90c8c64dSAndroid Build Coastguard Worker        """convenient way to output one line at a time with EOL."""
216*90c8c64dSAndroid Build Coastguard Worker        assert self.outf
217*90c8c64dSAndroid Build Coastguard Worker        self.outf.write(s + "\n")
218*90c8c64dSAndroid Build Coastguard Worker
219*90c8c64dSAndroid Build Coastguard Worker    def find_cargo_dir(self):
220*90c8c64dSAndroid Build Coastguard Worker        """Deepest directory with Cargo.toml and contains the main_src."""
221*90c8c64dSAndroid Build Coastguard Worker        if not is_dependent_file_path(self.main_src):
222*90c8c64dSAndroid Build Coastguard Worker            dir_name = os.path.dirname(self.main_src)
223*90c8c64dSAndroid Build Coastguard Worker            while dir_name:
224*90c8c64dSAndroid Build Coastguard Worker                if os.path.exists(dir_name + "/Cargo.toml"):
225*90c8c64dSAndroid Build Coastguard Worker                    self.cargo_dir = dir_name
226*90c8c64dSAndroid Build Coastguard Worker                    return
227*90c8c64dSAndroid Build Coastguard Worker                dir_name = os.path.dirname(dir_name)
228*90c8c64dSAndroid Build Coastguard Worker
229*90c8c64dSAndroid Build Coastguard Worker    def add_codegens_flag(self, flag):
230*90c8c64dSAndroid Build Coastguard Worker        """Ignore options not used by Trusty build system"""
231*90c8c64dSAndroid Build Coastguard Worker        # 'prefer-dynamic' may be set by library.mk
232*90c8c64dSAndroid Build Coastguard Worker        # 'embed-bitcode' is ignored; we might control LTO with other flags
233*90c8c64dSAndroid Build Coastguard Worker        # 'codegen-units' is set globally in engine.mk
234*90c8c64dSAndroid Build Coastguard Worker        # 'relocation-model' and 'target-feature=+reserve-x18' may be set by
235*90c8c64dSAndroid Build Coastguard Worker        # common_flags.mk
236*90c8c64dSAndroid Build Coastguard Worker        if not (
237*90c8c64dSAndroid Build Coastguard Worker            flag.startswith("codegen-units=")
238*90c8c64dSAndroid Build Coastguard Worker            or flag.startswith("debuginfo=")
239*90c8c64dSAndroid Build Coastguard Worker            or flag.startswith("embed-bitcode=")
240*90c8c64dSAndroid Build Coastguard Worker            or flag.startswith("extra-filename=")
241*90c8c64dSAndroid Build Coastguard Worker            or flag.startswith("incremental=")
242*90c8c64dSAndroid Build Coastguard Worker            or flag.startswith("metadata=")
243*90c8c64dSAndroid Build Coastguard Worker            or flag.startswith("relocation-model=")
244*90c8c64dSAndroid Build Coastguard Worker            or flag == "prefer-dynamic"
245*90c8c64dSAndroid Build Coastguard Worker            or flag == "target-feature=+reserve-x18"
246*90c8c64dSAndroid Build Coastguard Worker        ):
247*90c8c64dSAndroid Build Coastguard Worker            self.codegens.append(flag)
248*90c8c64dSAndroid Build Coastguard Worker
249*90c8c64dSAndroid Build Coastguard Worker    def get_dependencies(self):
250*90c8c64dSAndroid Build Coastguard Worker        """Use output from cargo metadata to determine crate dependencies"""
251*90c8c64dSAndroid Build Coastguard Worker        cargo_metadata = subprocess.run(
252*90c8c64dSAndroid Build Coastguard Worker            [
253*90c8c64dSAndroid Build Coastguard Worker                self.runner.cargo_path,
254*90c8c64dSAndroid Build Coastguard Worker                "metadata",
255*90c8c64dSAndroid Build Coastguard Worker                "--no-deps",
256*90c8c64dSAndroid Build Coastguard Worker                "--format-version",
257*90c8c64dSAndroid Build Coastguard Worker                "1",
258*90c8c64dSAndroid Build Coastguard Worker            ],
259*90c8c64dSAndroid Build Coastguard Worker            cwd=os.path.abspath(self.cargo_dir),
260*90c8c64dSAndroid Build Coastguard Worker            stdout=subprocess.PIPE,
261*90c8c64dSAndroid Build Coastguard Worker            check=False,
262*90c8c64dSAndroid Build Coastguard Worker        )
263*90c8c64dSAndroid Build Coastguard Worker        if cargo_metadata.returncode:
264*90c8c64dSAndroid Build Coastguard Worker            self.errors += (
265*90c8c64dSAndroid Build Coastguard Worker                "ERROR: unable to get cargo metadata to determine "
266*90c8c64dSAndroid Build Coastguard Worker                f"dependencies; return code {cargo_metadata.returncode}\n"
267*90c8c64dSAndroid Build Coastguard Worker            )
268*90c8c64dSAndroid Build Coastguard Worker        else:
269*90c8c64dSAndroid Build Coastguard Worker            metadata_json = json.loads(cargo_metadata.stdout)
270*90c8c64dSAndroid Build Coastguard Worker
271*90c8c64dSAndroid Build Coastguard Worker            for package in metadata_json["packages"]:
272*90c8c64dSAndroid Build Coastguard Worker                # package names containing '-' are changed to '_' in crate_name
273*90c8c64dSAndroid Build Coastguard Worker                if package["name"].replace("-", "_") == self.crate_name:
274*90c8c64dSAndroid Build Coastguard Worker                    self.dependencies = package["dependencies"]
275*90c8c64dSAndroid Build Coastguard Worker                    for feat, props in package["features"].items():
276*90c8c64dSAndroid Build Coastguard Worker                        feat_deps = [
277*90c8c64dSAndroid Build Coastguard Worker                            d[4:] for d in props if d.startswith("dep:")
278*90c8c64dSAndroid Build Coastguard Worker                        ]
279*90c8c64dSAndroid Build Coastguard Worker                        if feat_deps and feat in self.feature_dependencies:
280*90c8c64dSAndroid Build Coastguard Worker                            self.feature_dependencies[feat].extend(feat_deps)
281*90c8c64dSAndroid Build Coastguard Worker                        else:
282*90c8c64dSAndroid Build Coastguard Worker                            self.feature_dependencies[feat] = feat_deps
283*90c8c64dSAndroid Build Coastguard Worker                    break
284*90c8c64dSAndroid Build Coastguard Worker            else:  # package name not found in metadata
285*90c8c64dSAndroid Build Coastguard Worker                if is_build_crate_name(self.crate_name):
286*90c8c64dSAndroid Build Coastguard Worker                    print(
287*90c8c64dSAndroid Build Coastguard Worker                        "### WARNING: unable to determine dependencies for "
288*90c8c64dSAndroid Build Coastguard Worker                        + f"{self.crate_name} from cargo metadata"
289*90c8c64dSAndroid Build Coastguard Worker                    )
290*90c8c64dSAndroid Build Coastguard Worker
291*90c8c64dSAndroid Build Coastguard Worker    def parse(self, line_num, line):
292*90c8c64dSAndroid Build Coastguard Worker        """Find important rustc arguments to convert to makefile rules."""
293*90c8c64dSAndroid Build Coastguard Worker        self.line_num = line_num
294*90c8c64dSAndroid Build Coastguard Worker        self.line = line
295*90c8c64dSAndroid Build Coastguard Worker        args = [unquote(l) for l in line.split()]
296*90c8c64dSAndroid Build Coastguard Worker        i = 0
297*90c8c64dSAndroid Build Coastguard Worker        # Loop through every argument of rustc.
298*90c8c64dSAndroid Build Coastguard Worker        while i < len(args):
299*90c8c64dSAndroid Build Coastguard Worker            arg = args[i]
300*90c8c64dSAndroid Build Coastguard Worker            if arg == "--crate-name":
301*90c8c64dSAndroid Build Coastguard Worker                i += 1
302*90c8c64dSAndroid Build Coastguard Worker                self.crate_name = args[i]
303*90c8c64dSAndroid Build Coastguard Worker            elif arg == "--crate-type":
304*90c8c64dSAndroid Build Coastguard Worker                i += 1
305*90c8c64dSAndroid Build Coastguard Worker                # cargo calls rustc with multiple --crate-type flags.
306*90c8c64dSAndroid Build Coastguard Worker                # rustc can accept:
307*90c8c64dSAndroid Build Coastguard Worker                #  --crate-type [bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]
308*90c8c64dSAndroid Build Coastguard Worker                self.crate_types.append(args[i])
309*90c8c64dSAndroid Build Coastguard Worker            elif arg == "--test":
310*90c8c64dSAndroid Build Coastguard Worker                self.crate_types.append("test")
311*90c8c64dSAndroid Build Coastguard Worker            elif arg == "--target":
312*90c8c64dSAndroid Build Coastguard Worker                i += 1
313*90c8c64dSAndroid Build Coastguard Worker                self.target = args[i]
314*90c8c64dSAndroid Build Coastguard Worker            elif arg == "--cfg":
315*90c8c64dSAndroid Build Coastguard Worker                i += 1
316*90c8c64dSAndroid Build Coastguard Worker                if args[i].startswith("feature="):
317*90c8c64dSAndroid Build Coastguard Worker                    self.features.append(
318*90c8c64dSAndroid Build Coastguard Worker                        unquote(args[i].replace("feature=", ""))
319*90c8c64dSAndroid Build Coastguard Worker                    )
320*90c8c64dSAndroid Build Coastguard Worker                else:
321*90c8c64dSAndroid Build Coastguard Worker                    self.cfgs.append(args[i])
322*90c8c64dSAndroid Build Coastguard Worker            elif arg == "--extern":
323*90c8c64dSAndroid Build Coastguard Worker                i += 1
324*90c8c64dSAndroid Build Coastguard Worker                pass  # ignored; get all dependencies from cargo metadata
325*90c8c64dSAndroid Build Coastguard Worker            elif arg == "-C":  # codegen options
326*90c8c64dSAndroid Build Coastguard Worker                i += 1
327*90c8c64dSAndroid Build Coastguard Worker                self.add_codegens_flag(args[i])
328*90c8c64dSAndroid Build Coastguard Worker            elif arg.startswith("-C"):
329*90c8c64dSAndroid Build Coastguard Worker                # cargo has been passing "-C <xyz>" flag to rustc,
330*90c8c64dSAndroid Build Coastguard Worker                # but newer cargo could pass '-Cembed-bitcode=no' to rustc.
331*90c8c64dSAndroid Build Coastguard Worker                self.add_codegens_flag(arg[2:])
332*90c8c64dSAndroid Build Coastguard Worker            elif arg == "--cap-lints":
333*90c8c64dSAndroid Build Coastguard Worker                i += 1
334*90c8c64dSAndroid Build Coastguard Worker                self.cap_lints = args[i]
335*90c8c64dSAndroid Build Coastguard Worker            elif arg == "-L":
336*90c8c64dSAndroid Build Coastguard Worker                i += 1
337*90c8c64dSAndroid Build Coastguard Worker                if args[i].startswith("dependency=") and args[i].endswith(
338*90c8c64dSAndroid Build Coastguard Worker                    "/deps"
339*90c8c64dSAndroid Build Coastguard Worker                ):
340*90c8c64dSAndroid Build Coastguard Worker                    if "/" + TARGET_TMP + "/" in args[i]:
341*90c8c64dSAndroid Build Coastguard Worker                        self.root_pkg = re.sub(
342*90c8c64dSAndroid Build Coastguard Worker                            "^.*/",
343*90c8c64dSAndroid Build Coastguard Worker                            "",
344*90c8c64dSAndroid Build Coastguard Worker                            re.sub("/" + TARGET_TMP + "/.*/deps$", "", args[i]),
345*90c8c64dSAndroid Build Coastguard Worker                        )
346*90c8c64dSAndroid Build Coastguard Worker                    else:
347*90c8c64dSAndroid Build Coastguard Worker                        self.root_pkg = re.sub(
348*90c8c64dSAndroid Build Coastguard Worker                            "^.*/",
349*90c8c64dSAndroid Build Coastguard Worker                            "",
350*90c8c64dSAndroid Build Coastguard Worker                            re.sub("/[^/]+/[^/]+/deps$", "", args[i]),
351*90c8c64dSAndroid Build Coastguard Worker                        )
352*90c8c64dSAndroid Build Coastguard Worker                    self.root_pkg = remove_version_suffix(self.root_pkg)
353*90c8c64dSAndroid Build Coastguard Worker            elif arg == "-l":
354*90c8c64dSAndroid Build Coastguard Worker                i += 1
355*90c8c64dSAndroid Build Coastguard Worker                if args[i].startswith("static="):
356*90c8c64dSAndroid Build Coastguard Worker                    self.static_libs.append(re.sub("static=", "", args[i]))
357*90c8c64dSAndroid Build Coastguard Worker                elif args[i].startswith("dylib="):
358*90c8c64dSAndroid Build Coastguard Worker                    self.shared_libs.append(re.sub("dylib=", "", args[i]))
359*90c8c64dSAndroid Build Coastguard Worker                else:
360*90c8c64dSAndroid Build Coastguard Worker                    self.shared_libs.append(args[i])
361*90c8c64dSAndroid Build Coastguard Worker            elif arg in ("--out-dir", "--color"):  # ignored
362*90c8c64dSAndroid Build Coastguard Worker                i += 1
363*90c8c64dSAndroid Build Coastguard Worker            elif arg.startswith("--error-format=") or arg.startswith("--json="):
364*90c8c64dSAndroid Build Coastguard Worker                pass  # ignored
365*90c8c64dSAndroid Build Coastguard Worker            elif arg.startswith("--emit="):
366*90c8c64dSAndroid Build Coastguard Worker                self.emit_list = arg.replace("--emit=", "")
367*90c8c64dSAndroid Build Coastguard Worker            elif arg.startswith("--edition="):
368*90c8c64dSAndroid Build Coastguard Worker                self.edition = arg.replace("--edition=", "")
369*90c8c64dSAndroid Build Coastguard Worker            elif arg.startswith("-Aclippy") or arg.startswith("-Wclippy"):
370*90c8c64dSAndroid Build Coastguard Worker                pass  # TODO: emit these flags in rules.mk
371*90c8c64dSAndroid Build Coastguard Worker            elif arg.startswith("-W"):
372*90c8c64dSAndroid Build Coastguard Worker                pass  # ignored
373*90c8c64dSAndroid Build Coastguard Worker            elif arg.startswith("-Z"):
374*90c8c64dSAndroid Build Coastguard Worker                pass  # ignore unstable flags
375*90c8c64dSAndroid Build Coastguard Worker            elif arg.startswith("-D"):
376*90c8c64dSAndroid Build Coastguard Worker                pass  # TODO: emit these flags in rules.mk
377*90c8c64dSAndroid Build Coastguard Worker            elif not arg.startswith("-"):
378*90c8c64dSAndroid Build Coastguard Worker                # shorten imported crate main source paths like $HOME/.cargo/
379*90c8c64dSAndroid Build Coastguard Worker                # registry/src/github.com-1ecc6299db9ec823/memchr-2.3.3/src/
380*90c8c64dSAndroid Build Coastguard Worker                # lib.rs
381*90c8c64dSAndroid Build Coastguard Worker                self.main_src = re.sub(
382*90c8c64dSAndroid Build Coastguard Worker                    r"^/[^ ]*/registry/src/", ".../", args[i]
383*90c8c64dSAndroid Build Coastguard Worker                )
384*90c8c64dSAndroid Build Coastguard Worker                self.main_src = re.sub(
385*90c8c64dSAndroid Build Coastguard Worker                    r"^\.\.\./github.com-[0-9a-f]*/", ".../", self.main_src
386*90c8c64dSAndroid Build Coastguard Worker                )
387*90c8c64dSAndroid Build Coastguard Worker                self.find_cargo_dir()
388*90c8c64dSAndroid Build Coastguard Worker                if self.cargo_dir:  # for a subdirectory
389*90c8c64dSAndroid Build Coastguard Worker                    if (
390*90c8c64dSAndroid Build Coastguard Worker                        self.runner.args.no_subdir
391*90c8c64dSAndroid Build Coastguard Worker                    ):  # all .mk content to /dev/null
392*90c8c64dSAndroid Build Coastguard Worker                        self.outf_name = "/dev/null"
393*90c8c64dSAndroid Build Coastguard Worker                    elif not self.runner.args.onefile:
394*90c8c64dSAndroid Build Coastguard Worker                        # Write to rules.mk in the subdirectory with Cargo.toml.
395*90c8c64dSAndroid Build Coastguard Worker                        self.outf_name = self.cargo_dir + "/rules.mk"
396*90c8c64dSAndroid Build Coastguard Worker                        self.main_src = self.main_src[len(self.cargo_dir) + 1 :]
397*90c8c64dSAndroid Build Coastguard Worker
398*90c8c64dSAndroid Build Coastguard Worker            else:
399*90c8c64dSAndroid Build Coastguard Worker                self.errors += "ERROR: unknown " + arg + "\n"
400*90c8c64dSAndroid Build Coastguard Worker            i += 1
401*90c8c64dSAndroid Build Coastguard Worker        if not self.crate_name:
402*90c8c64dSAndroid Build Coastguard Worker            self.errors += "ERROR: missing --crate-name\n"
403*90c8c64dSAndroid Build Coastguard Worker        if not self.main_src:
404*90c8c64dSAndroid Build Coastguard Worker            self.errors += "ERROR: missing main source file\n"
405*90c8c64dSAndroid Build Coastguard Worker        else:
406*90c8c64dSAndroid Build Coastguard Worker            self.srcs.append(self.main_src)
407*90c8c64dSAndroid Build Coastguard Worker        if not self.crate_types:
408*90c8c64dSAndroid Build Coastguard Worker            # Treat "--cfg test" as "--test"
409*90c8c64dSAndroid Build Coastguard Worker            if "test" in self.cfgs:
410*90c8c64dSAndroid Build Coastguard Worker                self.crate_types.append("test")
411*90c8c64dSAndroid Build Coastguard Worker            else:
412*90c8c64dSAndroid Build Coastguard Worker                self.errors += "ERROR: missing --crate-type or --test\n"
413*90c8c64dSAndroid Build Coastguard Worker        elif len(self.crate_types) > 1:
414*90c8c64dSAndroid Build Coastguard Worker            if "test" in self.crate_types:
415*90c8c64dSAndroid Build Coastguard Worker                self.errors += (
416*90c8c64dSAndroid Build Coastguard Worker                    "ERROR: cannot handle both --crate-type and --test\n"
417*90c8c64dSAndroid Build Coastguard Worker                )
418*90c8c64dSAndroid Build Coastguard Worker            if "lib" in self.crate_types and "rlib" in self.crate_types:
419*90c8c64dSAndroid Build Coastguard Worker                self.errors += (
420*90c8c64dSAndroid Build Coastguard Worker                    "ERROR: cannot generate both lib and rlib crate types\n"
421*90c8c64dSAndroid Build Coastguard Worker                )
422*90c8c64dSAndroid Build Coastguard Worker        if not self.root_pkg:
423*90c8c64dSAndroid Build Coastguard Worker            self.root_pkg = self.crate_name
424*90c8c64dSAndroid Build Coastguard Worker
425*90c8c64dSAndroid Build Coastguard Worker        # get the package dependencies by running cargo metadata
426*90c8c64dSAndroid Build Coastguard Worker        if not self.skip_crate():
427*90c8c64dSAndroid Build Coastguard Worker            self.get_dependencies()
428*90c8c64dSAndroid Build Coastguard Worker        self.cfgs = sorted(set(self.cfgs))
429*90c8c64dSAndroid Build Coastguard Worker        self.features = sorted(set(self.features))
430*90c8c64dSAndroid Build Coastguard Worker        self.codegens = sorted(set(self.codegens))
431*90c8c64dSAndroid Build Coastguard Worker        self.static_libs = sorted(set(self.static_libs))
432*90c8c64dSAndroid Build Coastguard Worker        self.shared_libs = sorted(set(self.shared_libs))
433*90c8c64dSAndroid Build Coastguard Worker        self.crate_types = sorted(set(self.crate_types))
434*90c8c64dSAndroid Build Coastguard Worker        self.module_name = self.stem
435*90c8c64dSAndroid Build Coastguard Worker        return self
436*90c8c64dSAndroid Build Coastguard Worker
437*90c8c64dSAndroid Build Coastguard Worker    def dump_line(self):
438*90c8c64dSAndroid Build Coastguard Worker        self.write("\n// Line " + str(self.line_num) + " " + self.line)
439*90c8c64dSAndroid Build Coastguard Worker
440*90c8c64dSAndroid Build Coastguard Worker    def feature_list(self):
441*90c8c64dSAndroid Build Coastguard Worker        """Return a string of main_src + "feature_list"."""
442*90c8c64dSAndroid Build Coastguard Worker        pkg = self.main_src
443*90c8c64dSAndroid Build Coastguard Worker        if pkg.startswith(".../"):  # keep only the main package name
444*90c8c64dSAndroid Build Coastguard Worker            pkg = re.sub("/.*", "", pkg[4:])
445*90c8c64dSAndroid Build Coastguard Worker        elif pkg.startswith("/"):  # use relative path for a local package
446*90c8c64dSAndroid Build Coastguard Worker            pkg = os.path.relpath(pkg)
447*90c8c64dSAndroid Build Coastguard Worker        if not self.features:
448*90c8c64dSAndroid Build Coastguard Worker            return pkg
449*90c8c64dSAndroid Build Coastguard Worker        return pkg + ' "' + ",".join(self.features) + '"'
450*90c8c64dSAndroid Build Coastguard Worker
451*90c8c64dSAndroid Build Coastguard Worker    def dump_skip_crate(self, kind):
452*90c8c64dSAndroid Build Coastguard Worker        if self.debug:
453*90c8c64dSAndroid Build Coastguard Worker            self.write("\n// IGNORED: " + kind + " " + self.main_src)
454*90c8c64dSAndroid Build Coastguard Worker        return self
455*90c8c64dSAndroid Build Coastguard Worker
456*90c8c64dSAndroid Build Coastguard Worker    def skip_crate(self):
457*90c8c64dSAndroid Build Coastguard Worker        """Return crate_name or a message if this crate should be skipped."""
458*90c8c64dSAndroid Build Coastguard Worker        if (
459*90c8c64dSAndroid Build Coastguard Worker            is_build_crate_name(self.crate_name)
460*90c8c64dSAndroid Build Coastguard Worker            or self.crate_name in EXCLUDED_CRATES
461*90c8c64dSAndroid Build Coastguard Worker        ):
462*90c8c64dSAndroid Build Coastguard Worker            return self.crate_name
463*90c8c64dSAndroid Build Coastguard Worker        if is_dependent_file_path(self.main_src):
464*90c8c64dSAndroid Build Coastguard Worker            return "dependent crate"
465*90c8c64dSAndroid Build Coastguard Worker        return ""
466*90c8c64dSAndroid Build Coastguard Worker
467*90c8c64dSAndroid Build Coastguard Worker    def dump(self):
468*90c8c64dSAndroid Build Coastguard Worker        """Dump all error/debug/module code to the output rules.mk file."""
469*90c8c64dSAndroid Build Coastguard Worker        self.runner.init_rules_file(self.outf_name)
470*90c8c64dSAndroid Build Coastguard Worker        with open(self.outf_name, "a", encoding="utf-8") as outf:
471*90c8c64dSAndroid Build Coastguard Worker            self.outf = outf
472*90c8c64dSAndroid Build Coastguard Worker            if self.errors:
473*90c8c64dSAndroid Build Coastguard Worker                self.dump_line()
474*90c8c64dSAndroid Build Coastguard Worker                self.write(self.errors)
475*90c8c64dSAndroid Build Coastguard Worker            elif self.skip_crate():
476*90c8c64dSAndroid Build Coastguard Worker                self.dump_skip_crate(self.skip_crate())
477*90c8c64dSAndroid Build Coastguard Worker            else:
478*90c8c64dSAndroid Build Coastguard Worker                if self.debug:
479*90c8c64dSAndroid Build Coastguard Worker                    self.dump_debug_info()
480*90c8c64dSAndroid Build Coastguard Worker                self.dump_trusty_module()
481*90c8c64dSAndroid Build Coastguard Worker            self.outf = None
482*90c8c64dSAndroid Build Coastguard Worker
483*90c8c64dSAndroid Build Coastguard Worker    def dump_debug_info(self):
484*90c8c64dSAndroid Build Coastguard Worker        """Dump parsed data, when cargo2rulesmk is called with --debug."""
485*90c8c64dSAndroid Build Coastguard Worker
486*90c8c64dSAndroid Build Coastguard Worker        def dump(name, value):
487*90c8c64dSAndroid Build Coastguard Worker            self.write(f"//{name:>12} = {value}")
488*90c8c64dSAndroid Build Coastguard Worker
489*90c8c64dSAndroid Build Coastguard Worker        def opt_dump(name, value):
490*90c8c64dSAndroid Build Coastguard Worker            if value:
491*90c8c64dSAndroid Build Coastguard Worker                dump(name, value)
492*90c8c64dSAndroid Build Coastguard Worker
493*90c8c64dSAndroid Build Coastguard Worker        def dump_list(fmt, values):
494*90c8c64dSAndroid Build Coastguard Worker            for v in values:
495*90c8c64dSAndroid Build Coastguard Worker                self.write(fmt % v)
496*90c8c64dSAndroid Build Coastguard Worker
497*90c8c64dSAndroid Build Coastguard Worker        self.dump_line()
498*90c8c64dSAndroid Build Coastguard Worker        dump("module_name", self.module_name)
499*90c8c64dSAndroid Build Coastguard Worker        dump("crate_name", self.crate_name)
500*90c8c64dSAndroid Build Coastguard Worker        dump("crate_types", self.crate_types)
501*90c8c64dSAndroid Build Coastguard Worker        dump("main_src", self.main_src)
502*90c8c64dSAndroid Build Coastguard Worker        dump("has_warning", self.has_warning)
503*90c8c64dSAndroid Build Coastguard Worker        opt_dump("target", self.target)
504*90c8c64dSAndroid Build Coastguard Worker        opt_dump("edition", self.edition)
505*90c8c64dSAndroid Build Coastguard Worker        opt_dump("emit_list", self.emit_list)
506*90c8c64dSAndroid Build Coastguard Worker        opt_dump("cap_lints", self.cap_lints)
507*90c8c64dSAndroid Build Coastguard Worker        dump_list("//         cfg = %s", self.cfgs)
508*90c8c64dSAndroid Build Coastguard Worker        dump_list("//         cfg = 'feature \"%s\"'", self.features)
509*90c8c64dSAndroid Build Coastguard Worker        # TODO(chh): escape quotes in self.features, but not in other dump_list
510*90c8c64dSAndroid Build Coastguard Worker        dump_list("//     codegen = %s", self.codegens)
511*90c8c64dSAndroid Build Coastguard Worker        dump_list("//   -l static = %s", self.static_libs)
512*90c8c64dSAndroid Build Coastguard Worker        dump_list("//  -l (dylib) = %s", self.shared_libs)
513*90c8c64dSAndroid Build Coastguard Worker
514*90c8c64dSAndroid Build Coastguard Worker    def dump_trusty_module(self):
515*90c8c64dSAndroid Build Coastguard Worker        """Dump one or more module definitions, depending on crate_types."""
516*90c8c64dSAndroid Build Coastguard Worker        if len(self.crate_types) > 1:
517*90c8c64dSAndroid Build Coastguard Worker            if "test" in self.crate_types:
518*90c8c64dSAndroid Build Coastguard Worker                self.write("\nERROR: multiple crate types cannot include test type")
519*90c8c64dSAndroid Build Coastguard Worker                return
520*90c8c64dSAndroid Build Coastguard Worker
521*90c8c64dSAndroid Build Coastguard Worker            if "lib" in self.crate_types:
522*90c8c64dSAndroid Build Coastguard Worker                print(f"### WARNING: crate {self.crate_name} has multiple "
523*90c8c64dSAndroid Build Coastguard Worker                      f"crate types ({str(self.crate_types)}). Treating as 'lib'")
524*90c8c64dSAndroid Build Coastguard Worker                self.crate_types = ["lib"]
525*90c8c64dSAndroid Build Coastguard Worker            else:
526*90c8c64dSAndroid Build Coastguard Worker                self.write("\nERROR: don't know how to handle crate types of "
527*90c8c64dSAndroid Build Coastguard Worker                           f"crate {self.crate_name}: {str(self.crate_types)}")
528*90c8c64dSAndroid Build Coastguard Worker                return
529*90c8c64dSAndroid Build Coastguard Worker
530*90c8c64dSAndroid Build Coastguard Worker        self.dump_single_type_trusty_module()
531*90c8c64dSAndroid Build Coastguard Worker
532*90c8c64dSAndroid Build Coastguard Worker    def dump_srcs_list(self):
533*90c8c64dSAndroid Build Coastguard Worker        """Dump the srcs list, for defaults or regular modules."""
534*90c8c64dSAndroid Build Coastguard Worker        if len(self.srcs) > 1:
535*90c8c64dSAndroid Build Coastguard Worker            srcs = sorted(set(self.srcs))  # make a copy and dedup
536*90c8c64dSAndroid Build Coastguard Worker        else:
537*90c8c64dSAndroid Build Coastguard Worker            srcs = [self.main_src]
538*90c8c64dSAndroid Build Coastguard Worker        self.write("MODULE_SRCS := \\")
539*90c8c64dSAndroid Build Coastguard Worker        for src in srcs:
540*90c8c64dSAndroid Build Coastguard Worker            self.write(f"\t$(LOCAL_DIR)/{src} \\")
541*90c8c64dSAndroid Build Coastguard Worker        self.write("")
542*90c8c64dSAndroid Build Coastguard Worker
543*90c8c64dSAndroid Build Coastguard Worker        # add rust file generated by build.rs to MODULE_SRCDEPS, if any
544*90c8c64dSAndroid Build Coastguard Worker        # TODO(perlarsen): is there a need to support more than one output file?
545*90c8c64dSAndroid Build Coastguard Worker        if srcdeps := [
546*90c8c64dSAndroid Build Coastguard Worker            f for f in self.runner.build_out_files if f.endswith(".rs")
547*90c8c64dSAndroid Build Coastguard Worker        ]:
548*90c8c64dSAndroid Build Coastguard Worker            assert len(srcdeps) == 1
549*90c8c64dSAndroid Build Coastguard Worker            outfile = srcdeps.pop()
550*90c8c64dSAndroid Build Coastguard Worker            lines = [
551*90c8c64dSAndroid Build Coastguard Worker                f"OUT_FILE := $(call TOBUILDDIR,$(MODULE))/{outfile}",
552*90c8c64dSAndroid Build Coastguard Worker                f"$(OUT_FILE): $(MODULE)/out/{outfile}",
553*90c8c64dSAndroid Build Coastguard Worker                "\t@echo copying $< to $@",
554*90c8c64dSAndroid Build Coastguard Worker                "\t@$(MKDIR)",
555*90c8c64dSAndroid Build Coastguard Worker                "\tcp $< $@",
556*90c8c64dSAndroid Build Coastguard Worker                "",
557*90c8c64dSAndroid Build Coastguard Worker                "MODULE_RUST_ENV += OUT_DIR=$(dir $(OUT_FILE))",
558*90c8c64dSAndroid Build Coastguard Worker                "",
559*90c8c64dSAndroid Build Coastguard Worker                "MODULE_SRCDEPS := $(OUT_FILE)",
560*90c8c64dSAndroid Build Coastguard Worker            ]
561*90c8c64dSAndroid Build Coastguard Worker            self.write("\n".join(lines))
562*90c8c64dSAndroid Build Coastguard Worker
563*90c8c64dSAndroid Build Coastguard Worker    def dump_single_type_trusty_module(self):
564*90c8c64dSAndroid Build Coastguard Worker        """Dump one simple Trusty module, which has only one crate_type."""
565*90c8c64dSAndroid Build Coastguard Worker        crate_type = self.crate_types[0]
566*90c8c64dSAndroid Build Coastguard Worker        assert crate_type != "test"
567*90c8c64dSAndroid Build Coastguard Worker        self.dump_one_trusty_module(crate_type)
568*90c8c64dSAndroid Build Coastguard Worker
569*90c8c64dSAndroid Build Coastguard Worker    def dump_one_trusty_module(self, crate_type):
570*90c8c64dSAndroid Build Coastguard Worker        """Dump one Trusty module definition."""
571*90c8c64dSAndroid Build Coastguard Worker        if crate_type in ["test", "bin"]:  # TODO: support test crates
572*90c8c64dSAndroid Build Coastguard Worker            print(
573*90c8c64dSAndroid Build Coastguard Worker                f"### WARNING: ignoring {crate_type} crate: {self.crate_name}")
574*90c8c64dSAndroid Build Coastguard Worker            return
575*90c8c64dSAndroid Build Coastguard Worker        if self.codegens:  # TODO: support crates that require codegen flags
576*90c8c64dSAndroid Build Coastguard Worker            print(
577*90c8c64dSAndroid Build Coastguard Worker                f"ERROR: {self.crate_name} uses unexpected codegen flags: " +
578*90c8c64dSAndroid Build Coastguard Worker                str(self.codegens)
579*90c8c64dSAndroid Build Coastguard Worker            )
580*90c8c64dSAndroid Build Coastguard Worker            return
581*90c8c64dSAndroid Build Coastguard Worker
582*90c8c64dSAndroid Build Coastguard Worker        self.dump_core_properties()
583*90c8c64dSAndroid Build Coastguard Worker        if not self.defaults:
584*90c8c64dSAndroid Build Coastguard Worker            self.dump_edition_flags_libs()
585*90c8c64dSAndroid Build Coastguard Worker
586*90c8c64dSAndroid Build Coastguard Worker        # NOTE: a crate may list the same dependency as required and optional
587*90c8c64dSAndroid Build Coastguard Worker        library_deps = set()
588*90c8c64dSAndroid Build Coastguard Worker        for dependency in self.dependencies:
589*90c8c64dSAndroid Build Coastguard Worker            if dependency["kind"] in ["dev", "build"]:
590*90c8c64dSAndroid Build Coastguard Worker                continue
591*90c8c64dSAndroid Build Coastguard Worker            name = (
592*90c8c64dSAndroid Build Coastguard Worker                rename
593*90c8c64dSAndroid Build Coastguard Worker                if (rename := dependency["rename"])
594*90c8c64dSAndroid Build Coastguard Worker                else dependency["name"]
595*90c8c64dSAndroid Build Coastguard Worker            )
596*90c8c64dSAndroid Build Coastguard Worker            if dependency["target"]:
597*90c8c64dSAndroid Build Coastguard Worker                print(
598*90c8c64dSAndroid Build Coastguard Worker                    f"### WARNING: ignoring target-specific dependency: {name}")
599*90c8c64dSAndroid Build Coastguard Worker                continue
600*90c8c64dSAndroid Build Coastguard Worker            path = CUSTOM_MODULE_CRATES.get(
601*90c8c64dSAndroid Build Coastguard Worker                name, f"external/rust/crates/{name}"
602*90c8c64dSAndroid Build Coastguard Worker            )
603*90c8c64dSAndroid Build Coastguard Worker            if dependency["optional"]:
604*90c8c64dSAndroid Build Coastguard Worker                if not any(
605*90c8c64dSAndroid Build Coastguard Worker                    name in self.feature_dependencies.get(f, [])
606*90c8c64dSAndroid Build Coastguard Worker                    for f in self.features
607*90c8c64dSAndroid Build Coastguard Worker                ):
608*90c8c64dSAndroid Build Coastguard Worker                    continue
609*90c8c64dSAndroid Build Coastguard Worker            library_deps.add(path)
610*90c8c64dSAndroid Build Coastguard Worker        if library_deps:
611*90c8c64dSAndroid Build Coastguard Worker            self.write("MODULE_LIBRARY_DEPS := \\")
612*90c8c64dSAndroid Build Coastguard Worker            for path in sorted(library_deps):
613*90c8c64dSAndroid Build Coastguard Worker                self.write(f"\t{path} \\")
614*90c8c64dSAndroid Build Coastguard Worker            self.write("")
615*90c8c64dSAndroid Build Coastguard Worker        if crate_type == "test" and not self.default_srcs:
616*90c8c64dSAndroid Build Coastguard Worker            raise NotImplementedError("Crates with test data are not supported")
617*90c8c64dSAndroid Build Coastguard Worker
618*90c8c64dSAndroid Build Coastguard Worker        assert crate_type in LIBRARY_CRATE_TYPES
619*90c8c64dSAndroid Build Coastguard Worker        self.write("include make/library.mk")
620*90c8c64dSAndroid Build Coastguard Worker
621*90c8c64dSAndroid Build Coastguard Worker    def dump_edition_flags_libs(self):
622*90c8c64dSAndroid Build Coastguard Worker        if self.edition:
623*90c8c64dSAndroid Build Coastguard Worker            self.write(f"MODULE_RUST_EDITION := {self.edition}")
624*90c8c64dSAndroid Build Coastguard Worker        if self.features or self.cfgs:
625*90c8c64dSAndroid Build Coastguard Worker            self.write("MODULE_RUSTFLAGS += \\")
626*90c8c64dSAndroid Build Coastguard Worker            for feature in self.features:
627*90c8c64dSAndroid Build Coastguard Worker                self.write(f"\t--cfg 'feature=\"{feature}\"' \\")
628*90c8c64dSAndroid Build Coastguard Worker            for cfg in self.cfgs:
629*90c8c64dSAndroid Build Coastguard Worker                self.write(f"\t--cfg '{cfg}' \\")
630*90c8c64dSAndroid Build Coastguard Worker            self.write("")
631*90c8c64dSAndroid Build Coastguard Worker
632*90c8c64dSAndroid Build Coastguard Worker        if self.static_libs or self.shared_libs:
633*90c8c64dSAndroid Build Coastguard Worker            print("### WARNING: Crates with depend on static or shared "
634*90c8c64dSAndroid Build Coastguard Worker                  "libraries are not supported")
635*90c8c64dSAndroid Build Coastguard Worker
636*90c8c64dSAndroid Build Coastguard Worker    def main_src_basename_path(self):
637*90c8c64dSAndroid Build Coastguard Worker        return re.sub("/", "_", re.sub(".rs$", "", self.main_src))
638*90c8c64dSAndroid Build Coastguard Worker
639*90c8c64dSAndroid Build Coastguard Worker    def test_module_name(self):
640*90c8c64dSAndroid Build Coastguard Worker        """Return a unique name for a test module."""
641*90c8c64dSAndroid Build Coastguard Worker        # root_pkg+(_host|_device) + '_test_'+source_file_name
642*90c8c64dSAndroid Build Coastguard Worker        suffix = self.main_src_basename_path()
643*90c8c64dSAndroid Build Coastguard Worker        return self.root_pkg + "_test_" + suffix
644*90c8c64dSAndroid Build Coastguard Worker
645*90c8c64dSAndroid Build Coastguard Worker    def dump_core_properties(self):
646*90c8c64dSAndroid Build Coastguard Worker        """Dump the module header, name, stem, etc."""
647*90c8c64dSAndroid Build Coastguard Worker        self.write("LOCAL_DIR := $(GET_LOCAL_DIR)")
648*90c8c64dSAndroid Build Coastguard Worker        self.write("MODULE := $(LOCAL_DIR)")
649*90c8c64dSAndroid Build Coastguard Worker        self.write(f"MODULE_CRATE_NAME := {self.crate_name}")
650*90c8c64dSAndroid Build Coastguard Worker
651*90c8c64dSAndroid Build Coastguard Worker        # Trusty's module system only supports bin, rlib, and proc-macro so map
652*90c8c64dSAndroid Build Coastguard Worker        # lib->rlib
653*90c8c64dSAndroid Build Coastguard Worker        if self.crate_types != ["lib"]:
654*90c8c64dSAndroid Build Coastguard Worker            crate_types = set(
655*90c8c64dSAndroid Build Coastguard Worker                "rlib" if ct == "lib" else ct for ct in self.crate_types
656*90c8c64dSAndroid Build Coastguard Worker            )
657*90c8c64dSAndroid Build Coastguard Worker            self.write(f'MODULE_RUST_CRATE_TYPES := {" ".join(crate_types)}')
658*90c8c64dSAndroid Build Coastguard Worker
659*90c8c64dSAndroid Build Coastguard Worker        if not self.default_srcs:
660*90c8c64dSAndroid Build Coastguard Worker            self.dump_srcs_list()
661*90c8c64dSAndroid Build Coastguard Worker
662*90c8c64dSAndroid Build Coastguard Worker        if hasattr(self.runner.args, "module_add_implicit_deps"):
663*90c8c64dSAndroid Build Coastguard Worker            if hasattr(self.runner.args, "module_add_implicit_deps_reason"):
664*90c8c64dSAndroid Build Coastguard Worker                self.write(self.runner.args.module_add_implicit_deps_reason)
665*90c8c64dSAndroid Build Coastguard Worker
666*90c8c64dSAndroid Build Coastguard Worker            if self.runner.args.module_add_implicit_deps in [True, "yes"]:
667*90c8c64dSAndroid Build Coastguard Worker                self.write("MODULE_ADD_IMPLICIT_DEPS := true")
668*90c8c64dSAndroid Build Coastguard Worker            elif self.runner.args.module_add_implicit_deps in [False, "no"]:
669*90c8c64dSAndroid Build Coastguard Worker                self.write("MODULE_ADD_IMPLICIT_DEPS := false")
670*90c8c64dSAndroid Build Coastguard Worker            else:
671*90c8c64dSAndroid Build Coastguard Worker                sys.exit(
672*90c8c64dSAndroid Build Coastguard Worker                    "ERROR: invalid value for module_add_implicit_deps: " +
673*90c8c64dSAndroid Build Coastguard Worker                    str(self.runner.args.module_add_implicit_deps)
674*90c8c64dSAndroid Build Coastguard Worker                )
675*90c8c64dSAndroid Build Coastguard Worker
676*90c8c64dSAndroid Build Coastguard Worker
677*90c8c64dSAndroid Build Coastguard Workerclass Runner(object):
678*90c8c64dSAndroid Build Coastguard Worker    """Main class to parse cargo -v output and print Trusty makefile modules."""
679*90c8c64dSAndroid Build Coastguard Worker
680*90c8c64dSAndroid Build Coastguard Worker    def __init__(self, args):
681*90c8c64dSAndroid Build Coastguard Worker        self.mk_files = set()  # Remember all Trusty module files.
682*90c8c64dSAndroid Build Coastguard Worker        self.root_pkg = ""  # name of package in ./Cargo.toml
683*90c8c64dSAndroid Build Coastguard Worker        # Saved flags, modes, and data.
684*90c8c64dSAndroid Build Coastguard Worker        self.args = args
685*90c8c64dSAndroid Build Coastguard Worker        self.dry_run = not args.run
686*90c8c64dSAndroid Build Coastguard Worker        self.skip_cargo = args.skipcargo
687*90c8c64dSAndroid Build Coastguard Worker        self.cargo_path = "./cargo"  # path to cargo, will be set later
688*90c8c64dSAndroid Build Coastguard Worker        self.checked_out_files = False  # to check only once
689*90c8c64dSAndroid Build Coastguard Worker        self.build_out_files = []  # output files generated by build.rs
690*90c8c64dSAndroid Build Coastguard Worker        self.crates: List[Crate] = []
691*90c8c64dSAndroid Build Coastguard Worker        self.warning_files = set()
692*90c8c64dSAndroid Build Coastguard Worker        # Keep a unique mapping from (module name) to crate
693*90c8c64dSAndroid Build Coastguard Worker        self.name_owners = {}
694*90c8c64dSAndroid Build Coastguard Worker        # Save and dump all errors from cargo to rules.mk.
695*90c8c64dSAndroid Build Coastguard Worker        self.errors = ""
696*90c8c64dSAndroid Build Coastguard Worker        self.test_errors = ""
697*90c8c64dSAndroid Build Coastguard Worker        self.setup_cargo_path()
698*90c8c64dSAndroid Build Coastguard Worker        # Default action is cargo clean, followed by build or user given actions
699*90c8c64dSAndroid Build Coastguard Worker        if args.cargo:
700*90c8c64dSAndroid Build Coastguard Worker            self.cargo = ["clean"] + args.cargo
701*90c8c64dSAndroid Build Coastguard Worker        else:
702*90c8c64dSAndroid Build Coastguard Worker            default_target = "--target x86_64-unknown-linux-gnu"
703*90c8c64dSAndroid Build Coastguard Worker            # Use the same target for both host and default device builds.
704*90c8c64dSAndroid Build Coastguard Worker            # Same target is used as default in host x86_64 Android compilation.
705*90c8c64dSAndroid Build Coastguard Worker            # Note: b/169872957, prebuilt cargo failed to build vsock
706*90c8c64dSAndroid Build Coastguard Worker            # on x86_64-unknown-linux-musl systems.
707*90c8c64dSAndroid Build Coastguard Worker            self.cargo = ["clean", "build " + default_target]
708*90c8c64dSAndroid Build Coastguard Worker            if args.tests:
709*90c8c64dSAndroid Build Coastguard Worker                self.cargo.append("build --tests " + default_target)
710*90c8c64dSAndroid Build Coastguard Worker        self.empty_tests = set()
711*90c8c64dSAndroid Build Coastguard Worker        self.empty_unittests = False
712*90c8c64dSAndroid Build Coastguard Worker
713*90c8c64dSAndroid Build Coastguard Worker    def setup_cargo_path(self):
714*90c8c64dSAndroid Build Coastguard Worker        """Find cargo in the --cargo_bin or prebuilt rust bin directory."""
715*90c8c64dSAndroid Build Coastguard Worker        if self.args.cargo_bin:
716*90c8c64dSAndroid Build Coastguard Worker            self.cargo_path = os.path.join(self.args.cargo_bin, "cargo")
717*90c8c64dSAndroid Build Coastguard Worker            if not os.path.isfile(self.cargo_path):
718*90c8c64dSAndroid Build Coastguard Worker                sys.exit("ERROR: cannot find cargo in " + self.args.cargo_bin)
719*90c8c64dSAndroid Build Coastguard Worker            print("INFO: using cargo in " + self.args.cargo_bin)
720*90c8c64dSAndroid Build Coastguard Worker            return
721*90c8c64dSAndroid Build Coastguard Worker
722*90c8c64dSAndroid Build Coastguard Worker        # We have only tested this on Linux.
723*90c8c64dSAndroid Build Coastguard Worker        if platform.system() != "Linux":
724*90c8c64dSAndroid Build Coastguard Worker            sys.exit(
725*90c8c64dSAndroid Build Coastguard Worker                "ERROR: this script has only been tested on Linux with cargo."
726*90c8c64dSAndroid Build Coastguard Worker            )
727*90c8c64dSAndroid Build Coastguard Worker
728*90c8c64dSAndroid Build Coastguard Worker        # Assuming that this script is in development/scripts
729*90c8c64dSAndroid Build Coastguard Worker        env_setup_sh = os.path.join(
730*90c8c64dSAndroid Build Coastguard Worker            TOP_DIR, "trusty/vendor/google/aosp/scripts/envsetup.sh"
731*90c8c64dSAndroid Build Coastguard Worker        )
732*90c8c64dSAndroid Build Coastguard Worker        if not os.path.exists(env_setup_sh):
733*90c8c64dSAndroid Build Coastguard Worker            sys.exit("ERROR: missing " + env_setup_sh)
734*90c8c64dSAndroid Build Coastguard Worker        rust_version = self.find_rust_version(env_setup_sh)
735*90c8c64dSAndroid Build Coastguard Worker        self.cargo_path =  os.path.join(
736*90c8c64dSAndroid Build Coastguard Worker            TOP_DIR, f"prebuilts/rust/linux-x86/{rust_version}/bin/cargo"
737*90c8c64dSAndroid Build Coastguard Worker        )
738*90c8c64dSAndroid Build Coastguard Worker
739*90c8c64dSAndroid Build Coastguard Worker        if not os.path.isfile(self.cargo_path):
740*90c8c64dSAndroid Build Coastguard Worker            sys.exit(
741*90c8c64dSAndroid Build Coastguard Worker                "ERROR: no cargo at "
742*90c8c64dSAndroid Build Coastguard Worker                + self.cargo_path
743*90c8c64dSAndroid Build Coastguard Worker                + "; consider using the --cargo_bin= flag."
744*90c8c64dSAndroid Build Coastguard Worker            )
745*90c8c64dSAndroid Build Coastguard Worker
746*90c8c64dSAndroid Build Coastguard Worker        if self.args.verbose:
747*90c8c64dSAndroid Build Coastguard Worker            print(f"### INFO: using cargo from {self.cargo_path}")
748*90c8c64dSAndroid Build Coastguard Worker
749*90c8c64dSAndroid Build Coastguard Worker    def find_rust_version(self, env_setup_sh):
750*90c8c64dSAndroid Build Coastguard Worker        """find the Rust version used by Trusty from envsetup.sh"""
751*90c8c64dSAndroid Build Coastguard Worker
752*90c8c64dSAndroid Build Coastguard Worker        version_pat = re.compile(r"prebuilts/rust/linux-x86/([0-9]+\.[0-9]+\..+)/bin")
753*90c8c64dSAndroid Build Coastguard Worker
754*90c8c64dSAndroid Build Coastguard Worker        with open(env_setup_sh) as fh:
755*90c8c64dSAndroid Build Coastguard Worker            for line in fh.readlines():
756*90c8c64dSAndroid Build Coastguard Worker                if line.lstrip().startswith("export RUST_BINDIR"):
757*90c8c64dSAndroid Build Coastguard Worker                    if not (result := version_pat.search(line)):
758*90c8c64dSAndroid Build Coastguard Worker                        sys.exit("ERROR: failed to parse rust version "
759*90c8c64dSAndroid Build Coastguard Worker                                 + "from RUST_BINDIR in envsetup.sh: "
760*90c8c64dSAndroid Build Coastguard Worker                                 + line
761*90c8c64dSAndroid Build Coastguard Worker                        )
762*90c8c64dSAndroid Build Coastguard Worker                    version = result.group(1)
763*90c8c64dSAndroid Build Coastguard Worker
764*90c8c64dSAndroid Build Coastguard Worker                    if self.args.verbose:
765*90c8c64dSAndroid Build Coastguard Worker                        print(f"### INFO: using rust version {version}")
766*90c8c64dSAndroid Build Coastguard Worker
767*90c8c64dSAndroid Build Coastguard Worker                    return version
768*90c8c64dSAndroid Build Coastguard Worker
769*90c8c64dSAndroid Build Coastguard Worker        sys.exit("ERROR: failed to parse {env_setup_sh}; is RUST_BINDIR exported?")
770*90c8c64dSAndroid Build Coastguard Worker
771*90c8c64dSAndroid Build Coastguard Worker    def find_out_files(self):
772*90c8c64dSAndroid Build Coastguard Worker        # list1 has build.rs output for normal crates
773*90c8c64dSAndroid Build Coastguard Worker        list1 = glob.glob(
774*90c8c64dSAndroid Build Coastguard Worker            TARGET_TMP + "/*/*/build/" + self.root_pkg + "-*/out/*"
775*90c8c64dSAndroid Build Coastguard Worker        )
776*90c8c64dSAndroid Build Coastguard Worker        # list2 has build.rs output for proc-macro crates
777*90c8c64dSAndroid Build Coastguard Worker        list2 = glob.glob(TARGET_TMP + "/*/build/" + self.root_pkg + "-*/out/*")
778*90c8c64dSAndroid Build Coastguard Worker        return list1 + list2
779*90c8c64dSAndroid Build Coastguard Worker
780*90c8c64dSAndroid Build Coastguard Worker    def copy_out_files(self):
781*90c8c64dSAndroid Build Coastguard Worker        """Copy build.rs output files to ./out and set up build_out_files."""
782*90c8c64dSAndroid Build Coastguard Worker        if self.checked_out_files:
783*90c8c64dSAndroid Build Coastguard Worker            return
784*90c8c64dSAndroid Build Coastguard Worker        self.checked_out_files = True
785*90c8c64dSAndroid Build Coastguard Worker        cargo_out_files = self.find_out_files()
786*90c8c64dSAndroid Build Coastguard Worker        out_files = set()
787*90c8c64dSAndroid Build Coastguard Worker        if cargo_out_files:
788*90c8c64dSAndroid Build Coastguard Worker            os.makedirs("out", exist_ok=True)
789*90c8c64dSAndroid Build Coastguard Worker        for path in cargo_out_files:
790*90c8c64dSAndroid Build Coastguard Worker            file_name = path.split("/")[-1]
791*90c8c64dSAndroid Build Coastguard Worker            out_files.add(file_name)
792*90c8c64dSAndroid Build Coastguard Worker            shutil.copy(path, "out/" + file_name)
793*90c8c64dSAndroid Build Coastguard Worker        self.build_out_files = sorted(out_files)
794*90c8c64dSAndroid Build Coastguard Worker
795*90c8c64dSAndroid Build Coastguard Worker    def has_used_out_dir(self):
796*90c8c64dSAndroid Build Coastguard Worker        """Returns true if env!("OUT_DIR") is found."""
797*90c8c64dSAndroid Build Coastguard Worker        return 0 == os.system(
798*90c8c64dSAndroid Build Coastguard Worker            "grep -rl --exclude build.rs --include \\*.rs"
799*90c8c64dSAndroid Build Coastguard Worker            + " 'env!(\"OUT_DIR\")' * > /dev/null"
800*90c8c64dSAndroid Build Coastguard Worker        )
801*90c8c64dSAndroid Build Coastguard Worker
802*90c8c64dSAndroid Build Coastguard Worker    def init_rules_file(self, name):
803*90c8c64dSAndroid Build Coastguard Worker        # name could be rules.mk or sub_dir_path/rules.mk
804*90c8c64dSAndroid Build Coastguard Worker        if name not in self.mk_files:
805*90c8c64dSAndroid Build Coastguard Worker            self.mk_files.add(name)
806*90c8c64dSAndroid Build Coastguard Worker            with open(name, "w", encoding="utf-8") as outf:
807*90c8c64dSAndroid Build Coastguard Worker                print_args = sys.argv[1:].copy()
808*90c8c64dSAndroid Build Coastguard Worker                if "--cargo_bin" in print_args:
809*90c8c64dSAndroid Build Coastguard Worker                    index = print_args.index("--cargo_bin")
810*90c8c64dSAndroid Build Coastguard Worker                    del print_args[index : index + 2]
811*90c8c64dSAndroid Build Coastguard Worker                outf.write(RULES_MK_HEADER.format(args=" ".join(print_args)))
812*90c8c64dSAndroid Build Coastguard Worker
813*90c8c64dSAndroid Build Coastguard Worker    def find_root_pkg(self):
814*90c8c64dSAndroid Build Coastguard Worker        """Read name of [package] in ./Cargo.toml."""
815*90c8c64dSAndroid Build Coastguard Worker        if not os.path.exists("./Cargo.toml"):
816*90c8c64dSAndroid Build Coastguard Worker            return
817*90c8c64dSAndroid Build Coastguard Worker        with open("./Cargo.toml", "r", encoding="utf-8") as inf:
818*90c8c64dSAndroid Build Coastguard Worker            pkg_section = re.compile(r"^ *\[package\]")
819*90c8c64dSAndroid Build Coastguard Worker            name = re.compile('^ *name *= * "([^"]*)"')
820*90c8c64dSAndroid Build Coastguard Worker            in_pkg = False
821*90c8c64dSAndroid Build Coastguard Worker            for line in inf:
822*90c8c64dSAndroid Build Coastguard Worker                if in_pkg:
823*90c8c64dSAndroid Build Coastguard Worker                    if match := name.match(line):
824*90c8c64dSAndroid Build Coastguard Worker                        self.root_pkg = match.group(1)
825*90c8c64dSAndroid Build Coastguard Worker                        break
826*90c8c64dSAndroid Build Coastguard Worker                else:
827*90c8c64dSAndroid Build Coastguard Worker                    in_pkg = pkg_section.match(line) is not None
828*90c8c64dSAndroid Build Coastguard Worker
829*90c8c64dSAndroid Build Coastguard Worker    def run_cargo(self):
830*90c8c64dSAndroid Build Coastguard Worker        """Calls cargo -v and save its output to ./cargo.out."""
831*90c8c64dSAndroid Build Coastguard Worker        if self.skip_cargo:
832*90c8c64dSAndroid Build Coastguard Worker            return self
833*90c8c64dSAndroid Build Coastguard Worker        cargo_toml = "./Cargo.toml"
834*90c8c64dSAndroid Build Coastguard Worker        cargo_out = "./cargo.out"
835*90c8c64dSAndroid Build Coastguard Worker
836*90c8c64dSAndroid Build Coastguard Worker        # Do not use Cargo.lock, because Trusty makefile rules are designed
837*90c8c64dSAndroid Build Coastguard Worker        # to run with the latest available vendored crates in Trusty.
838*90c8c64dSAndroid Build Coastguard Worker        cargo_lock = "./Cargo.lock"
839*90c8c64dSAndroid Build Coastguard Worker        cargo_lock_saved = "./cargo.lock.saved"
840*90c8c64dSAndroid Build Coastguard Worker        had_cargo_lock = os.path.exists(cargo_lock)
841*90c8c64dSAndroid Build Coastguard Worker        if not os.access(cargo_toml, os.R_OK):
842*90c8c64dSAndroid Build Coastguard Worker            print("ERROR: Cannot find or read", cargo_toml)
843*90c8c64dSAndroid Build Coastguard Worker            return self
844*90c8c64dSAndroid Build Coastguard Worker        if not self.dry_run:
845*90c8c64dSAndroid Build Coastguard Worker            if os.path.exists(cargo_out):
846*90c8c64dSAndroid Build Coastguard Worker                os.remove(cargo_out)
847*90c8c64dSAndroid Build Coastguard Worker            if not self.args.use_cargo_lock and had_cargo_lock:  # save it
848*90c8c64dSAndroid Build Coastguard Worker                os.rename(cargo_lock, cargo_lock_saved)
849*90c8c64dSAndroid Build Coastguard Worker        cmd_tail_target = " --target-dir " + TARGET_TMP
850*90c8c64dSAndroid Build Coastguard Worker        cmd_tail_redir = " >> " + cargo_out + " 2>&1"
851*90c8c64dSAndroid Build Coastguard Worker        # set up search PATH for cargo to find the correct rustc
852*90c8c64dSAndroid Build Coastguard Worker        saved_path = os.environ["PATH"]
853*90c8c64dSAndroid Build Coastguard Worker        os.environ["PATH"] = os.path.dirname(self.cargo_path) + ":" + saved_path
854*90c8c64dSAndroid Build Coastguard Worker        # Add [workspace] to Cargo.toml if it is not there.
855*90c8c64dSAndroid Build Coastguard Worker        added_workspace = False
856*90c8c64dSAndroid Build Coastguard Worker        cargo_toml_lines = None
857*90c8c64dSAndroid Build Coastguard Worker        if self.args.add_workspace:
858*90c8c64dSAndroid Build Coastguard Worker            with open(cargo_toml, "r", encoding="utf-8") as in_file:
859*90c8c64dSAndroid Build Coastguard Worker                cargo_toml_lines = in_file.readlines()
860*90c8c64dSAndroid Build Coastguard Worker            found_workspace = "[workspace]\n" in cargo_toml_lines
861*90c8c64dSAndroid Build Coastguard Worker            if found_workspace:
862*90c8c64dSAndroid Build Coastguard Worker                print("### WARNING: found [workspace] in Cargo.toml")
863*90c8c64dSAndroid Build Coastguard Worker            else:
864*90c8c64dSAndroid Build Coastguard Worker                with open(cargo_toml, "a", encoding="utf-8") as out_file:
865*90c8c64dSAndroid Build Coastguard Worker                    out_file.write("\n\n[workspace]\n")
866*90c8c64dSAndroid Build Coastguard Worker                    added_workspace = True
867*90c8c64dSAndroid Build Coastguard Worker                    if self.args.verbose:
868*90c8c64dSAndroid Build Coastguard Worker                        print("### INFO: added [workspace] to Cargo.toml")
869*90c8c64dSAndroid Build Coastguard Worker        features = ""
870*90c8c64dSAndroid Build Coastguard Worker        for c in self.cargo:
871*90c8c64dSAndroid Build Coastguard Worker            features = ""
872*90c8c64dSAndroid Build Coastguard Worker            if c != "clean":
873*90c8c64dSAndroid Build Coastguard Worker                if self.args.features is not None:
874*90c8c64dSAndroid Build Coastguard Worker                    features = " --no-default-features"
875*90c8c64dSAndroid Build Coastguard Worker                if self.args.features:
876*90c8c64dSAndroid Build Coastguard Worker                    features += " --features " + self.args.features
877*90c8c64dSAndroid Build Coastguard Worker            cmd_v_flag = " -vv " if self.args.vv else " -v "
878*90c8c64dSAndroid Build Coastguard Worker            cmd = self.cargo_path + cmd_v_flag
879*90c8c64dSAndroid Build Coastguard Worker            cmd += c + features + cmd_tail_target + cmd_tail_redir
880*90c8c64dSAndroid Build Coastguard Worker            if c != "clean":
881*90c8c64dSAndroid Build Coastguard Worker                rustflags = self.args.rustflags if self.args.rustflags else ""
882*90c8c64dSAndroid Build Coastguard Worker                # linting issues shouldn't prevent us from generating rules.mk
883*90c8c64dSAndroid Build Coastguard Worker                rustflags = f'RUSTFLAGS="{rustflags} --cap-lints allow" '
884*90c8c64dSAndroid Build Coastguard Worker                cmd = rustflags + cmd
885*90c8c64dSAndroid Build Coastguard Worker            self.run_cmd(cmd, cargo_out)
886*90c8c64dSAndroid Build Coastguard Worker        if self.args.tests:
887*90c8c64dSAndroid Build Coastguard Worker            cmd = (
888*90c8c64dSAndroid Build Coastguard Worker                self.cargo_path
889*90c8c64dSAndroid Build Coastguard Worker                + " test"
890*90c8c64dSAndroid Build Coastguard Worker                + features
891*90c8c64dSAndroid Build Coastguard Worker                + cmd_tail_target
892*90c8c64dSAndroid Build Coastguard Worker                + " -- --list"
893*90c8c64dSAndroid Build Coastguard Worker                + cmd_tail_redir
894*90c8c64dSAndroid Build Coastguard Worker            )
895*90c8c64dSAndroid Build Coastguard Worker            self.run_cmd(cmd, cargo_out)
896*90c8c64dSAndroid Build Coastguard Worker        if added_workspace:  # restore original Cargo.toml
897*90c8c64dSAndroid Build Coastguard Worker            with open(cargo_toml, "w", encoding="utf-8") as out_file:
898*90c8c64dSAndroid Build Coastguard Worker                assert cargo_toml_lines
899*90c8c64dSAndroid Build Coastguard Worker                out_file.writelines(cargo_toml_lines)
900*90c8c64dSAndroid Build Coastguard Worker            if self.args.verbose:
901*90c8c64dSAndroid Build Coastguard Worker                print("### INFO: restored original Cargo.toml")
902*90c8c64dSAndroid Build Coastguard Worker        os.environ["PATH"] = saved_path
903*90c8c64dSAndroid Build Coastguard Worker        if not self.dry_run:
904*90c8c64dSAndroid Build Coastguard Worker            if not had_cargo_lock:  # restore to no Cargo.lock state
905*90c8c64dSAndroid Build Coastguard Worker                if os.path.exists(cargo_lock):
906*90c8c64dSAndroid Build Coastguard Worker                    os.remove(cargo_lock)
907*90c8c64dSAndroid Build Coastguard Worker            elif not self.args.use_cargo_lock:  # restore saved Cargo.lock
908*90c8c64dSAndroid Build Coastguard Worker                os.rename(cargo_lock_saved, cargo_lock)
909*90c8c64dSAndroid Build Coastguard Worker        return self
910*90c8c64dSAndroid Build Coastguard Worker
911*90c8c64dSAndroid Build Coastguard Worker    def run_cmd(self, cmd, cargo_out):
912*90c8c64dSAndroid Build Coastguard Worker        if self.dry_run:
913*90c8c64dSAndroid Build Coastguard Worker            print("Dry-run skip:", cmd)
914*90c8c64dSAndroid Build Coastguard Worker        else:
915*90c8c64dSAndroid Build Coastguard Worker            if self.args.verbose:
916*90c8c64dSAndroid Build Coastguard Worker                print("Running:", cmd)
917*90c8c64dSAndroid Build Coastguard Worker            with open(cargo_out, "a+", encoding="utf-8") as out_file:
918*90c8c64dSAndroid Build Coastguard Worker                out_file.write("### Running: " + cmd + "\n")
919*90c8c64dSAndroid Build Coastguard Worker            ret = os.system(cmd)
920*90c8c64dSAndroid Build Coastguard Worker            if ret != 0:
921*90c8c64dSAndroid Build Coastguard Worker                print(
922*90c8c64dSAndroid Build Coastguard Worker                    "*** There was an error while running cargo.  "
923*90c8c64dSAndroid Build Coastguard Worker                    + f"See the {cargo_out} file for details."
924*90c8c64dSAndroid Build Coastguard Worker                )
925*90c8c64dSAndroid Build Coastguard Worker
926*90c8c64dSAndroid Build Coastguard Worker    def apply_patch(self):
927*90c8c64dSAndroid Build Coastguard Worker        """Apply local patch file if it is given."""
928*90c8c64dSAndroid Build Coastguard Worker        if self.args.patch:
929*90c8c64dSAndroid Build Coastguard Worker            if self.dry_run:
930*90c8c64dSAndroid Build Coastguard Worker                print("Dry-run skip patch file:", self.args.patch)
931*90c8c64dSAndroid Build Coastguard Worker            else:
932*90c8c64dSAndroid Build Coastguard Worker                if not os.path.exists(self.args.patch):
933*90c8c64dSAndroid Build Coastguard Worker                    self.append_to_rules(
934*90c8c64dSAndroid Build Coastguard Worker                        "ERROR cannot find patch file: " + self.args.patch
935*90c8c64dSAndroid Build Coastguard Worker                    )
936*90c8c64dSAndroid Build Coastguard Worker                    return self
937*90c8c64dSAndroid Build Coastguard Worker                if self.args.verbose:
938*90c8c64dSAndroid Build Coastguard Worker                    print(
939*90c8c64dSAndroid Build Coastguard Worker                        "### INFO: applying local patch file:", self.args.patch
940*90c8c64dSAndroid Build Coastguard Worker                    )
941*90c8c64dSAndroid Build Coastguard Worker                subprocess.run(
942*90c8c64dSAndroid Build Coastguard Worker                    [
943*90c8c64dSAndroid Build Coastguard Worker                        "patch",
944*90c8c64dSAndroid Build Coastguard Worker                        "-s",
945*90c8c64dSAndroid Build Coastguard Worker                        "--no-backup-if-mismatch",
946*90c8c64dSAndroid Build Coastguard Worker                        "./rules.mk",
947*90c8c64dSAndroid Build Coastguard Worker                        self.args.patch,
948*90c8c64dSAndroid Build Coastguard Worker                    ],
949*90c8c64dSAndroid Build Coastguard Worker                    check=True,
950*90c8c64dSAndroid Build Coastguard Worker                )
951*90c8c64dSAndroid Build Coastguard Worker        return self
952*90c8c64dSAndroid Build Coastguard Worker
953*90c8c64dSAndroid Build Coastguard Worker    def gen_rules(self):
954*90c8c64dSAndroid Build Coastguard Worker        """Parse cargo.out and generate Trusty makefile rules"""
955*90c8c64dSAndroid Build Coastguard Worker        if self.dry_run:
956*90c8c64dSAndroid Build Coastguard Worker            print("Dry-run skip: read", CARGO_OUT, "write rules.mk")
957*90c8c64dSAndroid Build Coastguard Worker        elif os.path.exists(CARGO_OUT):
958*90c8c64dSAndroid Build Coastguard Worker            self.find_root_pkg()
959*90c8c64dSAndroid Build Coastguard Worker            if self.args.copy_out:
960*90c8c64dSAndroid Build Coastguard Worker                self.copy_out_files()
961*90c8c64dSAndroid Build Coastguard Worker            elif self.find_out_files() and self.has_used_out_dir():
962*90c8c64dSAndroid Build Coastguard Worker                print(
963*90c8c64dSAndroid Build Coastguard Worker                    "WARNING: "
964*90c8c64dSAndroid Build Coastguard Worker                    + self.root_pkg
965*90c8c64dSAndroid Build Coastguard Worker                    + " has cargo output files; "
966*90c8c64dSAndroid Build Coastguard Worker                    + "please rerun with the --copy-out flag."
967*90c8c64dSAndroid Build Coastguard Worker                )
968*90c8c64dSAndroid Build Coastguard Worker            with open(CARGO_OUT, "r", encoding="utf-8") as cargo_out:
969*90c8c64dSAndroid Build Coastguard Worker                self.parse(cargo_out, "rules.mk")
970*90c8c64dSAndroid Build Coastguard Worker            self.crates.sort(key=get_module_name)
971*90c8c64dSAndroid Build Coastguard Worker            for crate in self.crates:
972*90c8c64dSAndroid Build Coastguard Worker                crate.dump()
973*90c8c64dSAndroid Build Coastguard Worker            if self.errors:
974*90c8c64dSAndroid Build Coastguard Worker                self.append_to_rules("\n" + ERRORS_LINE + "\n" + self.errors)
975*90c8c64dSAndroid Build Coastguard Worker            if self.test_errors:
976*90c8c64dSAndroid Build Coastguard Worker                self.append_to_rules(
977*90c8c64dSAndroid Build Coastguard Worker                    "\n// Errors when listing tests:\n" + self.test_errors
978*90c8c64dSAndroid Build Coastguard Worker                )
979*90c8c64dSAndroid Build Coastguard Worker        return self
980*90c8c64dSAndroid Build Coastguard Worker
981*90c8c64dSAndroid Build Coastguard Worker    def add_crate(self, crate: Crate):
982*90c8c64dSAndroid Build Coastguard Worker        """Append crate to list unless it meets criteria for being skipped."""
983*90c8c64dSAndroid Build Coastguard Worker        if crate.skip_crate():
984*90c8c64dSAndroid Build Coastguard Worker            if self.args.debug:  # include debug info of all crates
985*90c8c64dSAndroid Build Coastguard Worker                self.crates.append(crate)
986*90c8c64dSAndroid Build Coastguard Worker        elif crate.crate_types == set(["bin"]):
987*90c8c64dSAndroid Build Coastguard Worker            print("WARNING: skipping binary crate: " + crate.crate_name)
988*90c8c64dSAndroid Build Coastguard Worker        else:
989*90c8c64dSAndroid Build Coastguard Worker            self.crates.append(crate)
990*90c8c64dSAndroid Build Coastguard Worker
991*90c8c64dSAndroid Build Coastguard Worker    def find_warning_owners(self):
992*90c8c64dSAndroid Build Coastguard Worker        """For each warning file, find its owner crate."""
993*90c8c64dSAndroid Build Coastguard Worker        missing_owner = False
994*90c8c64dSAndroid Build Coastguard Worker        for f in self.warning_files:
995*90c8c64dSAndroid Build Coastguard Worker            cargo_dir = ""  # find lowest crate, with longest path
996*90c8c64dSAndroid Build Coastguard Worker            owner = None  # owner crate of this warning
997*90c8c64dSAndroid Build Coastguard Worker            for c in self.crates:
998*90c8c64dSAndroid Build Coastguard Worker                if f.startswith(c.cargo_dir + "/") and len(cargo_dir) < len(
999*90c8c64dSAndroid Build Coastguard Worker                    c.cargo_dir
1000*90c8c64dSAndroid Build Coastguard Worker                ):
1001*90c8c64dSAndroid Build Coastguard Worker                    cargo_dir = c.cargo_dir
1002*90c8c64dSAndroid Build Coastguard Worker                    owner = c
1003*90c8c64dSAndroid Build Coastguard Worker            if owner:
1004*90c8c64dSAndroid Build Coastguard Worker                owner.has_warning = True
1005*90c8c64dSAndroid Build Coastguard Worker            else:
1006*90c8c64dSAndroid Build Coastguard Worker                missing_owner = True
1007*90c8c64dSAndroid Build Coastguard Worker        if missing_owner and os.path.exists("Cargo.toml"):
1008*90c8c64dSAndroid Build Coastguard Worker            # owner is the root cargo, with empty cargo_dir
1009*90c8c64dSAndroid Build Coastguard Worker            for c in self.crates:
1010*90c8c64dSAndroid Build Coastguard Worker                if not c.cargo_dir:
1011*90c8c64dSAndroid Build Coastguard Worker                    c.has_warning = True
1012*90c8c64dSAndroid Build Coastguard Worker
1013*90c8c64dSAndroid Build Coastguard Worker    def rustc_command(self, n, rustc_line, line, outf_name):
1014*90c8c64dSAndroid Build Coastguard Worker        """Process a rustc command line from cargo -vv output."""
1015*90c8c64dSAndroid Build Coastguard Worker        # cargo build -vv output can have multiple lines for a rustc command
1016*90c8c64dSAndroid Build Coastguard Worker        # due to '\n' in strings for environment variables.
1017*90c8c64dSAndroid Build Coastguard Worker        # strip removes leading spaces and '\n' at the end
1018*90c8c64dSAndroid Build Coastguard Worker        new_rustc = (rustc_line.strip() + line) if rustc_line else line
1019*90c8c64dSAndroid Build Coastguard Worker        # Use an heuristic to detect the completions of a multi-line command.
1020*90c8c64dSAndroid Build Coastguard Worker        # This might fail for some very rare case, but easy to fix manually.
1021*90c8c64dSAndroid Build Coastguard Worker        if not line.endswith("`\n") or (new_rustc.count("`") % 2) != 0:
1022*90c8c64dSAndroid Build Coastguard Worker            return new_rustc
1023*90c8c64dSAndroid Build Coastguard Worker        if match := RUSTC_VV_CMD_ARGS.match(new_rustc):
1024*90c8c64dSAndroid Build Coastguard Worker            args = match.group(2)
1025*90c8c64dSAndroid Build Coastguard Worker            self.add_crate(Crate(self, outf_name).parse(n, args))
1026*90c8c64dSAndroid Build Coastguard Worker        else:
1027*90c8c64dSAndroid Build Coastguard Worker            self.assert_empty_vv_line(new_rustc)
1028*90c8c64dSAndroid Build Coastguard Worker        return ""
1029*90c8c64dSAndroid Build Coastguard Worker
1030*90c8c64dSAndroid Build Coastguard Worker    def append_to_rules(self, line):
1031*90c8c64dSAndroid Build Coastguard Worker        self.init_rules_file("rules.mk")
1032*90c8c64dSAndroid Build Coastguard Worker        with open("rules.mk", "a", encoding="utf-8") as outf:
1033*90c8c64dSAndroid Build Coastguard Worker            outf.write(line)
1034*90c8c64dSAndroid Build Coastguard Worker
1035*90c8c64dSAndroid Build Coastguard Worker    def assert_empty_vv_line(self, line):
1036*90c8c64dSAndroid Build Coastguard Worker        if line:  # report error if line is not empty
1037*90c8c64dSAndroid Build Coastguard Worker            self.append_to_rules("ERROR -vv line: " + line)
1038*90c8c64dSAndroid Build Coastguard Worker        return ""
1039*90c8c64dSAndroid Build Coastguard Worker
1040*90c8c64dSAndroid Build Coastguard Worker    def add_empty_test(self, name):
1041*90c8c64dSAndroid Build Coastguard Worker        if name.startswith("unittests"):
1042*90c8c64dSAndroid Build Coastguard Worker            self.empty_unittests = True
1043*90c8c64dSAndroid Build Coastguard Worker        else:
1044*90c8c64dSAndroid Build Coastguard Worker            self.empty_tests.add(name)
1045*90c8c64dSAndroid Build Coastguard Worker
1046*90c8c64dSAndroid Build Coastguard Worker    def should_ignore_test(self, src):
1047*90c8c64dSAndroid Build Coastguard Worker        # cargo test outputs the source file for integration tests but
1048*90c8c64dSAndroid Build Coastguard Worker        # "unittests" for unit tests. To figure out to which crate this
1049*90c8c64dSAndroid Build Coastguard Worker        # corresponds, we check if the current source file is the main source of
1050*90c8c64dSAndroid Build Coastguard Worker        # a non-test crate, e.g., a library or a binary.
1051*90c8c64dSAndroid Build Coastguard Worker        return (
1052*90c8c64dSAndroid Build Coastguard Worker            src in self.args.test_blocklist
1053*90c8c64dSAndroid Build Coastguard Worker            or src in self.empty_tests
1054*90c8c64dSAndroid Build Coastguard Worker            or (
1055*90c8c64dSAndroid Build Coastguard Worker                self.empty_unittests
1056*90c8c64dSAndroid Build Coastguard Worker                and src
1057*90c8c64dSAndroid Build Coastguard Worker                in [
1058*90c8c64dSAndroid Build Coastguard Worker                    c.main_src for c in self.crates if c.crate_types != ["test"]
1059*90c8c64dSAndroid Build Coastguard Worker                ]
1060*90c8c64dSAndroid Build Coastguard Worker            )
1061*90c8c64dSAndroid Build Coastguard Worker        )
1062*90c8c64dSAndroid Build Coastguard Worker
1063*90c8c64dSAndroid Build Coastguard Worker    def parse(self, inf, outf_name):
1064*90c8c64dSAndroid Build Coastguard Worker        """Parse rustc, test, and warning messages in input file."""
1065*90c8c64dSAndroid Build Coastguard Worker        n = 0  # line number
1066*90c8c64dSAndroid Build Coastguard Worker        # We read the file in two passes, where the first simply checks for
1067*90c8c64dSAndroid Build Coastguard Worker        # empty tests. Otherwise we would add and merge tests before seeing
1068*90c8c64dSAndroid Build Coastguard Worker        # they're empty.
1069*90c8c64dSAndroid Build Coastguard Worker        cur_test_name = None
1070*90c8c64dSAndroid Build Coastguard Worker        for line in inf:
1071*90c8c64dSAndroid Build Coastguard Worker            if match := CARGO_TEST_LIST_START_PAT.match(line):
1072*90c8c64dSAndroid Build Coastguard Worker                cur_test_name = match.group(1)
1073*90c8c64dSAndroid Build Coastguard Worker            elif cur_test_name and (
1074*90c8c64dSAndroid Build Coastguard Worker                match := CARGO_TEST_LIST_END_PAT.match(line)
1075*90c8c64dSAndroid Build Coastguard Worker            ):
1076*90c8c64dSAndroid Build Coastguard Worker                if int(match.group(1)) + int(match.group(2)) == 0:
1077*90c8c64dSAndroid Build Coastguard Worker                    self.add_empty_test(cur_test_name)
1078*90c8c64dSAndroid Build Coastguard Worker                cur_test_name = None
1079*90c8c64dSAndroid Build Coastguard Worker        inf.seek(0)
1080*90c8c64dSAndroid Build Coastguard Worker        prev_warning = False  # true if the previous line was warning: ...
1081*90c8c64dSAndroid Build Coastguard Worker        rustc_line = ""  # previous line(s) matching RUSTC_VV_PAT
1082*90c8c64dSAndroid Build Coastguard Worker        in_tests = False
1083*90c8c64dSAndroid Build Coastguard Worker        for line in inf:
1084*90c8c64dSAndroid Build Coastguard Worker            n += 1
1085*90c8c64dSAndroid Build Coastguard Worker            if line.startswith("warning: "):
1086*90c8c64dSAndroid Build Coastguard Worker                prev_warning = True
1087*90c8c64dSAndroid Build Coastguard Worker                rustc_line = self.assert_empty_vv_line(rustc_line)
1088*90c8c64dSAndroid Build Coastguard Worker                continue
1089*90c8c64dSAndroid Build Coastguard Worker            new_rustc = ""
1090*90c8c64dSAndroid Build Coastguard Worker            if match := RUSTC_PAT.match(line):
1091*90c8c64dSAndroid Build Coastguard Worker                args_line = match.group(2)
1092*90c8c64dSAndroid Build Coastguard Worker                self.add_crate(Crate(self, outf_name).parse(n, args_line))
1093*90c8c64dSAndroid Build Coastguard Worker                self.assert_empty_vv_line(rustc_line)
1094*90c8c64dSAndroid Build Coastguard Worker            elif rustc_line or RUSTC_VV_PAT.match(line):
1095*90c8c64dSAndroid Build Coastguard Worker                new_rustc = self.rustc_command(n, rustc_line, line, outf_name)
1096*90c8c64dSAndroid Build Coastguard Worker            elif CC_AR_VV_PAT.match(line):
1097*90c8c64dSAndroid Build Coastguard Worker                raise NotImplementedError("$CC or $AR commands not supported")
1098*90c8c64dSAndroid Build Coastguard Worker            elif prev_warning and (match := WARNING_FILE_PAT.match(line)):
1099*90c8c64dSAndroid Build Coastguard Worker                self.assert_empty_vv_line(rustc_line)
1100*90c8c64dSAndroid Build Coastguard Worker                fpath = match.group(1)
1101*90c8c64dSAndroid Build Coastguard Worker                if fpath[0] != "/":  # ignore absolute path
1102*90c8c64dSAndroid Build Coastguard Worker                    self.warning_files.add(fpath)
1103*90c8c64dSAndroid Build Coastguard Worker            elif line.startswith("error: ") or line.startswith("error[E"):
1104*90c8c64dSAndroid Build Coastguard Worker                if not self.args.ignore_cargo_errors:
1105*90c8c64dSAndroid Build Coastguard Worker                    if in_tests:
1106*90c8c64dSAndroid Build Coastguard Worker                        self.test_errors += "// " + line
1107*90c8c64dSAndroid Build Coastguard Worker                    else:
1108*90c8c64dSAndroid Build Coastguard Worker                        self.errors += line
1109*90c8c64dSAndroid Build Coastguard Worker            elif CARGO2ANDROID_RUNNING_PAT.match(line):
1110*90c8c64dSAndroid Build Coastguard Worker                in_tests = "cargo test" in line and "--list" in line
1111*90c8c64dSAndroid Build Coastguard Worker            prev_warning = False
1112*90c8c64dSAndroid Build Coastguard Worker            rustc_line = new_rustc
1113*90c8c64dSAndroid Build Coastguard Worker        self.find_warning_owners()
1114*90c8c64dSAndroid Build Coastguard Worker
1115*90c8c64dSAndroid Build Coastguard Worker
1116*90c8c64dSAndroid Build Coastguard Workerdef get_parser():
1117*90c8c64dSAndroid Build Coastguard Worker    """Parse main arguments."""
1118*90c8c64dSAndroid Build Coastguard Worker    parser = argparse.ArgumentParser("cargo2rulesmk")
1119*90c8c64dSAndroid Build Coastguard Worker    parser.add_argument(
1120*90c8c64dSAndroid Build Coastguard Worker        "--add_workspace",
1121*90c8c64dSAndroid Build Coastguard Worker        action="store_true",
1122*90c8c64dSAndroid Build Coastguard Worker        default=False,
1123*90c8c64dSAndroid Build Coastguard Worker        help=(
1124*90c8c64dSAndroid Build Coastguard Worker            "append [workspace] to Cargo.toml before calling cargo,"
1125*90c8c64dSAndroid Build Coastguard Worker            + " to treat current directory as root of package source;"
1126*90c8c64dSAndroid Build Coastguard Worker            + " otherwise the relative source file path in generated"
1127*90c8c64dSAndroid Build Coastguard Worker            + " rules.mk file will be from the parent directory."
1128*90c8c64dSAndroid Build Coastguard Worker        ),
1129*90c8c64dSAndroid Build Coastguard Worker    )
1130*90c8c64dSAndroid Build Coastguard Worker    parser.add_argument(
1131*90c8c64dSAndroid Build Coastguard Worker        "--cargo",
1132*90c8c64dSAndroid Build Coastguard Worker        action="append",
1133*90c8c64dSAndroid Build Coastguard Worker        metavar="args_string",
1134*90c8c64dSAndroid Build Coastguard Worker        help=(
1135*90c8c64dSAndroid Build Coastguard Worker            "extra cargo build -v args in a string, "
1136*90c8c64dSAndroid Build Coastguard Worker            + "each --cargo flag calls cargo build -v once"
1137*90c8c64dSAndroid Build Coastguard Worker        ),
1138*90c8c64dSAndroid Build Coastguard Worker    )
1139*90c8c64dSAndroid Build Coastguard Worker    parser.add_argument(
1140*90c8c64dSAndroid Build Coastguard Worker        "--cargo_bin",
1141*90c8c64dSAndroid Build Coastguard Worker        type=str,
1142*90c8c64dSAndroid Build Coastguard Worker        help="use cargo in the cargo_bin directory instead of the prebuilt one",
1143*90c8c64dSAndroid Build Coastguard Worker    )
1144*90c8c64dSAndroid Build Coastguard Worker    parser.add_argument(
1145*90c8c64dSAndroid Build Coastguard Worker        "--copy-out",
1146*90c8c64dSAndroid Build Coastguard Worker        action="store_true",
1147*90c8c64dSAndroid Build Coastguard Worker        default=False,
1148*90c8c64dSAndroid Build Coastguard Worker        help=(
1149*90c8c64dSAndroid Build Coastguard Worker            "only for root directory, "
1150*90c8c64dSAndroid Build Coastguard Worker            + "copy build.rs output to ./out/* and declare source deps "
1151*90c8c64dSAndroid Build Coastguard Worker            + "for ./out/*.rs; for crates with code pattern: "
1152*90c8c64dSAndroid Build Coastguard Worker            + 'include!(concat!(env!("OUT_DIR"), "/<some_file>.rs"))'
1153*90c8c64dSAndroid Build Coastguard Worker        ),
1154*90c8c64dSAndroid Build Coastguard Worker    )
1155*90c8c64dSAndroid Build Coastguard Worker    parser.add_argument(
1156*90c8c64dSAndroid Build Coastguard Worker        "--debug",
1157*90c8c64dSAndroid Build Coastguard Worker        action="store_true",
1158*90c8c64dSAndroid Build Coastguard Worker        default=False,
1159*90c8c64dSAndroid Build Coastguard Worker        help="dump debug info into rules.mk",
1160*90c8c64dSAndroid Build Coastguard Worker    )
1161*90c8c64dSAndroid Build Coastguard Worker    parser.add_argument(
1162*90c8c64dSAndroid Build Coastguard Worker        "--features",
1163*90c8c64dSAndroid Build Coastguard Worker        type=str,
1164*90c8c64dSAndroid Build Coastguard Worker        help=(
1165*90c8c64dSAndroid Build Coastguard Worker            "pass features to cargo build, "
1166*90c8c64dSAndroid Build Coastguard Worker            + "empty string means no default features"
1167*90c8c64dSAndroid Build Coastguard Worker        ),
1168*90c8c64dSAndroid Build Coastguard Worker    )
1169*90c8c64dSAndroid Build Coastguard Worker    parser.add_argument(
1170*90c8c64dSAndroid Build Coastguard Worker        "--ignore-cargo-errors",
1171*90c8c64dSAndroid Build Coastguard Worker        action="store_true",
1172*90c8c64dSAndroid Build Coastguard Worker        default=False,
1173*90c8c64dSAndroid Build Coastguard Worker        help="do not append cargo/rustc error messages to rules.mk",
1174*90c8c64dSAndroid Build Coastguard Worker    )
1175*90c8c64dSAndroid Build Coastguard Worker    parser.add_argument(
1176*90c8c64dSAndroid Build Coastguard Worker        "--no-subdir",
1177*90c8c64dSAndroid Build Coastguard Worker        action="store_true",
1178*90c8c64dSAndroid Build Coastguard Worker        default=False,
1179*90c8c64dSAndroid Build Coastguard Worker        help="do not output anything for sub-directories",
1180*90c8c64dSAndroid Build Coastguard Worker    )
1181*90c8c64dSAndroid Build Coastguard Worker    parser.add_argument(
1182*90c8c64dSAndroid Build Coastguard Worker        "--onefile",
1183*90c8c64dSAndroid Build Coastguard Worker        action="store_true",
1184*90c8c64dSAndroid Build Coastguard Worker        default=False,
1185*90c8c64dSAndroid Build Coastguard Worker        help=(
1186*90c8c64dSAndroid Build Coastguard Worker            "output all into one ./rules.mk, default will generate "
1187*90c8c64dSAndroid Build Coastguard Worker            + "one rules.mk per Cargo.toml in subdirectories"
1188*90c8c64dSAndroid Build Coastguard Worker        ),
1189*90c8c64dSAndroid Build Coastguard Worker    )
1190*90c8c64dSAndroid Build Coastguard Worker    parser.add_argument(
1191*90c8c64dSAndroid Build Coastguard Worker        "--patch",
1192*90c8c64dSAndroid Build Coastguard Worker        type=str,
1193*90c8c64dSAndroid Build Coastguard Worker        help="apply the given patch file to generated ./rules.mk",
1194*90c8c64dSAndroid Build Coastguard Worker    )
1195*90c8c64dSAndroid Build Coastguard Worker    parser.add_argument(
1196*90c8c64dSAndroid Build Coastguard Worker        "--run",
1197*90c8c64dSAndroid Build Coastguard Worker        action="store_true",
1198*90c8c64dSAndroid Build Coastguard Worker        default=False,
1199*90c8c64dSAndroid Build Coastguard Worker        help="run it, default is dry-run",
1200*90c8c64dSAndroid Build Coastguard Worker    )
1201*90c8c64dSAndroid Build Coastguard Worker    parser.add_argument("--rustflags", type=str, help="passing flags to rustc")
1202*90c8c64dSAndroid Build Coastguard Worker    parser.add_argument(
1203*90c8c64dSAndroid Build Coastguard Worker        "--skipcargo",
1204*90c8c64dSAndroid Build Coastguard Worker        action="store_true",
1205*90c8c64dSAndroid Build Coastguard Worker        default=False,
1206*90c8c64dSAndroid Build Coastguard Worker        help="skip cargo command, parse cargo.out, and generate ./rules.mk",
1207*90c8c64dSAndroid Build Coastguard Worker    )
1208*90c8c64dSAndroid Build Coastguard Worker    parser.add_argument(
1209*90c8c64dSAndroid Build Coastguard Worker        "--tests",
1210*90c8c64dSAndroid Build Coastguard Worker        action="store_true",
1211*90c8c64dSAndroid Build Coastguard Worker        default=False,
1212*90c8c64dSAndroid Build Coastguard Worker        help="run cargo build --tests after normal build",
1213*90c8c64dSAndroid Build Coastguard Worker    )
1214*90c8c64dSAndroid Build Coastguard Worker    parser.add_argument(
1215*90c8c64dSAndroid Build Coastguard Worker        "--use-cargo-lock",
1216*90c8c64dSAndroid Build Coastguard Worker        action="store_true",
1217*90c8c64dSAndroid Build Coastguard Worker        default=False,
1218*90c8c64dSAndroid Build Coastguard Worker        help=(
1219*90c8c64dSAndroid Build Coastguard Worker            "run cargo build with existing Cargo.lock "
1220*90c8c64dSAndroid Build Coastguard Worker            + "(used when some latest dependent crates failed)"
1221*90c8c64dSAndroid Build Coastguard Worker        ),
1222*90c8c64dSAndroid Build Coastguard Worker    )
1223*90c8c64dSAndroid Build Coastguard Worker    parser.add_argument(
1224*90c8c64dSAndroid Build Coastguard Worker        "--test-data",
1225*90c8c64dSAndroid Build Coastguard Worker        nargs="*",
1226*90c8c64dSAndroid Build Coastguard Worker        default=[],
1227*90c8c64dSAndroid Build Coastguard Worker        help=(
1228*90c8c64dSAndroid Build Coastguard Worker            "Add the given file to the given test's data property. "
1229*90c8c64dSAndroid Build Coastguard Worker            + "Usage: test-path=data-path"
1230*90c8c64dSAndroid Build Coastguard Worker        ),
1231*90c8c64dSAndroid Build Coastguard Worker    )
1232*90c8c64dSAndroid Build Coastguard Worker    parser.add_argument(
1233*90c8c64dSAndroid Build Coastguard Worker        "--dependency-blocklist",
1234*90c8c64dSAndroid Build Coastguard Worker        nargs="*",
1235*90c8c64dSAndroid Build Coastguard Worker        default=[],
1236*90c8c64dSAndroid Build Coastguard Worker        help="Do not emit the given dependencies (without lib prefixes).",
1237*90c8c64dSAndroid Build Coastguard Worker    )
1238*90c8c64dSAndroid Build Coastguard Worker    parser.add_argument(
1239*90c8c64dSAndroid Build Coastguard Worker        "--test-blocklist",
1240*90c8c64dSAndroid Build Coastguard Worker        nargs="*",
1241*90c8c64dSAndroid Build Coastguard Worker        default=[],
1242*90c8c64dSAndroid Build Coastguard Worker        help=(
1243*90c8c64dSAndroid Build Coastguard Worker            "Do not emit the given tests. "
1244*90c8c64dSAndroid Build Coastguard Worker            + "Pass the path to the test file to exclude."
1245*90c8c64dSAndroid Build Coastguard Worker        ),
1246*90c8c64dSAndroid Build Coastguard Worker    )
1247*90c8c64dSAndroid Build Coastguard Worker    parser.add_argument(
1248*90c8c64dSAndroid Build Coastguard Worker        "--cfg-blocklist",
1249*90c8c64dSAndroid Build Coastguard Worker        nargs="*",
1250*90c8c64dSAndroid Build Coastguard Worker        default=[],
1251*90c8c64dSAndroid Build Coastguard Worker        help="Do not emit the given cfg.",
1252*90c8c64dSAndroid Build Coastguard Worker    )
1253*90c8c64dSAndroid Build Coastguard Worker    parser.add_argument(
1254*90c8c64dSAndroid Build Coastguard Worker        "--verbose",
1255*90c8c64dSAndroid Build Coastguard Worker        action="store_true",
1256*90c8c64dSAndroid Build Coastguard Worker        default=False,
1257*90c8c64dSAndroid Build Coastguard Worker        help="echo executed commands",
1258*90c8c64dSAndroid Build Coastguard Worker    )
1259*90c8c64dSAndroid Build Coastguard Worker    parser.add_argument(
1260*90c8c64dSAndroid Build Coastguard Worker        "--vv",
1261*90c8c64dSAndroid Build Coastguard Worker        action="store_true",
1262*90c8c64dSAndroid Build Coastguard Worker        default=False,
1263*90c8c64dSAndroid Build Coastguard Worker        help="run cargo with -vv instead of default -v",
1264*90c8c64dSAndroid Build Coastguard Worker    )
1265*90c8c64dSAndroid Build Coastguard Worker    parser.add_argument(
1266*90c8c64dSAndroid Build Coastguard Worker        "--dump-config-and-exit",
1267*90c8c64dSAndroid Build Coastguard Worker        type=str,
1268*90c8c64dSAndroid Build Coastguard Worker        help=(
1269*90c8c64dSAndroid Build Coastguard Worker            "Dump command-line arguments (minus this flag) to a config file and"
1270*90c8c64dSAndroid Build Coastguard Worker            " exit. This is intended to help migrate from command line options "
1271*90c8c64dSAndroid Build Coastguard Worker            "to config files."
1272*90c8c64dSAndroid Build Coastguard Worker        ),
1273*90c8c64dSAndroid Build Coastguard Worker    )
1274*90c8c64dSAndroid Build Coastguard Worker    parser.add_argument(
1275*90c8c64dSAndroid Build Coastguard Worker        "--config",
1276*90c8c64dSAndroid Build Coastguard Worker        type=str,
1277*90c8c64dSAndroid Build Coastguard Worker        help=(
1278*90c8c64dSAndroid Build Coastguard Worker            "Load command-line options from the given config file. Options in "
1279*90c8c64dSAndroid Build Coastguard Worker            "this file will override those passed on the command line."
1280*90c8c64dSAndroid Build Coastguard Worker        ),
1281*90c8c64dSAndroid Build Coastguard Worker    )
1282*90c8c64dSAndroid Build Coastguard Worker    return parser
1283*90c8c64dSAndroid Build Coastguard Worker
1284*90c8c64dSAndroid Build Coastguard Worker
1285*90c8c64dSAndroid Build Coastguard Workerdef parse_args(parser):
1286*90c8c64dSAndroid Build Coastguard Worker    """Parses command-line options."""
1287*90c8c64dSAndroid Build Coastguard Worker    args = parser.parse_args()
1288*90c8c64dSAndroid Build Coastguard Worker    # Use the values specified in a config file if one was found.
1289*90c8c64dSAndroid Build Coastguard Worker    if args.config:
1290*90c8c64dSAndroid Build Coastguard Worker        with open(args.config, "r", encoding="utf-8") as f:
1291*90c8c64dSAndroid Build Coastguard Worker            config = json.load(f)
1292*90c8c64dSAndroid Build Coastguard Worker            args_dict = vars(args)
1293*90c8c64dSAndroid Build Coastguard Worker            for arg in config:
1294*90c8c64dSAndroid Build Coastguard Worker                args_dict[arg.replace("-", "_")] = config[arg]
1295*90c8c64dSAndroid Build Coastguard Worker    return args
1296*90c8c64dSAndroid Build Coastguard Worker
1297*90c8c64dSAndroid Build Coastguard Worker
1298*90c8c64dSAndroid Build Coastguard Workerdef dump_config(parser, args):
1299*90c8c64dSAndroid Build Coastguard Worker    """Writes the non-default command-line options to the specified file."""
1300*90c8c64dSAndroid Build Coastguard Worker    args_dict = vars(args)
1301*90c8c64dSAndroid Build Coastguard Worker    # Filter out the arguments that have their default value.
1302*90c8c64dSAndroid Build Coastguard Worker    # Also filter certain "temporary" arguments.
1303*90c8c64dSAndroid Build Coastguard Worker    non_default_args = {}
1304*90c8c64dSAndroid Build Coastguard Worker    for arg in args_dict:
1305*90c8c64dSAndroid Build Coastguard Worker        if (
1306*90c8c64dSAndroid Build Coastguard Worker            args_dict[arg] != parser.get_default(arg)
1307*90c8c64dSAndroid Build Coastguard Worker            and arg != "dump_config_and_exit"
1308*90c8c64dSAndroid Build Coastguard Worker            and arg != "config"
1309*90c8c64dSAndroid Build Coastguard Worker            and arg != "cargo_bin"
1310*90c8c64dSAndroid Build Coastguard Worker        ):
1311*90c8c64dSAndroid Build Coastguard Worker            non_default_args[arg.replace("_", "-")] = args_dict[arg]
1312*90c8c64dSAndroid Build Coastguard Worker    # Write to the specified file.
1313*90c8c64dSAndroid Build Coastguard Worker    with open(args.dump_config_and_exit, "w", encoding="utf-8") as f:
1314*90c8c64dSAndroid Build Coastguard Worker        json.dump(non_default_args, f, indent=2, sort_keys=True)
1315*90c8c64dSAndroid Build Coastguard Worker
1316*90c8c64dSAndroid Build Coastguard Worker
1317*90c8c64dSAndroid Build Coastguard Workerdef main():
1318*90c8c64dSAndroid Build Coastguard Worker    parser = get_parser()
1319*90c8c64dSAndroid Build Coastguard Worker    args = parse_args(parser)
1320*90c8c64dSAndroid Build Coastguard Worker    if not args.run:  # default is dry-run
1321*90c8c64dSAndroid Build Coastguard Worker        print(DRY_RUN_NOTE)
1322*90c8c64dSAndroid Build Coastguard Worker    if args.dump_config_and_exit:
1323*90c8c64dSAndroid Build Coastguard Worker        dump_config(parser, args)
1324*90c8c64dSAndroid Build Coastguard Worker    else:
1325*90c8c64dSAndroid Build Coastguard Worker        Runner(args).run_cargo().gen_rules().apply_patch()
1326*90c8c64dSAndroid Build Coastguard Worker
1327*90c8c64dSAndroid Build Coastguard Worker
1328*90c8c64dSAndroid Build Coastguard Workerif __name__ == "__main__":
1329*90c8c64dSAndroid Build Coastguard Worker    main()
1330