3162b9648a
This reverts commit 71e862d942
.
After properly switching to public kleaf APIs, we no longer need to
pass --nocheck_bzl_visibility to the build.
Change-Id: I3ff04efd2cbebdeb176f6b67b099f7a6ff6b2fab
Signed-off-by: John Moon <quic_johmoo@quicinc.com>
381 lines
13 KiB
Python
Executable File
381 lines
13 KiB
Python
Executable File
#!/usr/bin/env python
|
|
# SPDX-License-Identifier: GPL-2.0-only
|
|
# Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
|
|
|
|
import argparse
|
|
import errno
|
|
import glob
|
|
import logging
|
|
import os
|
|
import re
|
|
import sys
|
|
import subprocess
|
|
|
|
HOST_TARGETS = ["dtc"]
|
|
DEFAULT_SKIP_LIST = ["abi"]
|
|
MSM_EXTENSIONS = "build/msm_kernel_extensions.bzl"
|
|
ABL_EXTENSIONS = "build/abl_extensions.bzl"
|
|
DEFAULT_MSM_EXTENSIONS_SRC = "../msm-kernel/msm_kernel_extensions.bzl"
|
|
DEFAULT_ABL_EXTENSIONS_SRC = "../bootable/bootloader/edk2/abl_extensions.bzl"
|
|
DEFAULT_OUT_DIR = "{workspace}/out/msm-kernel-{target}-{variant}"
|
|
|
|
class Target:
|
|
def __init__(self, workspace, target, variant, bazel_label, out_dir=None):
|
|
self.workspace = workspace
|
|
self.target = target
|
|
self.variant = variant
|
|
self.bazel_label = bazel_label
|
|
self.out_dir = out_dir
|
|
|
|
def __lt__(self, other):
|
|
return len(self.bazel_label) < len(other.bazel_label)
|
|
|
|
def get_out_dir(self, suffix=None):
|
|
if self.out_dir:
|
|
out_dir = self.out_dir
|
|
else:
|
|
# Mirror the logic in msm_common.bzl:get_out_dir()
|
|
if "allyes" in self.target:
|
|
target_norm = self.target.replace("_", "-")
|
|
else:
|
|
target_norm = self.target.replace("-", "_")
|
|
|
|
variant_norm = self.variant.replace("-", "_")
|
|
|
|
out_dir = DEFAULT_OUT_DIR.format(
|
|
workspace = self.workspace, target=target_norm, variant=variant_norm
|
|
)
|
|
|
|
if suffix:
|
|
return os.path.join(out_dir, suffix)
|
|
else:
|
|
return out_dir
|
|
|
|
class BazelBuilder:
|
|
"""Helper class for building with Bazel"""
|
|
|
|
def __init__(self, target_list, skip_list, out_dir, dry_run, user_opts):
|
|
self.workspace = os.path.realpath(
|
|
os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")
|
|
)
|
|
self.bazel_bin = os.path.join(self.workspace, "tools", "bazel")
|
|
if not os.path.exists(self.bazel_bin):
|
|
logging.error("failed to find Bazel binary at %s", self.bazel_bin)
|
|
sys.exit(1)
|
|
self.kernel_dir = os.path.basename(
|
|
(os.path.dirname(os.path.realpath(__file__)))
|
|
)
|
|
|
|
for t, v in target_list:
|
|
if not t or not v:
|
|
logging.error("invalid target_variant combo \"%s_%s\"", t, v)
|
|
sys.exit(1)
|
|
|
|
self.target_list = target_list
|
|
self.skip_list = skip_list
|
|
self.dry_run = dry_run
|
|
self.user_opts = user_opts
|
|
self.process_list = []
|
|
if len(self.target_list) > 1 and out_dir:
|
|
logging.error("cannot specify multiple targets with one out dir")
|
|
sys.exit(1)
|
|
else:
|
|
self.out_dir = out_dir
|
|
|
|
self.setup_extensions()
|
|
|
|
def __del__(self):
|
|
for proc in self.process_list:
|
|
try:
|
|
proc.kill()
|
|
proc.wait()
|
|
except OSError:
|
|
pass
|
|
|
|
def setup_extensions(self):
|
|
"""Set up the extension files if needed"""
|
|
for (ext, def_src) in [
|
|
(MSM_EXTENSIONS, DEFAULT_MSM_EXTENSIONS_SRC),
|
|
(ABL_EXTENSIONS, DEFAULT_ABL_EXTENSIONS_SRC),
|
|
]:
|
|
ext_path = os.path.join(self.workspace, ext)
|
|
# If the file doesn't exist or is a dead link, link to the default
|
|
try:
|
|
os.stat(ext_path)
|
|
except OSError as e:
|
|
if e.errno == errno.ENOENT:
|
|
logging.info(
|
|
"%s does not exist or is a broken symlink... linking to default at %s",
|
|
ext,
|
|
def_src,
|
|
)
|
|
if os.path.islink(ext_path):
|
|
os.unlink(ext_path)
|
|
os.symlink(def_src, ext_path)
|
|
else:
|
|
raise e
|
|
|
|
def get_build_targets(self):
|
|
"""Query for build targets"""
|
|
logging.info("Querying build targets...")
|
|
|
|
targets = []
|
|
for t, v in self.target_list:
|
|
if v == "ALL":
|
|
if self.out_dir:
|
|
logging.error("cannot specify multiple targets (ALL variants) with one out dir")
|
|
sys.exit(1)
|
|
|
|
skip_list_re = [
|
|
re.compile(r"//{}:{}_.*_{}_dist".format(self.kernel_dir, t, s))
|
|
for s in self.skip_list
|
|
]
|
|
query = 'filter("{}_.*_dist$", attr(generator_function, define_msm_platforms, {}/...))'.format(
|
|
t, self.kernel_dir
|
|
)
|
|
else:
|
|
skip_list_re = [
|
|
re.compile(r"//{}:{}_{}_{}_dist".format(self.kernel_dir, t, v, s))
|
|
for s in self.skip_list
|
|
]
|
|
query = 'filter("{}_{}.*_dist$", attr(generator_function, define_msm_platforms, {}/...))'.format(
|
|
t, v, self.kernel_dir
|
|
)
|
|
|
|
cmdline = [
|
|
self.bazel_bin,
|
|
"query",
|
|
"--ui_event_filters=-info",
|
|
"--noshow_progress",
|
|
query,
|
|
]
|
|
|
|
logging.debug('Running "%s"', " ".join(cmdline))
|
|
|
|
try:
|
|
query_cmd = subprocess.Popen(
|
|
cmdline, cwd=self.workspace, stdout=subprocess.PIPE
|
|
)
|
|
self.process_list.append(query_cmd)
|
|
label_list = [l.decode("utf-8") for l in query_cmd.stdout.read().splitlines()]
|
|
except Exception as e:
|
|
logging.error(e)
|
|
sys.exit(1)
|
|
|
|
self.process_list.remove(query_cmd)
|
|
|
|
if not label_list:
|
|
logging.error(
|
|
"failed to find any Bazel targets for target/variant combo %s_%s",
|
|
t,
|
|
v,
|
|
)
|
|
sys.exit(1)
|
|
|
|
for label in label_list:
|
|
if any((skip_re.match(label) for skip_re in skip_list_re)):
|
|
continue
|
|
|
|
if v == "ALL":
|
|
real_variant = re.search(
|
|
r"//{}:{}_([^_]+)_".format(self.kernel_dir, t), label
|
|
).group(1)
|
|
else:
|
|
real_variant = v
|
|
|
|
targets.append(
|
|
Target(self.workspace, t, real_variant, label, self.out_dir)
|
|
)
|
|
|
|
# Sort build targets by label string length to guarantee the base target goes
|
|
# first when copying to output directory
|
|
targets.sort()
|
|
|
|
return targets
|
|
|
|
def clean_legacy_generated_files(self):
|
|
"""Clean generated files from legacy build to avoid conflicts with Bazel"""
|
|
for f in glob.glob("{}/msm-kernel/arch/arm64/configs/vendor/*_defconfig".format(self.workspace)):
|
|
os.remove(f)
|
|
|
|
f = os.path.join(self.workspace, "bootable", "bootloader", "edk2", "Conf", ".AutoGenIdFile.txt")
|
|
if os.path.exists(f):
|
|
os.remove(f)
|
|
|
|
for root, _, files in os.walk(os.path.join(self.workspace, "bootable")):
|
|
for f in files:
|
|
if f.endswith(".pyc"):
|
|
os.remove(os.path.join(root, f))
|
|
|
|
def bazel(
|
|
self,
|
|
bazel_subcommand,
|
|
targets,
|
|
extra_options=None,
|
|
bazel_target_opts=None,
|
|
):
|
|
"""Execute a bazel command"""
|
|
cmdline = [self.bazel_bin, bazel_subcommand]
|
|
if extra_options:
|
|
cmdline.extend(extra_options)
|
|
cmdline.extend([t.bazel_label for t in targets])
|
|
if bazel_target_opts is not None:
|
|
cmdline.extend(["--"] + bazel_target_opts)
|
|
|
|
cmdline_str = " ".join(cmdline)
|
|
try:
|
|
logging.info('Running "%s"', cmdline_str)
|
|
build_proc = subprocess.Popen(cmdline_str, cwd=self.workspace, shell=True)
|
|
self.process_list.append(build_proc)
|
|
build_proc.wait()
|
|
if build_proc.returncode != 0:
|
|
sys.exit(build_proc.returncode)
|
|
except Exception as e:
|
|
logging.error(e)
|
|
sys.exit(1)
|
|
|
|
self.process_list.remove(build_proc)
|
|
|
|
def build_targets(self, targets):
|
|
"""Run "bazel build" on all targets in parallel"""
|
|
self.bazel("build", targets, extra_options=self.user_opts)
|
|
|
|
def run_targets(self, targets):
|
|
"""Run "bazel run" on all targets in serial (since bazel run cannot have multiple targets)"""
|
|
for target in targets:
|
|
# Set the output directory based on if it's a host target
|
|
if any(
|
|
re.match(r"//{}:.*_{}_dist".format(self.kernel_dir, h), target.bazel_label)
|
|
for h in HOST_TARGETS
|
|
):
|
|
out_dir = target.get_out_dir("host")
|
|
else:
|
|
out_dir = target.get_out_dir("dist")
|
|
self.bazel(
|
|
"run",
|
|
[target],
|
|
extra_options=self.user_opts,
|
|
bazel_target_opts=["--dist_dir", out_dir]
|
|
)
|
|
self.write_opts(out_dir)
|
|
|
|
def run_menuconfig(self):
|
|
"""Run menuconfig on all target-variant combos class is initialized with"""
|
|
for t, v in self.target_list:
|
|
menuconfig_label = "//{}:{}_{}_config".format(self.kernel_dir, t, v)
|
|
menuconfig_target = [Target(self.workspace, t, v, menuconfig_label, self.out_dir)]
|
|
self.bazel("run", menuconfig_target, bazel_target_opts=["menuconfig"])
|
|
|
|
def write_opts(self, out_dir):
|
|
with open(os.path.join(out_dir, "build_opts.txt"), "w") as opt_file:
|
|
if self.user_opts:
|
|
opt_file.write("{}".format("\n".join(self.user_opts)))
|
|
opt_file.write("\n")
|
|
|
|
def build(self):
|
|
"""Determine which targets to build, then build them"""
|
|
targets_to_build = self.get_build_targets()
|
|
|
|
if not targets_to_build:
|
|
logging.error("no targets to build")
|
|
sys.exit(1)
|
|
|
|
if self.skip_list:
|
|
self.user_opts.extend(["--//msm-kernel:skip_{}=true".format(s) for s in self.skip_list])
|
|
|
|
self.user_opts.extend([
|
|
"--user_kmi_symbol_lists=//msm-kernel:android/abi_gki_aarch64_qcom",
|
|
"--ignore_missing_projects",
|
|
])
|
|
|
|
if self.dry_run:
|
|
self.user_opts.append("--nobuild")
|
|
|
|
logging.debug(
|
|
"Building the following targets:\n%s",
|
|
"\n".join([t.bazel_label for t in targets_to_build])
|
|
)
|
|
|
|
self.clean_legacy_generated_files()
|
|
|
|
logging.info("Building targets...")
|
|
self.build_targets(targets_to_build)
|
|
|
|
if not self.dry_run:
|
|
self.run_targets(targets_to_build)
|
|
|
|
def main():
|
|
"""Main script entrypoint"""
|
|
parser = argparse.ArgumentParser(description="Build kernel platform with Bazel")
|
|
|
|
parser.add_argument(
|
|
"-t",
|
|
"--target",
|
|
metavar=("TARGET", "VARIANT"),
|
|
action="append",
|
|
nargs=2,
|
|
required=True,
|
|
help='Target and variant to build (e.g. -t kalama gki). May be passed multiple times. A special VARIANT may be passed, "ALL", which will build all variants for a particular target',
|
|
)
|
|
parser.add_argument(
|
|
"-s",
|
|
"--skip",
|
|
metavar="BUILD_RULE",
|
|
action="append",
|
|
default=[],
|
|
help="Skip specific build rules (e.g. --skip abl will skip the //msm-kernel:<target>_<variant>_abl build)",
|
|
)
|
|
parser.add_argument(
|
|
"-o",
|
|
"--out_dir",
|
|
metavar="OUT_DIR",
|
|
help='Specify the output distribution directory (by default, "$PWD/out/msm-kernel-<target>-variant")',
|
|
)
|
|
parser.add_argument(
|
|
"--log",
|
|
metavar="LEVEL",
|
|
default="info",
|
|
choices=["debug", "info", "warning", "error"],
|
|
help="Log level (debug, info, warning, error)",
|
|
)
|
|
parser.add_argument(
|
|
"-c",
|
|
"--menuconfig",
|
|
action="store_true",
|
|
help="Run menuconfig for <target>-<variant> and exit without building",
|
|
)
|
|
parser.add_argument(
|
|
"-d",
|
|
"--dry-run",
|
|
action="store_true",
|
|
help="Perform a dry-run of the build which will perform loading/analysis of build files",
|
|
)
|
|
|
|
args, user_opts = parser.parse_known_args(sys.argv[1:])
|
|
|
|
logging.basicConfig(
|
|
level=getattr(logging, args.log.upper()),
|
|
format="[{}] %(levelname)s: %(message)s".format(os.path.basename(sys.argv[0])),
|
|
)
|
|
|
|
args.skip.extend(DEFAULT_SKIP_LIST)
|
|
|
|
builder = BazelBuilder(args.target, args.skip, args.out_dir, args.dry_run, user_opts)
|
|
try:
|
|
if args.menuconfig:
|
|
builder.run_menuconfig()
|
|
else:
|
|
builder.build()
|
|
except KeyboardInterrupt:
|
|
logging.info("Received keyboard interrupt... exiting")
|
|
del builder
|
|
sys.exit(1)
|
|
|
|
if args.dry_run:
|
|
logging.info("Dry-run completed successfully!")
|
|
else:
|
|
logging.info("Build completed successfully!")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|