2023-01-10 23:20:47 +00:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
# Copyright 2023 The ChromiumOS Authors
|
|
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
|
|
# found in the LICENSE file.
|
|
|
|
|
|
|
|
import argparse
|
|
|
|
import json
|
|
|
|
import os
|
2023-02-23 22:43:23 +00:00
|
|
|
import subprocess
|
|
|
|
import sys
|
2023-03-23 01:24:35 +00:00
|
|
|
from itertools import chain, product, starmap
|
|
|
|
from pathlib import Path
|
2023-02-23 22:43:23 +00:00
|
|
|
from typing import Dict, Iterable, List, NamedTuple
|
2023-01-10 23:20:47 +00:00
|
|
|
|
2023-02-23 22:43:23 +00:00
|
|
|
from impl.common import CROSVM_ROOT, Triple, verbose
|
2023-05-04 17:16:12 +00:00
|
|
|
from impl.test_config import DO_NOT_BUILD_RISCV64
|
2023-01-10 23:20:47 +00:00
|
|
|
|
|
|
|
USAGE = """\
|
|
|
|
Build crosvm with release (optimized) profile.
|
|
|
|
|
|
|
|
To target local machine:
|
|
|
|
|
|
|
|
$ ./tools/build_release
|
|
|
|
|
|
|
|
To cross-compile for aarch64, armhf or windows you can use:
|
|
|
|
|
|
|
|
$ ./tools/build_release --platform=aarch64
|
|
|
|
$ ./tools/build_release --platform=armhf
|
|
|
|
$ ./tools/build_release --platform=mingw64
|
|
|
|
"""
|
|
|
|
|
2023-03-23 01:24:35 +00:00
|
|
|
# We only need PGO for main binary, but for consistency, only exclude incompatible parts from PGO
|
|
|
|
PGO_EXCLUDE = ["crosvm_control"]
|
|
|
|
|
2023-01-10 23:20:47 +00:00
|
|
|
|
2023-02-23 22:43:23 +00:00
|
|
|
class Executable(NamedTuple):
|
|
|
|
"""Container for info about an executable generated by cargo build/test."""
|
|
|
|
|
|
|
|
binary_path: Path
|
|
|
|
crate_name: str
|
|
|
|
cargo_target: str
|
|
|
|
kind: str
|
|
|
|
is_test: bool
|
|
|
|
is_fresh: bool
|
|
|
|
|
|
|
|
@property
|
|
|
|
def name(self):
|
|
|
|
return f"{self.crate_name}:{self.cargo_target}"
|
|
|
|
|
|
|
|
|
|
|
|
def cargo(
|
|
|
|
cargo_command: str,
|
|
|
|
cwd: Path,
|
|
|
|
flags: List[str],
|
|
|
|
env: Dict[str, str],
|
|
|
|
) -> Iterable[Executable]:
|
|
|
|
"""
|
|
|
|
Executes a cargo command and returns the list of test binaries generated.
|
|
|
|
|
|
|
|
The build log will be hidden by default and only printed if the build
|
|
|
|
fails. In VERBOSE mode the output will be streamed directly.
|
|
|
|
|
|
|
|
Note: Exits the program if the build fails.
|
|
|
|
"""
|
|
|
|
message_format = "json-diagnostic-rendered-ansi" if sys.stdout.isatty() else "json"
|
|
|
|
cmd = [
|
|
|
|
"cargo",
|
|
|
|
cargo_command,
|
|
|
|
f"--message-format={message_format}",
|
|
|
|
*flags,
|
|
|
|
]
|
|
|
|
if verbose():
|
|
|
|
print("$", " ".join(cmd))
|
|
|
|
process = subprocess.Popen(
|
|
|
|
cmd,
|
|
|
|
cwd=cwd,
|
|
|
|
stdout=subprocess.PIPE,
|
|
|
|
stderr=subprocess.STDOUT,
|
|
|
|
text=True,
|
|
|
|
env=env,
|
|
|
|
)
|
|
|
|
|
|
|
|
messages: List[str] = []
|
|
|
|
|
|
|
|
# Read messages as cargo is running.
|
|
|
|
assert process.stdout
|
|
|
|
for line in iter(process.stdout.readline, ""):
|
|
|
|
# any non-json line is a message to print
|
|
|
|
if not line.startswith("{"):
|
|
|
|
if verbose():
|
|
|
|
print(line.rstrip())
|
|
|
|
messages.append(line.rstrip())
|
|
|
|
continue
|
|
|
|
json_line = json.loads(line)
|
|
|
|
|
|
|
|
# 'message' type lines will be printed
|
|
|
|
if json_line.get("message"):
|
|
|
|
message = json_line.get("message").get("rendered")
|
|
|
|
if verbose():
|
|
|
|
print(message)
|
|
|
|
messages.append(message)
|
|
|
|
|
|
|
|
# Collect info about test executables produced
|
|
|
|
elif json_line.get("executable"):
|
|
|
|
yield Executable(
|
|
|
|
Path(json_line.get("executable")),
|
|
|
|
crate_name=json_line.get("package_id", "").split(" ")[0],
|
|
|
|
cargo_target=json_line.get("target").get("name"),
|
|
|
|
kind=json_line.get("target").get("kind")[0],
|
|
|
|
is_test=json_line.get("profile", {}).get("test", False),
|
|
|
|
is_fresh=json_line.get("fresh", False),
|
|
|
|
)
|
|
|
|
|
|
|
|
if process.wait() != 0:
|
|
|
|
if not verbose():
|
|
|
|
for message in messages:
|
|
|
|
print(message)
|
|
|
|
sys.exit(-1)
|
|
|
|
|
|
|
|
|
2023-01-10 23:20:47 +00:00
|
|
|
def main():
|
|
|
|
parser = argparse.ArgumentParser(usage=USAGE)
|
|
|
|
parser.add_argument(
|
|
|
|
"--build-target",
|
|
|
|
"--platform",
|
|
|
|
"-p",
|
|
|
|
help=(
|
|
|
|
"Override the cargo triple to build. Shorthands are available: (x86_64, armhf, "
|
|
|
|
+ "aarch64, mingw64, msvc64)."
|
|
|
|
),
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
"--json",
|
|
|
|
action="store_true",
|
|
|
|
help="Output in JSON instead of human readable format.",
|
|
|
|
)
|
2023-03-23 01:24:35 +00:00
|
|
|
parser.add_argument("--strip", action="store_true", help="Strip output binaries")
|
|
|
|
pgo_group = parser.add_mutually_exclusive_group()
|
|
|
|
pgo_group.add_argument(
|
|
|
|
"--profile-generate",
|
|
|
|
help="Target directory to generate profile when running, must use absolute path",
|
|
|
|
)
|
|
|
|
pgo_group.add_argument(
|
|
|
|
"--profile-use", help="Profile file used for PGO, must use absolute path"
|
|
|
|
)
|
2023-02-23 23:21:55 +00:00
|
|
|
parser.add_argument("cargo_arguments", nargs="*", help="Extra arguments pass to cargo")
|
|
|
|
|
2023-01-10 23:20:47 +00:00
|
|
|
args = parser.parse_args()
|
2023-03-23 01:24:35 +00:00
|
|
|
|
|
|
|
if args.profile_generate and (
|
|
|
|
not os.path.isabs(args.profile_generate) or not os.path.isdir(args.profile_generate)
|
|
|
|
):
|
|
|
|
raise ValueError("--profile-generate argument is not an absolute path to a folder")
|
|
|
|
if args.profile_use and (
|
|
|
|
not os.path.isabs(args.profile_use) or not os.path.isfile(args.profile_use)
|
|
|
|
):
|
|
|
|
raise ValueError("--profile-use argument is not an absolute path to a file")
|
|
|
|
|
2023-01-10 23:20:47 +00:00
|
|
|
build_target = Triple.from_shorthand(args.build_target) if args.build_target else None
|
|
|
|
build_target = build_target or Triple.host_default()
|
|
|
|
|
2023-05-04 17:16:12 +00:00
|
|
|
exclude_args = [
|
|
|
|
f"--exclude={x}" for x in PGO_EXCLUDE if args.profile_generate or args.profile_use
|
|
|
|
]
|
|
|
|
if build_target == Triple.from_shorthand("riscv64"):
|
|
|
|
exclude_args += ["--exclude=" + s for s in DO_NOT_BUILD_RISCV64]
|
|
|
|
|
2023-01-10 23:20:47 +00:00
|
|
|
features = build_target.feature_flag
|
|
|
|
cargo_args = [
|
|
|
|
"--release",
|
|
|
|
"--features=" + features,
|
|
|
|
f"--target={build_target}",
|
|
|
|
"--workspace",
|
2023-05-04 17:16:12 +00:00
|
|
|
*exclude_args,
|
2023-02-23 23:21:55 +00:00
|
|
|
*args.cargo_arguments,
|
2023-01-10 23:20:47 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
build_env = os.environ.copy()
|
|
|
|
build_env.update(build_target.get_cargo_env())
|
|
|
|
build_env.setdefault("RUSTFLAGS", "")
|
|
|
|
build_env["RUSTFLAGS"] += " -D warnings"
|
2023-02-14 23:25:30 +00:00
|
|
|
if args.strip:
|
|
|
|
build_env["RUSTFLAGS"] += " -C strip=symbols"
|
2023-03-23 01:24:35 +00:00
|
|
|
if args.profile_generate:
|
|
|
|
build_env["RUSTFLAGS"] += " -C profile-generate=" + args.profile_generate
|
|
|
|
if args.profile_use:
|
|
|
|
build_env["RUSTFLAGS"] += " -C profile-use=" + args.profile_use
|
2023-01-10 23:20:47 +00:00
|
|
|
|
|
|
|
executables = list(cargo("build", CROSVM_ROOT, cargo_args, build_env))
|
|
|
|
|
|
|
|
if args.json:
|
|
|
|
result = {}
|
|
|
|
for exe in executables:
|
|
|
|
assert exe.cargo_target not in result
|
|
|
|
result[exe.cargo_target] = str(exe.binary_path)
|
|
|
|
print(json.dumps(result))
|
|
|
|
else:
|
|
|
|
print("Release binaries:")
|
|
|
|
for exe in executables:
|
|
|
|
print(f"Name: {exe.cargo_target}")
|
|
|
|
print(f"Path: {str(exe.binary_path)}")
|
|
|
|
print()
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|