summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--BUILD.bazel29
-rwxr-xr-xabi/process_symbols.py82
-rw-r--r--abi/symbols.allow2
-rwxr-xr-xabi_compliance.sh35
-rwxr-xr-xbuild-tools/kleaf_internal_do_not_use_path/linux-x86/rsync3
-rwxr-xr-xbuild-tools/kleaf_internal_do_not_use_path/linux-x86/tar8
-rw-r--r--build_utils.sh11
-rwxr-xr-xgki/download_from_ci59
-rw-r--r--init/init_ddk.py164
-rw-r--r--init/init_ddk_test.py63
-rw-r--r--kleaf/BUILD.bazel13
-rw-r--r--kleaf/README.md4
-rw-r--r--kleaf/artifact_tests/BUILD.bazel2
-rw-r--r--kleaf/artifact_tests/device_modules_test.bzl10
-rw-r--r--kleaf/artifact_tests/kernel_test.bzl30
-rw-r--r--kleaf/artifact_tests/py_test_hack.bzl31
-rwxr-xr-xkleaf/bazel.py44
-rw-r--r--kleaf/bazelrc/canary.bazelrc4
-rw-r--r--kleaf/bazelrc/flags.bazelrc2
-rw-r--r--kleaf/bazelrc/silent.bazelrc2
-rw-r--r--kleaf/bzlmod/bazel.MODULE.bazel32
-rw-r--r--kleaf/common_kernels.bzl1
-rw-r--r--kleaf/docs/abi.md23
-rw-r--r--kleaf/docs/api_reference/hermetic_tools.md3
-rw-r--r--kleaf/docs/api_reference/kernel.md12
-rw-r--r--kleaf/docs/api_reference/kernel_prebuilt_ext.md5
-rw-r--r--kleaf/docs/bzlmod.md34
-rw-r--r--kleaf/docs/canary.md9
-rw-r--r--kleaf/docs/ddk/main.md2
-rw-r--r--kleaf/docs/ddk/workspace.md241
-rw-r--r--kleaf/docs/download_prebuilt.md8
-rw-r--r--kleaf/docs/impl.md29
-rw-r--r--kleaf/docs/workspace.md10
-rw-r--r--kleaf/hermetic_tools.bzl157
-rw-r--r--kleaf/impl/BUILD.bazel7
-rw-r--r--kleaf/impl/abi/abi_stgdiff.bzl10
-rw-r--r--kleaf/impl/abi/abi_update.bzl35
-rw-r--r--kleaf/impl/arg_wrapper.cpp110
-rw-r--r--kleaf/impl/declare_kernel_prebuilts.bzl19
-rw-r--r--kleaf/impl/image/boot_images.bzl9
-rw-r--r--kleaf/impl/image/kernel_images.bzl5
-rw-r--r--kleaf/impl/kernel_config.bzl7
-rw-r--r--kleaf/impl/kernel_prebuilt_repo.bzl53
-rw-r--r--kleaf/impl/kernel_prebuilt_utils.bzl14
-rw-r--r--kleaf/impl/kmi_symbol_list.bzl7
-rw-r--r--kleaf/tests/diff_test.bzl42
-rw-r--r--kleaf/tests/hermetic_test.bzl31
-rw-r--r--kleaf/tests/integration_test/BUILD.bazel14
-rw-r--r--kleaf/tests/integration_test/ddk_workspace_test/.gitignore10
-rw-r--r--kleaf/tests/integration_test/ddk_workspace_test/README.md15
-rw-r--r--kleaf/tests/integration_test/ddk_workspace_test/WORKSPACE.bzlmod2
-rw-r--r--kleaf/tests/integration_test/ddk_workspace_test/external/kleaf/README.md1
-rw-r--r--kleaf/tests/integration_test/ddk_workspace_test/extra_setup.py56
-rw-r--r--kleaf/tests/integration_test/ddk_workspace_test/in_tree/BUILD.bazel79
-rw-r--r--kleaf/tests/integration_test/ddk_workspace_test/in_tree/build.config.in_tree3
-rw-r--r--kleaf/tests/integration_test/ddk_workspace_test/in_tree/in_tree_defconfig2
-rw-r--r--kleaf/tests/integration_test/ddk_workspace_test/in_tree/mydriver.c21
-rw-r--r--kleaf/tests/integration_test/ddk_workspace_test/out_of_tree/BUILD.bazel29
-rw-r--r--kleaf/tests/integration_test/ddk_workspace_test/out_of_tree/mydriver.c21
-rw-r--r--kleaf/tests/integration_test/ddk_workspace_test/tests/BUILD.bazel42
-rw-r--r--kleaf/tests/integration_test/fake_repo_root/.gitignore1
-rw-r--r--kleaf/tests/integration_test/fake_repo_root/fake_workspace_root/README.md1
-rw-r--r--kleaf/tests/integration_test/integration_test.py396
-rw-r--r--kleaf/tests/kernel_build_config_test/BUILD.bazel14
-rw-r--r--kleaf/workspace.bzl1
-rw-r--r--kleaf/workspace_status_stamp.py60
66 files changed, 1744 insertions, 537 deletions
diff --git a/BUILD.bazel b/BUILD.bazel
index 958984d..9690bae 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -100,7 +100,6 @@ _HERMETIC_TOOLS = [
"//prebuilts/kernel-build-tools:linux-x86/bin/swig",
"//prebuilts/kernel-build-tools:linux-x86/bin/tune2fs",
"//prebuilts/kernel-build-tools:linux-x86/bin/ufdt_apply_overlay",
- "//prebuilts/clang/host/linux-x86/clang-{}:bin/llvm-strings".format(VARS["CLANG_VERSION"]),
] + glob([
# Intentionally glob here to test for existance of alias destination
# buildifier: disable=constant-glob
@@ -155,6 +154,7 @@ _TOYS = [
"sort",
"stat",
"tail",
+ "tar",
"tee",
"test",
"timeout",
@@ -172,12 +172,20 @@ _TOYS = [
"whoami",
"xargs",
"xxd",
- # Real tar binary that shouldn't be used directly
- "kleaf_internal_do_not_use/tar",
]
hermetic_tools(
name = "hermetic-tools",
+ extra_args = {
+ "rsync": ["--no-group"],
+ "tar": [
+ "--mtime=@0",
+ "--owner=0",
+ "--group=0",
+ "--numeric-owner",
+ "--sort=name",
+ ],
+ },
symlinks = select({
"//build/kernel/kleaf:debug_cache_dir_conflict_needs_flock": {
":toybox_flock": "flock",
@@ -204,6 +212,11 @@ hermetic_tools(
"//conditions:default": {
":toybox_gzip": "gzip",
},
+ }) | select({
+ "//build/kernel/kleaf:remove_strings_from_hermetic_tools_is_true": {},
+ "//conditions:default": {
+ "//prebuilts/clang/host/linux-x86/clang-{}:bin/llvm-strings".format(VARS["CLANG_VERSION"]): "llvm-strings",
+ },
}) | {
":toybox": ":".join(_TOYS),
@@ -211,14 +224,7 @@ hermetic_tools(
"@kleaf_host_tools//:bash": "bash",
"@kleaf_host_tools//:sh": "sh",
"@kleaf_host_tools//:perl": "perl",
-
- # Real rsync that shouldn't be used directly
- "@kleaf_host_tools//:rsync": "kleaf_internal_do_not_use/rsync",
- # Embed additional args that calls kleaf_internal_do_not_use/rsync
- "build-tools/kleaf_internal_do_not_use_path/linux-x86/rsync": "rsync",
-
- # Embeds additional args that calls kleaf_internal_do_not_use/tar
- "build-tools/kleaf_internal_do_not_use_path/linux-x86/tar": "tar",
+ "@kleaf_host_tools//:rsync": "rsync",
# Additional symlinks other than in //prebuilts/build-tools
"//prebuilts/kernel-build-tools:linux-x86/bin/depmod": ":".join([
@@ -305,7 +311,6 @@ py_binary(
name = "abi_process_symbols",
srcs = ["abi/process_symbols.py"],
data = [
- "abi/symbols.allow",
"abi/symbols.deny",
],
main = "abi/process_symbols.py",
diff --git a/abi/process_symbols.py b/abi/process_symbols.py
index 5d7bada..0d43da9 100755
--- a/abi/process_symbols.py
+++ b/abi/process_symbols.py
@@ -15,7 +15,6 @@
# limitations under the License.
import argparse
-import enum
import os
import sys
@@ -24,12 +23,6 @@ _TRACE_POINT = '__tracepoint_'
_TRACE_ITER = '__traceiter_'
-class Status(enum.Enum):
- UNKNOWN = 0
- ALLOWED = 1
- FORBIDDEN = 2
-
-
def _validate_symbols(symbol_list, symbols):
"""Validates Tracepoints consistenty in a given symbol list."""
missing = []
@@ -55,31 +48,27 @@ def _validate_symbols(symbol_list, symbols):
sys.exit(1)
-def _read_config(allow_file, deny_file):
- """Reads symbol configuration file."""
- config = {}
+def _read_denied_symbols_config(deny_file):
+ """Reads denied symbols configuration file."""
+ denied_symbols = {}
- def read_file(status, config_file):
- with open(config_file) as file:
- for line in file:
- fields = line.rstrip('\n').split(None, 1)
- if not fields:
- continue
- symbol = fields[0]
- if symbol.startswith('#'):
- continue
- reason = ''
- if len(fields) > 1:
- reason = fields[1]
- if symbol in config:
- print(f"symbol '{symbol}' duplicate configuration", file=sys.stderr)
- continue
- config[symbol] = (status, reason)
+ with open(deny_file) as file:
+ for line in file:
+ fields = line.rstrip('\n').split(None, 1)
+ if not fields:
+ continue
+ symbol = fields[0]
+ if symbol.startswith('#'):
+ continue
+ reason = ''
+ if len(fields) > 1:
+ reason = fields[1]
+ if symbol in denied_symbols:
+ print(f"symbol '{symbol}' duplicate configuration", file=sys.stderr)
+ continue
+ denied_symbols[symbol] = reason
- read_file(Status.FORBIDDEN, deny_file)
- read_file(Status.ALLOWED, allow_file)
-
- return config
+ return denied_symbols
def _read_symbol_lists(symbol_lists):
@@ -106,21 +95,8 @@ def _get_symbols(lines):
return symbols
-def _check_symbols(config, symbols):
- """Checks symbols against configuration."""
- report = []
- for symbol in sorted(symbols):
- if symbol in config:
- status, reason = config[symbol]
- report.append([symbol, status, reason])
- else:
- report.append([symbol, Status.UNKNOWN, ''])
- return report
-
-
def main():
dir = os.path.dirname(sys.argv[0])
- allow_file = os.path.join(dir, 'symbols.allow')
deny_file = os.path.join(dir, 'symbols.deny')
parser = argparse.ArgumentParser()
@@ -143,9 +119,6 @@ def main():
'--out-file', required=True, help='combined symbol list file name'
)
parser.add_argument(
- '--report-file', required=True, help='symbol list report file name'
- )
- parser.add_argument(
'--verbose', action='store_true', help='increase verbosity of the output'
)
@@ -155,12 +128,10 @@ def main():
out_directory = args.out_dir
symbol_lists = [os.path.join(in_directory, s) for s in args.symbol_lists]
out_file = os.path.join(out_directory, args.out_file)
- report_file = os.path.join(out_directory, args.report_file)
- config = _read_config(allow_file, deny_file)
+ denied_symbols = _read_denied_symbols_config(deny_file)
lines = _read_symbol_lists(symbol_lists)
symbols = _get_symbols(lines)
- report = _check_symbols(config, symbols)
if args.verbose:
print('========================================================')
@@ -170,13 +141,12 @@ def main():
exit_status = 0
if args.verbose:
- print(f'Generating ABI symbol report {report_file}')
- with open(report_file, 'w') as rf:
- for symbol, status, reason in report:
- rf.write(f'{symbol}\t{status.name}\t{reason}\n')
- if status == Status.FORBIDDEN:
- print(f"symbol '{symbol}' is not allowed: {reason}", file=sys.stderr)
- exit_status = 1
+ print('Checking symbols are not forbidden')
+ for symbol in symbols:
+ if symbol in denied_symbols:
+ reason = denied_symbols[symbol]
+ print(f"symbol '{symbol}' is not allowed: {reason}", file=sys.stderr)
+ exit_status = 1
return exit_status
diff --git a/abi/symbols.allow b/abi/symbols.allow
deleted file mode 100644
index 4ecdb38..0000000
--- a/abi/symbols.allow
+++ /dev/null
@@ -1,2 +0,0 @@
-__put_task_struct
-module_layout
diff --git a/abi_compliance.sh b/abi_compliance.sh
new file mode 100755
index 0000000..71a5160
--- /dev/null
+++ b/abi_compliance.sh
@@ -0,0 +1,35 @@
+#!/bin/bash -e
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This script is used as a test after
+# `tools/bazel //common:kernel_aarch64_abi_dist``
+# to check if the build reported ABI differences. It exits with non-zero error
+# code if ABI report file is missing or when it is not empty.
+
+# Example:
+# tools/bazel //common:kernel_aarch64_abi_dist
+# build/kernel/abi_compliance.sh out_abi/kernel_aarch64/dist
+
+dist_dir="$1"
+abi_report=$(cat "${dist_dir}/abi_stgdiff/abi.report.short")
+
+if [ -n "${abi_report}" ]; then
+ echo 'ERROR: ABI DIFFERENCES HAVE BEEN DETECTED!' >&2
+ echo "ERROR: ${abi_report}" >&2
+ exit 1
+fi
+
+echo 'INFO: no ABI differences reported by dependency target build.'
diff --git a/build-tools/kleaf_internal_do_not_use_path/linux-x86/rsync b/build-tools/kleaf_internal_do_not_use_path/linux-x86/rsync
deleted file mode 100755
index 5abcde9..0000000
--- a/build-tools/kleaf_internal_do_not_use_path/linux-x86/rsync
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-
-${0%/*}/kleaf_internal_do_not_use/rsync "$@" --no-group
diff --git a/build-tools/kleaf_internal_do_not_use_path/linux-x86/tar b/build-tools/kleaf_internal_do_not_use_path/linux-x86/tar
deleted file mode 100755
index 46c7d65..0000000
--- a/build-tools/kleaf_internal_do_not_use_path/linux-x86/tar
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/sh
-
-${0%/*}/kleaf_internal_do_not_use/tar "$@" \
- --mtime=@0 \
- --owner=0 \
- --group=0 \
- --numeric-owner \
- --sort=name
diff --git a/build_utils.sh b/build_utils.sh
index 50260e3..d204ac7 100644
--- a/build_utils.sh
+++ b/build_utils.sh
@@ -637,11 +637,18 @@ function build_boot_images() {
exit 1
fi
+ MKBOOTFS_ARGS=()
+ if [ -n "${VENDOR_RAMDISK_DEV_NODES}" ]; then
+ for vendor_ramdisk_dev_nodes in ${VENDOR_RAMDISK_DEV_NODES}; do
+ MKBOOTFS_ARGS+=("-n" "${vendor_ramdisk_dev_nodes}")
+ done
+ fi
+
if [ -n "${SKIP_UNPACKING_RAMDISK}" ] && [ -e "${VENDOR_RAMDISK_BINARY}" ]; then
cp "${VENDOR_RAMDISK_BINARY}" "${DIST_DIR}/ramdisk.${RAMDISK_EXT}"
- elif [ "${#MKBOOTIMG_RAMDISK_DIRS[@]}" -gt 0 ]; then
+ elif [ "${#MKBOOTIMG_RAMDISK_DIRS[@]}" -gt 0 ] || [ "${#MKBOOTFS_ARGS[@]}" -gt 0 ]; then
MKBOOTIMG_RAMDISK_CPIO="${MKBOOTIMG_STAGING_DIR}/ramdisk.cpio"
- mkbootfs "${MKBOOTIMG_RAMDISK_DIRS[@]}" >"${MKBOOTIMG_RAMDISK_CPIO}"
+ mkbootfs "${MKBOOTIMG_RAMDISK_DIRS[@]}" "${MKBOOTFS_ARGS[@]}" >"${MKBOOTIMG_RAMDISK_CPIO}"
${RAMDISK_COMPRESS} "${MKBOOTIMG_RAMDISK_CPIO}" >"${DIST_DIR}/ramdisk.${RAMDISK_EXT}"
fi
diff --git a/gki/download_from_ci b/gki/download_from_ci
index ca504d4..4cb3a26 100755
--- a/gki/download_from_ci
+++ b/gki/download_from_ci
@@ -47,8 +47,6 @@ ARM64_TARGETS = ["kernel_aarch64",
"u-boot_gem5_aarch64",
"u-boot_qemu_aarch64",
"u-boot_rockpi4"]
-RISCV64_TARGETS = ["kernel_riscv64",
- "u-boot_qemu_riscv64"]
X86_64_TARGETS = ["kernel_x86_64",
"kernel_debug_x86_64",
"kernel_microdroid_x86_64",
@@ -92,20 +90,21 @@ def parse_args():
nargs="?",
type=str,
help='the build target to download, e.g. "kernel_aarch64"')
- parser.add_argument(
+ update_group = parser.add_mutually_exclusive_group()
+ update_group.add_argument(
"-u",
"--update-gki",
action="store_true",
help="update GKI kernel prebuilts in Android platform (set $ANDROID_BUILD_TOP)")
- parser.add_argument(
+ update_group.add_argument(
"--update-16k",
action="store_true",
help="update 16k kernel prebuilts in Android platform (set $ANDROID_BUILD_TOP)")
- parser.add_argument(
+ update_group.add_argument(
"--update-u-boot",
action="store_true",
help="update U-Boot prebuilts in Android platform (set $ANDROID_BUILD_TOP)")
- parser.add_argument(
+ update_group.add_argument(
"--update-microdroid",
action="store_true",
help="update Microdroid kernel prebuilts in Android platform (set $ANDROID_BUILD_TOP)")
@@ -167,6 +166,8 @@ def get_build_kernel_version(data):
branch = json.loads(data)["branch"]
if branch == "aosp_kernel-common-android-mainline":
return "mainline"
+ elif branch == "aosp_kernel-common-android-mainline-riscv64":
+ return "mainline-riscv64"
else:
pattern = "android(\d\d)?-(?P<version>\d+\.\d+|\w\+)(-stable)?(-\d{4}-\d{2})?$"
result = re.search(pattern, branch)
@@ -538,12 +539,20 @@ def update_androidx(output_dir, version, android_version):
update_androidx_virtual_device_modules(gitlog, output_dir, version)
update_androidx_cf(gitlog, output_dir, version)
-def update_android_mainline_cf(gitlog, output_dir):
+def update_android_mainline_riscv64(output_dir):
+ # Although we don't update the x86_64 kernel, we use it's path to compute the
+ # binary kernel version, as the version parser only supports x86 images.
+ x86_64_kernel = os.path.join(output_dir, "kernel", "prebuilts", "mainline",
+ "x86_64", "kernel-mainline-allsyms")
+ (old_version, old_sha) = get_binary_kernel_version(x86_64_kernel)
+ (new_version, new_sha) = get_binary_kernel_version(x86_64_kernel)
+ gitlog = get_git_log(old_sha, new_sha)
+
cf_dir = os.path.join(output_dir, "device", "google", "cuttlefish_prebuilts",
"kernel")
riscv64_cf_dir = os.path.join(cf_dir, "mainline-riscv64")
- subprocess.check_call(["git", "rm", "-rf", riscv64_cf_dir], cwd=cf_dir)
- download_kernel("kernel_virt_riscv64", "mainline", riscv64_cf_dir)
+
+ download_kernel("kernel_virt_riscv64", "mainline-riscv64", riscv64_cf_dir)
download_kernel_modules("kernel_virt_riscv64", riscv64_cf_dir)
riscv64_cf_system_dlkm_dir = os.path.join(riscv64_cf_dir, "system_dlkm")
@@ -573,8 +582,6 @@ def update_android_mainline(output_dir):
gitlog = get_git_log(old_sha, new_sha)
commit_prebuilts(arm64_dir, gitlog)
- update_android_mainline_cf(gitlog, output_dir)
-
def update_16k():
url_base = BASE_URL.format(build_id=_args.build_id, target="kernel_aarch64")
url = os.path.join(url_base, "BUILD_INFO")
@@ -603,11 +610,25 @@ def update_16k():
download_kernel_modules("kernel_virt_aarch64_16k", arm64_16k_mods_dir, fetch_initramfs=True)
commit_prebuilts(arm64_16k_mods_dir, gitlog, "16KB page size kernel")
+def get_build_info(targets):
+ for target in targets:
+ try:
+ url_base = BASE_URL.format(build_id=_args.build_id, target=target)
+ url = os.path.join(url_base, "BUILD_INFO")
+ response = urllib.request.urlopen(url)
+ data = response.read().decode("utf-8")
+ # Test if we can load an expected value in the response data.
+ json.loads(data)["branch"]
+ return data
+ except json.decoder.JSONDecodeError:
+ pass
+ raise json.decoder.JSONDecodeError("URL data did not contain BUILD_INFO.")
+
def update_gki():
- url_base = BASE_URL.format(build_id=_args.build_id, target="kernel_aarch64")
- url = os.path.join(url_base, "BUILD_INFO")
- response = urllib.request.urlopen(url)
- data = response.read().decode("utf-8")
+ # The 'kernel_riscv64' target is on a separate branch that does not include
+ # 'kernel_aarch64' as a target. If we fail to download the build information
+ # from 'kernel_aarch64', try again with 'kernel_riscv64' as a fallback.
+ data = get_build_info(["kernel_aarch64", "kernel_riscv64"])
branch = json.loads(data)["branch"]
output_dir = os.environ["ANDROID_BUILD_TOP"]
@@ -622,6 +643,8 @@ def update_gki():
update_android11_5_4(output_dir)
elif branch == "aosp_kernel-common-android-mainline":
update_android_mainline(output_dir)
+ elif branch == "aosp_kernel-common-android-mainline-riscv64":
+ update_android_mainline_riscv64(output_dir)
else:
update_androidx(output_dir, get_build_kernel_version(data), get_android_version(branch))
@@ -634,7 +657,7 @@ def download_u_boot(target, output_dir):
extension = "elf"
elif target == "u-boot_rockpi4":
extension = "itb"
- elif target in ARM_TARGETS + ARM64_TARGETS + RISCV64_TARGETS:
+ elif target in ARM_TARGETS + ARM64_TARGETS:
extension = "bin"
files = [f"u-boot.{extension}"]
files.append("System.map")
@@ -668,7 +691,6 @@ def update_u_boot_mainline(output_dir):
gem5_aarch64_dir = os.path.join(bootloader_dir, "gem5_aarch64")
qemu_aarch64_dir = os.path.join(bootloader_dir, "qemu_aarch64")
qemu_arm_dir = os.path.join(bootloader_dir, "qemu_arm")
- qemu_riscv64_dir = os.path.join(bootloader_dir, "qemu_riscv64")
qemu_x86_64_dir = os.path.join(bootloader_dir, "qemu_x86_64")
rockpi_aarch64_dir = os.path.join(bootloader_dir, "rockpi_aarch64")
@@ -697,9 +719,6 @@ def update_u_boot_mainline(output_dir):
if os.path.exists(qemu_arm_dir):
subprocess.check_call(["git", "rm", "-rf", "*"], cwd=qemu_arm_dir)
download_u_boot("u-boot_qemu_arm", qemu_arm_dir)
- if os.path.exists(qemu_riscv64_dir):
- subprocess.check_call(["git", "rm", "-rf", "*"], cwd=qemu_riscv64_dir)
- download_u_boot("u-boot_qemu_riscv64", qemu_riscv64_dir)
if os.path.exists(qemu_x86_64_dir):
subprocess.check_call(["git", "rm", "-rf", "*"], cwd=qemu_x86_64_dir)
download_u_boot("u-boot_qemu_x86_64", qemu_x86_64_dir)
diff --git a/init/init_ddk.py b/init/init_ddk.py
index 2798924..998dd02 100644
--- a/init/init_ddk.py
+++ b/init/init_ddk.py
@@ -25,9 +25,9 @@ import pathlib
import shutil
import subprocess
import sys
+import tarfile
import tempfile
-import textwrap
-import urllib
+import urllib.parse
_TOOLS_BAZEL = "tools/bazel"
_DEVICE_BAZELRC = "device.bazelrc"
@@ -51,7 +51,6 @@ kernel_prebuilt_ext = use_extension(
)
kernel_prebuilt_ext.declare_kernel_prebuilts(
name = "gki_prebuilts",
- download_configs = {download_configs},
local_artifact_path = "{prebuilts_dir_relative}",
)
use_repo(kernel_prebuilt_ext, "gki_prebuilts")
@@ -87,7 +86,7 @@ class KleafProjectSetter:
tools_bazel.symlink_to(kleaf_tools_bazel)
@staticmethod
- def _update_file(path: pathlib.Path | str, update: str):
+ def _update_file(path: pathlib.Path, update: str):
"""Updates the content of a section between markers in a file."""
add_content: bool = False
skip_line: bool = False
@@ -123,6 +122,10 @@ class KleafProjectSetter:
def _try_rel_workspace(self, path: pathlib.Path):
"""Tries to convert |path| to be relative to ddk_workspace."""
+ if not self.ddk_workspace:
+ raise KleafProjectSetterError(
+ "ERROR: _try_rel_workspace called without --ddk_workspace set!"
+ )
try:
return path.relative_to(self.ddk_workspace)
except ValueError:
@@ -134,12 +137,30 @@ class KleafProjectSetter:
)
return path
- def _read_download_configs(self) -> str:
- """Reads the previously downloaded download_configs.json file."""
- download_configs = self.prebuilts_dir / "download_configs.json"
- with open(download_configs, "r", encoding="utf-8") as config:
- # Compress the representation by removing empty spaces to save some space.
- return repr(json.dumps(json.load(config), separators=(",", ":")))
+ def _get_local_path_overrides(self):
+ """Naive algorithm to extract local_path_override()'s from local @kleaf."""
+ path_attr_prefix = 'path = "'
+ section = []
+ overrides = []
+ module_bazel = self.kleaf_repo / _MODULE_BAZEL_FILE
+ # Modify path so it is relative to the current DDK workspace.
+ kleaf_repo = self._try_rel_workspace(self.kleaf_repo)
+ with open(module_bazel, "r", encoding="utf-8") as src:
+ for line in src:
+ if line.startswith("local_path_override("):
+ section.append(line)
+ continue
+ if not section:
+ continue
+ elif line.lstrip().startswith(path_attr_prefix):
+ line = line.strip().removeprefix(path_attr_prefix)
+ line = line.removesuffix('",')
+ line = f' path = "{kleaf_repo / line}",\n'
+ section.append(line)
+ if line.strip() == ")":
+ overrides.append("".join(section))
+ section.clear()
+ return "".join(overrides)
def _generate_module_bazel(self):
"""Configures the dependencies for the DDK workspace."""
@@ -151,20 +172,23 @@ class KleafProjectSetter:
module_bazel_content += _KLEAF_DEPENDENCY_TEMPLATE.format(
kleaf_repo_relative=self._try_rel_workspace(self.kleaf_repo),
)
+ module_bazel_content += self._get_local_path_overrides()
+ # b/338440785 Due to an issue in Bazel, rules_cc seems to be
+ # implicitly added in a fallback WORKSPACE.bzlmod file, hence
+ # forcing an empty one here.
+ workspace_bzlmod = self.ddk_workspace / "WORKSPACE.bzlmod"
+ workspace_bzlmod.touch(exist_ok=True)
if self.prebuilts_dir:
module_bazel_content += "\n"
module_bazel_content += _LOCAL_PREBUILTS_CONTENT_TEMPLATE.format(
- # TODO: b/328770706 - Use download_configs_file when available.
- download_configs=self._read_download_configs(),
# The prebuilts directory must be relative to the DDK workspace.
prebuilts_dir_relative=self._try_rel_workspace(
self.prebuilts_dir
),
)
- if module_bazel_content:
- self._update_file(module_bazel, module_bazel_content)
- else:
+ if not module_bazel_content:
logging.info("Nothing to update in %s", module_bazel)
+ self._update_file(module_bazel, module_bazel_content)
def _generate_bazelrc(self):
"""Creates a Bazel configuration file with the minimum setup required."""
@@ -174,18 +198,27 @@ class KleafProjectSetter:
kleaf_repo = self._try_rel_workspace(self.kleaf_repo)
if not kleaf_repo.is_absolute():
- kleaf_repo = (pathlib.Path("%workspace%") / kleaf_repo)
+ kleaf_repo = pathlib.Path("%workspace%") / kleaf_repo
+
+ bazelrc_content = []
+ bazelrc_content.append((
+ "common"
+ f" --registry=file://{kleaf_repo}/external/bazelbuild-bazel-central-registry"
+ ))
+ # Explicitly disable internet usage.
+ bazelrc_content.append("common --config=no_internet")
self._update_file(
bazelrc,
- textwrap.dedent(f"""\
- common --config=internet
- common --registry=file://{kleaf_repo}/external/bazelbuild-bazel-central-registry
- """),
+ "\n".join(bazelrc_content),
)
- def _get_url(self, remote_filename: str) -> str:
+ def _get_url(self, remote_filename: str) -> str | None:
"""Returns a valid url when it can be formed with target and id."""
+ if not self.url_fmt:
+ raise KleafProjectSetterError(
+ "ERROR: _get_url called without url_fmt set!"
+ )
url = self.url_fmt.format(
build_id=self.build_id,
build_target=self.build_target,
@@ -204,6 +237,7 @@ class KleafProjectSetter:
"""Validates that download are possible within the current context."""
if not self.url_fmt:
return False
+ # Check if build_id is missing and url_fmt has an anchor depending on it.
if self._get_url("") is None:
return False
return True
@@ -224,6 +258,10 @@ class KleafProjectSetter:
not be downloaded.
"""
url = self._get_url(remote_filename)
+ if not url:
+ raise KleafProjectSetterError(
+ f"ERROR: Unable to download {remote_filename}: can't infer URL"
+ )
# Workaround: Rely on host keychain to download files.
# This is needed otheriwese downloads fail when running this script
# using the hermetic Python toolchain.
@@ -240,13 +278,21 @@ class KleafProjectSetter:
def _infer_download_list(self) -> dict[str, dict]:
"""Infers the list of files to be downloaded using download_configs.json."""
+ if not self.prebuilts_dir:
+ raise KleafProjectSetterError(
+ "ERROR: _infer_download_list called without --prebuilts_dir!"
+ )
download_configs = self.prebuilts_dir / "download_configs.json"
with open(download_configs, "w+", encoding="utf-8") as config:
- self._download("download_configs.json", config.name)
+ self._download("download_configs.json", pathlib.Path(config.name))
return json.load(config)
def _download_prebuilts(self) -> None:
"""Downloads prebuilts from a given build_id when provided."""
+ if not self.prebuilts_dir:
+ raise KleafProjectSetterError(
+ "ERROR: _download_prebuilts called without --prebuilts_dir!"
+ )
logging.info("Downloading prebuilts into %s", self.prebuilts_dir)
files_dict = self._infer_download_list()
with concurrent.futures.ThreadPoolExecutor() as executor:
@@ -273,13 +319,75 @@ class KleafProjectSetter:
self.kleaf_repo.mkdir(parents=True, exist_ok=True)
# TODO: b/328770706 - According to the needs, syncing git repos logic should go here.
+ self._populate_kleaf_repo_extra_files()
+
def _handle_prebuilts(self) -> None:
- if not self.ddk_workspace or not self.prebuilts_dir:
+ if not self.prebuilts_dir:
return
self.prebuilts_dir.mkdir(parents=True, exist_ok=True)
if self._can_download_artifacts():
self._download_prebuilts()
+ def _populate_kleaf_repo_extra_files(self) -> None:
+ """Populates kleaf_repo by adding extra files"""
+ if self.local:
+ logging.info("Skipped populating kleaf_repo with --local.")
+ # --local assumes the kernel source tree is complete.
+ return
+ if not self.kleaf_repo:
+ logging.info(
+ "Skipped populating --kleaf_repo because it is unspecified"
+ )
+ return
+ if not self.prebuilts_dir:
+ logging.info(
+ "No prebuilts specified, skip populating %s", self.kleaf_repo
+ )
+ return
+ self._extract_headers_archive(self.prebuilts_dir, self.kleaf_repo)
+
+ build_config_constants = self.prebuilts_dir / "build.config.constants"
+ if not build_config_constants.is_file():
+ logging.warning(
+ "%s is not a file, skip copying", build_config_constants
+ )
+ return
+ shutil.copy(
+ build_config_constants,
+ self.kleaf_repo / "common/build.config.constants",
+ )
+ if not (self.kleaf_repo / "common/BUILD.bazel").is_file():
+ (self.kleaf_repo / "common/BUILD.bazel").write_text("")
+
+ @staticmethod
+ def _extract_headers_archive(
+ prebuilts_dir: pathlib.Path, kleaf_repo: pathlib.Path
+ ):
+ """Extracts DDK headers archive from prebuilts_dir into kleaf_repo"""
+ # TODO: This should be target-specific. The name of the output is
+ # currently (2024-05-16) defined by common/BUILD.bazel, but it may
+ # change in the future.
+ header_archives = list(
+ prebuilts_dir.glob("*_ddk_headers_archive.tar.gz")
+ )
+ if not header_archives:
+ logging.warning(
+ "No _ddk_headers_archive.tar.gz found in %s, "
+ "skipping header extraction.",
+ prebuilts_dir,
+ )
+ return
+ if len(header_archives) > 1:
+ raise KleafProjectSetterError(
+ "Multiple _ddk_headers_archive.tar.gz found in "
+ f"{prebuilts_dir}: {header_archives}"
+ )
+ logging.info(
+ "Extracting header archive %s to %s", header_archives[0], kleaf_repo
+ )
+ with tarfile.open(header_archives[0]) as tar:
+ tar.extractall(kleaf_repo)
+
def _run(self) -> None:
self._symlink_tools_bazel()
self._generate_module_bazel()
@@ -287,15 +395,15 @@ class KleafProjectSetter:
def run(self) -> None:
self._handle_ddk_workspace()
- self._handle_kleaf_repo()
self._handle_prebuilts()
+ self._handle_kleaf_repo()
self._run()
if __name__ == "__main__":
- def abs_path(path: str) -> pathlib.Path | None:
- path = pathlib.Path(path)
+ def abs_path(path_string: str) -> pathlib.Path | None:
+ path = pathlib.Path(path_string)
if not path.is_absolute():
raise ValueError(f"{path} is not an absolute path.")
return path
@@ -349,7 +457,9 @@ if __name__ == "__main__":
args = parser.parse_args()
logging.basicConfig(level=logging.INFO,
format="%(levelname)s: %(message)s")
-
+ # Validate pre-condition.
+ if args.local and not args.kleaf_repo:
+ parser.error("--local requires --kleaf_repo.")
try:
KleafProjectSetter(**vars(args)).run()
except KleafProjectSetterError as e:
diff --git a/init/init_ddk_test.py b/init/init_ddk_test.py
index db6773f..77fa3ba 100644
--- a/init/init_ddk_test.py
+++ b/init/init_ddk_test.py
@@ -177,7 +177,6 @@ class KleafProjectSetterTest(parameterized.TestCase):
"""Tests prebuilts setup is correct for relative and non-relative to workspace dirs."""
with tempfile.TemporaryDirectory() as tmp:
ddk_workspace = pathlib.Path(tmp) / "ddk_workspace"
-
# Verify the right local_artifact_path is set for prebuilts
# in a relative to workspace directory.
prebuilts_dir_rel = ddk_workspace / "prebuilts_dir"
@@ -226,13 +225,15 @@ class KleafProjectSetterTest(parameterized.TestCase):
prebuilts_dir = ddk_workspace / "prebuilts_dir"
download_configs = ddk_workspace / "download_configs.json"
download_configs.parent.mkdir(parents=True, exist_ok=True)
- download_configs.write_text(json.dumps({
- "non-existent-file": {
- "target_suffix": "non-existent-file",
- "mandatory": False,
- "remote_filename_fmt": "non-existent-file",
- }
- }))
+ download_configs.write_text(
+ json.dumps({
+ "non-existent-file": {
+ "target_suffix": "non-existent-file",
+ "mandatory": False,
+ "remote_filename_fmt": "non-existent-file",
+ }
+ })
+ )
with open(download_configs, "r", encoding="utf-8"):
url_fmt = f"file://{str(download_configs.parent)}/{{filename}}"
init_ddk.KleafProjectSetter(
@@ -245,6 +246,52 @@ class KleafProjectSetterTest(parameterized.TestCase):
url_fmt=url_fmt,
).run()
+ @parameterized.named_parameters(
+ # (Name, MODULE.bazel in @kleaf, expectation)
+ ("Empty", "", ""),
+ (
+ "Dependencies",
+ """
+local_path_override(
+ module_name = "abseil-py",
+ path = "external/python/absl-py",
+)
+local_path_override(
+ module_name = "apple_support",
+ path = "external/bazelbuild-apple_support",
+)
+ """,
+ """local_path_override(
+ module_name = "abseil-py",
+ path = "kleaf_repo/external/python/absl-py",
+)
+local_path_override(
+ module_name = "apple_support",
+ path = "kleaf_repo/external/bazelbuild-apple_support",
+)\n""",
+ ),
+ )
+ def test_local_path_overrides_extraction(
+ self, current_content, wanted_content
+ ):
+ """Tests extraction of local path overrides works correctly."""
+ with tempfile.TemporaryDirectory() as tmp:
+ ddk_workspace = pathlib.Path(tmp) / "ddk_workspace"
+ kleaf_repo = ddk_workspace / "kleaf_repo"
+ kleaf_repo.mkdir(parents=True, exist_ok=True)
+ kleaf_repo_module_bazel = kleaf_repo / init_ddk._MODULE_BAZEL_FILE
+ kleaf_repo_module_bazel.write_text(current_content)
+ got_content = init_ddk.KleafProjectSetter(
+ build_id=None,
+ build_target=None,
+ ddk_workspace=ddk_workspace,
+ kleaf_repo=kleaf_repo,
+ local=True,
+ prebuilts_dir=None,
+ url_fmt=None,
+ )._get_local_path_overrides()
+ self.assertEqual(got_content, wanted_content)
+
# This could be run as: tools/bazel test //build/kernel:init_ddk_test --test_output=all
if __name__ == "__main__":
diff --git a/kleaf/BUILD.bazel b/kleaf/BUILD.bazel
index ce58f2e..b45d048 100644
--- a/kleaf/BUILD.bazel
+++ b/kleaf/BUILD.bazel
@@ -635,6 +635,19 @@ config_setting(
visibility = ["//visibility:public"],
)
+# If true, drop llvm-strings from hermetic-tools.
+bool_flag(
+ name = "remove_strings_from_hermetic_tools",
+ build_setting_default = False,
+ visibility = ["//visibility:private"],
+)
+
+config_setting(
+ name = "remove_strings_from_hermetic_tools_is_true",
+ flag_values = {":remove_strings_from_hermetic_tools": "1"},
+ visibility = ["//build/kernel:__pkg__"],
+)
+
# The values of --config, as passed by command line and bazelrc files.
_config_values = (
"local",
diff --git a/kleaf/README.md b/kleaf/README.md
index 02900e9..e7e7c3a 100644
--- a/kleaf/README.md
+++ b/kleaf/README.md
@@ -56,6 +56,8 @@
[Kleaf Development](docs/kleaf_development.md)
+[Discover newest Kleaf features in Canary](docs/canary.md)
+
### Configurations in command line
`--config=fast`: [Make local builds faster](docs/fast.md)
@@ -66,6 +68,8 @@
`--config=stamp`: [Handling SCM version](docs/scmversion.md)
+`--config=canary`: [Kleaf Canary Features](docs/canary.md)
+
### Flags
For a full list of flags, run
diff --git a/kleaf/artifact_tests/BUILD.bazel b/kleaf/artifact_tests/BUILD.bazel
index 2c95478..28d3526 100644
--- a/kleaf/artifact_tests/BUILD.bazel
+++ b/kleaf/artifact_tests/BUILD.bazel
@@ -24,12 +24,12 @@ bzl_library(
srcs = [
"device_modules_test.bzl",
"kernel_test.bzl",
- "py_test_hack.bzl",
],
visibility = ["//build/kernel/kleaf:__subpackages__"],
deps = [
"//build/kernel/kleaf/impl",
"//build/kernel/kleaf/tests:empty_test",
+ "//build/kernel/kleaf/tests:hermetic_test",
"@bazel_skylib//rules:write_file",
],
)
diff --git a/kleaf/artifact_tests/device_modules_test.bzl b/kleaf/artifact_tests/device_modules_test.bzl
index bcc1b40..9cfe20e 100644
--- a/kleaf/artifact_tests/device_modules_test.bzl
+++ b/kleaf/artifact_tests/device_modules_test.bzl
@@ -16,12 +16,11 @@
load("@bazel_skylib//rules:write_file.bzl", "write_file")
load("//build/kernel/kleaf/impl:common_providers.bzl", "KernelModuleInfo")
-load("//build/kernel/kleaf/impl:hermetic_exec.bzl", "hermetic_exec_test")
load("//build/kernel/kleaf/impl:kernel_build.bzl", "kernel_build")
load("//build/kernel/kleaf/impl:kernel_modules_install.bzl", "kernel_modules_install")
load("//build/kernel/kleaf/impl:utils.bzl", "kernel_utils")
load("//build/kernel/kleaf/tests:empty_test.bzl", "empty_test")
-load(":py_test_hack.bzl", "run_py_binary_cmd")
+load("//build/kernel/kleaf/tests:hermetic_test.bzl", "hermetic_test")
visibility("//build/kernel/kleaf/...")
@@ -48,13 +47,12 @@ def _check_signature(
base_kernel_module,
expect_signature,
directory):
- test_binary = Label("//build/kernel/kleaf/artifact_tests:check_module_signature")
args = [
"--module",
base_kernel_module,
"--expect_signature" if expect_signature else "--noexpect_signature",
]
- data = [test_binary]
+ data = []
if directory:
args += [
"--dir",
@@ -62,10 +60,10 @@ def _check_signature(
]
data.append(directory)
- hermetic_exec_test(
+ hermetic_test(
name = name,
+ actual = Label("//build/kernel/kleaf/artifact_tests:check_module_signature"),
data = data,
- script = run_py_binary_cmd(test_binary),
args = args,
timeout = "short",
)
diff --git a/kleaf/artifact_tests/kernel_test.bzl b/kleaf/artifact_tests/kernel_test.bzl
index 2bd35e7..ef9a551 100644
--- a/kleaf/artifact_tests/kernel_test.bzl
+++ b/kleaf/artifact_tests/kernel_test.bzl
@@ -15,8 +15,7 @@
Tests for artifacts produced by kernel_module.
"""
-load("//build/kernel/kleaf/impl:hermetic_exec.bzl", "hermetic_exec_test")
-load(":py_test_hack.bzl", "run_py_binary_cmd")
+load("//build/kernel/kleaf/tests:hermetic_test.bzl", "hermetic_test")
visibility("//build/kernel/kleaf/...")
@@ -35,18 +34,17 @@ def kernel_module_test(
See complete list
[here](https://docs.bazel.build/versions/main/be/common-definitions.html#common-attributes).
"""
- test_binary = Label("//build/kernel/kleaf/artifact_tests:kernel_module_test")
args = []
- data = [test_binary]
+ data = []
if modules:
args.append("--modules")
args += ["$(rootpaths {})".format(module) for module in modules]
data += modules
- hermetic_exec_test(
+ hermetic_test(
name = name,
+ actual = Label("//build/kernel/kleaf/artifact_tests:kernel_module_test"),
data = data,
- script = run_py_binary_cmd(test_binary),
args = args,
timeout = "short",
**kwargs
@@ -66,17 +64,17 @@ def kernel_build_test(
See complete list
[here](https://docs.bazel.build/versions/main/be/common-definitions.html#common-attributes).
"""
- test_binary = Label("//build/kernel/kleaf/artifact_tests:kernel_build_test")
args = []
- data = [test_binary]
+ data = []
if target:
args += ["--artifacts", "$(rootpaths {})".format(target)]
data.append(target)
- hermetic_exec_test(
+ hermetic_test(
name = name,
+ actual = Label("//build/kernel/kleaf/artifact_tests:kernel_build_test"),
+ use_cc_toolchain = True,
data = data,
- script = run_py_binary_cmd(test_binary),
args = args,
timeout = "short",
**kwargs
@@ -98,21 +96,19 @@ def initramfs_modules_options_test(
See complete list
[here](https://docs.bazel.build/versions/main/be/common-definitions.html#common-attributes).
"""
- test_binary = Label("//build/kernel/kleaf/artifact_tests:initramfs_modules_options_test")
args = [
"--expected",
"$(rootpath {})".format(expected_modules_options),
"$(rootpaths {})".format(kernel_images),
]
- hermetic_exec_test(
+ hermetic_test(
name = name,
+ actual = Label("//build/kernel/kleaf/artifact_tests:initramfs_modules_options_test"),
data = [
expected_modules_options,
kernel_images,
- test_binary,
],
- script = run_py_binary_cmd(test_binary),
args = args,
timeout = "short",
**kwargs
@@ -142,7 +138,6 @@ def initramfs_modules_lists_test(
See complete list
[here](https://docs.bazel.build/versions/main/be/common-definitions.html#common-attributes).
"""
- test_binary = Label("//build/kernel/kleaf/artifact_tests:initramfs_modules_lists_test")
args = []
if expected_modules_list:
@@ -170,16 +165,15 @@ def initramfs_modules_lists_test(
args.append("$(rootpaths {})".format(kernel_images))
- hermetic_exec_test(
+ hermetic_test(
name = name,
+ actual = Label("//build/kernel/kleaf/artifact_tests:initramfs_modules_lists_test"),
data = [
expected_modules_list,
expected_modules_recovery_list,
expected_modules_charger_list,
kernel_images,
- test_binary,
],
- script = run_py_binary_cmd(test_binary),
args = args,
timeout = "short",
**kwargs
diff --git a/kleaf/artifact_tests/py_test_hack.bzl b/kleaf/artifact_tests/py_test_hack.bzl
deleted file mode 100644
index c6cc05b..0000000
--- a/kleaf/artifact_tests/py_test_hack.bzl
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright (C) 2023 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""Hacks for running `py_binary` in custom rules."""
-
-def run_py_binary_cmd(test_binary):
- """Returns a cmd that runs `test_binary` with $@.
-
- Args:
- test_binary: label to a `py_binary`.
- """
-
- # https://github.com/bazelbuild/bazel/issues/20038
- # HACK: py_binary puts multiple File's in DefaultInfo, including the
- # source files. Only filter the one with executable bit.
- return """
- $(find $(rootpaths {test_binary}) $(rootpaths {test_binary}) -executable | head -n1) "$@"
- """.format(
- test_binary = test_binary,
- )
diff --git a/kleaf/bazel.py b/kleaf/bazel.py
index 2d3c689..7f0e640 100755
--- a/kleaf/bazel.py
+++ b/kleaf/bazel.py
@@ -104,6 +104,21 @@ def _require_absolute_path(p: str | pathlib.Path) -> pathlib.Path:
return p
+def _check_repo_manifest(value: str) \
+ -> tuple[pathlib.Path | None, pathlib.Path | None]:
+ tokens = value.split(":")
+ match len(tokens):
+ case 0: return (None, None)
+ case 1: return (None, _require_absolute_path(value))
+ case 2:
+ repo_root, repo_manifest = tokens
+ return (_require_absolute_path(repo_root),
+ _require_absolute_path(repo_manifest))
+ raise argparse.ArgumentTypeError(
+ "Must be <REPO_MANIFEST> or <REPO_ROOT>:<REPO_MANIFEST>"
+ )
+
+
def _partition(lst: list[str], index: Optional[int]) \
-> Tuple[list[str], Optional[str], list[str]]:
"""Returns the triple split by index.
@@ -294,10 +309,18 @@ class BazelWrapper(KleafHelpPrinter):
default=absolute_cache_dir,
help="Cache directory for --config=local.")
group.add_argument(
- "--repo_manifest", metavar="<manifest.xml>",
- help="""Absolute path to repo manifest file, generated with """
- """`repo manifest -r`.""",
- type=_require_absolute_path,
+ "--repo_manifest", metavar="<repo_root>:<manifest.xml>",
+ help=textwrap.dedent("""\
+ One of the following:
+ - <REPO_MANIFEST>, an absolute path to the repo manifest file,
+ generated with `repo manifest -r`. In this case REPO_ROOT is
+ assumed to be the workspace root. This usage is deprecated
+ and may be removed in the future.
+ - <REPO_ROOT>:<REPO_MANIFEST>, where REPO_ROOT is the absolute
+ path to the repo root where `repo manifest -r` was executed.
+ """),
+ type=_check_repo_manifest,
+ default=(None, None),
)
group.add_argument(
"--ignore_missing_projects",
@@ -375,8 +398,13 @@ class BazelWrapper(KleafHelpPrinter):
self.env["KLEAF_MAKE_KEEP_GOING"] = "true" if self.known_args.make_keep_going else "false"
- if self.known_args.repo_manifest is not None:
- self.env["KLEAF_REPO_MANIFEST"] = self.known_args.repo_manifest
+ repo_root, repo_manifest = self.known_args.repo_manifest
+ if repo_root is None:
+ repo_root = self.workspace_dir
+ if repo_manifest is not None:
+ self.env["KLEAF_REPO_MANIFEST"] = f"{repo_root}:{repo_manifest}"
+ else:
+ self.env["KLEAF_REPO_MANIFEST"] = f"{repo_root}:"
if self.known_args.ignore_missing_projects:
self.env["KLEAF_IGNORE_MISSING_PROJECTS"] = "true"
@@ -408,7 +436,6 @@ class BazelWrapper(KleafHelpPrinter):
self.transformed_startup_options += self._transform_bazelrc_files([
# Add support for various configs
# Do not sort, the order here might matter.
- self.kleaf_repo_dir / "build/kernel/kleaf/bazelrc/canary.bazelrc",
self.kleaf_repo_dir / "build/kernel/kleaf/bazelrc/ants.bazelrc",
self.kleaf_repo_dir / "build/kernel/kleaf/bazelrc/android_ci.bazelrc",
self.kleaf_repo_dir / "build/kernel/kleaf/bazelrc/local.bazelrc",
@@ -461,6 +488,9 @@ class BazelWrapper(KleafHelpPrinter):
# Experimental bzlmod support
self.kleaf_repo_dir / "build/kernel/kleaf/bazelrc/bzlmod.bazelrc",
+ # Canary goes to the end because it uses flags / configs from elsewhere.
+ self.kleaf_repo_dir / "build/kernel/kleaf/bazelrc/canary.bazelrc",
+
self.kleaf_repo_dir / "build/kernel/kleaf/common.bazelrc",
])
diff --git a/kleaf/bazelrc/canary.bazelrc b/kleaf/bazelrc/canary.bazelrc
index afa8455..d4603bd 100644
--- a/kleaf/bazelrc/canary.bazelrc
+++ b/kleaf/bazelrc/canary.bazelrc
@@ -18,5 +18,7 @@
# releases and some of them might get discontinued.
# Enable building the toolchain from sources.
-build:canary --//build/kernel/kleaf:toolchain_from_sources
+build:canary --toolchain_from_sources
+# Drop llvm-strings from hermetic tools.
+build:canary --remove_strings_from_hermetic_tools
diff --git a/kleaf/bazelrc/flags.bazelrc b/kleaf/bazelrc/flags.bazelrc
index f8b6764..a487ccb 100644
--- a/kleaf/bazelrc/flags.bazelrc
+++ b/kleaf/bazelrc/flags.bazelrc
@@ -28,6 +28,8 @@ build --flag_alias=gzip_is_pigz=//build/kernel/kleaf:gzip_is_pigz
build --flag_alias=nogzip_is_pigz=no//build/kernel/kleaf:gzip_is_pigz
build --flag_alias=toolchain_from_sources=//build/kernel/kleaf:toolchain_from_sources
build --flag_alias=notoolchain_from_sources=no//build/kernel/kleaf:toolchain_from_sources
+build --flag_alias=remove_strings_from_hermetic_tools=//build/kernel/kleaf:remove_strings_from_hermetic_tools
+build --flag_alias=noremove_strings_from_hermetic_tools=no//build/kernel/kleaf:remove_strings_from_hermetic_tools
# flags that control kleaf integrity checking
build --flag_alias=allow_ddk_unsafe_headers=//build/kernel/kleaf:allow_ddk_unsafe_headers
diff --git a/kleaf/bazelrc/silent.bazelrc b/kleaf/bazelrc/silent.bazelrc
index 1416c5e..e0ad696 100644
--- a/kleaf/bazelrc/silent.bazelrc
+++ b/kleaf/bazelrc/silent.bazelrc
@@ -20,7 +20,7 @@ common:silent --noshow_progress
# Suppresses warnings like
# WARNING: Build option [...] has changed, discarding analysis cache
# We only care about error and failures from Bazel.
-common:silent --ui_event_filters=error,fail
+common:silent --ui_event_filters=,+error,+fail
# suppresses "Target X up-to-date" "bazel-bin/..." lines
common:silent --show_result=0
diff --git a/kleaf/bzlmod/bazel.MODULE.bazel b/kleaf/bzlmod/bazel.MODULE.bazel
index fc6cfe3..dc08b32 100644
--- a/kleaf/bzlmod/bazel.MODULE.bazel
+++ b/kleaf/bzlmod/bazel.MODULE.bazel
@@ -95,7 +95,7 @@ bazel_dep(
)
bazel_dep(
name = "platforms",
- version = "0.0.8",
+ version = "0.0.10",
)
bazel_dep(
name = "rules_cc",
@@ -107,31 +107,6 @@ bazel_dep(
)
bazel_dep(
- name = "rules_rust",
- version = "0.40.0",
-
- # If RUSTC_VERSION is set in kernel_toolchain_ext, kernel_build depends
- # on rules_rust, so rules_rust is needed for at least GKI.
- # However, if @kleaf is used as a dependent module in a DDKv2 set up and
- # drivers are building against prebuilts, kernel_build might not be
- # involved, and rules_rust might not be needed. If a DDKv2 driver is
- # building against a core kernel built from source, it needs to have
- # a direct dependency against rules_rust and call rust.toolchain() as well.
- dev_dependency = True,
-)
-rust = use_extension(
- "@rules_rust//rust:extensions.bzl",
- "rust",
- # See bazel_dep(name = "rules_rust")
- dev_dependency = True,
-)
-rust.toolchain(
- edition = "2021",
- # If in a future we need to specify a version, keep it in sync with //prebuilts/rust/linux-x86/
- # versions = ["1.70.2"],
-)
-
-bazel_dep(
name = "stardoc",
version = "0.6.2",
dev_dependency = True,
@@ -182,8 +157,3 @@ local_path_override(
module_name = "rules_python",
path = "external/bazelbuild-rules_python",
)
-
-local_path_override(
- module_name = "rules_rust",
- path = "external/bazelbuild-rules_rust",
-)
diff --git a/kleaf/common_kernels.bzl b/kleaf/common_kernels.bzl
index 3269365..0a2f5ff 100644
--- a/kleaf/common_kernels.bzl
+++ b/kleaf/common_kernels.bzl
@@ -476,6 +476,7 @@ def define_common_kernels(
# Workaround to set KERNEL_DIR correctly and
# avoid using the fallback (directory of the config).
+ # TODO(b/338438451): Clean this up with kernel_build.kernel_dir attr.
set_kernel_dir_cmd = "KERNEL_DIR=\"{kernel_dir}\"".format(
kernel_dir = paths.join(
native.package_relative_label(":x").workspace_root,
diff --git a/kleaf/docs/abi.md b/kleaf/docs/abi.md
index 5a9217b..266dcaa 100644
--- a/kleaf/docs/abi.md
+++ b/kleaf/docs/abi.md
@@ -77,14 +77,29 @@ of `//common:kernel_aarch64`, which is `common/android/abi_gki_aarch64.stg`. The
exit code reflects whether an ABI change is detected in the comparison, just
like `build_abi.sh --update`.
-Running the script with `--commit` creates a git commit with
-pre-filled message. For example:
+Running the script with `--print_git_commands` prints git commands you
+may run to create a commit with pre-filled message. For example:
```shell
-# -- is needed before --commit to pass the argument to the script.
-$ tools/bazel run //common:kernel_aarch64_abi_update -- --commit
+# -- is needed before --print_git_commands to pass the argument to the script.
+$ tools/bazel run //common:kernel_aarch64_abi_update -- --print_git_commands
```
+Example output:
+
+```
+$ tools/bazel run //common:kernel_aarch64_abi_update -- --print_git_commands
+[...]
+INFO: Running command line: bazel-bin/common/kernel_aarch64_abi_update.sh --print_git_commands
+ git -C /path/to/common/android commit \
+ -F /path/to/bazel-out/k8-fastbuild/bin/common/kernel_aarch64_abi_diff/git_message.txt \
+ --signoff --edit -- abi_gki_aarch64.stg
+```
+
+**NOTE**: The printed command (especially the path to git_message.txt) may
+occasionally change. Always run with `--print_git_commands` to get the current
+command for your source tree.
+
The command brings up your pre-configured text editor for git to edit the
commit message. You may edit the subject line, add additional message, and add
a bug number.
diff --git a/kleaf/docs/api_reference/hermetic_tools.md b/kleaf/docs/api_reference/hermetic_tools.md
index 6075eaa..510717e 100644
--- a/kleaf/docs/api_reference/hermetic_tools.md
+++ b/kleaf/docs/api_reference/hermetic_tools.md
@@ -107,7 +107,7 @@ _HermeticToolchainInfo (see hermetic_tools.bzl).
## hermetic_tools
<pre>
-hermetic_tools(<a href="#hermetic_tools-name">name</a>, <a href="#hermetic_tools-deps">deps</a>, <a href="#hermetic_tools-symlinks">symlinks</a>, <a href="#hermetic_tools-aliases">aliases</a>, <a href="#hermetic_tools-kwargs">kwargs</a>)
+hermetic_tools(<a href="#hermetic_tools-name">name</a>, <a href="#hermetic_tools-deps">deps</a>, <a href="#hermetic_tools-symlinks">symlinks</a>, <a href="#hermetic_tools-kwargs">kwargs</a>)
</pre>
Provide tools for a hermetic build.
@@ -120,7 +120,6 @@ Provide tools for a hermetic build.
| <a id="hermetic_tools-name"></a>name | Name of the target. | none |
| <a id="hermetic_tools-deps"></a>deps | additional dependencies. These aren't added to the `PATH`. | `None` |
| <a id="hermetic_tools-symlinks"></a>symlinks | A dictionary, where keys are labels to an executable, and values are names to the tool, separated with `:`. e.g.<br><br><pre><code>{"//label/to:toybox": "cp:realpath"}</code></pre> | `None` |
-| <a id="hermetic_tools-aliases"></a>aliases | **Deprecated; do not use.**<br><br>[nonconfigurable](https://bazel.build/reference/be/common-definitions#configurable-attributes).<br><br>List of aliases to create to refer to a `fail_rule`.<br><br>For example, if `aliases = ["cp"],` then usage of `<name>/cp` will fail.<br><br>**Note**: It is not allowed to rely on these targets. Consider using the full hermetic toolchain with [`hermetic_toolchain`](#hermetic_toolchainget) or [`hermetic_genrule`](#hermetic_genrule), etc. | `None` |
| <a id="hermetic_tools-kwargs"></a>kwargs | Additional attributes to the internal rule, e.g. [`visibility`](https://docs.bazel.build/versions/main/visibility.html). See complete list [here](https://docs.bazel.build/versions/main/be/common-definitions.html#common | none |
diff --git a/kleaf/docs/api_reference/kernel.md b/kleaf/docs/api_reference/kernel.md
index d617e70..a97c14b 100644
--- a/kleaf/docs/api_reference/kernel.md
+++ b/kleaf/docs/api_reference/kernel.md
@@ -258,11 +258,11 @@ Define an executable that creates `compile_commands.json` from a `kernel_build`.
## kernel_filegroup
<pre>
-kernel_filegroup(<a href="#kernel_filegroup-name">name</a>, <a href="#kernel_filegroup-deps">deps</a>, <a href="#kernel_filegroup-srcs">srcs</a>, <a href="#kernel_filegroup-outs">outs</a>, <a href="#kernel_filegroup-collect_unstripped_modules">collect_unstripped_modules</a>, <a href="#kernel_filegroup-config_out_dir">config_out_dir</a>,
- <a href="#kernel_filegroup-config_out_dir_files">config_out_dir_files</a>, <a href="#kernel_filegroup-ddk_module_defconfig_fragments">ddk_module_defconfig_fragments</a>, <a href="#kernel_filegroup-debug">debug</a>, <a href="#kernel_filegroup-env_setup_script">env_setup_script</a>,
- <a href="#kernel_filegroup-exec_platform">exec_platform</a>, <a href="#kernel_filegroup-gki_artifacts">gki_artifacts</a>, <a href="#kernel_filegroup-images">images</a>, <a href="#kernel_filegroup-internal_outs">internal_outs</a>, <a href="#kernel_filegroup-kasan">kasan</a>, <a href="#kernel_filegroup-kasan_generic">kasan_generic</a>,
- <a href="#kernel_filegroup-kasan_sw_tags">kasan_sw_tags</a>, <a href="#kernel_filegroup-kcsan">kcsan</a>, <a href="#kernel_filegroup-kernel_release">kernel_release</a>, <a href="#kernel_filegroup-kernel_uapi_headers">kernel_uapi_headers</a>, <a href="#kernel_filegroup-lto">lto</a>, <a href="#kernel_filegroup-module_env_archive">module_env_archive</a>,
- <a href="#kernel_filegroup-module_outs_file">module_outs_file</a>, <a href="#kernel_filegroup-modules_prepare_archive">modules_prepare_archive</a>, <a href="#kernel_filegroup-protected_modules_list">protected_modules_list</a>, <a href="#kernel_filegroup-strip_modules">strip_modules</a>,
+kernel_filegroup(<a href="#kernel_filegroup-name">name</a>, <a href="#kernel_filegroup-deps">deps</a>, <a href="#kernel_filegroup-srcs">srcs</a>, <a href="#kernel_filegroup-outs">outs</a>, <a href="#kernel_filegroup-all_module_names">all_module_names</a>, <a href="#kernel_filegroup-collect_unstripped_modules">collect_unstripped_modules</a>,
+ <a href="#kernel_filegroup-config_out_dir">config_out_dir</a>, <a href="#kernel_filegroup-config_out_dir_files">config_out_dir_files</a>, <a href="#kernel_filegroup-ddk_module_defconfig_fragments">ddk_module_defconfig_fragments</a>, <a href="#kernel_filegroup-debug">debug</a>,
+ <a href="#kernel_filegroup-env_setup_script">env_setup_script</a>, <a href="#kernel_filegroup-exec_platform">exec_platform</a>, <a href="#kernel_filegroup-gki_artifacts">gki_artifacts</a>, <a href="#kernel_filegroup-images">images</a>, <a href="#kernel_filegroup-internal_outs">internal_outs</a>, <a href="#kernel_filegroup-kasan">kasan</a>,
+ <a href="#kernel_filegroup-kasan_generic">kasan_generic</a>, <a href="#kernel_filegroup-kasan_sw_tags">kasan_sw_tags</a>, <a href="#kernel_filegroup-kcsan">kcsan</a>, <a href="#kernel_filegroup-kernel_release">kernel_release</a>, <a href="#kernel_filegroup-kernel_uapi_headers">kernel_uapi_headers</a>, <a href="#kernel_filegroup-lto">lto</a>,
+ <a href="#kernel_filegroup-module_env_archive">module_env_archive</a>, <a href="#kernel_filegroup-modules_prepare_archive">modules_prepare_archive</a>, <a href="#kernel_filegroup-protected_modules_list">protected_modules_list</a>, <a href="#kernel_filegroup-strip_modules">strip_modules</a>,
<a href="#kernel_filegroup-target_platform">target_platform</a>, <a href="#kernel_filegroup-trim_nonlisted_kmi">trim_nonlisted_kmi</a>)
</pre>
@@ -288,6 +288,7 @@ It can be used in the `base_kernel` attribute of a [`kernel_build`](#kernel_buil
| <a id="kernel_filegroup-deps"></a>deps | A list of additional labels that participates in implementing the providers.<br><br>This usually contains a list of prebuilts.<br><br>Unlike srcs, these labels are NOT added to the [`DefaultInfo`](https://docs.bazel.build/versions/main/skylark/lib/DefaultInfo.html) | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
| <a id="kernel_filegroup-srcs"></a>srcs | The list of labels that are members of this file group.<br><br>This usually contains a list of prebuilts, e.g. `vmlinux`, `Image.lz4`, `kernel-headers.tar.gz`, etc.<br><br>Not to be confused with [`kernel_srcs`](#kernel_filegroup-kernel_srcs). | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
| <a id="kernel_filegroup-outs"></a>outs | Keys: from `_kernel_build.outs`. Values: path under `$OUT_DIR`. | <a href="https://bazel.build/rules/lib/dict">Dictionary: Label -> String</a> | optional | `{}` |
+| <a id="kernel_filegroup-all_module_names"></a>all_module_names | `module_outs` and `module_implicit_outs` of the original [`kernel_build`](#kernel_build) target. | List of strings | optional | `[]` |
| <a id="kernel_filegroup-collect_unstripped_modules"></a>collect_unstripped_modules | See [`kernel_build.collect_unstripped_modules`](#kernel_build-collect_unstripped_modules).<br><br>Unlike `kernel_build`, this has default value `True` because [`kernel_abi`](#kernel_abi) sets [`define_abi_targets`](#kernel_abi-define_abi_targets) to `True` by default, which in turn sets `collect_unstripped_modules` to `True` by default. | Boolean | optional | `True` |
| <a id="kernel_filegroup-config_out_dir"></a>config_out_dir | Directory to support `kernel_config` | <a href="https://bazel.build/concepts/labels">Label</a> | optional | `None` |
| <a id="kernel_filegroup-config_out_dir_files"></a>config_out_dir_files | Files in `config_out_dir` | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
@@ -306,7 +307,6 @@ It can be used in the `base_kernel` attribute of a [`kernel_build`](#kernel_buil
| <a id="kernel_filegroup-kernel_uapi_headers"></a>kernel_uapi_headers | The label pointing to `kernel-uapi-headers.tar.gz`.<br><br>This attribute should be set to the `kernel-uapi-headers.tar.gz` artifact built by the [`kernel_build`](#kernel_build) macro if the `kernel_filegroup` rule were a `kernel_build`.<br><br>Setting this attribute allows [`merged_kernel_uapi_headers`](#merged_kernel_uapi_headers) to work properly when this `kernel_filegroup` is set to the `base_kernel`.<br><br>For example: <pre><code>kernel_filegroup(&#10; name = "kernel_aarch64_prebuilts",&#10; srcs = [&#10; "vmlinux",&#10; # ...&#10; ],&#10; kernel_uapi_headers = "kernel-uapi-headers.tar.gz",&#10;)&#10;&#10;kernel_build(&#10; name = "tuna",&#10; base_kernel = ":kernel_aarch64_prebuilts",&#10; # ...&#10;)&#10;&#10;merged_kernel_uapi_headers(&#10; name = "tuna_merged_kernel_uapi_headers",&#10; kernel_build = "tuna",&#10; # ...&#10;)</code></pre> | <a href="https://bazel.build/concepts/labels">Label</a> | optional | `None` |
| <a id="kernel_filegroup-lto"></a>lto | - | String | optional | `"default"` |
| <a id="kernel_filegroup-module_env_archive"></a>module_env_archive | Archive from `kernel_build.pack_module_env` that contains necessary files to build external modules. | <a href="https://bazel.build/concepts/labels">Label</a> | optional | `None` |
-| <a id="kernel_filegroup-module_outs_file"></a>module_outs_file | A file containing `module_outs` of the original [`kernel_build`](#kernel_build) target. | <a href="https://bazel.build/concepts/labels">Label</a> | required | |
| <a id="kernel_filegroup-modules_prepare_archive"></a>modules_prepare_archive | Archive from `modules_prepare` | <a href="https://bazel.build/concepts/labels">Label</a> | optional | `None` |
| <a id="kernel_filegroup-protected_modules_list"></a>protected_modules_list | - | <a href="https://bazel.build/concepts/labels">Label</a> | optional | `None` |
| <a id="kernel_filegroup-strip_modules"></a>strip_modules | See [`kernel_build.strip_modules`](#kernel_build-strip_modules). | Boolean | optional | `False` |
diff --git a/kleaf/docs/api_reference/kernel_prebuilt_ext.md b/kleaf/docs/api_reference/kernel_prebuilt_ext.md
index cc7f46f..68aef2a 100644
--- a/kleaf/docs/api_reference/kernel_prebuilt_ext.md
+++ b/kleaf/docs/api_reference/kernel_prebuilt_ext.md
@@ -8,8 +8,7 @@ Extension that helps building Android kernel and drivers.
<pre>
kernel_prebuilt_ext = use_extension("@kleaf//build/kernel/kleaf:kernel_prebuilt_ext.bzl", "kernel_prebuilt_ext")
-kernel_prebuilt_ext.declare_kernel_prebuilts(<a href="#kernel_prebuilt_ext.declare_kernel_prebuilts-name">name</a>, <a href="#kernel_prebuilt_ext.declare_kernel_prebuilts-auto_download_config">auto_download_config</a>, <a href="#kernel_prebuilt_ext.declare_kernel_prebuilts-download_configs">download_configs</a>,
- <a href="#kernel_prebuilt_ext.declare_kernel_prebuilts-local_artifact_path">local_artifact_path</a>, <a href="#kernel_prebuilt_ext.declare_kernel_prebuilts-target">target</a>)
+kernel_prebuilt_ext.declare_kernel_prebuilts(<a href="#kernel_prebuilt_ext.declare_kernel_prebuilts-name">name</a>, <a href="#kernel_prebuilt_ext.declare_kernel_prebuilts-local_artifact_path">local_artifact_path</a>, <a href="#kernel_prebuilt_ext.declare_kernel_prebuilts-target">target</a>)
</pre>
Extension that manages what prebuilts Kleaf should use.
@@ -28,8 +27,6 @@ Declares a repo that contains kernel prebuilts
| Name | Description | Type | Mandatory | Default |
| :------------- | :------------- | :------------- | :------------- | :------------- |
| <a id="kernel_prebuilt_ext.declare_kernel_prebuilts-name"></a>name | name of repository | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
-| <a id="kernel_prebuilt_ext.declare_kernel_prebuilts-auto_download_config"></a>auto_download_config | If `True`, infer download configs from `target`. | Boolean | optional | `False` |
-| <a id="kernel_prebuilt_ext.declare_kernel_prebuilts-download_configs"></a>download_configs | A JSON dictionary that configure the list of files to download.<br><br>Key: local file name.<br><br>Value: A dictionary with the following keys: * `mandatory`: Whether the files in `outs_mapping` is mandatory. If mandatory, failure to download the file results in a build failure. * `remote_filename_fmt`: remote file name format string, with the following anchors: * {build_number} * {target} | String | optional | `""` |
| <a id="kernel_prebuilt_ext.declare_kernel_prebuilts-local_artifact_path"></a>local_artifact_path | Directory to local artifacts.<br><br>If set, `artifact_url_fmt` is ignored.<br><br>Only the root module may call `declare()` with this attribute set.<br><br>If relative, it is interpreted against workspace root.<br><br>If absolute, this is similar to setting `artifact_url_fmt` to `file://<absolute local_artifact_path>/{filename}`, but avoids using `download()`. Files are symlinked not copied, and `--config=internet` is not necessary. | String | optional | `""` |
| <a id="kernel_prebuilt_ext.declare_kernel_prebuilts-target"></a>target | Name of the build target as identified by the remote build server.<br><br>This attribute has two effects:<br><br>* Replaces the `{target}` anchor in `artifact_url_fmt`. If `artifact_url_fmt` does not have the `{target}` anchor, this has no effect.<br><br>* If `auto_download_config` is `True`, `download_config` and `mandatory` is inferred from a list of known configs keyed on `target`. | String | optional | `"kernel_aarch64"` |
diff --git a/kleaf/docs/bzlmod.md b/kleaf/docs/bzlmod.md
index 0e21198..513de6f 100644
--- a/kleaf/docs/bzlmod.md
+++ b/kleaf/docs/bzlmod.md
@@ -2,14 +2,23 @@
## Migrate to bzlmod
-### Use @kleaf as root module
+### Use @kleaf as dependent module (recommended)
+
+If you are setting up a new workspace, it is recommended
+to use `@kleaf` as a dependent module. See [Setting up DDK workspace](ddk/workspace.md).
+
+### Use @kleaf as root module (legacy)
+
+If you are migrating from non-Bzlmod, `WORKSPACE`-style
+setup, this may be the easier option because it resembles the directory
+structure of `WORKSPACE`-style setup.
Set up your repo manifest to conform with the following filesystem layout.
```text
<workspace_root>/
|- WORKSPACE -> build/kernel/kleaf/bazel.WORKSPACE # Note 1
- |- WORKSPACE.bzlmod -> build/kernel/kleaf/bzlmod/bazel.WORKSPACE.bzlmod
+ |- WORKSPACE.bzlmod -> build/kernel/kleaf/bzlmod/bazel.WORKSPACE.bzlmod # Note 1
|- MODULE.bazel -> build/kernel/kleaf/bzlmod/bazel.MODULE.bazel
|- build/
| `- kernel/
@@ -20,19 +29,28 @@ Set up your repo manifest to conform with the following filesystem layout.
`- <other external repositories> # Note 3
```
-**Note 1**: The root `WORKSPACE` file is present to support pre-bzlmod builds.
-After bzlmod migration, this file may be removed.
+**Note 1**: The root `WORKSPACE` and `WORKSPACE.bzlmod` files are present to
+support switching between bzlmod and non-bzlmod builds. During migration to
+bzlmod, you may have an non-empty `WORKSPACE.bzlmod` file for dependencies
+that has not been migrated to bzlmod. After all dependencies and the
+root module migrated to Bzlmod, both files may be removed.
+
+See
+[hybrid mode for gradual migration](https://bazel.build/external/migration#hybrid-mode)
+for details.
**Note 2**: If `build.config.constants` exists elsewhere other than `common/`,
create the symlink `common/build.config.constants` to the file. This may be
done with `<linkfile>` in your repo manifest.
**Note 3**: A list of external repositories are required for bzlmod to work.
-For the up-to-date list, refer to the repo manifest of the ACK branch.
-
-### Use @kleaf as dependency
+For the up-to-date list, refer to the repo manifest of the correspoding ACK
+branch.
-This will be supported in the near future. Stay tuned!
+See example manifests for
+[Pixel 6 and Pixel 6 Pro](https://android.googlesource.com/kernel/manifest/+/refs/heads/gs-android-gs-raviole-mainline/default.xml)
+and for
+[Android Common Kernel and Cloud Android Kernel](https://android.googlesource.com/kernel/manifest/+/refs/heads/common-android-mainline/default.xml).
## Versions of dependent modules
diff --git a/kleaf/docs/canary.md b/kleaf/docs/canary.md
index 624df94..807caa3 100644
--- a/kleaf/docs/canary.md
+++ b/kleaf/docs/canary.md
@@ -11,3 +11,12 @@ As of now, `--config=canary` enables:
- `--toolchain_from_sources`: Build (some) build time dependencies from
sources, like `toybox`.
+
+To opt-in a local workspace without having to pass the command line flag for
+every invocation, add to `user.bazelrc`:
+
+```text
+# Opt into future features of Kleaf and the DDK.
+build --config=canary
+```
+
diff --git a/kleaf/docs/ddk/main.md b/kleaf/docs/ddk/main.md
index 1f6ad8c..7dc80e3 100644
--- a/kleaf/docs/ddk/main.md
+++ b/kleaf/docs/ddk/main.md
@@ -28,6 +28,8 @@ The virtual device serves as a reference implementation for DDK modules. See
## Read more
+[Setting up workspace](workspace.md)
+
[Rules and macros](rules.md)
[Using headers from the common kernel](common_headers.md)
diff --git a/kleaf/docs/ddk/workspace.md b/kleaf/docs/ddk/workspace.md
new file mode 100644
index 0000000..3cb1c2a
--- /dev/null
+++ b/kleaf/docs/ddk/workspace.md
@@ -0,0 +1,241 @@
+# Setting up a DDK Workspace
+
+## Use the bootstrapping script
+
+The bootstrapping script may be found in the
+[kernel/build/bootstrap](https://android.googlesource.com/kernel/build/bootstrap)
+project. The following one-line command downloads and executes it:
+
+```shell
+$ curl https://android.googlesource.com/kernel/build/bootstrap/+/refs/heads/main/init.py?format=TEXT | base64 --decode | python3 - [flags]
+```
+
+This command will be referred to as `python3 init.py` below.
+
+### Typical usage
+
+In the examples below, `/path/to/ddk/workspace` is the path to the
+DDK workspace the script will populate. Usually, the script generates at
+least the following in the directory:
+* [`MODULE.bazel`](#root-modulebazel)
+* [`device.bazelrc`](#devicebazelrc)
+* `tools/bazel` symlink
+
+#### Local sources
+
+Populate DDK workspace against a local, full
+checkout of ACK source tree at `/path/to/ddk/workspace/external/kleaf`:
+
+```shell
+$ python init.py --local \
+ --ddk_workspace /path/to/ddk/workspace \
+ --kleaf_repo /path/to/ddk/workspace/external/kleaf
+```
+
+#### Local sources + prebuilts
+
+Populate DDK workspace against a local checkout of
+Kleaf projects at `/path/to/ddk/workspace/external/kleaf` and prebuilts at
+`/path/to/ddk/workspace/prebuilts/kernel`:
+
+```shell
+$ python init.py --local \
+ --ddk_workspace /path/to/ddk/workspace \
+ --kleaf_repo /path/to/ddk/workspace/external/kleaf \
+ --prebuilts_dir /path/to/ddk/workspace/prebuilts/kernel
+```
+
+Other usages will be added here soon. Stay tuned!
+
+## Root MODULE.bazel
+
+**Note**: The content below is automatically generated if you are using
+the [bootstrapping script](#use-the-bootstrapping-script).
+
+### @kleaf dependency
+
+The `MODULE.bazel` file of the root module should declare a dependency
+to `@kleaf`. In this example, the module is checked out at
+`external/kleaf` relative to the workspace root:
+
+```python
+bazel_dep(name = "kleaf")
+local_path_override(
+ module_name = "kleaf",
+ path = "external/kleaf", # or absolute path
+)
+```
+
+You may now use rules in the `@kleaf` repository. For example, in `BUILD.bazel`:
+
+```python
+load("@kleaf//build/kernel/kleaf:kernel.bzl", "ddk_module")
+```
+
+If the full kernel source tree exists in `external/kleaf/common`, you may also
+use the kernel built from source. For example:
+
+```python
+ddk_module(
+ name = "mymodule",
+ kernel_build = "@kleaf//common:kernel_aarch64",
+ deps = [
+ "@kleaf//common:all_headers_aarch64",
+ ],
+ # other attrs
+)
+```
+
+### Declare prebuilts repository
+
+The `MODULE.bazel` file of the root module may declare a repository containing
+kernel prebuilts, if they exist. For example:
+
+```python
+kernel_prebuilt_ext = use_extension(
+ "@kleaf//build/kernel/kleaf:kernel_prebuilt_ext.bzl",
+ "kernel_prebuilt_ext",
+)
+kernel_prebuilt_ext.declare_kernel_prebuilts(
+ name = "gki_prebuilts", # name of your choice
+ local_artifact_path = "prebuilts/kernel", # or absolute path
+ # other attrs
+)
+```
+
+See
+[kernel_prebuilt_ext.declare_kernel_prebuilts](../api_reference/kernel_prebuilt_ext.md) for its API.
+**Note**: the API may undergo changes when this feature is in experiemental
+stage.
+
+You may now use `@gki_prebuilts//kernel_aarch64` in the `kernel_build`
+attributes of various rules. For example:
+
+```python
+ddk_module(
+ name = "mymodule",
+ kernel_build = "@gki_prebuilts//kernel_aarch64",
+ # other attrs
+)
+```
+
+**Advanced usage**: If you have both `@kleaf//common:kernel_aarch64` and
+`@gki_prebuilts//kernel_aarch64`, you may define flags to switch between
+them. For example:
+
+```python
+# mypackage/BUILD.bazel
+label_flag(
+ name = "base_kernel",
+ build_setting_default = "@gki_prebuilts//kernel_aarch64",
+)
+
+ddk_module(
+ name = "mymodule",
+ kernel_build = ":base_kernel",
+ # other attrs
+)
+```
+
+```shell
+# Build against prebuilt kernel
+$ tools/bazel build //mypackage:mymodule
+
+# Build against kernel built from sources
+$ tools/bazel build --//mypackage:base_kernel=@kleaf//common:kernel_aarch64 \
+ //mypackage:mymodule
+```
+
+You may add flag aliases and configs to `device.bazelrc`. See
+[.bazelrc files](../impl.md#bazelrc-files).
+
+### Transitive dependencies
+
+This refers to the list of dependencies of `@kleaf` that are not
+directly needed by the root module, plus the Bazel Central Registry.
+
+#### Download from the Internet
+
+To rely on the Internet for transitive dependencies from the `@kleaf`,
+add [`--config=internet` flag](../network.md). You may add the flag to
+[`device.bazelrc`](#devicebazelrc).
+
+#### True local builds
+
+To build in an air-gapped environment without Internet access, all
+dependencies must be vendored locally.
+
+##### Registry
+
+You may checkout a registry from one of the following:
+
+* [Bazel Central Registry (BCR)](https://bcr.bazel.build/)
+* [AOSP mirror of BCR](https://android.googlesource.com/platform/external/bazelbuild-bazel-central-registry).
+ This may be slightly outdated.
+
+Assuming that you have checked out the registry somewhere on your disk,
+you may specify the `--registry` flag. For example, in
+[`device.bazelrc`](#devicebazelrc):
+
+```text
+common --registry=file://%workspace%/external/bazelbuild-bazel-central-registry
+```
+
+##### Dependent modules
+
+You may collect the list of all dependent modules with the following command:
+
+```shell
+$ tools/bazel mod graph --include_builtin
+```
+
+For details, see [bazel mod command](https://bazel.build/external/mod-command).
+
+Among them, the list of modules that `@kleaf` module depends on may be
+found by looking at `external/kleaf/MODULE.bazel`, assuming that the module is
+located at `external/kleaf`. Its content may also be found in
+[this file](../../bzlmod/bazel.MODULE.bazel).
+
+Then, declare `local_path_override()` for each dependency and transitive
+dependency in your **root `MODULE.bazel`**. You may also use other
+[non-registry overrides](https://bazel.build/external/module#non-registry_overrides)
+if applicable. For example:
+
+```python
+local_path_override(
+ module_name = "rules_cc",
+ path = "external/bazelbuild-rules_cc",
+)
+```
+
+**Note**: `local_path_override()` in dependent modules (e.g. the ones
+in `@kleaf`) has no effect. `local_path_override()` is only effective
+when used at the root MODULE.bazel. See
+[documentation for local_path_override()](https://bazel.build/rules/lib/globals/module#local_path_override).
+
+**Note**: If `path` is relative, it is interpreted against the workspace root.
+In the above example, `@rules_cc` is found at
+`/path/to/ddk/workspace/external/bazelbuild-rules_cc`.
+
+## device.bazelrc
+
+The `device.bazelrc` file in your workspace root may contain the following
+lines. See [.bazelrc files](../impl.md#bazelrc-files) for details.
+
+Bazel may fetch dependencies from the Internet if there are no
+`local_path_override`
+declarations for the dependency. The following
+allows Internet access. For details, see [Internet access](../network.md).
+
+```text
+common --config=internet
+```
+
+Kleaf sets `--registry` by default; see
+[bzlmod.bazelrc](../../bazelrc/bzlmod.bazelrc). If the registry is not checked
+out at `external/bazelbuild-bazel-central-registry` under the workspace root,
+override its value. For example:
+
+```text
+common:bzlmod --registry=file:///path/to/bcr
+```
diff --git a/kleaf/docs/download_prebuilt.md b/kleaf/docs/download_prebuilt.md
index 05f201c..2e46e12 100644
--- a/kleaf/docs/download_prebuilt.md
+++ b/kleaf/docs/download_prebuilt.md
@@ -1,7 +1,9 @@
-# Build against downloaded prebuilt GKI
+# Build against downloaded prebuilt GKI (deprecated)
-**WARNING**: Building against downloaded prebuilts is currently experimental. If
-you encounter any errors, see [common errors](#common-errors).
+**WARNING**: Contents in this page are deprecated, including
+the `--use_prebuilt_gki` flag and `*_download_or_build` targets.
+To build against downloaded prebuilt GKI with bzlmod, see
+[Setting up DDK workspace](ddk/workspace.md#declare-prebuilts-repository).
## Step 1: Replace reference to GKI targets with downloaded targets
diff --git a/kleaf/docs/impl.md b/kleaf/docs/impl.md
index aa57a35..8095366 100644
--- a/kleaf/docs/impl.md
+++ b/kleaf/docs/impl.md
@@ -5,27 +5,14 @@ You may view the documentation for the following Bazel rules and macros on
Android Continuous Integration. See
[API Reference and Documentation for all rules](api_reference.md).
-## Manifest changes
-
-Make the following changes to the kernel manifest to support Bazel build.
-
-* Add `tools/bazel` symlink to `build/kernel/kleaf/bazel.sh`
-* Add `WORKSPACE` symlink to `build/kernel/kleaf/bazel.WORKSPACE`
- * See [workspace.md](workspace.md) for building with a custom workspace.
-* Dependent repositories for Bazel, including:
- * [prebuilts/bazel/linux-x86\_64](https://android.googlesource.com/platform/prebuilts/bazel/linux-x86_64/)
- * [prebuilts/jdk/jdk11](https://android.googlesource.com/platform/prebuilts/jdk/jdk11/)
- * [build/bazel\_common\_rules](https://android.googlesource.com/platform/build/bazel_common_rules/)
- * [external/bazel-skylib](https://android.googlesource.com/platform/external/bazel-skylib/)
- * [external/stardoc](https://android.googlesource.com/platform/external/stardoc/)
-
-Example for Pixel 2021:
-
-[https://android.googlesource.com/kernel/manifest/+/refs/heads/gs-android-gs-raviole-mainline/default.xml](https://android.googlesource.com/kernel/manifest/+/refs/heads/gs-android-gs-raviole-mainline/default.xml)
-
-Example for Android Common Kernel and Cloud Android kernel:
-
-[https://android.googlesource.com/kernel/manifest/+/refs/heads/common-android-mainline/default.xml](https://android.googlesource.com/kernel/manifest/+/refs/heads/common-android-mainline/default.xml)
+## Setting up the workspace
+
+* (Recommended) To use Kleaf tooling as a dependent Bazel module, see
+ [Setting up DDK workspace](ddk/workspace.md).
+* To use Kleaf tooling as the root Bazel module, see
+ [Use @kleaf as root module (legacy)](bzlmod.md#use-kleaf-as-root-module-legacy).
+* Building without Bzlmod is deprecated and will not be supported in
+ Android 16 and above.
## Building a custom kernel
diff --git a/kleaf/docs/workspace.md b/kleaf/docs/workspace.md
index 93b1f5b..b88fcb7 100644
--- a/kleaf/docs/workspace.md
+++ b/kleaf/docs/workspace.md
@@ -2,10 +2,18 @@
## Using bzlmod (recommended)
-See [bzlmod support in Kleaf.](bzlmod.md)
+Recommended: If you are using `@kleaf` as a dependent module,
+see [Setting up DDK workspace](ddk/workspace.md).
+
+If you are using `@kleaf` as the root module, see
+[bzlmod support in Kleaf.](bzlmod.md)
## Legacy `WORKSPACE` support
+**Warning**: Support for non-Bzlmod builds are deprecated and will be
+removed in Android 16 branches. Information below are
+outdated and not supported with Bzlmod enabled.
+
### Using the provided `WORKSPACE` file
Usually, the common kernel is checked out to `common/`. In this case, it is
diff --git a/kleaf/hermetic_tools.bzl b/kleaf/hermetic_tools.bzl
index 13aa70d..b5093a2 100644
--- a/kleaf/hermetic_tools.bzl
+++ b/kleaf/hermetic_tools.bzl
@@ -57,41 +57,66 @@ def _get_single_file(ctx, target):
))
return files_list[0]
-def _handle_hermetic_symlinks(ctx):
- hermetic_symlinks_dict = {}
- for actual_target, tool_names in ctx.attr.symlinks.items():
- for tool_name in tool_names.split(":"):
- out = ctx.actions.declare_file("{}/{}".format(ctx.attr.name, tool_name))
- target_file = _get_single_file(ctx, actual_target)
- ctx.actions.symlink(
- output = out,
- target_file = target_file,
- is_executable = True,
- progress_message = "Creating symlinks to in-tree tools {}/{}".format(
- ctx.label,
- tool_name,
- ),
- )
- hermetic_symlinks_dict[tool_name] = out
-
- return hermetic_symlinks_dict
+def _handle_tool(ctx, tool_name, actual_target):
+ out = ctx.actions.declare_file("{}/{}".format(ctx.attr.name, tool_name))
+ target_file = _get_single_file(ctx, actual_target)
+
+ if tool_name not in ctx.attr.extra_args:
+ ctx.actions.symlink(
+ output = out,
+ target_file = target_file,
+ is_executable = True,
+ progress_message = "Creating symlink to in-tree tool {}/{}".format(
+ ctx.label,
+ tool_name,
+ ),
+ )
+ return [out]
+
+ internal_symlink = ctx.actions.declare_file("{}/kleaf_internal_do_not_use/{}".format(ctx.attr.name, tool_name))
+ ctx.actions.symlink(
+ output = internal_symlink,
+ target_file = target_file,
+ is_executable = True,
+ progress_message = "Creating internal symlink to in-tree tool {}/{}".format(
+ ctx.label,
+ tool_name,
+ ),
+ )
-def _hermetic_tools_impl(ctx):
- deps = [] + ctx.files.deps
+ ctx.actions.symlink(
+ output = out,
+ target_file = ctx.executable._arg_wrapper,
+ is_executable = True,
+ progress_message = "Creating symlink to in-tree tool {}/{}".format(
+ ctx.label,
+ tool_name,
+ ),
+ )
+ extra_args = "\n".join(ctx.attr.extra_args[tool_name])
+ extra_args_file = ctx.actions.declare_file("{}/kleaf_internal_do_not_use/{}_args.txt".format(ctx.attr.name, tool_name))
+ ctx.actions.write(extra_args_file, extra_args)
+ return [out, internal_symlink, extra_args_file]
+
+def _handle_hermetic_symlinks(ctx, symlinks_attr):
all_outputs = []
+ for actual_target, tool_names in symlinks_attr.items():
+ for tool_name in tool_names.split(":"):
+ tool_outs = _handle_tool(ctx, tool_name, actual_target)
+ all_outputs.extend(tool_outs)
- hermetic_outs_dict = _handle_hermetic_symlinks(ctx)
+ return all_outputs
- hermetic_outs = hermetic_outs_dict.values()
- all_outputs += hermetic_outs
- deps += hermetic_outs
+def _hermetic_tools_impl(ctx):
+ all_outputs = _handle_hermetic_symlinks(ctx, ctx.attr.symlinks)
if ctx.attr._disable_symlink_source[BuildSettingInfo].value:
transitive_deps = []
else:
transitive_deps = [target.files for target in ctx.attr.symlinks]
- deps_depset = depset(deps, transitive = transitive_deps)
+ transitive_deps += [target.files for target in ctx.attr.deps]
+ transitive_deps.append(ctx.attr._arg_wrapper.files)
fail_hard = """
# error on failures
@@ -125,76 +150,58 @@ def _hermetic_tools_impl(ctx):
""".format(path = hermetic_base_short)
hermetic_toolchain_info = _HermeticToolchainInfo(
- deps = deps_depset,
+ deps = depset(all_outputs, transitive = transitive_deps),
setup = setup,
run_setup = run_setup,
run_additional_setup = run_additional_setup,
)
- default_info_files = [
- file
- for file in all_outputs
- if "kleaf_internal_do_not_use" not in file.path
- ]
-
infos = [
- DefaultInfo(files = depset(default_info_files)),
+ DefaultInfo(files = depset(all_outputs)),
platform_common.ToolchainInfo(
hermetic_toolchain_info = hermetic_toolchain_info,
),
- OutputGroupInfo(
- **{file.basename: depset([file]) for file in all_outputs}
- ),
+ OutputGroupInfo(**{
+ file.basename: depset([file])
+ for file in all_outputs
+ if "kleaf_internal_do_not_use" not in file.path
+ }),
]
return infos
-_hermetic_tools = rule(
+hermetic_tools = rule(
implementation = _hermetic_tools_impl,
- doc = "",
+ doc = "Provide tools for a hermetic build.",
attrs = {
- "deps": attr.label_list(doc = "Additional_deps", allow_files = True),
+ "deps": attr.label_list(
+ doc = "additional dependencies. These aren't added to the `PATH`.",
+ allow_files = True,
+ ),
"symlinks": attr.label_keyed_string_dict(
- doc = "symlinks to labels",
allow_files = True,
+ doc = """A dictionary, where keys are labels to an executable, and
+ values are names to the tool, separated with `:`. e.g.
+
+ ```
+ {"//label/to:toybox": "cp:realpath"}
+ ```
+ """,
+ ),
+ "extra_args": attr.string_list_dict(
+ doc = """Keys are names to the tool (see `symlinks`). Values are
+ extra arguments added to the tool at the end.""",
),
"_disable_symlink_source": attr.label(
default = "//build/kernel/kleaf:incompatible_disable_hermetic_tools_symlink_source",
),
+ "_arg_wrapper": attr.label(
+ default = "//build/kernel/kleaf/impl:arg_wrapper",
+ executable = True,
+ # Prevent inadvertent exec transition that messes up the
+ # path calculation. Exec transition needs to be done on the whole
+ # hermetic_tools target.
+ cfg = "target",
+ ),
},
)
-
-def hermetic_tools(
- name,
- deps = None,
- symlinks = None,
- **kwargs):
- """Provide tools for a hermetic build.
-
- Args:
- name: Name of the target.
- symlinks: A dictionary, where keys are labels to an executable, and
- values are names to the tool, separated with `:`. e.g.
-
- ```
- {"//label/to:toybox": "cp:realpath"}
- ```
- deps: additional dependencies. These aren't added to the `PATH`.
- **kwargs: Additional attributes to the internal rule, e.g.
- [`visibility`](https://docs.bazel.build/versions/main/visibility.html).
- See complete list
- [here](https://docs.bazel.build/versions/main/be/common-definitions.html#common
- """
-
- if symlinks == None:
- symlinks = {}
-
- if deps == None:
- deps = []
-
- _hermetic_tools(
- name = name,
- deps = deps,
- symlinks = symlinks,
- **kwargs
- )
diff --git a/kleaf/impl/BUILD.bazel b/kleaf/impl/BUILD.bazel
index 51964f9..e8cd6fc 100644
--- a/kleaf/impl/BUILD.bazel
+++ b/kleaf/impl/BUILD.bazel
@@ -407,6 +407,7 @@ kernel_platform_toolchain(
kernel_platform_toolchain(
name = "kernel_toolchain_exec",
+ visibility = ["//build/kernel/kleaf/tests:__subpackages__"],
deps = [
"//prebuilts/kernel-build-tools:linux_x86_imported_libs",
],
@@ -485,3 +486,9 @@ python_runtime_files(
name = "python_runtime_files",
visibility = ["//build/kernel:__pkg__"],
)
+
+cc_binary(
+ name = "arg_wrapper",
+ srcs = ["arg_wrapper.cpp"],
+ visibility = ["//build/kernel:__pkg__"],
+)
diff --git a/kleaf/impl/abi/abi_stgdiff.bzl b/kleaf/impl/abi/abi_stgdiff.bzl
index 4c0106c..1b048ef 100644
--- a/kleaf/impl/abi/abi_stgdiff.bzl
+++ b/kleaf/impl/abi/abi_stgdiff.bzl
@@ -77,7 +77,9 @@ EOF
echo "INFO: $(cat {error_msg_file})"
elif [[ $rc == {change_code} ]]; then
echo "{log_level}: ABI DIFFERENCES HAVE BEEN DETECTED!" >&2
- echo "{log_level}: $(cat {short_report})" >&2
+ echo "{log_level}: Report summary:" >&2
+ echo >&2
+ cat {short_report} >&2
else
echo "ERROR: $(cat {error_msg_file})" >&2
echo "WARNING: exit code is not checked. 'tools/bazel run {label}' to check the exit code." >&2
@@ -116,8 +118,10 @@ EOF
if [[ $rc == 0 ]]; then
echo "INFO: $(cat {error_msg_file})"
elif [[ $rc == 4 ]]; then
- echo "{log_level}: ABI DIFFERENCES HAVE BEEN DETECTED!"
- echo "{log_level}: $(cat {short_report})"
+ echo "{log_level}: ABI DIFFERENCES HAVE BEEN DETECTED!" >&2
+ echo "{log_level}: Report summary:" >&2
+ echo >&2
+ cat {short_report} >&2
else
echo "ERROR: $(cat {error_msg_file})" >&2
fi
diff --git a/kleaf/impl/abi/abi_update.bzl b/kleaf/impl/abi/abi_update.bzl
index 18306c7..11e60f1 100644
--- a/kleaf/impl/abi/abi_update.bzl
+++ b/kleaf/impl/abi/abi_update.bzl
@@ -14,6 +14,7 @@
"""When `bazel run`, updates an ABI definition."""
+load("@bazel_skylib//lib:paths.bzl", "paths")
load(":hermetic_toolchain.bzl", "hermetic_toolchain")
visibility("//build/kernel/kleaf/...")
@@ -22,37 +23,31 @@ def _abi_update_impl(ctx):
hermetic_tools = hermetic_toolchain.get(ctx)
script = """
- # run_additional_setup keeps the original PATH to host tools at the
- # end of PATH. This is intentionally not hermetic and uses git
- # from the host machine.
- {semi_hermetic_setup}
-
- # nodiff_update is self-contained and hermetic.
+ {hermetic_setup}
{nodiff_update}
- # Use the semi-hermetic environment to execute git commands
- # Create git commit if requested
if [[ $1 == "--commit" ]]; then
- real_abi_def="$(realpath {abi_definition})"
- git -C $(dirname ${{real_abi_def}}) add $(basename ${{real_abi_def}})
- git -C $(dirname ${{real_abi_def}}) commit -F $(realpath {git_message})
+ echo "WARNING: --commit is deprecated. Please add {abi_definition} and commit manually." >&2
+ echo " You may use --print_git_commands to print sample git commands to run." >&2
fi
- # Re-instate a hermetic environment
- {hermetic_setup}
- {diff}
- if [[ $1 == "--commit" ]]; then
- echo
- echo "INFO: git commit created. Execute the following to edit the commit message:"
- echo " git -C $(dirname $(rootpath {abi_definition})) commit --amend"
+ if [[ $1 == "--commit" ]] || [[ $1 == "--print_git_commands" ]]; then
+ echo " git -C ${{BUILD_WORKSPACE_DIRECTORY}}/{abi_definition_dir} commit \\\\"
+ echo " -F ${{BUILD_WORKSPACE_DIRECTORY}}/{git_message} \\\\"
+ echo " --signoff --edit -- {abi_definition_name}"
fi
+
+ {diff}
""".format(
hermetic_setup = hermetic_tools.run_setup,
- semi_hermetic_setup = hermetic_tools.run_additional_setup,
nodiff_update = ctx.executable.nodiff_update.short_path,
abi_definition = ctx.file.abi_definition_stg.short_path,
- git_message = ctx.file.git_message.short_path,
diff = ctx.executable.diff.short_path,
+ # Use .path because these are displayed to the user relative
+ # to BUILD_WORKSPACE_DIRECTORY
+ abi_definition_dir = paths.dirname(ctx.file.abi_definition_stg.path),
+ abi_definition_name = paths.basename(ctx.file.abi_definition_stg.path),
+ git_message = ctx.file.git_message.path,
)
executable = ctx.actions.declare_file("{}.sh".format(ctx.attr.name))
diff --git a/kleaf/impl/arg_wrapper.cpp b/kleaf/impl/arg_wrapper.cpp
new file mode 100644
index 0000000..ee9c95c
--- /dev/null
+++ b/kleaf/impl/arg_wrapper.cpp
@@ -0,0 +1,110 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Helper wrapper for hermetic tools to wrap arguments.
+//
+// This roughly equivalent to:
+// 1. readlink /proc/self/exe, then dirname multiple times to determine the path
+// internal_dir =
+// <execroot>/build/kernel/hermetic-tools/kleaf_internal_do_not_use
+// 2. tool_name = basename($0)
+// 3. call <internal_dir>/<tool_name> $@ \\
+// $(cat <internal_dir>/<tool_name>_args.txt)
+//
+// This is a C++ binary instead of a shell / Python script so that
+// /proc/self/exe is a proper anchor to find internal_dir. If this were a
+// script, /proc/self/exe would be the path to the interpreter.
+// This also avoids using any hermetic tools in order to determine the path to
+// them.
+
+#include <linux/limits.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include <filesystem>
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <vector>
+
+namespace {
+
+// <execroot>/build/kernel/hermetic-tools/kleaf_internal_do_not_use
+std::filesystem::path get_kleaf_internal_dir() {
+ std::error_code ec;
+ auto my_path = std::filesystem::read_symlink("/proc/self/exe", ec);
+ if (ec.value() != 0) {
+ std::cerr << "ERROR: read_symlink /proc/self/exe: " << ec.message()
+ << std::endl;
+ exit(EX_SOFTWARE);
+ }
+ return my_path.parent_path().parent_path().parent_path() / "hermetic-tools" /
+ "kleaf_internal_do_not_use";
+}
+
+// Loads <tool_name>_args.txt from hermetic_tools.extra_args
+std::vector<std::string> load_arg_file(const std::filesystem::path& path) {
+ std::ifstream ifs(path);
+ if (!ifs) {
+ int saved_errno = errno;
+ std::cerr << "Unable to open " << path << ": " << strerror(saved_errno)
+ << std::endl;
+ exit(EX_SOFTWARE);
+ }
+ std::vector<std::string> args;
+ for (std::string arg; std::getline(ifs, arg);) {
+ args.push_back(arg);
+ }
+ return args;
+}
+
+} // namespace
+
+int main(int argc, char* argv[]) {
+ auto internal_dir = get_kleaf_internal_dir();
+
+ if (argc < 1) {
+ std::cerr << "ERROR: argc == " << argc << " < 1" << std::endl;
+ return EX_SOFTWARE;
+ }
+ std::string tool_name(std::filesystem::path(argv[0]).filename());
+
+ // The actual executable we are going to call. Cast to string to use
+ // in new_argv.
+ std::string real_executable = internal_dir / tool_name;
+
+ std::vector<char*> new_argv;
+ new_argv.push_back(real_executable.data());
+
+ for (int i = 1; i < argc; i++) {
+ new_argv.push_back(argv[i]);
+ }
+
+ auto extra_args_file = internal_dir / (tool_name + "_args.txt");
+ auto preset_args = load_arg_file(extra_args_file);
+ for (auto& preset_arg : preset_args) {
+ new_argv.push_back(preset_arg.data());
+ }
+ new_argv.push_back(nullptr);
+
+ if (-1 != execv(real_executable.c_str(), new_argv.data())) {
+ int saved_errno = errno;
+ std::cerr << "ERROR: execv: " << real_executable << ": "
+ << strerror(saved_errno) << std::endl;
+ return EX_SOFTWARE;
+ }
+ std::cerr << "ERROR: execv returns!" << std::endl;
+ return EX_SOFTWARE;
+}
diff --git a/kleaf/impl/declare_kernel_prebuilts.bzl b/kleaf/impl/declare_kernel_prebuilts.bzl
index 0aba35d..993b670 100644
--- a/kleaf/impl/declare_kernel_prebuilts.bzl
+++ b/kleaf/impl/declare_kernel_prebuilts.bzl
@@ -40,23 +40,6 @@ _tag_class = tag_class(
`--config=internet` is not necessary.
""",
),
- "auto_download_config": attr.bool(
- doc = """If `True`, infer download configs from `target`.""",
- ),
- "download_configs": attr.string(
- doc = """A JSON dictionary that configure the list of files to download.
-
- Key: local file name.
-
- Value: A dictionary with the following keys:
- * `mandatory`: Whether the files in `outs_mapping` is mandatory.
- If mandatory, failure to download the
- file results in a build failure.
- * `remote_filename_fmt`: remote file name format string, with the following anchors:
- * {build_number}
- * {target}
- """,
- ),
"target": attr.string(
doc = """Name of the build target as identified by the remote build server.
@@ -82,8 +65,6 @@ def _declare_repos(module_ctx, tag_name):
name = module_tag.name,
apparent_name = module_tag.name,
local_artifact_path = module_tag.local_artifact_path,
- auto_download_config = module_tag.auto_download_config,
- download_configs = module_tag.download_configs,
target = module_tag.target,
)
diff --git a/kleaf/impl/image/boot_images.bzl b/kleaf/impl/image/boot_images.bzl
index e297bc1..5ba9152 100644
--- a/kleaf/impl/image/boot_images.bzl
+++ b/kleaf/impl/image/boot_images.bzl
@@ -65,6 +65,7 @@ def _boot_images_impl(ctx):
]
inputs += ctx.files.deps
inputs += ctx.files.vendor_ramdisk_binaries
+ inputs += ctx.files.vendor_ramdisk_dev_nodes
transitive_inputs = [
kernel_build_outs,
@@ -117,6 +118,13 @@ def _boot_images_impl(ctx):
vendor_ramdisk_binaries = " ".join([file.path for file in ctx.files.vendor_ramdisk_binaries]),
)
+ if ctx.files.vendor_ramdisk_dev_nodes:
+ command += """
+ VENDOR_RAMDISK_DEV_NODES="{vendor_ramdisk_dev_nodes}"
+ """.format(
+ vendor_ramdisk_dev_nodes = " ".join([file.path for file in ctx.files.vendor_ramdisk_dev_nodes]),
+ )
+
command += """
# Create and restore DIST_DIR.
# We don't need all of *_for_dist. Copying all declared outputs of kernel_build is
@@ -241,6 +249,7 @@ Execute `build_boot_images` in `build_utils.sh`.""",
* If `None`, skip `vendor_boot`.
""", values = ["vendor_boot", "vendor_kernel_boot"]),
"vendor_ramdisk_binaries": attr.label_list(allow_files = True),
+ "vendor_ramdisk_dev_nodes": attr.label_list(allow_files = True),
"unpack_ramdisk": attr.bool(
doc = """ When false it skips unpacking the vendor ramdisk and copy it as
is, without modifications, into the boot image. Also skip the mkbootfs step.
diff --git a/kleaf/impl/image/kernel_images.bzl b/kleaf/impl/image/kernel_images.bzl
index 88027f6..1de3eff 100644
--- a/kleaf/impl/image/kernel_images.bzl
+++ b/kleaf/impl/image/kernel_images.bzl
@@ -52,6 +52,7 @@ def kernel_images(
modules_blocklist = None,
modules_options = None,
vendor_ramdisk_binaries = None,
+ vendor_ramdisk_dev_nodes = None,
system_dlkm_fs_type = None,
system_dlkm_fs_types = None,
system_dlkm_modules_list = None,
@@ -289,6 +290,9 @@ def kernel_images(
```
# do not sort
```
+ vendor_ramdisk_dev_nodes: List of dev nodes description files
+ which describes special device files to be added to the vendor
+ ramdisk. File format is as accepted by mkbootfs.
ramdisk_compression: If provided it specfies the format used for any ramdisks generated.
If not provided a fallback value from build.config is used.
Possible values are `lz4`, `gzip`, None.
@@ -467,6 +471,7 @@ def kernel_images(
initramfs = ":{}_initramfs".format(name) if build_initramfs else None,
mkbootimg = mkbootimg,
vendor_ramdisk_binaries = vendor_ramdisk_binaries,
+ vendor_ramdisk_dev_nodes = vendor_ramdisk_dev_nodes,
build_boot = build_boot,
vendor_boot_name = vendor_boot_name,
unpack_ramdisk = unpack_ramdisk,
diff --git a/kleaf/impl/kernel_config.bzl b/kleaf/impl/kernel_config.bzl
index b73874e..d1c5b2b 100644
--- a/kleaf/impl/kernel_config.bzl
+++ b/kleaf/impl/kernel_config.bzl
@@ -114,7 +114,6 @@ def _config_trim(ctx):
return []
return [
- _config.disable("UNUSED_SYMBOLS"),
_config.enable("TRIM_UNUSED_KSYMS"),
]
@@ -481,8 +480,10 @@ def get_config_setup_command(
rsync -aL {out_dir}/.config ${{OUT_DIR}}/.config
rsync -aL --chmod=D+w {out_dir}/include/ ${{OUT_DIR}}/include/
rsync -aL --chmod=F+w {out_dir}/localversion ${{OUT_DIR}}/localversion
- rsync -aL --chmod=F+w --ignore-missing-args \\
- {out_dir}/{raw_kmi_symbol_list_below_out_dir} ${{OUT_DIR}}/
+ if [[ -f {out_dir}/{raw_kmi_symbol_list_below_out_dir} ]]; then
+ rsync -aL --chmod=F+w \\
+ {out_dir}/{raw_kmi_symbol_list_below_out_dir} ${{OUT_DIR}}/
+ fi
# Restore real value of $ROOT_DIR in auto.conf.cmd
sed -i'' -e 's:${{ROOT_DIR}}:'"${{ROOT_DIR}}"':g' ${{OUT_DIR}}/include/config/auto.conf.cmd
diff --git a/kleaf/impl/kernel_prebuilt_repo.bzl b/kleaf/impl/kernel_prebuilt_repo.bzl
index 9f7e185..9841ba4 100644
--- a/kleaf/impl/kernel_prebuilt_repo.bzl
+++ b/kleaf/impl/kernel_prebuilt_repo.bzl
@@ -19,10 +19,6 @@ load(
"FILEGROUP_DEF_ARCHIVE_SUFFIX",
"FILEGROUP_DEF_BUILD_FRAGMENT_NAME",
)
-load(
- ":kernel_prebuilt_utils.bzl",
- "CI_TARGET_MAPPING",
-)
visibility("//build/kernel/kleaf/...")
@@ -68,14 +64,6 @@ def _get_build_number(repository_ctx):
build_number = repository_ctx.attr.build_number
return build_number
-def _infer_download_configs(target):
- """Returns inferred `download_config` and `mandatory` from target."""
- chosen_mapping = CI_TARGET_MAPPING.get(target)
- if not chosen_mapping:
- fail("auto_download_config with {} is not supported yet.".format(target))
-
- return chosen_mapping["download_configs"]
-
def _get_remote_filename(
repository_ctx,
build_number,
@@ -182,16 +170,22 @@ def _download_remote_file(repository_ctx, local_filename, remote_filename_fmt, f
block = False,
)
-def _kernel_prebuilt_repo_impl(repository_ctx):
- bazel_target_name = repository_ctx.attr.target
- if repository_ctx.attr.auto_download_config:
- if repository_ctx.attr.download_configs:
- fail("{}: download_configs should not be set when auto_download_config is True".format(
- repository_ctx.attr.name,
- ))
- download_configs = _infer_download_configs(bazel_target_name)
+def _get_download_configs(repository_ctx):
+ if repository_ctx.attr.local_artifact_path:
+ path = repository_ctx.workspace_root.get_child(repository_ctx.attr.local_artifact_path).get_child("download_configs.json")
else:
- download_configs = json.decode(repository_ctx.attr.download_configs)
+ _download_remote_file(
+ repository_ctx = repository_ctx,
+ local_filename = "download_configs.json",
+ remote_filename_fmt = "download_configs.json",
+ file_mandatory = True,
+ ).wait()
+ path = _get_local_path(repository_ctx, "download_configs.json")
+ content = repository_ctx.read(path)
+ return json.decode(content)
+
+def _kernel_prebuilt_repo_impl(repository_ctx):
+ download_configs = _get_download_configs(repository_ctx)
futures = {}
for local_filename, config in download_configs.items():
@@ -314,23 +308,6 @@ kernel_prebuilt_repo = repository_rule(
doc = "the default build number to use if the environment variable is not set.",
),
"apparent_name": attr.string(doc = "apparant repo name", mandatory = True),
- "auto_download_config": attr.bool(
- doc = """If `True`, infer `download_configs` from `target`.""",
- ),
- "download_configs": attr.string(
- doc = """A JSON dictionary that configure the list of files to download.
-
- Key: local file name.
-
- Value: A dictionary with the following keys:
- * `mandatory`: Whether the files in `outs_mapping` is mandatory.
- If mandatory, failure to download the
- file results in a build failure.
- * `remote_filename_fmt`: remote file name format string, with the following anchors:
- * {build_number}
- * {target}
- """,
- ),
"target": attr.string(doc = "Name of target on the download location, e.g. `kernel_aarch64`"),
"artifact_url_fmt": attr.string(
doc = """API endpoint for Android CI artifacts.
diff --git a/kleaf/impl/kernel_prebuilt_utils.bzl b/kleaf/impl/kernel_prebuilt_utils.bzl
index 4ebfbc6..b8ea097 100644
--- a/kleaf/impl/kernel_prebuilt_utils.bzl
+++ b/kleaf/impl/kernel_prebuilt_utils.bzl
@@ -28,6 +28,13 @@ load(
visibility("//build/kernel/kleaf/...")
+_AARCH64_INIT_DDK_FILES = [
+ # TODO(b/328770706): download_configs.json should be a proper rule to
+ # get the name of the file from :kernel_aarch64_ddk_headers_archive
+ "kernel_aarch64_ddk_headers_archive.tar.gz",
+ "build.config.constants",
+]
+
# Key: Bazel target name in common_kernels.bzl
# repo_name: name of download_artifacts_repo in bazel.WORKSPACE
# outs: list of outs associated with that target name
@@ -115,6 +122,13 @@ CI_TARGET_MAPPING = {
"remote_filename_fmt": item,
}
for item in GKI_ARTIFACTS_AARCH64_OUTS
+ } | {
+ item: {
+ "target_suffix": "init_ddk_files",
+ "mandatory": True,
+ "remote_filename_fmt": item,
+ }
+ for item in _AARCH64_INIT_DDK_FILES
},
},
}
diff --git a/kleaf/impl/kmi_symbol_list.bzl b/kleaf/impl/kmi_symbol_list.bzl
index 13ea008..ec6fd86 100644
--- a/kleaf/impl/kmi_symbol_list.bzl
+++ b/kleaf/impl/kmi_symbol_list.bzl
@@ -31,19 +31,16 @@ def _kmi_symbol_list_impl(ctx):
outputs = []
out_file = ctx.actions.declare_file("{}/abi_symbollist".format(ctx.attr.name))
- report_file = ctx.actions.declare_file("{}/abi_symbollist.report".format(ctx.attr.name))
- outputs = [out_file, report_file]
+ outputs = [out_file]
command = ctx.attr.env[KernelEnvInfo].setup + """
mkdir -p {out_dir}
{process_symbols} --out-dir={out_dir} --out-file={out_file_base} \
- --report-file={report_file_base} --in-dir="${{ROOT_DIR}}" \
- {srcs}
+ --in-dir="${{ROOT_DIR}}" {srcs}
""".format(
process_symbols = ctx.executable._process_symbols.path,
out_dir = out_file.dirname,
out_file_base = out_file.basename,
- report_file_base = report_file.basename,
srcs = " ".join([f.path for f in ctx.files.srcs]),
)
diff --git a/kleaf/tests/diff_test.bzl b/kleaf/tests/diff_test.bzl
deleted file mode 100644
index 38b70d4..0000000
--- a/kleaf/tests/diff_test.bzl
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copyright (C) 2022 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""A test that diffs two files."""
-
-load("//build/kernel/kleaf/impl:hermetic_exec.bzl", "hermetic_exec_test")
-
-def diff_test(
- name,
- expected,
- actual):
- """Defines a test that diff two files."""
-
- hermetic_exec_test(
- name = name,
- data = [
- expected,
- actual,
- ],
- script = """
-expected=$(rootpath {expected})
-actual=$(rootpath {actual})
-if ! diff -q $actual $expected; then
- echo "ERROR: test fails. expected:\n$(cat $expected)\nactual:\n$(cat $actual)" >&2
- exit 1
-fi
-""".format(
- expected = expected,
- actual = actual,
- ),
- )
diff --git a/kleaf/tests/hermetic_test.bzl b/kleaf/tests/hermetic_test.bzl
index fb7b95b..cdf5404 100644
--- a/kleaf/tests/hermetic_test.bzl
+++ b/kleaf/tests/hermetic_test.bzl
@@ -14,7 +14,9 @@
"""Rules that wraps a py_test / py_binary (for test purposes) so it is more hermetic."""
+load("@bazel_skylib//lib:shell.bzl", "shell")
load("//build/kernel/kleaf:hermetic_tools.bzl", "hermetic_toolchain")
+load("//build/kernel/kleaf/impl:common_providers.bzl", "KernelPlatformToolchainInfo")
def _hermetic_test_impl(ctx):
hermetic_tools = hermetic_toolchain.get(ctx)
@@ -25,7 +27,20 @@ def _hermetic_test_impl(ctx):
else:
run_setup = hermetic_tools.run_setup
- script = """#!/bin/bash -ex
+ runfiles_transitive_files = [
+ hermetic_tools.deps,
+ ]
+
+ if ctx.attr.use_cc_toolchain:
+ kernel_toolchain_exec = ctx.attr._kernel_toolchain_exec[KernelPlatformToolchainInfo]
+ run_setup += """
+export PATH={quoted_real_bin_path}":${{PATH}}"
+""".format(
+ quoted_real_bin_path = "${PWD}/" + shell.quote(kernel_toolchain_exec.bin_path),
+ )
+ runfiles_transitive_files.append(kernel_toolchain_exec.all_files)
+
+ script = """#!/bin/bash -e
{run_setup}
{actual} "$@"
""".format(
@@ -35,9 +50,6 @@ def _hermetic_test_impl(ctx):
ctx.actions.write(script_file, script, is_executable = True)
- runfiles_transitive_files = [
- hermetic_tools.deps,
- ]
transitive_runfiles = [
ctx.attr.actual[DefaultInfo].default_runfiles,
]
@@ -54,6 +66,11 @@ def _hermetic_test_impl(ctx):
runfiles = runfiles,
)
+def _get_kernel_toolchain_exec(use_cc_toolchain):
+ if use_cc_toolchain:
+ return Label("//build/kernel/kleaf/impl:kernel_toolchain_exec")
+ return None
+
_RULE_ATTRS = dict(
doc = "Wraps a test binary so it runs with hermetic tools.",
implementation = _hermetic_test_impl,
@@ -78,6 +95,12 @@ _RULE_ATTRS = dict(
"data": attr.label_list(allow_files = True, doc = """
See [data](https://bazel.build/reference/be/common-definitions#typical.data)
"""),
+ "use_cc_toolchain": attr.bool(
+ doc = "Also include CC toolchain",
+ ),
+ "_kernel_toolchain_exec": attr.label(
+ default = _get_kernel_toolchain_exec,
+ ),
},
toolchains = [hermetic_toolchain.type],
)
diff --git a/kleaf/tests/integration_test/BUILD.bazel b/kleaf/tests/integration_test/BUILD.bazel
index d8824c9..568a1c1 100644
--- a/kleaf/tests/integration_test/BUILD.bazel
+++ b/kleaf/tests/integration_test/BUILD.bazel
@@ -13,6 +13,7 @@
# limitations under the License.
load("@bazel_skylib//rules:common_settings.bzl", "string_flag")
+load("//build/kernel/kleaf/tests:hermetic_test.bzl", "hermetic_test_binary")
load("//build/kernel/kleaf/tests/utils:write_flag.bzl", "write_flag")
# tools/bazel run //build/kernel/kleaf/tests/integration_test
@@ -25,9 +26,20 @@ load("//build/kernel/kleaf/tests/utils:write_flag.bzl", "write_flag")
# - The test involves running `tools/bazel clean` in between. If this were executed
# with `tools/bazel test`, the directory structure in output_base would be broken
# before results are reported back to bazel.
-py_binary(
+hermetic_test_binary(
name = "integration_test",
+ actual = ":integration_test_internal",
+ # Preserve host PATH so:
+ # - The test can use `git`
+ # - The bazel wrapper can use `repo` when not running on CI
+ append_host_path = True,
+ use_cc_toolchain = True,
+)
+
+py_binary(
+ name = "integration_test_internal",
srcs = ["integration_test.py"],
+ main = "integration_test.py",
deps = [
"//build/kernel/kleaf:wrapper",
"//build/kernel/kleaf/analysis:inputs",
diff --git a/kleaf/tests/integration_test/ddk_workspace_test/.gitignore b/kleaf/tests/integration_test/ddk_workspace_test/.gitignore
new file mode 100644
index 0000000..39a231d
--- /dev/null
+++ b/kleaf/tests/integration_test/ddk_workspace_test/.gitignore
@@ -0,0 +1,10 @@
+out
+bazel-*
+MODULE.bazel.lock
+
+# Generated files by init_ddk.py
+device.bazelrc
+MODULE.bazel
+tools
+external/kleaf/
+gki_prebuilts
diff --git a/kleaf/tests/integration_test/ddk_workspace_test/README.md b/kleaf/tests/integration_test/ddk_workspace_test/README.md
new file mode 100644
index 0000000..6c1fa37
--- /dev/null
+++ b/kleaf/tests/integration_test/ddk_workspace_test/README.md
@@ -0,0 +1,15 @@
+# Experimental: DDKv2 tests
+
+This directory contains a list of tests for setting up the DDK workspace.
+For details, see [Setting up DDK workspace](../../../docs/ddk/workspace.md).
+
+**Note**: Some tests uses hacks to work around kinks. These hacks
+may be replaced by a better API in the future. **DO NOT** use these tests as a
+reference to set up your DDK workspace ... yet.
+
+## Running the test
+
+```shell
+$ tools/bazel run //build/kernel/kleaf/tests/integration_test \
+ -- KleafIntegrationTestShard2.test_ddk_workspace_setup
+```
diff --git a/kleaf/tests/integration_test/ddk_workspace_test/WORKSPACE.bzlmod b/kleaf/tests/integration_test/ddk_workspace_test/WORKSPACE.bzlmod
new file mode 100644
index 0000000..841ee62
--- /dev/null
+++ b/kleaf/tests/integration_test/ddk_workspace_test/WORKSPACE.bzlmod
@@ -0,0 +1,2 @@
+# empty file to work around rules_cc error
+# TODO(b/338440785): Delete this file.
diff --git a/kleaf/tests/integration_test/ddk_workspace_test/external/kleaf/README.md b/kleaf/tests/integration_test/ddk_workspace_test/external/kleaf/README.md
new file mode 100644
index 0000000..40acdaf
--- /dev/null
+++ b/kleaf/tests/integration_test/ddk_workspace_test/external/kleaf/README.md
@@ -0,0 +1 @@
+Empty directory that is used as a mount point for Kleaf tooling repository.
diff --git a/kleaf/tests/integration_test/ddk_workspace_test/extra_setup.py b/kleaf/tests/integration_test/ddk_workspace_test/extra_setup.py
new file mode 100644
index 0000000..71a3bf5
--- /dev/null
+++ b/kleaf/tests/integration_test/ddk_workspace_test/extra_setup.py
@@ -0,0 +1,56 @@
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# TODO: This functionality should be moved into init_ddk.py
+
+"""Script that fix-ups files generated by init_ddk.py for testing."""
+
+import argparse
+import dataclasses
+import pathlib
+import textwrap
+
+
+@dataclasses.dataclass
+class DdkExtraSetup:
+ """Additional fix-ups after init_ddk.py for integration tests."""
+
+ # path to @kleaf. Its value will be used as-is.
+ kleaf_repo_rel: pathlib.Path
+
+ # path to DDK workspace. Its value will be used as-is.
+ ddk_workspace: pathlib.Path
+
+ def _generate_module_bazel(self):
+ path = self.ddk_workspace / "MODULE.bazel"
+ with path.open("a") as out_file:
+ print(textwrap.dedent("""\
+ bazel_dep(name = "bazel_skylib")
+ """), file=out_file)
+
+ def run(self):
+ self._generate_module_bazel()
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument("--kleaf_repo_rel",
+ type=pathlib.Path,
+ help="If relative, it is against ddk_workspace",
+ )
+ parser.add_argument("--ddk_workspace",
+ type=pathlib.Path,
+ help="If relative, it is against cwd",)
+ args = parser.parse_args()
+ DdkExtraSetup(**vars(args)).run()
diff --git a/kleaf/tests/integration_test/ddk_workspace_test/in_tree/BUILD.bazel b/kleaf/tests/integration_test/ddk_workspace_test/in_tree/BUILD.bazel
new file mode 100644
index 0000000..9e02a17
--- /dev/null
+++ b/kleaf/tests/integration_test/ddk_workspace_test/in_tree/BUILD.bazel
@@ -0,0 +1,79 @@
+load("@bazel_skylib//lib:paths.bzl", "paths")
+load("@bazel_skylib//rules:write_file.bzl", "write_file")
+load(
+ "@kleaf//build/kernel/kleaf:kernel.bzl",
+ "ddk_module",
+ "kernel_build",
+ "kernel_build_config",
+ "kernel_images",
+ "kernel_modules_install",
+)
+
+# Workaround to set KERNEL_DIR correctly and
+# avoid using the fallback (directory of the config).
+# TODO(b/338438451): Clean this up with kernel_build.kernel_dir attr.
+_set_kernel_dir_cmd = "KERNEL_DIR=\"{kernel_dir}\"".format(
+ kernel_dir = paths.join(
+ package_relative_label("@kleaf//:x").workspace_root,
+ "common",
+ ),
+)
+
+write_file(
+ name = "set_kernel_dir_build_config",
+ out = "set_kernel_dir_build_config/build.config",
+ content = [
+ _set_kernel_dir_cmd,
+ "",
+ ],
+)
+
+kernel_build_config(
+ name = "in_tree_build_config",
+ srcs = [
+ # do not sort
+ ":set_kernel_dir_build_config",
+ "build.config.in_tree",
+ ],
+)
+
+# Test building in-tree module against kernel from sources
+kernel_build(
+ name = "in_tree_kernel_build",
+ srcs = ["@kleaf//common:kernel_aarch64_sources"],
+ outs = [],
+ base_kernel = "@kleaf//common:kernel_aarch64",
+ build_config = ":in_tree_build_config",
+ defconfig_fragments = [
+ "in_tree_defconfig",
+ ],
+ make_goals = [
+ "modules",
+ ],
+ module_outs = [
+ "psmouse.ko",
+ ],
+)
+
+# Test building out-of-tree module against kernel with in-tree modules
+ddk_module(
+ name = "out_of_tree_driver",
+ srcs = ["mydriver.c"],
+ out = "out_of_tree_driver.ko",
+ kernel_build = ":in_tree_kernel_build",
+ deps = ["@kleaf//common:all_headers_aarch64"],
+)
+
+kernel_modules_install(
+ name = "device_modules_install",
+ kernel_modules = [
+ ":out_of_tree_driver",
+ ],
+)
+
+kernel_images(
+ name = "device_images",
+ build_vendor_dlkm = True,
+ kernel_modules_install = ":device_modules_install",
+ visibility = ["//tests:__pkg__"],
+)
diff --git a/kleaf/tests/integration_test/ddk_workspace_test/in_tree/build.config.in_tree b/kleaf/tests/integration_test/ddk_workspace_test/in_tree/build.config.in_tree
new file mode 100644
index 0000000..6f7a45d
--- /dev/null
+++ b/kleaf/tests/integration_test/ddk_workspace_test/in_tree/build.config.in_tree
@@ -0,0 +1,3 @@
+. ${ROOT_DIR}/${KERNEL_DIR}/build.config.common
+. ${ROOT_DIR}/${KERNEL_DIR}/build.config.gki
+. ${ROOT_DIR}/${KERNEL_DIR}/build.config.aarch64
diff --git a/kleaf/tests/integration_test/ddk_workspace_test/in_tree/in_tree_defconfig b/kleaf/tests/integration_test/ddk_workspace_test/in_tree/in_tree_defconfig
new file mode 100644
index 0000000..04c5a18
--- /dev/null
+++ b/kleaf/tests/integration_test/ddk_workspace_test/in_tree/in_tree_defconfig
@@ -0,0 +1,2 @@
+CONFIG_MOUSE_PS2=m
+# CONFIG_MODULE_SIG_ALL is not set
diff --git a/kleaf/tests/integration_test/ddk_workspace_test/in_tree/mydriver.c b/kleaf/tests/integration_test/ddk_workspace_test/in_tree/mydriver.c
new file mode 100644
index 0000000..5ee4ba8
--- /dev/null
+++ b/kleaf/tests/integration_test/ddk_workspace_test/in_tree/mydriver.c
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2024 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+
+MODULE_DESCRIPTION("A test module for DDK testing purposes");
+MODULE_AUTHOR("Yifan Hong <elsk@google.com>");
+MODULE_LICENSE("GPL v2");
+
diff --git a/kleaf/tests/integration_test/ddk_workspace_test/out_of_tree/BUILD.bazel b/kleaf/tests/integration_test/ddk_workspace_test/out_of_tree/BUILD.bazel
new file mode 100644
index 0000000..0f438e4
--- /dev/null
+++ b/kleaf/tests/integration_test/ddk_workspace_test/out_of_tree/BUILD.bazel
@@ -0,0 +1,29 @@
+load(
+ "@kleaf//build/kernel/kleaf:kernel.bzl",
+ "ddk_module",
+ "kernel_images",
+ "kernel_modules_install",
+)
+
+# Test building no-op DDK module against kernel from sources
+ddk_module(
+ name = "out_of_tree",
+ srcs = ["mydriver.c"],
+ out = "out_of_tree.ko",
+ kernel_build = "//tests:kernel",
+ deps = ["@kleaf//common:all_headers_aarch64"],
+)
+
+kernel_modules_install(
+ name = "device_modules_install",
+ kernel_modules = [
+ ":out_of_tree",
+ ],
+)
+
+kernel_images(
+ name = "device_images",
+ build_vendor_dlkm = True,
+ kernel_modules_install = ":device_modules_install",
+ visibility = ["//tests:__pkg__"],
+)
diff --git a/kleaf/tests/integration_test/ddk_workspace_test/out_of_tree/mydriver.c b/kleaf/tests/integration_test/ddk_workspace_test/out_of_tree/mydriver.c
new file mode 100644
index 0000000..5ee4ba8
--- /dev/null
+++ b/kleaf/tests/integration_test/ddk_workspace_test/out_of_tree/mydriver.c
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2024 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+
+MODULE_DESCRIPTION("A test module for DDK testing purposes");
+MODULE_AUTHOR("Yifan Hong <elsk@google.com>");
+MODULE_LICENSE("GPL v2");
+
diff --git a/kleaf/tests/integration_test/ddk_workspace_test/tests/BUILD.bazel b/kleaf/tests/integration_test/ddk_workspace_test/tests/BUILD.bazel
new file mode 100644
index 0000000..d235ba0
--- /dev/null
+++ b/kleaf/tests/integration_test/ddk_workspace_test/tests/BUILD.bazel
@@ -0,0 +1,42 @@
+load("@bazel_skylib//rules:build_test.bzl", "build_test")
+
+label_flag(
+ name = "kernel",
+ build_setting_default = "@kleaf//common:kernel_aarch64",
+ visibility = ["//out_of_tree:__pkg__"],
+)
+
+config_setting(
+ name = "building_kernel_from_source",
+ flag_values = {
+ ":kernel": "@kleaf//common:kernel_aarch64",
+ },
+)
+
+filegroup(
+ name = "build_test_targets",
+ srcs = [
+ "//out_of_tree:device_images",
+ ] + select({
+ # If using prebuilts, we don't care about the tests against
+ # the kernel built from source.
+ ":building_kernel_from_source": [
+ "//in_tree:device_images",
+ ],
+ "//conditions:default": [],
+ }),
+)
+
+build_test(
+ name = "build_test",
+ targets = [":build_test_targets"],
+)
+
+# TODO: Add tests on the image content
+
+test_suite(
+ name = "tests",
+ tests = [
+ ":build_test",
+ ],
+)
diff --git a/kleaf/tests/integration_test/fake_repo_root/.gitignore b/kleaf/tests/integration_test/fake_repo_root/.gitignore
new file mode 100644
index 0000000..257c8be
--- /dev/null
+++ b/kleaf/tests/integration_test/fake_repo_root/.gitignore
@@ -0,0 +1 @@
+fake_workspace_root
diff --git a/kleaf/tests/integration_test/fake_repo_root/fake_workspace_root/README.md b/kleaf/tests/integration_test/fake_repo_root/fake_workspace_root/README.md
new file mode 100644
index 0000000..7958060
--- /dev/null
+++ b/kleaf/tests/integration_test/fake_repo_root/fake_workspace_root/README.md
@@ -0,0 +1 @@
+Mount point for workspace root for some tests.
diff --git a/kleaf/tests/integration_test/integration_test.py b/kleaf/tests/integration_test/integration_test.py
index 8676628..c677adf 100644
--- a/kleaf/tests/integration_test/integration_test.py
+++ b/kleaf/tests/integration_test/integration_test.py
@@ -36,8 +36,12 @@ Example:
"""
import argparse
+import collections
import contextlib
+import dataclasses
import hashlib
+import io
+import json
import os
import re
import shlex
@@ -48,6 +52,7 @@ import pathlib
import tempfile
import textwrap
import unittest
+import xml.dom.minidom
from typing import Any, Callable, Iterable, TextIO
from absl.testing import absltest
@@ -85,6 +90,20 @@ def load_arguments():
dest="include_abi_tests",
help="Include ABI Monitoring related tests." +
"NOTE: It requires a branch with ABI monitoring enabled.")
+ parser.add_argument("--mount-spec",
+ type=_deserialize_mount_spec,
+ help="""A JSON dictionary specifying bind mounts.
+
+ If not set, some tests will re-run itself
+ in an unshare-d namespace with the flag set.""",
+ default=MountSpec())
+ parser.add_argument("--link-spec",
+ type=_deserialize_link_spec,
+ help="""A JSON dictionary specifying symlinks.
+
+ If not set, some tests will re-run itself
+ with the flag set.""",
+ default=LinkSpec())
group = parser.add_argument_group("CI", "flags for ci.android.com")
group.add_argument("--test_result_dir",
type=_require_absolute_path,
@@ -105,6 +124,69 @@ def _require_absolute_path(p: str) -> pathlib.Path:
return path
+MountSpec = dict[pathlib.Path, pathlib.Path]
+
+
+def _serialize_mount_spec(val: MountSpec) -> str:
+ return json.dumps({str(key): str(value) for key, value in val.items()})
+
+
+def _deserialize_mount_spec(s: str) -> MountSpec:
+ return {pathlib.Path(key): pathlib.Path(value)
+ for key, value in json.loads(s).items()}
+
+
+@dataclasses.dataclass
+class Link:
+ # Value in repo manifest. Relative against repo root.
+ dest: pathlib.Path
+
+ # Unlike the value in repo manifest, this is relative against repo root
+ # not project path.
+ src: pathlib.Path
+
+ @classmethod
+ def from_element(cls, element: xml.dom.minidom.Element,
+ project_path: pathlib.Path) -> "Link":
+ return cls(dest=pathlib.Path(element.getAttribute("dest")),
+ src=project_path / element.getAttribute("src"))
+
+
+LinkSpec = list[Link]
+
+
+def _serialize_link_spec(links: LinkSpec) -> str:
+ return json.dumps([{"dest": str(link.dest), "src": str(link.src)}
+ for link in links])
+
+
+def _deserialize_link_spec(s: str) -> LinkSpec:
+ return [Link(dest=pathlib.Path(obj["dest"]), src=pathlib.Path(obj["src"]))
+ for obj in json.loads(s)]
+
+
+@dataclasses.dataclass
+class RepoProject:
+ # Project path
+ path: pathlib.Path
+
+ # List of symlinks to create
+ links: list[Link] = dataclasses.field(default_factory=list)
+
+ # List of groups
+ groups: list[str] = dataclasses.field(default_factory=list)
+
+ @classmethod
+ def from_element(cls, element: xml.dom.minidom.Element) -> "RepoProject":
+ path = pathlib.Path(
+ element.getAttribute("path") or element.getAttribute("name"))
+ project = cls(path=path)
+ for link_element in element.getElementsByTagName("linkfile"):
+ project.links.append(Link.from_element(link_element, path))
+ project.groups = re.split(r",| ", element.getAttribute("groups"))
+ return project
+
+
class Exec(object):
@staticmethod
@@ -115,6 +197,13 @@ class Exec(object):
subprocess.check_call(args, **kwargs)
@staticmethod
+ def call(args: list[str], **kwargs) -> None:
+ """Executes a shell command."""
+ kwargs.setdefault("text", True)
+ sys.stderr.write(f"+ {' '.join(args)}\n")
+ subprocess.call(args, **kwargs)
+
+ @staticmethod
def check_output(args: list[str], **kwargs) -> str:
"""Returns output of a shell command"""
kwargs.setdefault("text", True)
@@ -159,9 +248,19 @@ class KleafIntegrationTestBase(unittest.TestCase):
if use_bazelrc:
subprocess_args.append(f"--bazelrc={self._bazel_rc.name}")
subprocess_args.append(command)
- subprocess_args.extend(command_args)
+
+ if "--" in command_args:
+ idx = command_args.index("--")
+ bazel_command_args = command_args[:idx]
+ script_args = command_args[idx:]
+ else:
+ bazel_command_args = command_args
+ script_args = []
+
+ subprocess_args.extend(bazel_command_args)
if use_wrapper_args:
subprocess_args.extend(arguments.bazel_wrapper_args)
+ subprocess_args.extend(script_args)
# kwargs has known arguments filtered out.
return subprocess_args, kwargs
@@ -192,7 +291,8 @@ class KleafIntegrationTestBase(unittest.TestCase):
def setUp(self) -> None:
self.assertTrue(os.environ.get("BUILD_WORKSPACE_DIRECTORY"),
- "BUILD_WORKSPACE_DIRECTORY is not set")
+ "BUILD_WORKSPACE_DIRECTORY is not set. " +
+ "Did you use `tools/bazel test` instead of `tools/bazel run`?")
os.chdir(os.environ["BUILD_WORKSPACE_DIRECTORY"])
sys.stderr.write(
f"BUILD_WORKSPACE_DIRECTORY={os.environ['BUILD_WORKSPACE_DIRECTORY']}\n"
@@ -277,6 +377,112 @@ class KleafIntegrationTestBase(unittest.TestCase):
"""Returns the common package."""
return "common"
+ def _mount(self, kleaf_repo: pathlib.Path):
+ """Mount according to --mount-spec"""
+ for from_path, to_path in arguments.mount_spec.items():
+ to_path.mkdir(parents=True, exist_ok=True)
+ Exec.check_call([shutil.which("mount"), "--bind", "-o", "ro",
+ str(from_path), str(to_path)])
+ self.addCleanup(Exec.call,
+ [shutil.which("umount"), str(to_path)])
+
+ for link in arguments.link_spec:
+ real_dest = kleaf_repo / link.dest
+ real_src = kleaf_repo / link.src
+ relative_src = self._force_relative_to(real_src, real_dest.parent)
+ real_dest.parent.mkdir(parents=True, exist_ok=True)
+ real_dest.symlink_to(relative_src)
+
+ @staticmethod
+ def _force_relative_to(path: pathlib.Path, other: pathlib.Path):
+ """Naive implementation of pathlib.Path.relative_to(walk_up)"""
+ if sys.version_info[0] == 3 and sys.version_info[1] >= 12:
+ return path.relative_to(other, walk_up=True)
+
+ path = path.resolve()
+ other = other.resolve()
+
+ if path.is_relative_to(other):
+ return path.relative_to(other)
+
+ path_parts = collections.deque(path.parts)
+ other_parts = collections.deque(other.parts)
+ while path_parts and other_parts and path_parts[0] == other_parts[0]:
+ path_parts.popleft()
+ other_parts.popleft()
+ parts = [".."] * len(other_parts) + list(path_parts)
+ return pathlib.Path(*parts)
+
+ @classmethod
+ def _get_repo_manifest(cls) -> xml.dom.minidom.Document:
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--repo_manifest", type=_require_absolute_path)
+ known, _ = parser.parse_known_args(arguments.bazel_wrapper_args)
+ if known.repo_manifest:
+ with open(known.repo_manifest) as manifest_file:
+ return xml.dom.minidom.parse(manifest_file)
+
+ manifest_content = Exec.check_output(["repo", "manifest"])
+ return xml.dom.minidom.parseString(manifest_content)
+
+ @classmethod
+ def _get_projects(cls) -> list[RepoProject]:
+ manifest_element = cls._get_repo_manifest().documentElement
+ project_elements = manifest_element.getElementsByTagName("project")
+ return [RepoProject.from_element(element)
+ for element in project_elements]
+
+ def _get_project_mount_link_spec(self, kleaf_repo: pathlib.Path,
+ groups: list[str]) \
+ -> tuple[MountSpec, LinkSpec]:
+ """Returns MountSpec / LinkSpec for projects that matches any group.
+
+ Args:
+ groups: List of groups to match projects. Projects with the `groups`
+ attribute matching any of the given |groups| are included.
+ If empty, all projects are included.
+ kleaf_repo: The root of the mount point where projects will be
+ mounted below.
+ """
+
+ projects = self._get_projects()
+
+ relevant_projects = list[RepoProject]()
+ for project in projects:
+ if not groups or any(group in project.groups for group in groups):
+ relevant_projects.append(project)
+
+ real_kleaf_repo = pathlib.Path(".").resolve()
+ mount_spec = MountSpec()
+ link_spec = LinkSpec()
+ for project in relevant_projects:
+ mount_spec[real_kleaf_repo / project.path] = \
+ kleaf_repo / project.path
+ link_spec.extend(project.links)
+
+ return mount_spec, link_spec
+
+ def _unshare_mount_run(self, mount_spec: MountSpec, link_spec: list[Link]):
+ """Reruns the test in an unshare-d namespace with --mount-spec set."""
+ args = [shutil.which("unshare"), "--mount", "--map-root-user"]
+
+ # toybox unshare -R does not imply -U, so explicitly say so.
+ args.append("--user")
+
+ args.extend([shutil.which("bash"), "-c"])
+
+ test_args = [sys.executable, __file__]
+ test_args.extend(f"--bazel-arg={arg}" for arg in arguments.bazel_args)
+ test_args.extend(f"--bazel-wrapper-arg={arg}"
+ for arg in arguments.bazel_wrapper_args)
+ test_args.append(f"--mount-spec={_serialize_mount_spec(mount_spec)}")
+ test_args.append(f"--link-spec={_serialize_link_spec(link_spec)}")
+ test_args.append(self.id().removeprefix("__main__."))
+ args.append(" ".join(shlex.quote(str(test_arg))
+ for test_arg in test_args))
+
+ Exec.check_call(args)
+
# NOTE: It requires a branch with ABI monitoring enabled.
# Include these using the flag --include-abi-tests
@@ -365,6 +571,7 @@ class KleafIntegrationTestShard1(KleafIntegrationTestBase):
output = subprocess.check_output([extract_ikconfig, vmlinux], text=True)
self.assertIn("CONFIG_UAPI_HEADER_TEST=y", output.splitlines())
+
class KleafIntegrationTestShard2(KleafIntegrationTestBase):
def test_user_clang_toolchain(self):
@@ -388,6 +595,131 @@ class KleafIntegrationTestShard2(KleafIntegrationTestBase):
] + _LTO_NONE
self._build(args)
+
+class DdkWorkspaceSetupTest(KleafIntegrationTestBase):
+ """Tests setting up a DDK workspace with @kleaf as dependency."""
+
+ def setUp(self):
+ super().setUp()
+
+ self.real_kleaf_repo = pathlib.Path(".").resolve()
+ self.ddk_workspace = (pathlib.Path(__file__).resolve().parent /
+ "ddk_workspace_test")
+
+ @classmethod
+ def _get_projects(cls) -> list[RepoProject]:
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--repo_manifest", type=_require_absolute_path)
+ known, _ = parser.parse_known_args(arguments.bazel_wrapper_args)
+ if known.repo_manifest:
+ with open(known.repo_manifest) as manifest_file:
+ return cls._get_projects_from_manifest(manifest_file)
+
+ manifest_content = Exec.check_output(["repo", "manifest"])
+ manifest_file = io.StringIO(manifest_content)
+ return cls._get_projects_from_manifest(manifest_file)
+
+ @staticmethod
+ def _get_projects_from_manifest(manifest_file: TextIO) -> list[RepoProject]:
+ dom = xml.dom.minidom.parse(manifest_file)
+ project_elements = dom.documentElement.getElementsByTagName("project")
+ return [RepoProject.from_element(element)
+ for element in project_elements]
+
+ def test_ddk_workspace_below_kleaf_module(self):
+ """Tests that DDK workspace is below @kleaf"""
+ self._run_ddk_workspace_setup_test(self.real_kleaf_repo)
+
+ def test_kleaf_module_below_ddk_workspace(self):
+ """Tests that @kelaf is below DDK workspace"""
+ kleaf_repo = self.ddk_workspace / "external/kleaf"
+ if not arguments.mount_spec:
+ mount_spec = {
+ self.real_kleaf_repo: kleaf_repo
+ }
+ self._unshare_mount_run(mount_spec=mount_spec, link_spec=LinkSpec())
+ return
+ self._run_ddk_workspace_setup_test(kleaf_repo)
+
+ def test_setup_with_prebuilts(self):
+ """Tests that init_ddk --prebuilts_dir & _ddk_headers_archive works."""
+ self._check_call("run", [f"//{self._common()}:kernel_aarch64_dist"])
+
+ kleaf_repo = self.ddk_workspace / "external/kleaf"
+
+ if not arguments.mount_spec:
+ mount_spec, link_spec = self._get_project_mount_link_spec(
+ kleaf_repo, ["ddk", "ddk-external"],
+ )
+
+ self._unshare_mount_run(mount_spec=mount_spec, link_spec=link_spec)
+ return
+
+ real_prebuilts_dir = self.real_kleaf_repo / "out/kernel_aarch64/dist"
+ prebuilts_dir = self.ddk_workspace / "gki_prebuilts"
+ self._run_ddk_workspace_setup_test(
+ kleaf_repo,
+ prebuilts_dir=prebuilts_dir,
+ local=False,
+ url_fmt=f"file://{real_prebuilts_dir}/{{filename}}")
+
+ def _run_ddk_workspace_setup_test(self,
+ kleaf_repo: pathlib.Path,
+ prebuilts_dir: pathlib.Path | None = None,
+ local: bool = True,
+ url_fmt: str | None = None):
+ # kleaf_repo relative to ddk_workspace
+ kleaf_repo_rel = self._force_relative_to(
+ kleaf_repo, self.ddk_workspace)
+
+ git_clean_args = [shutil.which("git"), "clean", "-fdx"]
+ if kleaf_repo.is_relative_to(self.ddk_workspace):
+ git_clean_args.extend([
+ "-e",
+ str(kleaf_repo.relative_to(self.ddk_workspace)),
+ ])
+
+ Exec.check_call(git_clean_args, cwd=self.ddk_workspace)
+
+ # Delete generated files at the end
+ self.addCleanup(Exec.check_call, git_clean_args,
+ cwd=self.ddk_workspace)
+
+ self._mount(kleaf_repo)
+
+ args = [
+ "//build/kernel:init_ddk",
+ "--",
+ f"--kleaf_repo={kleaf_repo}",
+ f"--ddk_workspace={self.ddk_workspace}",
+ ]
+ if local:
+ args.append("--local")
+ if prebuilts_dir:
+ args.append(f"--prebuilts_dir={prebuilts_dir}")
+ if url_fmt:
+ args.append(f"--url_fmt={url_fmt}")
+ self._check_call("run", args)
+ Exec.check_call([
+ sys.executable,
+ str(self.ddk_workspace / "extra_setup.py"),
+ f"--kleaf_repo_rel={kleaf_repo_rel}",
+ f"--ddk_workspace={self.ddk_workspace}",
+ ])
+
+ self._check_call("clean", ["--expunge"], cwd=self.ddk_workspace)
+
+ args = []
+ # Switch base kernel when using prebuilts
+ if prebuilts_dir:
+ args.append("--//tests:kernel=@gki_prebuilts//kernel_aarch64")
+ args.append("//tests")
+ self._check_call("test", args, cwd=self.ddk_workspace)
+
+ # Delete generated files
+ self._check_call("clean", ["--expunge"], cwd=self.ddk_workspace)
+
+
# Quick integration tests. Each test case should finish within 1 minute.
# The whole test suite should finish within 5 minutes. If the whole test suite
# takes too long, consider sharding QuickIntegrationTest too.
@@ -674,7 +1006,7 @@ class ScmversionIntegrationTest(KleafIntegrationTestBase):
def setUp(self) -> None:
super().setUp()
- self.strings = "bazel-bin/build/kernel/hermetic-tools/llvm-strings"
+ self.strings = shutil.which("llvm-strings")
self.uname_pattern_prefix = re.compile(
r"^Linux version [0-9]+[.][0-9]+[.][0-9]+(\S*)")
@@ -720,9 +1052,10 @@ class ScmversionIntegrationTest(KleafIntegrationTestBase):
lambda x: re.search(extraversion_pattern, x),
["EXTRAVERSION ="])
- def _get_vmlinux_scmversion(self):
+ def _get_vmlinux_scmversion(self, workspace_root=pathlib.Path(".")):
strings_output = Exec.check_output([
- self.strings, f"bazel-bin/{self._common()}/kernel_aarch64/vmlinux"
+ self.strings,
+ str(workspace_root / f"bazel-bin/{self._common()}/kernel_aarch64/vmlinux")
])
ret = []
for line in strings_output.splitlines():
@@ -836,6 +1169,59 @@ class ScmversionIntegrationTest(KleafIntegrationTestBase):
for scmversion in self._get_vmlinux_scmversion():
self.assertRegexpMatches(scmversion, scmversion_pat)
+ def test_stamp_repo_root_is_not_workspace_root(self):
+ """Tests that --config=stamp works when repo root is not Bazel workspace root."""
+
+ self._setup_mainline()
+
+ real_workspace_root = pathlib.Path(".").resolve()
+ repo_root = (pathlib.Path(__file__).resolve().parent /
+ "fake_repo_root")
+ workspace_root = repo_root / "fake_workspace_root"
+
+ if not arguments.mount_spec:
+ mount_spec = {
+ real_workspace_root : workspace_root
+ }
+
+ self._unshare_mount_run(mount_spec=mount_spec, link_spec=LinkSpec())
+ return
+
+ self._mount(workspace_root)
+
+ manifest = self._get_repo_manifest()
+ for project in manifest.documentElement.getElementsByTagName("project"):
+ path = project.getAttribute("path") or project.getAttribute("name")
+ project.setAttribute(
+ "path", str(pathlib.Path("fake_workspace_root") / path))
+
+ new_manifest_temp_file = tempfile.NamedTemporaryFile(
+ mode="w+", delete=False)
+ new_manifest = pathlib.Path(new_manifest_temp_file.name)
+ self.addCleanup(new_manifest.unlink)
+
+ with new_manifest_temp_file as file_handle:
+ manifest.writexml(file_handle)
+
+ # KI: For this build, git commands on certain projects (e.g.
+ # prebuilts/ndk-r26, prebuilts/clang) are slow because it needs to
+ # refresh index.
+ self._check_call(
+ "build",
+ _FASTEST + [
+ "--config=stamp",
+ f"--repo_manifest={repo_root}:{new_manifest}",
+ f"//{self._common()}:kernel_aarch64",
+ ],
+ cwd=workspace_root,
+ env=ScmversionIntegrationTest._env_without_build_number(),
+ )
+
+ scmversion_pat = re.compile(
+ r"^-rc999-mainline(-[0-9]{5,})?-g[0-9a-f]{12,40}(-dirty)?$")
+ for scmversion in self._get_vmlinux_scmversion(workspace_root):
+ self.assertRegexpMatches(scmversion, scmversion_pat)
+
# Class that mimics tee(1)
class Tee(object):
diff --git a/kleaf/tests/kernel_build_config_test/BUILD.bazel b/kleaf/tests/kernel_build_config_test/BUILD.bazel
index e7dc4a2..fd0159e 100644
--- a/kleaf/tests/kernel_build_config_test/BUILD.bazel
+++ b/kleaf/tests/kernel_build_config_test/BUILD.bazel
@@ -12,8 +12,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+load("@bazel_skylib//rules:diff_test.bzl", "diff_test")
load("//build/kernel/kleaf:kernel.bzl", "kernel_build_config")
-load("//build/kernel/kleaf/tests:diff_test.bzl", "diff_test")
+load("//build/kernel/kleaf/tests:hermetic_test.bzl", "hermetic_test")
# Test kernel_build_config
@@ -28,7 +29,14 @@ kernel_build_config(
)
diff_test(
+ name = "kernel_build_config_test_internal",
+ file1 = "build.config.expected",
+ file2 = ":kernel_build_config_test_actual",
+ # Avoid running it without the hermetic_test wrapper
+ tags = ["manual"],
+)
+
+hermetic_test(
name = "kernel_build_config_test",
- actual = ":kernel_build_config_test_actual",
- expected = "build.config.expected",
+ actual = ":kernel_build_config_test_internal",
)
diff --git a/kleaf/workspace.bzl b/kleaf/workspace.bzl
index 13eba05..5e95d72 100644
--- a/kleaf/workspace.bzl
+++ b/kleaf/workspace.bzl
@@ -186,7 +186,6 @@ WARNING: define_kleaf_workspace() should be called with common_kernel_package={}
name = value["repo_name"],
apparent_name = value["repo_name"],
artifact_url_fmt = artifact_url_fmt,
- download_configs = json.encode(value["download_configs"]),
target = target,
)
diff --git a/kleaf/workspace_status_stamp.py b/kleaf/workspace_status_stamp.py
index 66f3d9f..97effee 100644
--- a/kleaf/workspace_status_stamp.py
+++ b/kleaf/workspace_status_stamp.py
@@ -32,7 +32,7 @@ class PathCollectible(object):
path: pathlib.Path
def collect(self) -> str:
- return NotImplementedError
+ raise NotImplementedError
@dataclasses.dataclass
@@ -153,59 +153,49 @@ def list_projects() -> list[pathlib.Path]:
"""Lists projects in the repository.
Returns:
- a list of projects in the repository.
+ a list of Git projects relative to CWD.
"""
- if "KLEAF_REPO_MANIFEST" in os.environ:
- with open(os.environ["KLEAF_REPO_MANIFEST"]) as repo_prop_file:
- return parse_repo_manifest(repo_prop_file.read())
+ repo_root_s, repo_manifest = os.environ["KLEAF_REPO_MANIFEST"].split(":")
+ repo_root = pathlib.Path(repo_root_s)
+
+ if repo_manifest:
+ with open(repo_manifest) as repo_manifest_file:
+ return parse_repo_manifest(repo_root, repo_manifest_file.read())
try:
- output = subprocess.check_output(["repo", "list", "-f"], text=True)
- return parse_repo_list(output)
+ output = subprocess.check_output(["repo", "manifest", "-r"], text=True)
+ return parse_repo_manifest(repo_root, output)
except (subprocess.SubprocessError, FileNotFoundError) as e:
- logging.warning("Unable to execute repo list -f: %s", e)
+ logging.warning("Unable to execute repo manifest -r: %s", e)
return []
-def parse_repo_manifest(manifest: str) -> list[pathlib.Path]:
+def parse_repo_manifest(repo_root: pathlib.Path, manifest: str) \
+ -> list[pathlib.Path]:
"""Parses a repo manifest file.
Returns:
a list of paths to all projects in the repository.
"""
+ kleaf_repo_dir = pathlib.Path(".").resolve()
try:
dom = xml.dom.minidom.parseString(manifest)
except xml.parsers.expat.ExpatError as e:
logging.error("Unable to parse repo manifest: %s", e)
return []
projects = dom.documentElement.getElementsByTagName("project")
- # https://gerrit.googlesource.com/git-repo/+/master/docs/manifest-format.md#element-project
- return [
- pathlib.Path(proj.getAttribute("path") or proj.getAttribute("name"))
- for proj in projects
- ]
-
-
-def parse_repo_list(repo_list: str) -> list[pathlib.Path]:
- """Parses the result of `repo list -f`.
-
- Returns:
- a list of paths to all projects in the repository.
- """
- workspace = pathlib.Path(".").absolute()
- paths = []
- for line in repo_list.splitlines():
- line = line.strip()
- if not line or ":" not in line:
- continue
- proj = pathlib.Path(line.split(":", 2)[0].strip())
- if proj.is_relative_to(workspace):
- paths.append(proj.relative_to(workspace))
+ ret = list[pathlib.Path]()
+ for project in projects:
+ # https://gerrit.googlesource.com/git-repo/+/master/docs/manifest-format.md#element-project
+ path_below_repo = pathlib.Path(project.getAttribute("path") or
+ project.getAttribute("name"))
+ realpath = repo_root / path_below_repo
+ if realpath.is_relative_to(kleaf_repo_dir):
+ ret.append(realpath.relative_to(kleaf_repo_dir))
else:
- logging.info(
- "Ignoring project %s because it is not under the Bazel workspace",
- proj)
- return paths
+ logging.warning("Skipping project %s because it is not below %s",
+ realpath, kleaf_repo_dir)
+ return ret
def collect(popen_obj: subprocess.Popen) -> str: