xref: /aosp_15_r20/external/crosvm/tools/build_release (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1*bb4ee6a4SAndroid Build Coastguard Worker#!/usr/bin/env python3
2*bb4ee6a4SAndroid Build Coastguard Worker# Copyright 2023 The ChromiumOS Authors
3*bb4ee6a4SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be
4*bb4ee6a4SAndroid Build Coastguard Worker# found in the LICENSE file.
5*bb4ee6a4SAndroid Build Coastguard Worker
6*bb4ee6a4SAndroid Build Coastguard Workerimport argparse
7*bb4ee6a4SAndroid Build Coastguard Workerimport json
8*bb4ee6a4SAndroid Build Coastguard Workerimport os
9*bb4ee6a4SAndroid Build Coastguard Workerimport subprocess
10*bb4ee6a4SAndroid Build Coastguard Workerimport sys
11*bb4ee6a4SAndroid Build Coastguard Workerfrom itertools import chain, product, starmap
12*bb4ee6a4SAndroid Build Coastguard Workerfrom pathlib import Path
13*bb4ee6a4SAndroid Build Coastguard Workerfrom typing import Dict, Iterable, List, NamedTuple
14*bb4ee6a4SAndroid Build Coastguard Worker
15*bb4ee6a4SAndroid Build Coastguard Workerfrom impl.common import CROSVM_ROOT, Triple, verbose
16*bb4ee6a4SAndroid Build Coastguard Workerfrom impl.test_config import DO_NOT_BUILD_RISCV64
17*bb4ee6a4SAndroid Build Coastguard Worker
18*bb4ee6a4SAndroid Build Coastguard WorkerUSAGE = """\
19*bb4ee6a4SAndroid Build Coastguard WorkerBuild crosvm with release (optimized) profile.
20*bb4ee6a4SAndroid Build Coastguard Worker
21*bb4ee6a4SAndroid Build Coastguard WorkerTo target local machine:
22*bb4ee6a4SAndroid Build Coastguard Worker
23*bb4ee6a4SAndroid Build Coastguard Worker    $ ./tools/build_release
24*bb4ee6a4SAndroid Build Coastguard Worker
25*bb4ee6a4SAndroid Build Coastguard WorkerTo cross-compile for aarch64, armhf or windows you can use:
26*bb4ee6a4SAndroid Build Coastguard Worker
27*bb4ee6a4SAndroid Build Coastguard Worker    $ ./tools/build_release --platform=aarch64
28*bb4ee6a4SAndroid Build Coastguard Worker    $ ./tools/build_release --platform=armhf
29*bb4ee6a4SAndroid Build Coastguard Worker    $ ./tools/build_release --platform=mingw64
30*bb4ee6a4SAndroid Build Coastguard Worker"""
31*bb4ee6a4SAndroid Build Coastguard Worker
32*bb4ee6a4SAndroid Build Coastguard Worker# We only need PGO for main binary, but for consistency, only exclude incompatible parts from PGO
33*bb4ee6a4SAndroid Build Coastguard WorkerPGO_EXCLUDE = ["crosvm_control"]
34*bb4ee6a4SAndroid Build Coastguard Worker
35*bb4ee6a4SAndroid Build Coastguard Worker
36*bb4ee6a4SAndroid Build Coastguard Workerclass Executable(NamedTuple):
37*bb4ee6a4SAndroid Build Coastguard Worker    """Container for info about an executable generated by cargo build/test."""
38*bb4ee6a4SAndroid Build Coastguard Worker
39*bb4ee6a4SAndroid Build Coastguard Worker    binary_path: Path
40*bb4ee6a4SAndroid Build Coastguard Worker    crate_name: str
41*bb4ee6a4SAndroid Build Coastguard Worker    cargo_target: str
42*bb4ee6a4SAndroid Build Coastguard Worker    kind: str
43*bb4ee6a4SAndroid Build Coastguard Worker    is_test: bool
44*bb4ee6a4SAndroid Build Coastguard Worker    is_fresh: bool
45*bb4ee6a4SAndroid Build Coastguard Worker
46*bb4ee6a4SAndroid Build Coastguard Worker    @property
47*bb4ee6a4SAndroid Build Coastguard Worker    def name(self):
48*bb4ee6a4SAndroid Build Coastguard Worker        return f"{self.crate_name}:{self.cargo_target}"
49*bb4ee6a4SAndroid Build Coastguard Worker
50*bb4ee6a4SAndroid Build Coastguard Worker
51*bb4ee6a4SAndroid Build Coastguard Workerdef cargo(
52*bb4ee6a4SAndroid Build Coastguard Worker    cargo_command: str,
53*bb4ee6a4SAndroid Build Coastguard Worker    cwd: Path,
54*bb4ee6a4SAndroid Build Coastguard Worker    flags: List[str],
55*bb4ee6a4SAndroid Build Coastguard Worker    env: Dict[str, str],
56*bb4ee6a4SAndroid Build Coastguard Worker) -> Iterable[Executable]:
57*bb4ee6a4SAndroid Build Coastguard Worker    """
58*bb4ee6a4SAndroid Build Coastguard Worker    Executes a cargo command and returns the list of test binaries generated.
59*bb4ee6a4SAndroid Build Coastguard Worker
60*bb4ee6a4SAndroid Build Coastguard Worker    The build log will be hidden by default and only printed if the build
61*bb4ee6a4SAndroid Build Coastguard Worker    fails. In VERBOSE mode the output will be streamed directly.
62*bb4ee6a4SAndroid Build Coastguard Worker
63*bb4ee6a4SAndroid Build Coastguard Worker    Note: Exits the program if the build fails.
64*bb4ee6a4SAndroid Build Coastguard Worker    """
65*bb4ee6a4SAndroid Build Coastguard Worker    message_format = "json-diagnostic-rendered-ansi" if sys.stdout.isatty() else "json"
66*bb4ee6a4SAndroid Build Coastguard Worker    cmd = [
67*bb4ee6a4SAndroid Build Coastguard Worker        "cargo",
68*bb4ee6a4SAndroid Build Coastguard Worker        cargo_command,
69*bb4ee6a4SAndroid Build Coastguard Worker        f"--message-format={message_format}",
70*bb4ee6a4SAndroid Build Coastguard Worker        *flags,
71*bb4ee6a4SAndroid Build Coastguard Worker    ]
72*bb4ee6a4SAndroid Build Coastguard Worker    if verbose():
73*bb4ee6a4SAndroid Build Coastguard Worker        print("$", " ".join(cmd))
74*bb4ee6a4SAndroid Build Coastguard Worker    process = subprocess.Popen(
75*bb4ee6a4SAndroid Build Coastguard Worker        cmd,
76*bb4ee6a4SAndroid Build Coastguard Worker        cwd=cwd,
77*bb4ee6a4SAndroid Build Coastguard Worker        stdout=subprocess.PIPE,
78*bb4ee6a4SAndroid Build Coastguard Worker        stderr=subprocess.STDOUT,
79*bb4ee6a4SAndroid Build Coastguard Worker        text=True,
80*bb4ee6a4SAndroid Build Coastguard Worker        env=env,
81*bb4ee6a4SAndroid Build Coastguard Worker    )
82*bb4ee6a4SAndroid Build Coastguard Worker
83*bb4ee6a4SAndroid Build Coastguard Worker    messages: List[str] = []
84*bb4ee6a4SAndroid Build Coastguard Worker
85*bb4ee6a4SAndroid Build Coastguard Worker    # Read messages as cargo is running.
86*bb4ee6a4SAndroid Build Coastguard Worker    assert process.stdout
87*bb4ee6a4SAndroid Build Coastguard Worker    for line in iter(process.stdout.readline, ""):
88*bb4ee6a4SAndroid Build Coastguard Worker        # any non-json line is a message to print
89*bb4ee6a4SAndroid Build Coastguard Worker        if not line.startswith("{"):
90*bb4ee6a4SAndroid Build Coastguard Worker            if verbose():
91*bb4ee6a4SAndroid Build Coastguard Worker                print(line.rstrip())
92*bb4ee6a4SAndroid Build Coastguard Worker            messages.append(line.rstrip())
93*bb4ee6a4SAndroid Build Coastguard Worker            continue
94*bb4ee6a4SAndroid Build Coastguard Worker        json_line = json.loads(line)
95*bb4ee6a4SAndroid Build Coastguard Worker
96*bb4ee6a4SAndroid Build Coastguard Worker        # 'message' type lines will be printed
97*bb4ee6a4SAndroid Build Coastguard Worker        if json_line.get("message"):
98*bb4ee6a4SAndroid Build Coastguard Worker            message = json_line.get("message").get("rendered")
99*bb4ee6a4SAndroid Build Coastguard Worker            if verbose():
100*bb4ee6a4SAndroid Build Coastguard Worker                print(message)
101*bb4ee6a4SAndroid Build Coastguard Worker            messages.append(message)
102*bb4ee6a4SAndroid Build Coastguard Worker
103*bb4ee6a4SAndroid Build Coastguard Worker        # Collect info about test executables produced
104*bb4ee6a4SAndroid Build Coastguard Worker        elif json_line.get("executable"):
105*bb4ee6a4SAndroid Build Coastguard Worker            yield Executable(
106*bb4ee6a4SAndroid Build Coastguard Worker                Path(json_line.get("executable")),
107*bb4ee6a4SAndroid Build Coastguard Worker                crate_name=json_line.get("package_id", "").split(" ")[0],
108*bb4ee6a4SAndroid Build Coastguard Worker                cargo_target=json_line.get("target").get("name"),
109*bb4ee6a4SAndroid Build Coastguard Worker                kind=json_line.get("target").get("kind")[0],
110*bb4ee6a4SAndroid Build Coastguard Worker                is_test=json_line.get("profile", {}).get("test", False),
111*bb4ee6a4SAndroid Build Coastguard Worker                is_fresh=json_line.get("fresh", False),
112*bb4ee6a4SAndroid Build Coastguard Worker            )
113*bb4ee6a4SAndroid Build Coastguard Worker
114*bb4ee6a4SAndroid Build Coastguard Worker    if process.wait() != 0:
115*bb4ee6a4SAndroid Build Coastguard Worker        if not verbose():
116*bb4ee6a4SAndroid Build Coastguard Worker            for message in messages:
117*bb4ee6a4SAndroid Build Coastguard Worker                print(message)
118*bb4ee6a4SAndroid Build Coastguard Worker        sys.exit(-1)
119*bb4ee6a4SAndroid Build Coastguard Worker
120*bb4ee6a4SAndroid Build Coastguard Worker
121*bb4ee6a4SAndroid Build Coastguard Workerdef main():
122*bb4ee6a4SAndroid Build Coastguard Worker    parser = argparse.ArgumentParser(usage=USAGE)
123*bb4ee6a4SAndroid Build Coastguard Worker    parser.add_argument(
124*bb4ee6a4SAndroid Build Coastguard Worker        "--build-target",
125*bb4ee6a4SAndroid Build Coastguard Worker        "--platform",
126*bb4ee6a4SAndroid Build Coastguard Worker        "-p",
127*bb4ee6a4SAndroid Build Coastguard Worker        help=(
128*bb4ee6a4SAndroid Build Coastguard Worker            "Override the cargo triple to build. Shorthands are available: (x86_64, armhf, "
129*bb4ee6a4SAndroid Build Coastguard Worker            + "aarch64, mingw64, msvc64)."
130*bb4ee6a4SAndroid Build Coastguard Worker        ),
131*bb4ee6a4SAndroid Build Coastguard Worker    )
132*bb4ee6a4SAndroid Build Coastguard Worker    parser.add_argument(
133*bb4ee6a4SAndroid Build Coastguard Worker        "--json",
134*bb4ee6a4SAndroid Build Coastguard Worker        action="store_true",
135*bb4ee6a4SAndroid Build Coastguard Worker        help="Output in JSON instead of human readable format.",
136*bb4ee6a4SAndroid Build Coastguard Worker    )
137*bb4ee6a4SAndroid Build Coastguard Worker    parser.add_argument("--strip", action="store_true", help="Strip output binaries")
138*bb4ee6a4SAndroid Build Coastguard Worker    parser.add_argument(
139*bb4ee6a4SAndroid Build Coastguard Worker        "--build-profile", help="Select compile profile, default to release.", default="release"
140*bb4ee6a4SAndroid Build Coastguard Worker    )
141*bb4ee6a4SAndroid Build Coastguard Worker    pgo_group = parser.add_mutually_exclusive_group()
142*bb4ee6a4SAndroid Build Coastguard Worker    pgo_group.add_argument(
143*bb4ee6a4SAndroid Build Coastguard Worker        "--profile-generate",
144*bb4ee6a4SAndroid Build Coastguard Worker        help="Target directory to generate profile when running, must use absolute path",
145*bb4ee6a4SAndroid Build Coastguard Worker    )
146*bb4ee6a4SAndroid Build Coastguard Worker    pgo_group.add_argument(
147*bb4ee6a4SAndroid Build Coastguard Worker        "--profile-use", help="Profile file used for PGO, must use absolute path"
148*bb4ee6a4SAndroid Build Coastguard Worker    )
149*bb4ee6a4SAndroid Build Coastguard Worker    parser.add_argument("cargo_arguments", nargs="*", help="Extra arguments pass to cargo")
150*bb4ee6a4SAndroid Build Coastguard Worker
151*bb4ee6a4SAndroid Build Coastguard Worker    args = parser.parse_args()
152*bb4ee6a4SAndroid Build Coastguard Worker
153*bb4ee6a4SAndroid Build Coastguard Worker    if args.profile_generate and (
154*bb4ee6a4SAndroid Build Coastguard Worker        not os.path.isabs(args.profile_generate) or not os.path.isdir(args.profile_generate)
155*bb4ee6a4SAndroid Build Coastguard Worker    ):
156*bb4ee6a4SAndroid Build Coastguard Worker        raise ValueError("--profile-generate argument is not an absolute path to a folder")
157*bb4ee6a4SAndroid Build Coastguard Worker    if args.profile_use and (
158*bb4ee6a4SAndroid Build Coastguard Worker        not os.path.isabs(args.profile_use) or not os.path.isfile(args.profile_use)
159*bb4ee6a4SAndroid Build Coastguard Worker    ):
160*bb4ee6a4SAndroid Build Coastguard Worker        raise ValueError("--profile-use argument is not an absolute path to a file")
161*bb4ee6a4SAndroid Build Coastguard Worker
162*bb4ee6a4SAndroid Build Coastguard Worker    build_target = Triple.from_shorthand(args.build_target) if args.build_target else None
163*bb4ee6a4SAndroid Build Coastguard Worker    build_target = build_target or Triple.host_default()
164*bb4ee6a4SAndroid Build Coastguard Worker
165*bb4ee6a4SAndroid Build Coastguard Worker    exclude_args = [
166*bb4ee6a4SAndroid Build Coastguard Worker        f"--exclude={x}" for x in PGO_EXCLUDE if args.profile_generate or args.profile_use
167*bb4ee6a4SAndroid Build Coastguard Worker    ]
168*bb4ee6a4SAndroid Build Coastguard Worker    if build_target == Triple.from_shorthand("riscv64"):
169*bb4ee6a4SAndroid Build Coastguard Worker        exclude_args += ["--exclude=" + s for s in DO_NOT_BUILD_RISCV64]
170*bb4ee6a4SAndroid Build Coastguard Worker
171*bb4ee6a4SAndroid Build Coastguard Worker    features = build_target.feature_flag
172*bb4ee6a4SAndroid Build Coastguard Worker    cargo_args = [
173*bb4ee6a4SAndroid Build Coastguard Worker        "--profile",
174*bb4ee6a4SAndroid Build Coastguard Worker        args.build_profile,
175*bb4ee6a4SAndroid Build Coastguard Worker        "--features=" + features,
176*bb4ee6a4SAndroid Build Coastguard Worker        f"--target={build_target}",
177*bb4ee6a4SAndroid Build Coastguard Worker        "--workspace",
178*bb4ee6a4SAndroid Build Coastguard Worker        *exclude_args,
179*bb4ee6a4SAndroid Build Coastguard Worker        *args.cargo_arguments,
180*bb4ee6a4SAndroid Build Coastguard Worker    ]
181*bb4ee6a4SAndroid Build Coastguard Worker
182*bb4ee6a4SAndroid Build Coastguard Worker    build_env = os.environ.copy()
183*bb4ee6a4SAndroid Build Coastguard Worker    build_env.update(build_target.get_cargo_env())
184*bb4ee6a4SAndroid Build Coastguard Worker    build_env.setdefault("RUSTFLAGS", "")
185*bb4ee6a4SAndroid Build Coastguard Worker    build_env["RUSTFLAGS"] += " -D warnings"
186*bb4ee6a4SAndroid Build Coastguard Worker    if args.strip:
187*bb4ee6a4SAndroid Build Coastguard Worker        build_env["RUSTFLAGS"] += " -C strip=symbols"
188*bb4ee6a4SAndroid Build Coastguard Worker    if args.profile_generate:
189*bb4ee6a4SAndroid Build Coastguard Worker        build_env["RUSTFLAGS"] += " -C profile-generate=" + args.profile_generate
190*bb4ee6a4SAndroid Build Coastguard Worker    if args.profile_use:
191*bb4ee6a4SAndroid Build Coastguard Worker        build_env["RUSTFLAGS"] += " -C profile-use=" + args.profile_use
192*bb4ee6a4SAndroid Build Coastguard Worker
193*bb4ee6a4SAndroid Build Coastguard Worker    executables = list(cargo("build", CROSVM_ROOT, cargo_args, build_env))
194*bb4ee6a4SAndroid Build Coastguard Worker
195*bb4ee6a4SAndroid Build Coastguard Worker    if args.json:
196*bb4ee6a4SAndroid Build Coastguard Worker        result = {}
197*bb4ee6a4SAndroid Build Coastguard Worker        for exe in executables:
198*bb4ee6a4SAndroid Build Coastguard Worker            assert exe.cargo_target not in result
199*bb4ee6a4SAndroid Build Coastguard Worker            result[exe.cargo_target] = str(exe.binary_path)
200*bb4ee6a4SAndroid Build Coastguard Worker        print(json.dumps(result))
201*bb4ee6a4SAndroid Build Coastguard Worker    else:
202*bb4ee6a4SAndroid Build Coastguard Worker        print("Release binaries:")
203*bb4ee6a4SAndroid Build Coastguard Worker        for exe in executables:
204*bb4ee6a4SAndroid Build Coastguard Worker            print(f"Name: {exe.cargo_target}")
205*bb4ee6a4SAndroid Build Coastguard Worker            print(f"Path: {str(exe.binary_path)}")
206*bb4ee6a4SAndroid Build Coastguard Worker            print()
207*bb4ee6a4SAndroid Build Coastguard Worker
208*bb4ee6a4SAndroid Build Coastguard Worker
209*bb4ee6a4SAndroid Build Coastguard Workerif __name__ == "__main__":
210*bb4ee6a4SAndroid Build Coastguard Worker    main()
211