blob: 2e3733e044003cce9760b85ac64015f7ee338980 [file] [log] [blame]
# 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.
"""Given a kernel_build, generates corresponding kernel_filegroup target declaration."""
load("@bazel_skylib//lib:paths.bzl", "paths")
load(":common_providers.bzl", "KernelBuildFilegroupDeclInfo")
load(
":constants.bzl",
"FILEGROUP_DEF_ARCHIVE_SUFFIX",
"FILEGROUP_DEF_BUILD_FRAGMENT_NAME",
"GKI_ARTIFACTS_AARCH64_OUTS",
"SIGNED_GKI_ARTIFACTS_ARCHIVE",
)
load(":hermetic_toolchain.bzl", "hermetic_toolchain")
load(":utils.bzl", "utils")
visibility("//build/kernel/kleaf/...")
def _kernel_filegroup_declaration_impl(ctx):
info = ctx.attr.kernel_build[KernelBuildFilegroupDeclInfo]
# Not allowed because mod_full_env & mod_inst_env for kernel_build needs
# kbuild_mixed_tree_ret.outputs, but it cannot be vendored by
# kernel_filegroup.
if info.has_base_kernel:
fail("""{}: {} has base_kernel. kernel_filegroup_declaration on
device kernel build is not supported.""".format(ctx.label, ctx.attr.kernel_build.label))
# ddk_artifacts
deps_files = [
# _modules_prepare
info.modules_prepare_archive,
info.modules_staging_archive,
]
# Get the only file from the depset, so using to_list() here is fast.
kernel_uapi_headers_lst = info.kernel_uapi_headers.to_list()
if not kernel_uapi_headers_lst:
fail("{}: {} does not have kernel_uapi_headers.".format(ctx.label, ctx.attr.kernel_build.label))
if len(kernel_uapi_headers_lst) > 1:
fail("{}: kernel_filegroup_declaration on kernel_build {} with base_kernel is not supported yet.".format(
ctx.label,
ctx.attr.kernel_build.label,
))
kernel_uapi_headers = kernel_uapi_headers_lst[0]
system_dlkm_staging_archive = utils.find_file(
name = "system_dlkm_staging_archive.tar.gz",
files = ctx.files.images,
what = "{}: images".format(ctx.label),
required = False,
)
template_file = _write_template_file(
ctx = ctx,
has_system_dlkm_staging_archive = bool(system_dlkm_staging_archive),
)
filegroup_decl_file = _write_filegroup_decl_file(
ctx = ctx,
info = info,
deps_files = deps_files,
kernel_uapi_headers = kernel_uapi_headers,
system_dlkm_staging_archive = system_dlkm_staging_archive,
template_file = template_file,
)
filegroup_decl_archive = _create_archive(
ctx = ctx,
info = info,
deps_files = deps_files,
kernel_uapi_headers = kernel_uapi_headers,
filegroup_decl_file = filegroup_decl_file,
)
return DefaultInfo(files = depset([filegroup_decl_archive]))
def _write_template_file(
ctx,
has_system_dlkm_staging_archive):
template_content = """\
_ALL_MODULE_NAMES = {all_module_names_repr}
_ALL_GKI_ARTIFACTS = {all_gki_artifacts_repr}
platform(
name = {target_platform_repr},
constraint_values = [
"@platforms//os:android",
"@platforms//cpu:{arch}",
# @kleaf//prebuilts/clang/host/linux-x86/kleaf:{toolchain_version}
package_relative_label(_CLANG_KLEAF_PKG).same_package_label({toolchain_version_repr}),
],
visibility = ["//visibility:private"],
)
platform(
name = {exec_platform_repr},
parents = ["@platforms//host"],
constraint_values = [
# @kleaf//prebuilts/clang/host/linux-x86/kleaf:{toolchain_version}
package_relative_label(_CLANG_KLEAF_PKG).same_package_label({toolchain_version_repr}),
],
visibility = ["//visibility:private"],
)
platform(
name = {exec_musl_platform_repr},
parents = [{exec_platform_repr}],
constraint_values = [
# @kleaf//build/kernel/kleaf/platforms/libc:musl
_MUSL,
],
visibility = ["//visibility:private"],
)
extracted_gki_artifacts_archive(
name = {signed_gki_artifacts_repr},
src = {signed_gki_artifacts_src_repr},
outs = [artifact for artifact in _ALL_GKI_ARTIFACTS if artifact != "boot-img.tar.gz"],
visibility = ["//visibility:public"],
)
kernel_filegroup(
name = {name_repr},
srcs = {srcs_repr} + {copy_module_symvers_repr},
deps = {deps_repr} + {extra_deps_repr},
kernel_uapi_headers = {uapi_headers_repr},
collect_unstripped_modules = {collect_unstripped_modules_repr},
strip_modules = {strip_modules_repr},
all_module_names = _ALL_MODULE_NAMES,
kernel_release = {kernel_release_repr},
protected_modules_list = {protected_modules_repr},
ddk_module_defconfig_fragments = {ddk_module_defconfig_fragments_repr},
ddk_module_headers = {ddk_module_headers_repr},
config_out_dir_files = glob([{config_out_dir_repr} + "/**"]),
config_out_dir = {config_out_dir_repr},
env_setup_script = {env_setup_script_repr},
modules_prepare_archive = {modules_prepare_archive_repr},
module_env_archive = {module_env_archive_repr},
outs = {outs_repr},
internal_outs = {internal_outs_repr},
target_platform = {target_platform_repr},
exec_platform = select({
_MUSL_KBUILD_IS_TRUE: {exec_musl_platform_repr},
"//conditions:default": {exec_platform_repr},
}),
visibility = ["//visibility:public"],
)
"""
if has_system_dlkm_staging_archive:
template_content += """\
extracted_system_dlkm_staging_archive(
name = {modules_repr},
src = {system_dlkm_staging_archive_repr},
outs = _ALL_MODULE_NAMES,
visibility = ["//visibility:public"],
)
[filegroup(
name = "{}/{}".format({name_repr}, module_name),
srcs = [{modules_repr}],
output_group = module_name,
visibility = ["//visibility:public"],
) for module_name in _ALL_MODULE_NAMES]
"""
template_file = ctx.actions.declare_file("{}/{}_template.txt".format(
ctx.attr.kernel_build.label.name,
FILEGROUP_DEF_BUILD_FRAGMENT_NAME.removesuffix(".txt"),
))
ctx.actions.write(output = template_file, content = template_content)
return template_file
def _write_filegroup_decl_file(
ctx,
info,
deps_files,
kernel_uapi_headers,
system_dlkm_staging_archive,
template_file):
## Reused kwargs for TemplateDict: https://bazel.build/rules/lib/builtins/TemplateDict
# For a list of files, represented in a list
# Intentionally not adding comma for the last item so it works for the empty case.
join = dict(join_with = ",\n ", format_joined = "[\n %s\n ]")
# For a single file. Use add_joined so we can use map_each and delay calculation.
one = dict(join_with = "")
# For extra downloaded files, prefixed with "//"
extra = dict(
allow_closure = True,
map_each = lambda file: repr("//{}".format(file.basename) if file else None),
)
# For local files in this package. Files do not have any prefixes.
pkg = dict(
allow_closure = True,
map_each = lambda file: repr("{}".format(file.path) if file else None),
)
sub = ctx.actions.template_dict()
sub.add("{name_repr}", repr(ctx.attr.kernel_build.label.name))
sub.add_joined("{srcs_repr}", info.filegroup_srcs, **(join | extra))
sub.add_joined(
"{copy_module_symvers_repr}",
depset(info.copy_module_symvers_outputs),
**(join | pkg)
)
sub.add_joined("{deps_repr}", depset(deps_files), **(join | pkg))
sub.add_joined(
"{extra_deps_repr}",
depset(transitive = [target.files for target in ctx.attr.extra_deps]),
**(join | extra)
)
sub.add_joined("{uapi_headers_repr}", depset([kernel_uapi_headers]), **(one | extra))
sub.add("{collect_unstripped_modules_repr}", repr(info.collect_unstripped_modules))
sub.add("{strip_modules_repr}", repr(info.strip_modules))
sub.add_joined(
"{all_module_names_repr}",
depset(info.all_module_names),
map_each = repr,
**join
)
sub.add_joined("{kernel_release_repr}", depset([info.kernel_release]), **(one | pkg))
sub.add_joined(
"{protected_modules_repr}",
depset([info.src_protected_modules_list]),
**(one | pkg)
)
sub.add_joined(
"{ddk_module_defconfig_fragments_repr}",
info.ddk_module_defconfig_fragments,
**(join | pkg)
)
sub.add_joined("{config_out_dir_repr}", depset([info.config_out_dir]), **(one | pkg))
sub.add_joined("{env_setup_script_repr}", depset([info.env_setup_script]), **(one | pkg))
sub.add_joined(
"{modules_prepare_archive_repr}",
depset([info.modules_prepare_archive]),
**(one | pkg)
)
sub.add_joined("{module_env_archive_repr}", depset([info.module_env_archive]), **(one | pkg))
# {"//vmlinux": "vmlinux", ...}
sub.add_joined(
"{outs_repr}",
info.outs,
allow_closure = True,
map_each = lambda file: "{key}: {value}".format(
key = repr("//{}".format(file.basename) if file else None),
value = repr(paths.relativize(file.path, info.ruledir)),
),
join_with = ",\n ",
format_joined = "{\n %s\n }",
)
# {":bazel-out/k8-fastbuild/bin/common/kernel_aarch64/Module.symvers": "Module.symvers", ...}
sub.add_joined(
"{internal_outs_repr}",
info.internal_outs,
allow_closure = True,
map_each = lambda file: "{key}: {value}".format(
key = repr(file.path if file else None),
value = repr(paths.relativize(file.path, info.ruledir)),
),
join_with = ",\n ",
format_joined = "{\n %s\n }",
)
sub.add("{toolchain_version}", info.toolchain_version)
sub.add("{toolchain_version_repr}", repr(info.toolchain_version))
sub.add("{target_platform_repr}", repr(ctx.attr.kernel_build.label.name + "_platform_target"))
sub.add("{exec_platform_repr}", repr(ctx.attr.kernel_build.label.name + "_platform_exec"))
sub.add("{exec_musl_platform_repr}", repr(ctx.attr.kernel_build.label.name + "_platform_exec_musl"))
sub.add("{arch}", info.arch)
if system_dlkm_staging_archive:
sub.add("{modules_repr}", repr(ctx.attr.kernel_build.label.name + "_modules"))
sub.add_joined(
"{system_dlkm_staging_archive_repr}",
depset([system_dlkm_staging_archive]),
**(one | extra)
)
sub.add("{signed_gki_artifacts_repr}", repr(ctx.attr.kernel_build.label.name + "_signed_gki_artifacts"))
sub.add("{signed_gki_artifacts_src_repr}", repr("//{}".format(SIGNED_GKI_ARTIFACTS_ARCHIVE)))
sub.add_joined(
"{all_gki_artifacts_repr}",
depset(GKI_ARTIFACTS_AARCH64_OUTS),
map_each = repr,
**join
)
# This relies on the fact that common_kernels.ddk_headers_archive wraps the ddk_module_headers
# so that it has the exact same label inside @kleaf in the DDKv2 workspace.
sub.add("{ddk_module_headers_repr}", repr([
str(target.label).replace("@@//", "@kleaf//")
for target in ctx.attr.ddk_module_headers
]))
filegroup_decl_file = ctx.actions.declare_file("{}/{}".format(
ctx.attr.kernel_build.label.name,
FILEGROUP_DEF_BUILD_FRAGMENT_NAME,
))
ctx.actions.expand_template(
template = template_file,
output = filegroup_decl_file,
computed_substitutions = sub,
)
return filegroup_decl_file
def _create_archive(ctx, info, deps_files, kernel_uapi_headers, filegroup_decl_file):
hermetic_tools = hermetic_toolchain.get(ctx)
filegroup_decl_archive = ctx.actions.declare_file("{name}/{name}{suffix}".format(
name = ctx.attr.kernel_build.label.name,
suffix = FILEGROUP_DEF_ARCHIVE_SUFFIX,
))
direct_inputs = deps_files + [
filegroup_decl_file,
info.kernel_release,
kernel_uapi_headers,
info.config_out_dir,
info.env_setup_script,
info.modules_prepare_archive,
info.module_env_archive,
]
if info.src_protected_modules_list:
direct_inputs.append(info.src_protected_modules_list)
direct_inputs.extend(info.copy_module_symvers_outputs)
transitive_inputs = [
info.ddk_module_defconfig_fragments,
info.internal_outs,
]
inputs = depset(
direct_inputs,
transitive = transitive_inputs,
)
# FILEGROUP_DEF_BUILD_FRAGMENT_NAME stays at root so that
# kernel_prebuilt_repo can find it.
command = hermetic_tools.setup + """
tar cf {archive} --dereference \\
--transform 's:.*/{fragment}:{fragment}:g' \\
"$@"
""".format(
archive = filegroup_decl_archive.path,
fragment = FILEGROUP_DEF_BUILD_FRAGMENT_NAME,
)
args = ctx.actions.args()
args.add_all(inputs)
ctx.actions.run_shell(
inputs = inputs,
tools = hermetic_tools.deps,
outputs = [filegroup_decl_archive],
command = command,
arguments = [args],
progress_message = "Creating archive of kernel_filegroup declaration %{label}",
mnemonic = "KernelfilegroupDeclaration",
)
return filegroup_decl_archive
kernel_filegroup_declaration = rule(
implementation = _kernel_filegroup_declaration_impl,
doc = "Given a kernel_build, generates corresponding kernel_filegroup target declaration.",
attrs = {
"kernel_build": attr.label(
mandatory = True,
providers = [KernelBuildFilegroupDeclInfo],
),
"extra_deps": attr.label_list(
doc = """Extra files to be placed in the `deps` of the generated `kernel_filegroup`.
These files are downloaded separately by `kernel_prebuilt_repo`.
These files are not included in the generated archive.
""",
allow_files = True,
),
"images": attr.label(
doc = "Labels to look up system_dlkm_staging_archive.tar.gz",
allow_files = True,
),
"ddk_module_headers": attr.label_list(
doc = """
Label to kernel_build.ddk_module_headers.
Used as-is in the final kernel_filegroup invocation.""",
),
},
toolchains = [hermetic_toolchain.type],
)