blob: b5456eda51c1cc8fb2f54b64f2b38d1b9a06d029 [file]
#!/usr/bin/env python3
"""Builds Fuchsia VIM3 firmware and creates CIPD package files.
In case CIPD is not available it is expected that all necessary tools would be
in PATH.
Clang and GCC from CIPD are used for repeatable build.
"""
import argparse
import logging
import os
import shutil
import subprocess
import sys
import textwrap
from pathlib import Path
from typing import Optional
_MY_DIR = Path(__file__).parent.resolve()
_BUILD_SCRIPT_TARGET = [_MY_DIR / "mk", "kvim3_zircon"]
_CIPD_DEPS_DIR = _MY_DIR / "cipd_deps"
_GBL_CIPD_VERSION = "latest"
def build(gbl_path: Optional[Path] = None):
"""Builds the CIPD package."""
# Pass compilers for u-boot host tools build
host_clang = _CIPD_DEPS_DIR / "clang"
build_env = dict(
os.environ,
# Build bootloader binaries and generate combined images (as part of the signing process).
CONFIG_AML_SIGNED_UBOOT="y",
# Clang target for host tools
TARGET="x86_64-unknown-linux-gnu",
HOST_CLANG=host_clang,
HOSTCC=host_clang / "bin" / "clang",
HOSTCXX=host_clang / "bin" / "clang++",
HOSTLD=host_clang / "bin" / "lld",
CROSS_COMPILE="aarch64-elf-",
)
if gbl_path:
build_env["VIM3_GBL_PATH"] = str(gbl_path)
subprocess.run(
_BUILD_SCRIPT_TARGET,
env=build_env,
check=True,
)
def _parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter
)
parser.add_argument(
"--cipd", default="cipd", help="Path to CIPD tool; looks on $PATH by default"
)
parser.add_argument(
"--clean-cipd",
action="store_true",
help="Clear CIPD dependencies dir before start",
)
parser.add_argument(
"--gbl-path",
help="Path to local GBL efi binary. If provided, CIPD GBL is not used.",
)
return parser.parse_args()
# list_of_packages are in format:
# [(dir name in cipd_deps, CIPD package path, package version, dir to add to PATH),...]
def ensure_deps(cipd_tool: str, list_of_packages):
"""Ensures that build deps have been downloaded.
We should rely on as few host dependencies as possible, instead fetching
what we need from CIPD to ensure a hermetic and reproducible build env.
"""
try:
for (name, cipd_package, cipd_version, path_dir) in list_of_packages:
dep_dir = _CIPD_DEPS_DIR / name
subprocess.run(
[cipd_tool, "ensure", "-root", dep_dir, "-ensure-file", "-"],
check=True,
text=True,
input=f"{cipd_package} {cipd_version}",
)
_add_path_prefix(dep_dir / path_dir)
except FileNotFoundError as err:
print(f"WARNING: CIPD tool was likely not found: {err}.")
print("WARNING: Relying on system tools for the build.")
def _add_path_prefix(path):
"""Adds |path| to the beginning of $PATH."""
os.environ["PATH"] = str(path) + os.pathsep + os.environ["PATH"]
def cipd_dep_dir_clean():
logging.info(f"Removing CIPD deps dir: {_CIPD_DEPS_DIR}")
shutil.rmtree(_CIPD_DEPS_DIR, ignore_errors=True)
def fuchsia_build(cipd, clean_cipd, gbl_path=None):
if clean_cipd:
cipd_dep_dir_clean()
# Versions are pinned to current integration version in most cases.
# It is done to prevent unexpected breaking when integration moves forward.
# It is required to do manual testing before bumping the versions.
list_of_packages = [
("make", "fuchsia/third_party/make/linux-amd64", "version:4.3", "bin"),
# gcc version is 15.2.1,2.46.50
(
"gcc",
"fuchsia/third_party/gcc/linux-amd64",
"git_revision:26e4f8ad7220f692cd8010f9cf7620d690f1b094,1daba1c5ceb27d25265ba2801f5be307d21a6abd",
"bin",
),
(
"gcc-arm-none-eabi",
"pigweed/third_party/gcc-arm-none-eabi/linux-amd64",
"version:10-2020-q4-major",
"bin",
),
# For host u-boot tools
(
"clang",
"fuchsia/third_party/clang/linux-amd64",
"git_revision:80743bd43fd5b38fedc503308e7a652e23d3ec93",
"bin",
),
# Host zip tool for signing
(
"zip",
"skia/bots/zip_linux-amd64",
"version:0",
"",
),
# Host xxd tool for signing
(
"xxd",
"fuchsia/third_party/xxd/linux-amd64",
"version:2@9.1.1061",
"",
),
]
resolved_gbl_path = None
if gbl_path:
resolved_gbl_path = Path(gbl_path).resolve()
if not resolved_gbl_path.exists():
sys.exit(f"ERROR: Provided --gbl-path '{gbl_path}' does not exist.")
if not resolved_gbl_path:
list_of_packages.append((
"gbl",
"fuchsia/third_party/android/gbl/mainline",
_GBL_CIPD_VERSION,
"",
))
ensure_deps(cipd, list_of_packages)
if not resolved_gbl_path:
cipd_gbl_path = _CIPD_DEPS_DIR / "gbl" / "gbl_aarch64_dev.efi"
if cipd_gbl_path.exists():
resolved_gbl_path = cipd_gbl_path
print(f"Using GBL from CIPD: {resolved_gbl_path}")
else:
sys.exit("ERROR: GBL CIPD package downloaded but gbl_aarch64_dev.efi not found.")
else:
print(f"Using local GBL: {resolved_gbl_path}")
target_gbl = _MY_DIR / "board" / "khadas" / "kvim3" / "gbl.efi"
if resolved_gbl_path != target_gbl:
shutil.copyfile(resolved_gbl_path, target_gbl)
build(gbl_path=resolved_gbl_path)
uboot_bin = _MY_DIR / "build" / "u-boot.bin.unsigned"
if uboot_bin.exists():
size = uboot_bin.stat().st_size
max_size = 4 * 1024 * 1024
if size > max_size:
sys.exit(
f"ERROR: Final bootloader image size ({size} bytes / {size / (1024 * 1024):.2f} MiB) "
f"exceeds 4 MiB eMMC boot partition limit."
)
def _main() -> int:
logging.basicConfig(level=logging.INFO)
args = _parse_args()
fuchsia_build(
cipd=args.cipd,
clean_cipd=args.clean_cipd,
gbl_path=args.gbl_path,
)
if __name__ == "__main__":
sys.exit(_main())