blob: 1f16d2eb7dd5e1457a025fc96ca22f9c086d048b [file] [log] [blame]
# Copyright The Monogon Project Authors.
# SPDX-License-Identifier: Apache-2.0
"""
Rules for building Linux kernel images.
This currently performs the build in a fully hermetic manner, using
make/gcc/... from the toolchain bundle, and is only slightly better than a genrule. This
should be replaced by a hermetic build that just uses cc_library targets.
"""
load("@rules_cc//cc:find_cc_toolchain.bzl", "CC_TOOLCHAIN_ATTRS", "find_cpp_toolchain", "use_cc_toolchain")
load("@rules_cc//cc/common:cc_common.bzl", "cc_common")
load("@rules_cc//cc/common:cc_info.bzl", "CcInfo")
load("//build/utils:detect_root.bzl", "detect_root", "detect_roots")
load("//build/utils:foreign_build.bzl", "generate_foreign_build_env", "merge_env")
load("//build/utils:target_info.bzl", "TargetInfo")
def _linux_image_impl_resources(_os, _ninputs):
"""
Configures linux build resources.
See `resource_set` documentation in builtins.actions Bazel docs.
"""
# 16 threads seems about right - this fits well in both our build machines and
# development machines.
cpu = 16
# In MB. Picked based on observing build in htop.
mb_per_cpu = 256
return {
"cpu": cpu,
"memory": cpu * mb_per_cpu,
"local_test": 0,
}
TOOLCHAINS = [
"//build/toolchain/toolchain-bundle:make_toolchain",
"//build/toolchain/toolchain-bundle:flex_toolchain",
"//build/toolchain/toolchain-bundle:bison_toolchain",
"//build/toolchain/toolchain-bundle:m4_toolchain",
"//build/toolchain/toolchain-bundle:busybox_toolchain",
"//build/toolchain/toolchain-bundle:bc_toolchain",
"//build/toolchain/toolchain-bundle:diff_toolchain",
"//build/toolchain/toolchain-bundle:perl_toolchain",
"//build/toolchain/toolchain-bundle:lz4_toolchain",
]
def _linux_image_impl(ctx):
# Tuple containing information about how to build and access the resulting
# image.
# The first element (target) is the make target to build, the second
# (image_source) is the resulting file to be copied and the last
# (image_name) is the name of the image that will be generated by this
# rule.
(target, image_source, image_name) = {
"vmlinux": ("vmlinux modules", "vmlinux", "vmlinux"),
"Image": ("all modules", "arch/" + ctx.attr._target_arch[TargetInfo].value + "/boot/" + ctx.attr._image_name[TargetInfo].value, "Image"),
}[ctx.attr.image_format]
ssl_src, ssl_gen = detect_roots(ctx.attr._ssl[CcInfo].compilation_context.direct_public_headers)
crypto_src, crypto_gen = detect_roots(ctx.attr._crypto[CcInfo].compilation_context.direct_public_headers)
extra_env = {
"HOSTLDFLAGS": " -L ".join(
[
"", # First element empty, for force a the join prefix
detect_root(ctx.attr._zstd.files.to_list()).rsplit("/", 1)[0],
detect_root(ctx.attr._zlib.files.to_list()).rsplit("/", 1)[0],
detect_root(ctx.attr._libelf.files.to_list()).rsplit("/", 1)[0],
detect_root(ctx.attr._ssl.files.to_list()).rsplit("/", 1)[0],
detect_root(ctx.attr._crypto.files.to_list()).rsplit("/", 1)[0],
],
),
"HOSTCFLAGS": " -I ".join(
[
"", # First element empty, for force a the join prefix
detect_root(ctx.attr._libelf[CcInfo].compilation_context.direct_public_headers),
ssl_src + "/../",
ssl_gen + "/../include/",
crypto_src + "/../",
crypto_gen + "/../include/",
],
),
}
inputs = depset(
ctx.files.kernel_config +
ctx.files.kernel_src +
ctx.files._libelf +
ctx.attr._libelf[CcInfo].compilation_context.direct_public_headers +
ctx.files._zlib +
ctx.files._ssl +
ctx.attr._ssl[CcInfo].compilation_context.direct_public_headers +
ctx.files._crypto +
ctx.attr._crypto[CcInfo].compilation_context.direct_public_headers,
)
# Setup the environment for the foreign build.
toolchain_env, toolchain_inputs, toolchain_cmd = generate_foreign_build_env(
ctx = ctx,
target_toolchain = find_cpp_toolchain(ctx),
exec_toolchain = ctx.attr._exec_toolchain[cc_common.CcToolchainInfo],
toolchain_bundle_tools = TOOLCHAINS,
)
image = ctx.actions.declare_file(image_name)
modinfo = ctx.actions.declare_file("modules.builtin.modinfo")
modules = ctx.actions.declare_directory("modules")
ctx.actions.run_shell(
outputs = [image, modinfo, modules],
inputs = depset(transitive = [inputs, toolchain_inputs]),
resource_set = _linux_image_impl_resources,
env = merge_env(toolchain_env, extra_env),
progress_message = "Building Linux Kernel: {}".format(ctx.label.name),
mnemonic = "BuildLinux",
command = toolchain_cmd + """
export BISON_PKGDATADIR=$(realpath $(dirname $BISON)/../share/bison)
builddir=$(mktemp -d)
# All source files have the same timestamp, take it from an arbitrary file.
build_timestamp=$(date --utc -r {kernel_src}/README)
mkdir {kernel_src}/.bin
cp {kconfig} $builddir/.config
(
cd {kernel_src} &&
make -j 16 \
\
YACC="$BISON" LEX="$FLEX" \
\
CC="$CC" CXX="$CXX" LD="$LD" AR="$AR" NM="$NM" STRIP="$STRIP" \
OBJCOPY="$OBJCOPY" OBJDUMP="$OBJDUMP" READELF="$READELF" \
CFLAGS="$CFLAGS" LDFLAGS="$LDFLAGS" \
\
HOSTCC="$HOSTCC" HOSTCXX="$HOSTCXX" HOSTLD="$HOSTLD" \
HOSTAR="$HOSTAR" HOSTNM="$HOSTNM" HOSTSTRIP="$HOSTSTRIP" \
HOSTOBJCOPY="$HOSTOBJCOPY" HOSTOBJDUMP="$HOSTOBJDUMP" \
HOSTREADELF="$HOSTREADELF" HOSTCFLAGS="$HOSTCFLAGS" \
HOSTLDFLAGS="$HOSTLDFLAGS" \
\
KBUILD_BUILD_USER=builder \
KBUILD_BUILD_HOST=monogon \
KBUILD_BUILD_TIMESTAMP="$build_timestamp" \
KBUILD_OUTPUT="$builddir" \
ARCH="{target_arch}" \
olddefconfig {target}
) > /dev/null
cp "$builddir"/{image_source} {image}
cp "$builddir"/modules.builtin.modinfo {modinfo}
# Not using modules_install as it tries to run depmod and friends
for f in $(find "$builddir" -name '*.ko' -type f -printf "%P\n" ); do
install -D "$builddir/$f" "{modules}/$f"
done
rm -Rf "$builddir"
""".format(
kconfig = ctx.file.kernel_config.path,
target = target,
image_source = image_source,
kernel_src = detect_root(ctx.attr.kernel_src.files.to_list()),
image = image.path,
modinfo = modinfo.path,
modules = modules.path,
target_arch = ctx.attr._target_arch[TargetInfo].value,
),
use_default_shell_env = True,
)
return [
DefaultInfo(
files = depset([image]),
runfiles = ctx.runfiles(files = [image]),
),
OutputGroupInfo(
modinfo = depset([modinfo]),
modules = depset([modules]),
),
]
linux_image = rule(
doc = """
Build Linux kernel image hermetically in a given format.
""",
implementation = _linux_image_impl,
attrs = {
"kernel_config": attr.label(
doc = """
Linux kernel configuration file to build this kernel image with.
""",
allow_single_file = True,
),
"kernel_src": attr.label(
doc = """
Filegroup containing Linux kernel sources.
""",
),
"image_format": attr.string(
doc = """
Format of generated Linux image, one of 'vmlinux' or 'Image',
""",
values = [
"vmlinux",
"Image",
],
default = "Image",
),
"_libelf": attr.label(
default = "//third_party:libelf_elf",
cfg = "exec",
),
"_zstd": attr.label(
default = "//third_party:zstd_zstd",
cfg = "exec",
),
"_zlib": attr.label(
default = "//third_party:zlib_z",
cfg = "exec",
),
"_ssl": attr.label(
default = "//third_party:openssl_ssl",
cfg = "exec",
),
"_crypto": attr.label(
default = "//third_party:openssl_crypto",
cfg = "exec",
),
"_exec_toolchain": attr.label(
default = "@rules_cc//cc:current_cc_toolchain",
cfg = "exec",
),
"_image_name": attr.label(
default = "//third_party/linux:image_name",
),
"_target_arch": attr.label(
default = "//third_party/linux:target_arch",
),
} | CC_TOOLCHAIN_ATTRS,
fragments = ["cpp"],
toolchains = TOOLCHAINS + use_cc_toolchain(),
)