blob: 0ee4963320a7ac4a103835545cc21ae318838755 [file] [log] [blame]
# 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.
"""
Defines a kernel build target.
"""
load("@bazel_skylib//lib:dicts.bzl", "dicts")
load("@bazel_skylib//lib:paths.bzl", "paths")
load("@bazel_skylib//lib:sets.bzl", "sets")
load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
load("@kernel_toolchain_info//:dict.bzl", "VARS")
load(
"//build/kernel/kleaf/artifact_tests:kernel_test.bzl",
"kernel_build_test",
"kernel_module_test",
)
load(":abi/base_kernel_utils.bzl", "base_kernel_utils")
load(":abi/force_add_vmlinux_utils.bzl", "force_add_vmlinux_utils")
load(":abi/trim_nonlisted_kmi_utils.bzl", "trim_nonlisted_kmi_utils")
load(":btf.bzl", "btf")
load(":cache_dir.bzl", "cache_dir")
load(
":common_providers.bzl",
"CompileCommandsInfo",
"CompileCommandsSingleInfo",
"DdkHeadersInfo",
"GcovInfo",
"KernelBuildAbiInfo",
"KernelBuildExtModuleInfo",
"KernelBuildFilegroupDeclInfo",
"KernelBuildInTreeModulesInfo",
"KernelBuildInfo",
"KernelBuildMixedTreeInfo",
"KernelBuildOriginalEnvInfo",
"KernelBuildUapiInfo",
"KernelBuildUnameInfo",
"KernelCmdsInfo",
"KernelConfigInfo",
"KernelEnvAttrInfo",
"KernelEnvMakeGoalsInfo",
"KernelImagesInfo",
"KernelSerializedEnvInfo",
"KernelToolchainInfo",
"KernelUnstrippedModulesInfo",
"ModuleSymversFileInfo",
)
load(":compile_commands_utils.bzl", "compile_commands_utils")
load(
":constants.bzl",
"MODULES_STAGING_ARCHIVE",
"MODULE_ENV_ARCHIVE_SUFFIX",
)
load(
":ddk/ddk_headers.bzl",
"ddk_headers_common_impl",
)
load(":debug.bzl", "debug")
load(":file.bzl", "file")
load(":file_selector.bzl", "file_selector", "file_selector_bool")
load(":gcov_utils.bzl", "gcov_attrs", "get_grab_gcno_step")
load(":hermetic_toolchain.bzl", "hermetic_toolchain")
load(":kernel_config.bzl", "kernel_config")
load(":kernel_config_settings.bzl", "kernel_config_settings")
load(":kernel_env.bzl", "kernel_env")
load(":kernel_headers.bzl", "kernel_headers")
load(":kernel_uapi_headers.bzl", "kernel_uapi_headers")
load(":kgdb.bzl", "kgdb")
load(":kmi_symbol_list.bzl", _kmi_symbol_list = "kmi_symbol_list")
load(":modules_prepare.bzl", "modules_prepare")
load(":raw_kmi_symbol_list.bzl", "raw_kmi_symbol_list")
load(":rustavailable.bzl", "rustavailable")
load(":utils.bzl", "kernel_utils", "utils")
visibility("//build/kernel/kleaf/...")
# Outputs of a kernel_build rule needed to build kernel_* that depends on it
_kernel_build_internal_outs = [
"Module.symvers",
"include/config/kernel.release",
]
_KERNEL_BUILD_OUT_ATTRS = ("outs", "module_outs", "implicit_outs", "module_implicit_outs", "internal_outs")
_KERNEL_BUILD_MODULE_OUT_ATTRS = ("module_outs", "module_implicit_outs")
_MODULES_PREPARE_ARCHIVE = "modules_prepare_outdir.tar.gz"
def kernel_build(
name,
outs,
build_config = None,
makefile = None,
keep_module_symvers = None,
keep_dot_config = None,
srcs = None,
module_outs = None,
implicit_outs = None,
module_implicit_outs = None,
generate_vmlinux_btf = None,
deps = None,
arch = None,
base_kernel = None,
make_goals = None,
kconfig_ext = None,
dtstree = None,
kmi_symbol_list = None,
protected_exports_list = None,
protected_modules_list = None,
additional_kmi_symbol_lists = None,
trim_nonlisted_kmi = None,
kmi_symbol_list_strict_mode = None,
collect_unstripped_modules = None,
kbuild_symtypes = None,
strip_modules = None,
module_signing_key = None,
system_trusted_key = None,
modules_prepare_force_generate_headers = None,
defconfig = None,
pre_defconfig_fragments = None,
post_defconfig_fragments = None,
defconfig_fragments = None,
check_defconfig = None,
page_size = None,
pack_module_env = None,
sanitizers = None,
ddk_module_defconfig_fragments = None,
ddk_module_headers = None,
kcflags = None,
clang_autofdo_profile = None,
**kwargs):
"""Defines a kernel build target with all dependent targets.
It uses a `build_config` to construct a deterministic build environment (e.g.
`common/build.config.gki.aarch64`). The kernel sources need to be declared
via srcs (using a `glob()`). outs declares the output files that are surviving
the build. The effective output file names will be
`$(name)/$(output_file)`. Any other artifact is not guaranteed to be
accessible after the rule has run.
A few additional labels are generated.
For example, if name is `"kernel_aarch64"`:
- `kernel_aarch64_uapi_headers` provides the UAPI kernel headers.
- `kernel_aarch64_headers` provides the kernel headers.
Args:
name: The final kernel target name, e.g. `"kernel_aarch64"`.
build_config: Label of the build.config file, e.g. `"build.config.gki.aarch64"`.
If it contains no files, the list of constants in `@kernel_toolchain_info` is used. This
is `//common:build.config.constants` by default, unless otherwise specified.
If it contains no files, [`makefile`](#kernel_build-makefile) must be set as the anchor
to the directory to run `make`.
makefile: `Makefile` governing the kernel tree sources (see `srcs`).
Example values:
* `None` (default): Falls back to the value of `KERNEL_DIR` from `build_config`.
`kernel_build()` executes `make` in `KERNEL_DIR`.
Note: The usage of specifying `KERNEL_DIR` in `build_config` is deprecated and will
trigger a warning/error in the future.
* `"//common:Makefile"` (most common): the kernel sources are located in
`//common`. This means `kernel_build()` executes `make` to build the kernel image
and in-tree drivers in `common`.
This usually replaces `//common:set_kernel_dir_build_config` in your `build_config`;
that is, if you set `kernel_build.makefile`, it is likely that you may drop
`//common:set_kernel_dir_build_config` from components of
`kernel_build.build_config`.
This replaces `KERNEL_DIR=common` in your `build_config`.
* `"@kleaf//common:Makefile"`: If you set up a DDK workspace such that Kleaf
tooling and your kernel source tree are located in the `@kleaf` submodule, you
should specify the full label in the package.
* the `Makefile` next to the build config:
For example:
```
kernel_build(
name = "tuna",
build_config = "//package:build.config.tuna", # the build.config.tuna is in //package
makefile = "//package:Makefile", # so set KERNEL_DIR to "package"
)
```
In this example, `build.config.tuna` is in `//package`. Hence,
setting `makefile = "Makefile"` is equivalent to the
legacy behavior of not setting `KERNEL_DIR` in `build.config`, and allowing
`_setup_env.sh` to decide the value by inferring from the directory containing the
build config, which is the `//package`.
* `Makefile` in the current package: the kernel sources are in the current package
where `kernel_build()` is called.
For example:
```
kernel_build(
name = "tuna",
build_config = "build.config.tuna", # the build.config.tuna is in this package
makefile = "Makefile", # so set KERNEL_DIR to this package
)
```
kconfig_ext: Label of an external Kconfig.ext file sourced by the GKI kernel.
keep_module_symvers: If set to True, a copy of the default output `Module.symvers` is kept.
* To avoid collisions in mixed build distribution packages, the file is renamed
as `$(name)_Module.symvers`.
* Default is False.
keep_dot_config: If set to True, a copy of the default output `.config` is kept.
* To avoid collisions in mixed build distribution packages, the file is renamed
as `$(name)_dot_config`.
* Default is False.
srcs: The kernel sources (a `glob()`). If unspecified or `None`, it is the following:
```
glob(
["**"],
exclude = [
"**/.*", # Hidden files
"**/.*/**", # Files in hidden directories
"**/BUILD.bazel", # build files
"**/*.bzl", # build files
],
)
```
arch: [Nonconfigurable](https://bazel.build/reference/be/common-definitions#configurable-attributes).
Target architecture. Default is `arm64`.
Value should be one of:
* `arm64`
* `x86_64`
* `riscv64`
* `arm` (for 32-bit, uncommon)
* `i386` (for 32-bit, uncommon)
This must be consistent to `ARCH` in build configs if the latter
is specified. Otherwise, a warning / error may be raised.
base_kernel: A label referring the base kernel build.
If set, the list of files specified in the `DefaultInfo` of the rule specified in
`base_kernel` is copied to a directory, and `KBUILD_MIXED_TREE` is set to the directory.
Setting `KBUILD_MIXED_TREE` effectively enables mixed build.
To set additional flags for mixed build, change `build_config` to a `kernel_build_config`
rule, with a build config fragment that contains the additional flags.
The label specified by `base_kernel` must produce a list of files similar
to what a `kernel_build` rule does. Usually, this points to one of the following:
- `//common:kernel_{arch}`
- A `kernel_filegroup` rule, e.g.
```
load("//build/kernel/kleaf:constants.bzl, "DEFAULT_GKI_OUTS")
kernel_filegroup(
name = "my_kernel_filegroup",
srcs = DEFAULT_GKI_OUTS,
)
```
make_goals: A list of strings defining targets for the kernel build.
This overrides `MAKE_GOALS` from build config if provided.
generate_vmlinux_btf: If `True`, generates `vmlinux.btf` that is stripped of any debug
symbols, but contains type and symbol information within a .BTF section.
This is suitable for ABI analysis through BTF.
Requires that `"vmlinux"` is in `outs`.
deps: Additional dependencies to build this kernel.
module_outs: A list of in-tree drivers. Similar to `outs`, but for `*.ko` files.
If a `*.ko` kernel module should not be copied to `${DIST_DIR}`, it must be
included `implicit_outs` instead of `module_outs`. The list `implicit_outs + module_outs`
must include **all** `*.ko` files in `${OUT_DIR}`. If not, a build error is raised.
Like `outs`, `module_outs` are part of the
[`DefaultInfo`](https://docs.bazel.build/versions/main/skylark/lib/DefaultInfo.html)
that this `kernel_build` returns. For example:
```
kernel_build(name = "kernel", module_outs = ["foo.ko"], ...)
pkg_files(name = "kernel_files", srcs = ["kernel"], ...)
pkg_install(name = "kernel_dist", srcs = [":kernel_files"])
```
`foo.ko` will be included in the distribution.
Like `outs`, this may be a `dict`. If so, it is wrapped in
[`select()`](https://docs.bazel.build/versions/main/configurable-attributes.html). See
documentation for `outs` for more details.
outs: The expected output files.
Note: in-tree modules should be specified in `module_outs` instead.
This attribute must be either a `dict` or a `list`. If it is a `list`, for each item
in `out`:
- If `out` does not contain a slash, the build rule
automatically finds a file with name `out` in the kernel
build output directory `${OUT_DIR}`.
```
find ${OUT_DIR} -name {out}
```
There must be exactly one match.
The file is copied to the following in the output directory
`{name}/{out}`
Example:
```
kernel_build(name = "kernel_aarch64", outs = ["vmlinux"])
```
The bulid system copies `${OUT_DIR}/[<optional subdirectory>/]vmlinux`
to `kernel_aarch64/vmlinux`.
`kernel_aarch64/vmlinux` is the label to the file.
- If `out` contains a slash, the build rule locates the file in the
kernel build output directory `${OUT_DIR}` with path `out`
The file is copied to the following in the output directory
1. `{name}/{out}`
2. `{name}/$(basename {out})`
Example:
```
kernel_build(
name = "kernel_aarch64",
outs = ["arch/arm64/boot/vmlinux"])
```
The bulid system copies
`${OUT_DIR}/arch/arm64/boot/vmlinux`
to:
- `kernel_aarch64/arch/arm64/boot/vmlinux`
- `kernel_aarch64/vmlinux`
They are also the labels to the output files, respectively.
See `search_and_cp_output.py` for details.
Files in `outs` are part of the
[`DefaultInfo`](https://docs.bazel.build/versions/main/skylark/lib/DefaultInfo.html)
that this `kernel_build` returns. For example:
```
kernel_build(name = "kernel", outs = ["vmlinux"], ...)
pkg_files(name = "kernel_files", srcs = ["kernel"], ...)
pkg_install(name = "kernel_dist", srcs = [":kernel_files"])
```
`vmlinux` will be included in the distribution.
If it is a `dict`, it is wrapped in
[`select()`](https://docs.bazel.build/versions/main/configurable-attributes.html).
Example:
```
kernel_build(
name = "kernel_aarch64",
outs = {"config_foo": ["vmlinux"]})
```
If conditions in `config_foo` is met, the rule is equivalent to
```
kernel_build(
name = "kernel_aarch64",
outs = ["vmlinux"])
```
As explained above, the bulid system copies `${OUT_DIR}/[<optional subdirectory>/]vmlinux`
to `kernel_aarch64/vmlinux`.
`kernel_aarch64/vmlinux` is the label to the file.
Note that a `select()` may not be passed into `kernel_build()` because
[`select()` cannot be evaluated in macros](https://docs.bazel.build/versions/main/configurable-attributes.html#why-doesnt-select-work-in-macros).
Hence:
- [combining `select()`s](https://docs.bazel.build/versions/main/configurable-attributes.html#combining-selects)
is not allowed. Instead, expand the cartesian product.
- To use
[`AND` chaining](https://docs.bazel.build/versions/main/configurable-attributes.html#or-chaining)
or
[`OR` chaining](https://docs.bazel.build/versions/main/configurable-attributes.html#selectsconfig_setting_group),
use `selects.config_setting_group()`.
implicit_outs: Like `outs`, but not copied to the distribution directory.
Labels are created for each item in `implicit_outs` as in `outs`.
module_implicit_outs: like `module_outs`, but not copied to the distribution directory.
Labels are created for each item in `module_implicit_outs` as in `outs`.
kmi_symbol_list: A label referring to the main KMI symbol list file. See `additional_kmi_symbol_lists`.
This is the Bazel equivalent of `ADDITIONAL_KMI_SYMBOL_LISTS`.
additional_kmi_symbol_lists: A list of labels referring to additional KMI symbol list files.
This is the Bazel equivalent of `ADDITIONAL_KMI_SYMBOL_LISTS`.
Let
```
all_kmi_symbol_lists = [kmi_symbol_list] + additional_kmi_symbol_list
```
If `all_kmi_symbol_lists` is a non-empty list, `abi_symbollist` and
`abi_symbollist.report` are created and added to the
[`DefaultInfo`](https://docs.bazel.build/versions/main/skylark/lib/DefaultInfo.html),
and copied to `DIST_DIR` during distribution.
If `all_kmi_symbol_lists` is `None` or an empty list, `abi_symbollist` and
`abi_symbollist.report` are not created.
It is possible to use a `glob()` to determine whether `abi_symbollist`
and `abi_symbollist.report` should be generated at build time.
For example:
```
kmi_symbol_list = "android/abi_gki_aarch64",
additional_kmi_symbol_lists = glob(["android/abi_gki_aarch64*"], exclude = ["android/abi_gki_aarch64"]),
```
protected_exports_list: A file containing list of protected exports.
For example:
```
protected_exports_list = "//common:android/abi_gki_protected_exports"
```
protected_modules_list: A file containing list of protected modules,
For example:
```
protected_modules_list = "//common:android/gki_protected_modules"
```
trim_nonlisted_kmi: If `True`, trim symbols not listed in
`kmi_symbol_list` and `additional_kmi_symbol_lists`.
This is the Bazel equivalent of `TRIM_NONLISTED_KMI`.
Requires `all_kmi_symbol_lists` to be non-empty. If `kmi_symbol_list`
or `additional_kmi_symbol_lists`
is a `glob()`, it is possible to set `trim_nonlisted_kmi` to be a
value based on that `glob()`. For example:
```
trim_nonlisted_kmi = len(glob(["android/abi_gki_aarch64*"])) > 0
```
kmi_symbol_list_strict_mode: If `True`, add a build-time check between
`[kmi_symbol_list] + additional_kmi_symbol_lists`
and the KMI resulting from the build, to ensure
they match 1-1.
collect_unstripped_modules: If `True`, provide all unstripped in-tree.
kbuild_symtypes: The value of `KBUILD_SYMTYPES`.
This can be set to one of the following:
- `"true"`
- `"false"`
- `"auto"`
- `None`, which defaults to `"auto"`
If the value is `"auto"`, it is determined by the `--kbuild_symtypes`
flag.
If the value is `"true"`; or the value is `"auto"` and
`--kbuild_symtypes` is specified, then `KBUILD_SYMTYPES=1`.
**Note**: kernel build time can be significantly longer.
If the value is `"false"`; or the value is `"auto"` and
`--kbuild_symtypes` is not specified, then `KBUILD_SYMTYPES=`.
strip_modules: If `None` or not specified, default is `False`.
If set to `True`, debug information for distributed modules is stripped.
This corresponds to negated value of `DO_NOT_STRIP_MODULES` in `build.config`.
module_signing_key: A label referring to a module signing key.
This is to allow for dynamic setting of `CONFIG_MODULE_SIG_KEY` from Bazel.
system_trusted_key: A label referring to a trusted system key.
This is to allow for dynamic setting of `CONFIG_SYSTEM_TRUSTED_KEY` from Bazel.
dtstree: Device tree support.
modules_prepare_force_generate_headers: If `True` it forces generation of
additional headers as part of modules_prepare.
defconfig: Label to the base defconfig.
As a convention, files should usually be named `<device>_defconfig`
(e.g. `tuna_defconfig`) to provide human-readable hints during the build. The prefix
should be the name of the `kernel_build`. However, this is not a requirement.
These configs are also applied to external modules, including
`kernel_module`s and `ddk_module`s.
For mixed builds (`base_kernel` is set), this is usually set to the `defconfig`
of the `base_kernel`, e.g. `//common:arch/arm64/configs/gki_defconfig`.
If `check_defconfig` is not `disabled`,
Items must be present in the intermediate `.config` before `post_defconfig_fragments`
are applied. See `build/kernel/kleaf/docs/kernel_config.md` for details.
As a special case, if this is evaluated to `//build/kernel/kleaf:allmodconfig`, Kleaf
builds all modules except those exluded in `post_defconfig_fragments`. In this case,
`pre_defconfig_fragments` must not be set.
See [`build/kernel/kleaf/docs/kernel_config.md`](../kernel_config.md) for details.
pre_defconfig_fragments: A list of fragments that are applied to the defconfig
**before** `make defconfig`.
Even though this is a list, it is highly recommended that the list contains
**at most one item**. This is so that `tools/bazel run <name>_config` applies to
the single pre defconfig fragment correctly.
As a convention, files should usually be named `<prop>_defconfig`
(e.g. `16k_defconfig`) or `<prop>_<value>_defconfig` (e.g. `page_size_16k_defconfig`)
to provide human-readable hints during the build. The prefix should
describe what the defconfig does. However, this is not a requirement.
These configs are also applied to external modules, including
`kernel_module`s and `ddk_module`s.
For mixed builds (`base_kernel` is set), the file usually contains additional
in-tree modules to build on top of `gki_defconfig`, e.g. `CONFIG_FOO=m`.
**NOTE**: `pre_defconfig_fragments` are applied **before** `make defconfig`, similar
to `PRE_DEFCONFIG_CMDS`. If you had `POST_DEFCONFIG_CMDS` applying fragments in your
build configs, consider using `post_defconfig_fragments` instead.
**NOTE**: **Order matters**, unlike `post_defconfig_fragments`. If there are conflicting
items, later items overrides earlier items.
If `check_defconfig` is not `disabled`,
Items must be present in the intermediate `.config` before `post_defconfig_fragments`
are applied. See `build/kernel/kleaf/docs/kernel_config.md` for details.
post_defconfig_fragments: A list of fragments that are applied to the defconfig
**after** `make defconfig`.
As a convention, files should usually be named `<prop>_defconfig`
(e.g. `kasan_defconfig`) or `<prop>_<value>_defconfig` (e.g. `lto_none_defconfig`)
to provide human-readable hints during the build. The prefix should
describe what the defconfig does. However, this is not a requirement.
These configs are also applied to external modules, including
`kernel_module`s and `ddk_module`s.
Files usually contain debug options. If you want to build in-tree modules, adding them
to `pre_defconfig_fragments` may be a better choice.
**NOTE**: `post_defconfig_fragments` are applied **after** `make defconfig`, similar
to `POST_DEFCONFIG_CMDS`. If you had `PRE_DEFCONFIG_CMDS` applying fragments in your
build configs, consider using `pre_defconfig_fragments` instead.
If `check_defconfig` is not `disabled`,
Items must be present in the final `.config`. See
`build/kernel/kleaf/docs/kernel_config.md` for details.
defconfig_fragments: **Deprecated**. Same as `post_defconfig_fragments`.
check_defconfig: Default is `match`.
If `disabled`, no check is performed.
If `match`, checks `.config` against the `defconfig`, `pre_defconfig_fragments`
and ` post_defconfig_fragments`.
If `minimized`, checks `.config` against the result of
`make savedefconfig` right after `make defconfig`, but before
`post_defconfig_fragments` are applied.
This can be set to `minimized` **only if** `defconfig` is set and `pre_defconfig_fragments`
is not set.
page_size: Default is `"default"`. Page size of the kernel build.
Value may be one of `"default"`, `"4k"`, `"16k"` or `"64k"`. If
`"default"`, the defconfig is left as-is.
16k / 64k page size is only supported on `arch = "arm64"`.
pack_module_env: If `True`, create `{name}_module_env.tar.gz`
and other archives as part of the default output of this target.
These archives contains necessary files to build external modules.
sanitizers: **non-configurable**. A list of sanitizer configurations.
By default, no sanitizers are explicity configured; values in defconfig are
respected. Possible values are:
- `["kasan_any_mode"]`
- `["kasan_sw_tags"]`
- `["kasan_generic"]`
- `["kcsan"]`
ddk_module_defconfig_fragments: A list of additional defconfigs, to be used
in `ddk_module`s building against this kernel.
Unlike `post_defconfig_fragments`, `ddk_module_defconfig_fragments` is not applied
to this `kernel_build` target, nor dependent legacy `kernel_module`s.
ddk_module_headers: A list of `ddk_headers`, to be used in `ddk_module`s
building against this kernel.
Inherits `ddk_module_headers` from `base_kernel`, with a lower priority
than `ddk_module_headers` of this kernel_build.
These headers are not applied to this `kernel_build` target.
kcflags: Extra `KCFLAGS`. Empty by default.
To add common KCFLAGS, you must explicitly set
it to `COMMON_KCFLAGS` (see `//build/kernel/kleaf:constants.bzl`).
clang_autofdo_profile: Path to an AutoFDO profile,
For example:
```
clang_autofdo_profile = "//toolchain/pgo-profiles/kernel:aarch64/android16-6.12/kernel.afdo"
```
**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-attributes).
"""
env_target_name = name + "_env"
config_target_name = name + "_config"
modules_prepare_target_name = name + "_modules_prepare"
uapi_headers_target_name = name + "_uapi_headers"
headers_target_name = name + "_headers"
src_kmi_symbol_list_target_name = name + "_src_kmi_symbol_list"
kmi_symbol_list_target_name = name + "_kmi_symbol_list"
abi_symbollist_target_name = name + "_kmi_symbol_list_abi_symbollist"
raw_kmi_symbol_list_target_name = name + "_raw_kmi_symbol_list"
# Currently only support one sanitizer
if sanitizers and len(sanitizers) > 1:
fail("only one sanitizer may be passed to kernel_build.sanitizers")
if srcs == None:
srcs = native.glob(
["**"],
exclude = [
"**/.*",
"**/.*/**",
"**/BUILD.bazel",
"**/*.bzl",
],
)
if strip_modules == None:
strip_modules = False
if arch == None:
arch = "arm64"
internal_kwargs = dict(kwargs)
internal_kwargs.pop("visibility", None)
kwargs_with_manual = dict(kwargs)
kwargs_with_manual["tags"] = ["manual"]
if defconfig_fragments:
if post_defconfig_fragments:
fail("""{}: defconfig_fragments and post_defconfig_fragments cannot be set simultaneously.
Please merge defconfig_fragments into post_defconfig_fragments and delete defconfig_fragments.""".format(
native.package_relative_label(name),
))
# buildifier: disable=print
print("""
WARNING: {}: defconfig_fragments is deprecated; use post_defconfig_fragments instead.
If you want to apply defconfig fragments before `make defconfig`, use pre_defconfig_fragments instead.""".format(
native.package_relative_label(name),
))
post_defconfig_fragments = defconfig_fragments
post_defconfig_fragments = _get_post_defconfig_fragments(
kernel_build_name = name,
kernel_build_post_defconfig_fragments = post_defconfig_fragments,
kernel_build_arch = arch,
kernel_build_page_size = page_size,
kernel_build_sanitizers = sanitizers,
**internal_kwargs
)
trim_post_defconfig_fragment = _get_trim_post_defconfig_fragment_target(
kernel_build_name = name,
kernel_build_trim_nonlisted_kmi = trim_nonlisted_kmi,
**internal_kwargs
)
# Do not use append because the returned value may not be a list.
# buildifier: disable=list-append
post_defconfig_fragments += [trim_post_defconfig_fragment]
# Prevent accidental usage
trim_nonlisted_kmi = struct(message = "DO NOT USE ME! Use trim_post_defconfig_fragment instead.")
native.platform(
name = name + "_platform_target",
constraint_values = [
Label("@platforms//os:android"),
Label("@platforms//cpu:{}".format(arch)),
],
**internal_kwargs
)
native.platform(
name = name + "_platform_exec",
# Note that this does not respect --host_platform.
parents = [Label("@platforms//host")],
**internal_kwargs
)
native.platform(
name = name + "_platform_exec_musl",
parents = [name + "_platform_exec"],
constraint_values = [
Label("//build/kernel/kleaf/impl:musl"),
],
**internal_kwargs
)
kernel_env(
name = env_target_name,
build_config = build_config,
makefile = makefile,
kconfig_ext = kconfig_ext,
dtstree = dtstree,
srcs = srcs,
kbuild_symtypes = kbuild_symtypes,
make_goals = make_goals,
target_platform = name + "_platform_target",
exec_platform = select({
Label("//build/kernel/kleaf:musl_kbuild_is_true"): name + "_platform_exec_musl",
"//conditions:default": name + "_platform_exec",
}),
pre_defconfig_fragments = pre_defconfig_fragments,
post_defconfig_fragments = post_defconfig_fragments,
kcflags = kcflags,
clang_autofdo_profile = clang_autofdo_profile,
**internal_kwargs
)
# Wrap in a target so kmi_symbol_list is configurable. A select() value cannot be
# embedded in the all_kmi_symbol_lists below.
file(
name = src_kmi_symbol_list_target_name,
src = kmi_symbol_list,
**internal_kwargs
)
all_kmi_symbol_lists = [src_kmi_symbol_list_target_name]
if additional_kmi_symbol_lists:
all_kmi_symbol_lists += additional_kmi_symbol_lists
_kmi_symbol_list(
name = kmi_symbol_list_target_name,
env = env_target_name,
srcs = all_kmi_symbol_lists,
**internal_kwargs
)
native.filegroup(
name = abi_symbollist_target_name,
srcs = [kmi_symbol_list_target_name],
output_group = "abi_symbollist",
**internal_kwargs
)
raw_kmi_symbol_list(
name = raw_kmi_symbol_list_target_name,
env = env_target_name,
src = abi_symbollist_target_name,
**internal_kwargs
)
kernel_config(
name = config_target_name,
env = env_target_name,
srcs = srcs,
trim_nonlisted_kmi = trim_post_defconfig_fragment,
raw_kmi_symbol_list = raw_kmi_symbol_list_target_name,
module_signing_key = module_signing_key,
system_trusted_key = system_trusted_key,
defconfig = defconfig,
pre_defconfig_fragments = pre_defconfig_fragments,
post_defconfig_fragments = post_defconfig_fragments,
check_defconfig = check_defconfig,
**internal_kwargs
)
modules_prepare(
name = modules_prepare_target_name,
config = config_target_name,
srcs = srcs,
outdir_tar_gz = modules_prepare_target_name + "/" + _MODULES_PREPARE_ARCHIVE,
force_generate_headers = modules_prepare_force_generate_headers,
**internal_kwargs
)
_kernel_build(
name = name,
config = config_target_name,
keep_module_symvers = keep_module_symvers,
keep_dot_config = keep_dot_config,
srcs = srcs + all_kmi_symbol_lists,
outs = kernel_utils.transform_kernel_build_outs(name, "outs", outs),
module_outs = kernel_utils.transform_kernel_build_outs(name, "module_outs", module_outs),
implicit_outs = kernel_utils.transform_kernel_build_outs(name, "implicit_outs", implicit_outs),
module_implicit_outs = kernel_utils.transform_kernel_build_outs(name, "module_implicit_outs", module_implicit_outs),
internal_outs = kernel_utils.transform_kernel_build_outs(name, "internal_outs", _kernel_build_internal_outs),
deps = deps,
base_kernel = base_kernel,
modules_prepare = modules_prepare_target_name,
kmi_symbol_list_strict_mode = kmi_symbol_list_strict_mode,
raw_kmi_symbol_list = raw_kmi_symbol_list_target_name,
kernel_uapi_headers = uapi_headers_target_name,
collect_unstripped_modules = collect_unstripped_modules,
combined_abi_symbollist = abi_symbollist_target_name,
strip_modules = strip_modules,
src_protected_exports_list = protected_exports_list,
src_protected_modules_list = protected_modules_list,
src_kmi_symbol_list = kmi_symbol_list,
trim_nonlisted_kmi = trim_post_defconfig_fragment,
pack_module_env = pack_module_env,
sanitizers = sanitizers,
ddk_module_defconfig_fragments = ddk_module_defconfig_fragments,
ddk_module_headers = ddk_module_headers,
arch = arch,
**kwargs
)
# key = attribute name, value = a list of labels for that attribute
real_outs = {}
for out_name, out_attr_val in (
("outs", outs),
("module_outs", module_outs),
("implicit_outs", implicit_outs),
("module_implicit_outs", module_implicit_outs),
# internal_outs are opaque to the user, hence we don't create a alias (filegroup) for them.
):
if out_attr_val == None:
continue
if type(out_attr_val) == type([]):
for out in out_attr_val:
native.filegroup(name = name + "/" + out, srcs = [":" + name], output_group = out, **kwargs)
if out != paths.basename(out):
native.filegroup(name = name + "/" + paths.basename(out), srcs = [":" + name], output_group = out, **kwargs)
real_outs[out_name] = [name + "/" + out for out in out_attr_val]
elif type(out_attr_val) == type({}):
# out_attr_val = {config_setting: [out, ...], ...}
# => reverse_dict = {out: [config_setting, ...], ...}
for out, config_settings in utils.reverse_dict(out_attr_val).items():
native.filegroup(
name = name + "/" + out,
# Use a select() to prevent this rule to build when config_setting is not fulfilled.
srcs = select({
config_setting: [":" + name]
for config_setting in config_settings
}),
output_group = out,
# Use "manual" tags to prevent it to be built with ...
**kwargs_with_manual
)
if out != paths.basename(out):
native.filegroup(
name = name + "/" + paths.basename(out),
# Use a select() to prevent this rule to build when config_setting is not fulfilled.
srcs = select({
config_setting: [":" + name]
for config_setting in config_settings
}),
output_group = out,
# Use "manual" tags to prevent it to be built with ...
**kwargs_with_manual
)
real_outs[out_name] = [name + "/" + out for out, _ in utils.reverse_dict(out_attr_val).items()]
else:
fail("Unexpected type {} for {}: {}".format(type(out_attr_val), out_name, out_attr_val))
kernel_uapi_headers(
name = uapi_headers_target_name,
config = config_target_name,
srcs = srcs,
**kwargs
)
kernel_headers(
name = headers_target_name,
kernel_build = name,
env = env_target_name,
# TODO: We need arch/ and include/ only.
srcs = srcs,
**kwargs
)
if generate_vmlinux_btf:
btf_name = name + "_btf"
btf(
name = btf_name,
vmlinux = name + "/vmlinux",
env = env_target_name,
**kwargs
)
kernel_build_test(
name = name + "_test",
target = name,
**kwargs
)
kernel_module_test(
name = name + "_modules_test",
modules = (real_outs.get("module_outs") or []) + (real_outs.get("module_implicit_outs") or []),
**kwargs
)
# buildifier: disable=print
def _skip_build_checks(ctx, what):
# Skip for these flags as they are usually debug targets.
for flag in (
"kasan",
"kasan_sw_tags",
"kasan_generic",
"kcov",
"kcsan",
"kgdb",
"debug",
"gcov",
):
if getattr(ctx.attr, "_" + flag)[BuildSettingInfo].value:
print("\nWARNING: {this_label}: {what} was \
IGNORED because --{flag} is set!".format(this_label = ctx.label, what = what, flag = flag))
return True
if ctx.attr.sanitizers[0] != "default":
print("\nWARNING: {this_label}: {what} was \
IGNORED because kernel_build.sanitizers is set!".format(this_label = ctx.label, what = what))
return True
return False
def _get_post_defconfig_fragments(
kernel_build_name,
kernel_build_post_defconfig_fragments,
kernel_build_arch,
kernel_build_page_size,
kernel_build_sanitizers,
**internal_kwargs):
# Use a separate list to avoid .append on the provided object directly.
# kernel_build_post_defconfig_fragments could be a list or a select() expression.
additional_fragments = [
Label("//build/kernel/kleaf:defconfig_fragment"),
Label("//build/kernel/kleaf/impl/defconfig:debug"),
Label("//build/kernel/kleaf/impl/defconfig:gcov"),
Label("//build/kernel/kleaf/impl/defconfig:lto"),
Label("//build/kernel/kleaf/impl/defconfig:kcov"),
Label("//build/kernel/kleaf/impl/defconfig:rust"),
Label("//build/kernel/kleaf/impl/defconfig:rust_ashmem"),
Label("//build/kernel/kleaf/impl/defconfig:zstd_dwarf_compression"),
]
btf_debug_info_target = kernel_build_name + "_defconfig_fragment_btf_debug_info"
file_selector(
name = btf_debug_info_target,
first_selector = select({
Label("//build/kernel/kleaf:btf_debug_info_is_enabled"): "enable",
Label("//build/kernel/kleaf:btf_debug_info_is_disabled"): "disable",
# TODO(b/229662633): Add kernel_build.btf_debug_info. After that, this should be
# `kernel_build_btf_debug_info or "enable"`.
"//conditions:default": "default",
}),
files = {
Label("//build/kernel/kleaf/impl/defconfig:btf_debug_info_enabled_defconfig"): "enable",
Label("//build/kernel/kleaf/impl/defconfig:btf_debug_info_disabled_defconfig"): "disable",
# If --btf_debug_info=default, do not apply any defconfig fragments
Label("//build/kernel/kleaf/impl:empty_filegroup"): "default",
},
**internal_kwargs
)
additional_fragments.append(btf_debug_info_target)
page_size_target = kernel_build_name + "_defconfig_fragment_page_size"
file_selector(
name = page_size_target,
first_selector = select({
Label("//build/kernel/kleaf:page_size_4k"): "4k",
Label("//build/kernel/kleaf:page_size_16k"): "16k",
Label("//build/kernel/kleaf:page_size_64k"): "64k",
# If --page_size=default, use kernel_build.page_size; If kernel_build.page_size
# is also unset, use "default".
"//conditions:default": None,
}),
second_selector = kernel_build_page_size,
third_selector = "default",
files = {
Label("//build/kernel/kleaf/impl/defconfig:{}_4k_defconfig".format(kernel_build_arch)): "4k",
Label("//build/kernel/kleaf/impl/defconfig:{}_16k_defconfig".format(kernel_build_arch)): "16k",
Label("//build/kernel/kleaf/impl/defconfig:{}_64k_defconfig".format(kernel_build_arch)): "64k",
# If --page_size=default, do not apply any defconfig fragments
Label("//build/kernel/kleaf/impl:empty_filegroup"): "default",
},
**internal_kwargs
)
additional_fragments.append(page_size_target)
kernel_build_sanitizer = "default"
if kernel_build_sanitizers:
kernel_build_sanitizer = kernel_build_sanitizers[0]
sanitizer_target = kernel_build_name + "_defconfig_fragment_sanitizer"
file_selector(
name = sanitizer_target,
first_selector = select({
Label("//build/kernel/kleaf/impl:kasan_any_mode_is_set_to_true"): "kasan_any_mode",
Label("//build/kernel/kleaf/impl:kasan_sw_tags_is_set_to_true"): "kasan_sw_tags",
Label("//build/kernel/kleaf/impl:kasan_generic_is_set_to_true"): "kasan_generic",
Label("//build/kernel/kleaf/impl:kcsan_is_set_to_true"): "kcsan",
"//conditions:default": None,
}),
second_selector = kernel_build_sanitizer,
third_selector = "default",
files = {
Label("//build/kernel/kleaf/impl/defconfig:kasan_any_mode"): "kasan_any_mode",
Label("//build/kernel/kleaf/impl/defconfig:{}_kasan_sw_tags".format(kernel_build_arch)): "kasan_sw_tags",
Label("//build/kernel/kleaf/impl/defconfig:kasan_generic"): "kasan_generic",
Label("//build/kernel/kleaf/impl/defconfig:kcsan"): "kcsan",
Label("//build/kernel/kleaf/impl:empty_filegroup"): "default",
},
**internal_kwargs
)
additional_fragments.append(sanitizer_target)
if kernel_build_post_defconfig_fragments == None:
kernel_build_post_defconfig_fragments = []
# Do not call kernel_build_post_defconfig_fragments += ... to avoid
# modifying the incoming object from kernel_build.post_defconfig_fragments.
return kernel_build_post_defconfig_fragments + additional_fragments
def _get_trim_post_defconfig_fragment_target(
kernel_build_name,
kernel_build_trim_nonlisted_kmi,
**internal_kwargs):
trim_target = kernel_build_name + "_defconfig_fragment_trim"
file_selector_bool(
name = trim_target,
first_selector = select({
Label("//build/kernel/kleaf/impl:force_disable_trim_is_true"): False,
Label("//build/kernel/kleaf:debug_is_true"): False,
Label("//build/kernel/kleaf:gcov_is_true"): False,
Label("//build/kernel/kleaf:kcov_is_true"): False,
Label("//build/kernel/kleaf:kasan_is_true"): False,
Label("//build/kernel/kleaf:kcsan_is_true"): False,
Label("//build/kernel/kleaf:kgdb_is_true"): False,
"//conditions:default": None,
}),
second_selector = kernel_build_trim_nonlisted_kmi,
# When the value is not specified in the kernel_build rule, do nothing (the "" case)
third_selector = None,
files = {
Label("//build/kernel/kleaf/impl/defconfig:notrim_defconfig"): "False",
Label("//build/kernel/kleaf/impl/defconfig:trim_defconfig"): "True",
Label("//build/kernel/kleaf/impl:empty_filegroup"): "",
},
**internal_kwargs
)
return trim_target
def _uniq(lst):
"""Deduplicates items in lst."""
return sets.to_list(sets.make(lst))
def _progress_message_suffix(ctx):
"""Returns suffix for all progress messages for kernel_build."""
return "{} %{{label}}".format(
ctx.attr.config[KernelEnvAttrInfo].progress_message_note,
)
def _create_kbuild_mixed_tree(ctx):
"""Adds actions that creates the `KBUILD_MIXED_TREE`."""
if not base_kernel_utils.get_base_kernel(ctx):
return struct(
inputs = depset(),
cmd = "",
base_kernel_files = depset(),
arg = "",
)
if VARS.get("KLEAF_INTERNAL_KBUILD_MIXED_TREE_IS_OBJTREE") != "1":
return _create_kbuild_mixed_tree_legacy(ctx)
# Return a command line that copies KBUILD_MIXED_TREE files to $OUT_DIR
# (which is $objtree when building in-tree modules)
base_kernel_files = base_kernel_utils.get_base_kernel(ctx)[KernelBuildMixedTreeInfo].files
cmd = """
# Restore GKI artifacts for mixed build
export KBUILD_MIXED_TREE=${OUT_DIR}
mkdir -p ${KBUILD_MIXED_TREE}
"""
# This to_list() is acceptable because GKI's outs/module_outs is a small list
for base_kernel_file in base_kernel_files.to_list():
# Flatten the directory structure of the files that base_kernel_utils.get_base_kernel(ctx)
# provides because KBUILD_MIXED_TREE accepts a flattened directory.
cmd += """
cp -a -t ${{KBUILD_MIXED_TREE}} $(readlink -m {base_kernel_file})
""".format(
base_kernel_file = base_kernel_file.path,
)
arg = "--srcdir ${KBUILD_MIXED_TREE}"
return struct(
inputs = base_kernel_files,
cmd = cmd,
base_kernel_files = base_kernel_files,
arg = arg,
)
def _create_kbuild_mixed_tree_legacy(ctx):
"""Legacy way of handling KBUILD_MIXED_TREE before 6.13"""
hermetic_tools = hermetic_toolchain.get(ctx)
# Create a directory for KBUILD_MIXED_TREE.
# Flatten the directory structure of the files that base_kernel_utils.get_base_kernel(ctx)
# provides because KBUILD_MIXED_TREE accepts a flattened directory.
# declare_directory is sufficient because the directory should
# only change when the dependent base_kernel_utils.get_base_kernel(ctx) changes.
kbuild_mixed_tree = ctx.actions.declare_directory("{}_kbuild_mixed_tree".format(ctx.label.name))
returned_inputs = depset([kbuild_mixed_tree])
base_kernel_files = base_kernel_utils.get_base_kernel(ctx)[KernelBuildMixedTreeInfo].files
kbuild_mixed_tree_command = hermetic_tools.setup + """
# Restore GKI artifacts for mixed build
export KBUILD_MIXED_TREE=$(realpath {kbuild_mixed_tree})
rm -rf ${{KBUILD_MIXED_TREE}}
mkdir -p ${{KBUILD_MIXED_TREE}}
for base_kernel_file in {base_kernel_files}; do
cp -a -t ${{KBUILD_MIXED_TREE}} $(readlink -m ${{base_kernel_file}})
done
""".format(
# This to_list() is acceptable because GKI's outs/module_outs is a small list
base_kernel_files = " ".join([file.path for file in base_kernel_files.to_list()]),
kbuild_mixed_tree = kbuild_mixed_tree.path,
)
debug.print_scripts(ctx, kbuild_mixed_tree_command, what = "kbuild_mixed_tree")
ctx.actions.run_shell(
mnemonic = "KernelBuildKbuildMixedTree",
inputs = depset(transitive = [base_kernel_files]),
outputs = [kbuild_mixed_tree],
tools = hermetic_tools.deps,
progress_message = "Creating KBUILD_MIXED_TREE{}".format(_progress_message_suffix(ctx)),
command = kbuild_mixed_tree_command,
)
cmd = """
export KBUILD_MIXED_TREE=$(realpath {kbuild_mixed_tree})
""".format(
kbuild_mixed_tree = kbuild_mixed_tree.path,
)
arg = "--srcdir ${KBUILD_MIXED_TREE}"
return struct(
inputs = returned_inputs,
cmd = cmd,
base_kernel_files = base_kernel_files,
arg = arg,
)
def _get_base_kernel_all_module_names(ctx):
"""Returns the file containing all module names from the base kernel or `[]` if there's no base_kernel."""
base_kernel_for_module_names = base_kernel_utils.get_base_kernel_for_module_names(ctx)
if base_kernel_for_module_names:
return base_kernel_for_module_names[KernelBuildInTreeModulesInfo].all_module_names
return []
def _get_out_attr_vals(ctx):
"""Common implementation for getting all ctx.attr.*out.
This function should be used instead of actually inspecting ctx.attr.*out, because
this function also handles cases with additional outputs added by config settings.
"""
attr_vals = {attr: getattr(ctx.attr, attr) for attr in _KERNEL_BUILD_OUT_ATTRS}
# The list is immutable, so use x = x + ... to make a copy
attr_vals["outs"] = attr_vals["outs"] + force_add_vmlinux_utils.additional_outs(ctx)
return attr_vals
def _declare_all_output_files(ctx):
"""Declares output files based on `ctx.attr.*outs`."""
attr_vals = _get_out_attr_vals(ctx)
# kernel_build(name="kernel", outs=["out"])
# => _kernel_build(name="kernel", outs=["kernel/out"], internal_outs=["kernel/Module.symvers", ...])
# => all_output_names = ["foo", "Module.symvers", ...]
# all_output_files = {"out": {"foo": File(...)}, "internal_outs": {"Module.symvers": File(...)}, ...}
all_output_files = {}
for attr, val in attr_vals.items():
all_output_files[attr] = {
name: ctx.actions.declare_file("{}/{}".format(ctx.label.name, name))
for name in val
}
return all_output_files
def _split_out_attrs(ctx):
"""Partitions items in *outs into two lists: non-modules and modules."""
non_modules = []
modules = []
attr_vals = _get_out_attr_vals(ctx)
for attr, val in attr_vals.items():
if attr in _KERNEL_BUILD_MODULE_OUT_ATTRS:
modules += val
else:
non_modules += val
return struct(
non_modules = non_modules,
modules = modules,
)
def _write_module_names_to_file(ctx, filename, names):
"""Adds an action that writes |names| to a file named |filename|. Each item occupies a line."""
all_module_names_file = ctx.actions.declare_file("{}_all_module_names/{}".format(ctx.label.name, filename))
ctx.actions.write(
output = all_module_names_file,
content = "\n".join(names) + "\n",
)
return all_module_names_file
# A "step" contains these fields:
#
# * inputs: a list of source files for this step
# * tools: a list of required tools for this step
# * outputs: a list of generated files for this step
# * cmd (optional): the command for this step
# * Other special fields.
#
# In other words, a step is a weaker form of an [Action](https://bazel.build/rules/lib/Action),
# but because the `OUT_DIR` needs to be kept between the steps, they are stuffed into the main
# action.
def _get_grab_intree_modules_step(ctx, has_any_modules, modules_staging_dir, ruledir, all_module_names):
"""Returns a step for grabbing the in-tree modules from `OUT_DIR`.
Returns:
A struct with these fields:
* inputs
* tools
* cmd
* outputs
"""
tools = []
grab_intree_modules_cmd = ""
if has_any_modules:
tools.append(ctx.executable._search_and_cp_output)
grab_intree_modules_cmd = """
{search_and_cp_output} --srcdir {modules_staging_dir}/lib/modules/*/kernel --dstdir {ruledir} {all_module_names}
""".format(
search_and_cp_output = ctx.executable._search_and_cp_output.path,
modules_staging_dir = modules_staging_dir,
ruledir = ruledir,
all_module_names = " ".join(all_module_names),
)
return struct(
inputs = [],
tools = tools,
cmd = grab_intree_modules_cmd,
outputs = [],
)
def _get_grab_unstripped_modules_step(ctx, has_any_modules, all_module_basenames_file):
"""Returns a step for grabbing the unstripped in-tree modules from `OUT_DIR`.
Returns:
A struct with these fields:
* inputs
* tools
* cmd
* outputs
* unstripped_dir: A [File](https://bazel.build/rules/lib/File), which is a directory pointing
to a directory containing the unstripped modules.
"""
grab_unstripped_intree_modules_cmd = ""
inputs = []
tools = []
outputs = []
unstripped_dir = None
if ctx.attr.collect_unstripped_modules:
unstripped_dir = ctx.actions.declare_directory("{name}/unstripped".format(name = ctx.label.name))
outputs.append(unstripped_dir)
if has_any_modules:
tools.append(ctx.executable._search_and_cp_output)
inputs.append(all_module_basenames_file)
grab_unstripped_intree_modules_cmd = """
mkdir -p {unstripped_dir}
{search_and_cp_output} --srcdir ${{OUT_DIR}} --dstdir {unstripped_dir} $(cat {all_module_basenames_file})
""".format(
search_and_cp_output = ctx.executable._search_and_cp_output.path,
unstripped_dir = unstripped_dir.path,
all_module_basenames_file = all_module_basenames_file.path,
)
return struct(
inputs = inputs,
tools = tools,
cmd = grab_unstripped_intree_modules_cmd,
outputs = outputs,
unstripped_dir = unstripped_dir,
)
def _get_check_remaining_modules_step(
ctx,
all_module_names,
base_kernel_all_module_names,
modules_staging_dir):
"""Returns a step for checking remaining '*.ko' files in `OUT_DIR`.
Returns:
A struct with these fields:
* cmd
* inputs
* tools
* outputs
"""
if not ctx.attr._warn_undeclared_modules[BuildSettingInfo].value:
return struct(
cmd = """
echo "Check for undeclared modules in kernel_build skipped." >&2
""",
inputs = [],
tools = [],
outputs = [],
)
message_type = "ERROR"
epilog = "exit 1"
if ctx.attr._allow_undeclared_modules[BuildSettingInfo].value:
message_type = "WARNING"
epilog = ""
cmd = """
remaining_ko_files=$({check_declared_output_list} \\
--declared {all_module_names} {base_kernel_all_module_names} \\
--actual $(cd {modules_staging_dir}/lib/modules/*/kernel && find . -type f -name '*.ko' | sed 's:^[.]/::'))
if [[ ${{remaining_ko_files}} ]]; then
echo "{message_type}: The following kernel modules are built but not copied. Add these lines to the module_outs attribute of {label}:" >&2
for ko in ${{remaining_ko_files}}; do
echo ' "'"${{ko}}"'",' >&2
done
echo "Alternatively, install buildozer and execute:" >&2
echo " $ buildozer 'add module_outs ${{remaining_ko_files}}' {label}" >&2
echo "See https://github.com/bazelbuild/buildtools/blob/master/buildozer/README.md for reference" >&2
{epilog}
fi
""".format(
message_type = message_type,
check_declared_output_list = ctx.executable._check_declared_output_list.path,
all_module_names = " ".join(all_module_names),
base_kernel_all_module_names = " ".join(base_kernel_all_module_names),
modules_staging_dir = modules_staging_dir,
label = ctx.label,
epilog = epilog,
)
tools = [ctx.executable._check_declared_output_list]
return struct(
cmd = cmd,
inputs = [],
tools = tools,
outputs = [],
)
def _get_grab_symtypes_step(ctx):
"""Returns a step for grabbing the `*.symtypes` from `OUT_DIR`.
Returns:
A struct with these fields:
* inputs
* tools
* outputs
* cmd
"""
grab_symtypes_cmd = ""
outputs = []
if ctx.attr.config[KernelEnvAttrInfo].kbuild_symtypes:
symtypes_dir = ctx.actions.declare_directory("{name}/symtypes".format(name = ctx.label.name))
outputs.append(symtypes_dir)
grab_symtypes_cmd = """
rsync -a --prune-empty-dirs --include '*/' --include '*.symtypes' --exclude '*' ${{OUT_DIR}}/ {symtypes_dir}/
""".format(
symtypes_dir = symtypes_dir.path,
)
return struct(
inputs = [],
tools = [],
cmd = grab_symtypes_cmd,
outputs = outputs,
)
def _get_grab_kbuild_output_step(ctx):
"""Returns a step for grabbing the `*`files from `OUT_DIR`.
Returns:
A struct with fields (inputs, tools, outputs, cmd)
"""
grab_kbuild_output_cmd = ""
outputs = []
if ctx.attr._preserve_kbuild_output[BuildSettingInfo].value:
kbuild_output_target = ctx.actions.declare_directory("{name}/kbuild_output".format(name = ctx.label.name))
outputs.append(kbuild_output_target)
grab_kbuild_output_cmd = """
if [[ -L ${{OUT_DIR}}/source ]]; then
rm -f ${{OUT_DIR}}/source
fi
rsync -a --prune-empty-dirs --include '*/' ${{OUT_DIR}}/ {kbuild_output_target}/
""".format(
kbuild_output_target = kbuild_output_target.path,
)
return struct(
inputs = [],
tools = [],
cmd = grab_kbuild_output_cmd,
outputs = outputs,
)
def get_grab_cmd_step(ctx, src_dir):
"""Returns a step for grabbing the `*.cmd` from `src_dir`.
Args:
ctx: Context from the rule.
src_dir: Source directory.
Returns:
A struct with these fields:
* inputs
* tools
* outputs
* cmd
* cmd_dir
"""
cmd = ""
cmd_dir = None
outputs = []
if ctx.attr._preserve_cmd[BuildSettingInfo].value:
cmd_dir = ctx.actions.declare_directory("{name}/cmds".format(name = ctx.label.name))
outputs.append(cmd_dir)
cmd = """
rsync -a --chmod=F+w --prune-empty-dirs --include '*/' --include '*.cmd' --exclude '*' {src_dir}/ {cmd_dir}/
find {cmd_dir}/ -name '*.cmd' -exec sed -i'' -e 's:'"${{ROOT_DIR}}"':${{ROOT_DIR}}:g' {{}} \\+
""".format(
src_dir = src_dir,
cmd_dir = cmd_dir.path,
)
return struct(
inputs = [],
tools = [],
cmd = cmd,
outputs = outputs,
cmd_dir = cmd_dir,
)
def _get_copy_module_symvers_step(ctx):
"""Returns a step for keeping a copy of Module.symvers from `OUT_DIR`.
Returns:
A struct with fields (inputs, tools, outputs, cmd)
"""
copy_module_symvers_cmd = ""
outputs = []
if ctx.attr.keep_module_symvers:
module_symvers_copy = ctx.actions.declare_file("{}/{}_Module.symvers".format(
ctx.label.name,
ctx.label.name,
))
outputs.append(module_symvers_copy)
copy_module_symvers_cmd = """
cp -f ${{OUT_DIR}}/Module.symvers {module_symvers_copy}
""".format(
module_symvers_copy = module_symvers_copy.path,
)
return struct(
inputs = [],
tools = [],
cmd = copy_module_symvers_cmd,
outputs = outputs,
)
def _get_dot_config_impl(subrule_ctx, config_out_dir, hermetic_tools):
"""Gets .config from kernel_config's out_dir.
Args:
subrule_ctx: subrule_ctx
config_out_dir: out_dir from kernel_config()
hermetic_tools: the hermetic toolchain
"""
# Automatic Exec Groups needs to be enabled in kernel_build() so the subrule can use toolchain
# resolution. For now, just let kernel_build gives us the hermetic tools.
output = subrule_ctx.actions.declare_file("{name}/{name}_dot_config".format(name = subrule_ctx.label.name))
command = hermetic_tools.setup + """
cp {config_out_dir}/.config {output}
""".format(
config_out_dir = config_out_dir.path,
output = output.path,
)
subrule_ctx.actions.run_shell(
inputs = [config_out_dir],
outputs = [output],
tools = hermetic_tools.deps,
command = command,
mnemonic = "KernelBuildDotConfig",
progress_message = "Copying .config %{label}",
)
return output
_get_dot_config = subrule(
implementation = _get_dot_config_impl,
)
def _gen_symvers_step(all_output_names_minus_modules, kbuild_mixed_tree_ret):
"""Creates a step that generates various .symvers files.
Args:
all_output_names_minus_modules: all non-module output names in *outs
kbuild_mixed_tree_ret: from _create_kbuild_mixed_tree
"""
inputs = []
cmd = ""
# After 6.13, Kbuild no longer generates vmlinux.symvers. Manually generates this by
# filtering vmlinux lines from Module.symvers if the caller is requesting vmlinux.symvers in
# outs.
if "vmlinux.symvers" in all_output_names_minus_modules:
cmd += """
if [[ ! -f ${OUT_DIR}/vmlinux.symvers ]]; then
if [[ ! -f ${OUT_DIR}/Module.symvers ]]; then
echo "ERROR: Can't generate vmlinux.symvers because Kbuild did not generate Module.symvers." >&2
exit 1
fi
grep "\\<vmlinux\\s\\+EXPORT" ${OUT_DIR}/Module.symvers > ${OUT_DIR}/vmlinux.symvers
fi
"""
# After 6.13, for mixed builds, Kbuild only generates modules-only.symvers. Manually
# concatenate it with vmlinux.symvers to form Module.symvers.
if "Module.symvers" in all_output_names_minus_modules and kbuild_mixed_tree_ret.base_kernel_files: # is mixed build
# This to_list() is acceptable because GKI's outs/module_outs is a small list
symvers_srcs = [
file
for file in kbuild_mixed_tree_ret.base_kernel_files.to_list()
if file.basename == "vmlinux.symvers"
]
cmd += """
if [[ ! -f ${{OUT_DIR}}/Module.symvers ]]; then
if [[ ! -f ${{OUT_DIR}}/modules-only.symvers ]]; then
echo "ERROR: Can't generate Module.symvers because Kbuild did not generate modules-only.symvers." >&2
exit 1
fi
cat {symvers_srcs} ${{OUT_DIR}}/modules-only.symvers > ${{OUT_DIR}}/Module.symvers
fi
""".format(
symvers_srcs = " ".join([file.path for file in symvers_srcs]),
)
inputs += symvers_srcs
return struct(
inputs = inputs,
tools = [],
cmd = cmd,
outputs = [],
)
def _get_modinst_step(ctx, modules_staging_dir):
module_strip_flag = "INSTALL_MOD_STRIP="
if ctx.attr.strip_modules:
module_strip_flag += "1"
base_kernel = base_kernel_utils.get_base_kernel(ctx)
cmd = ""
inputs = []
tools = []
if base_kernel:
cmd += """
# Check that base_kernel has the same KMI as the current kernel_build
(
base_release=$(cat {base_kernel_release_file})
base_kmi=$({get_kmi_string} --keep_sublevel ${{base_release}})
my_release=$(cat ${{OUT_DIR}}/include/config/kernel.release)
my_kmi=$({get_kmi_string} --keep_sublevel ${{my_release}})
if [[ "${{base_kmi}}" != "${{my_kmi}}" ]]; then
echo "ERROR: KMI or sublevel mismatch before running make modules_install:" >&2
echo " {label}: ${{my_kmi}} (from ${{my_release}})" >&2
echo " {base_kernel_label}: ${{base_kmi}} (from ${{base_release}})" >&2
fi
)
# Fix up kernel.release to be the one from {base_kernel_label} before installing modules
cp -L {base_kernel_release_file} ${{OUT_DIR}}/include/config/kernel.release
""".format(
get_kmi_string = ctx.executable._get_kmi_string.path,
label = ctx.label,
base_kernel_label = base_kernel.label,
base_kernel_release_file = base_kernel[KernelBuildUnameInfo].kernel_release.path,
)
inputs.append(base_kernel[KernelBuildUnameInfo].kernel_release)
tools.append(ctx.executable._get_kmi_string)
cmd += """
# Set variables and create dirs for modules
mkdir -p {modules_staging_dir}
# Install modules
if grep -q "\\bmodules\\b" <<< "{make_goals}" ; then
make -C ${{KERNEL_DIR}} ${{TOOL_ARGS}} DEPMOD=true O=${{OUT_DIR}} {module_strip_flag} INSTALL_MOD_PATH=$(realpath {modules_staging_dir}) modules_install
else
# Workaround as this file is required, hence just produce a placeholder.
touch {internal_outs_under_out_dir}
fi
""".format(
modules_staging_dir = modules_staging_dir,
internal_outs_under_out_dir = " ".join(["${{OUT_DIR}}/{}".format(item) for item in _kernel_build_internal_outs]),
module_strip_flag = module_strip_flag,
make_goals = ctx.attr.config[KernelEnvMakeGoalsInfo].make_goals,
)
if base_kernel:
cmd += """
# Check that `make modules_install` does not revert include/config/kernel.release
if ! diff -q {base_kernel_release} ${{OUT_DIR}}/include/config/kernel.release; then
echo "ERROR: make modules_install modifies include/config/kernel.release." >&2
echo " This is not expected; please file a bug!" >&2
echo " expected: $(cat {base_kernel_release})" >&2
echo " actual: $(cat ${{OUT_DIR}}/include/config/kernel.release)" >&2
fi
""".format(
base_kernel_release = base_kernel[KernelBuildUnameInfo].kernel_release.path,
)
return struct(
inputs = inputs,
tools = tools,
cmd = cmd,
outputs = [],
)
def _build_main_action(
ctx,
kbuild_mixed_tree_ret,
all_output_names,
all_module_basenames_file):
"""Adds the main action for the `kernel_build`."""
base_kernel_all_module_names = _get_base_kernel_all_module_names(ctx)
# Declare outputs.
## Declare outputs based on the *outs attributes
all_output_files = _declare_all_output_files(ctx)
## Declare implicit outputs of the command
## This is like ctx.actions.declare_directory(ctx.label.name) without actually declaring it.
ruledir = paths.join(
utils.package_bin_dir(ctx),
ctx.label.name,
)
if base_kernel_utils.get_base_kernel(ctx):
# We will re-package MODULES_STAGING_ARCHIVE in _repack_module_staging_archive,
# so use a different name.
modules_staging_archive_self = ctx.actions.declare_file(
"{}/modules_staging_dir_self.tar.gz".format(ctx.label.name),
)
else:
modules_staging_archive_self = ctx.actions.declare_file(
"{}/{}".format(ctx.label.name, MODULES_STAGING_ARCHIVE),
)
out_dir_kernel_headers_tar = ctx.actions.declare_file(
"{name}/out-dir-kernel-headers.tar.gz".format(name = ctx.label.name),
)
modules_staging_dir = modules_staging_archive_self.dirname + "/staging"
# Individual steps of the final command.
gen_symvers_step = _gen_symvers_step(
all_output_names_minus_modules = all_output_names.non_modules,
kbuild_mixed_tree_ret = kbuild_mixed_tree_ret,
)
cache_dir_step = cache_dir.get_step(
ctx = ctx,
common_config_tags = ctx.attr.config[KernelEnvAttrInfo].common_config_tags,
symlink_name = "build",
)
modinst_step = _get_modinst_step(
ctx = ctx,
modules_staging_dir = modules_staging_dir,
)
grab_intree_modules_step = _get_grab_intree_modules_step(
ctx = ctx,
has_any_modules = bool(all_output_names.modules),
modules_staging_dir = modules_staging_dir,
ruledir = ruledir,
all_module_names = all_output_names.modules,
)
grab_unstripped_modules_step = _get_grab_unstripped_modules_step(
ctx = ctx,
has_any_modules = bool(all_output_names.modules),
all_module_basenames_file = all_module_basenames_file,
)
grab_symtypes_step = _get_grab_symtypes_step(ctx)
grab_gcno_step = get_grab_gcno_step(ctx, "${COMMON_OUT_DIR}", is_kernel_build = True)
grab_cmd_step = get_grab_cmd_step(ctx, "${OUT_DIR}")
compile_commands_step = compile_commands_utils.get_step(ctx, "${OUT_DIR}")
grab_gdb_scripts_step = kgdb.get_grab_gdb_scripts_step(ctx)
grab_kbuild_output_step = _get_grab_kbuild_output_step(ctx)
copy_module_symvers_step = _get_copy_module_symvers_step(ctx)
check_remaining_modules_step = _get_check_remaining_modules_step(
ctx = ctx,
all_module_names = all_output_names.modules,
base_kernel_all_module_names = base_kernel_all_module_names,
modules_staging_dir = modules_staging_dir,
)
steps = (
cache_dir_step,
modinst_step,
grab_intree_modules_step,
grab_unstripped_modules_step,
grab_symtypes_step,
grab_gcno_step,
grab_cmd_step,
compile_commands_step,
grab_gdb_scripts_step,
grab_kbuild_output_step,
copy_module_symvers_step,
check_remaining_modules_step,
)
# Build the command for the main action.
command = kernel_utils.setup_serialized_env_cmd(
serialized_env_info = ctx.attr.config[KernelSerializedEnvInfo],
restore_out_dir_cmd = cache_dir_step.cmd,
)
make_goals = ctx.attr.config[KernelEnvMakeGoalsInfo].make_goals
command += """
{kbuild_mixed_tree_cmd}
# Actual kernel build
make -C ${{KERNEL_DIR}} ${{TOOL_ARGS}} O=${{OUT_DIR}} {make_goals}
# Generate .symvers files that might be missing from Kbuild.
{gen_symvers_cmd}
# Install modules
{modinst_cmd}
# Archive headers in OUT_DIR
find ${{OUT_DIR}} -name *.h -print0 \
| tar czf {out_dir_kernel_headers_tar} \
--absolute-names \
--dereference \
--transform "s,.*$OUT_DIR,," \
--transform "s,^/,," \
--null -T -
# Grab outputs. If unable to find from OUT_DIR, look at KBUILD_MIXED_TREE as well.
{search_and_cp_output} --srcdir ${{OUT_DIR}} {kbuild_mixed_tree_arg} {dtstree_arg} --dstdir {ruledir} {all_output_names_minus_modules}
# Archive modules_staging_dir
tar czf {modules_staging_archive_self} -C {modules_staging_dir} .
# Grab *.symtypes
{grab_symtypes_cmd}
# Grab *.gcno files
{grab_gcno_step_cmd}
# Grab *.cmd
{grab_cmd_cmd}
# Grab files for compile_commands.json
{compile_commands_step}
# Grab GDB scripts
{grab_gdb_scripts_cmd}
# Grab * files
{grab_kbuild_output_step_cmd}
# Grab in-tree modules
{grab_intree_modules_cmd}
# Grab unstripped in-tree modules
{grab_unstripped_intree_modules_cmd}
# Make a copy of Module.symvers
{copy_module_symvers_cmd}
if grep -q "\\bmodules\\b" <<< "{make_goals}"; then
# Check if there are remaining *.ko files
{check_remaining_modules_cmd}
fi
# Clean up staging directories
rm -rf {modules_staging_dir}
# Create last_build symlink in cache_dir
{cache_dir_post_cmd}
""".format(
gen_symvers_cmd = gen_symvers_step.cmd,
cache_dir_post_cmd = cache_dir_step.post_cmd,
kbuild_mixed_tree_cmd = kbuild_mixed_tree_ret.cmd,
search_and_cp_output = ctx.executable._search_and_cp_output.path,
kbuild_mixed_tree_arg = kbuild_mixed_tree_ret.arg,
dtstree_arg = "--srcdir ${OUT_DIR}/${dtstree}",
ruledir = ruledir,
all_output_names_minus_modules = " ".join(all_output_names.non_modules),
modinst_cmd = modinst_step.cmd,
grab_intree_modules_cmd = grab_intree_modules_step.cmd,
grab_unstripped_intree_modules_cmd = grab_unstripped_modules_step.cmd,
grab_symtypes_cmd = grab_symtypes_step.cmd,
grab_gcno_step_cmd = grab_gcno_step.cmd,
grab_cmd_cmd = grab_cmd_step.cmd,
compile_commands_step = compile_commands_step.cmd,
grab_gdb_scripts_cmd = grab_gdb_scripts_step.cmd,
grab_kbuild_output_step_cmd = grab_kbuild_output_step.cmd,
check_remaining_modules_cmd = check_remaining_modules_step.cmd,
modules_staging_dir = modules_staging_dir,
modules_staging_archive_self = modules_staging_archive_self.path,
out_dir_kernel_headers_tar = out_dir_kernel_headers_tar.path,
label = ctx.label,
make_goals = " ".join(make_goals),
copy_module_symvers_cmd = copy_module_symvers_step.cmd,
)
# all inputs that |command| needs
transitive_inputs = [target.files for target in ctx.attr.srcs]
transitive_inputs += [target.files for target in ctx.attr.deps]
transitive_inputs.append(
ctx.attr.config[KernelSerializedEnvInfo].inputs,
)
transitive_inputs.append(kbuild_mixed_tree_ret.inputs)
inputs = []
for step in steps:
inputs += step.inputs
# All tools that |command| needs
tools = [
ctx.executable._search_and_cp_output,
]
transitive_tools = [
ctx.attr.config[KernelSerializedEnvInfo].tools,
]
for step in steps:
tools += step.tools
# all outputs that |command| generates
command_outputs = [
modules_staging_archive_self,
out_dir_kernel_headers_tar,
]
for d in all_output_files.values():
command_outputs += d.values()
for step in steps:
command_outputs += step.outputs
debug.print_scripts(ctx, command)
ctx.actions.run_shell(
mnemonic = "KernelBuild",
inputs = depset(_uniq(inputs), transitive = transitive_inputs),
outputs = command_outputs,
tools = depset(_uniq(tools), transitive = transitive_tools),
progress_message = "Building kernel{}".format(_progress_message_suffix(ctx)),
command = command,
execution_requirements = kernel_utils.local_exec_requirements(ctx),
)
return struct(
all_output_files = all_output_files,
out_dir_kernel_headers_tar = out_dir_kernel_headers_tar,
modules_staging_archive_self = modules_staging_archive_self,
unstripped_dir = grab_unstripped_modules_step.unstripped_dir,
ruledir = ruledir,
cmd_dir = grab_cmd_step.cmd_dir,
compile_commands_with_vars = compile_commands_step.compile_commands_with_vars,
compile_commands_common_out_dir = compile_commands_step.compile_commands_common_out_dir,
gcno_outputs = grab_gcno_step.outputs,
gcno_mapping = grab_gcno_step.gcno_mapping,
gcno_dir = grab_gcno_step.gcno_dir,
module_symvers_outputs = copy_module_symvers_step.outputs,
)
def create_serialized_env_info(
ctx,
setup_script_name,
pre_info,
outputs,
fake_system_map,
extra_restore_outputs_cmd,
extra_inputs):
"""Creates an KernelSerializedEnvInfo.
Args:
ctx: ctx,
setup_script_name: name of the setup script
pre_info: KernelSerializedEnvInfo
outputs: dictionary where
keys are `File`, and values are the relative paths under $OUT_DIR as the
destination
fake_system_map: Whether to create a fake `$OUT_DIR/System.map`
extra_restore_outputs_cmd: Extra CMD to restore outputs
extra_inputs: a depset attached to `inputs` of returned object
Returns:
A KernelSerializedEnvInfo that runs pre_info, then restore outputs given the list of
outputs and cmd."""
restore_outputs_cmd = \
_get_serialized_env_info_setup_restore_outputs_command(
outputs = outputs,
fake_system_map = fake_system_map,
)
restore_outputs_cmd += extra_restore_outputs_cmd
setup_script = ctx.actions.declare_file(setup_script_name)
setup_script_cmd = """
if [ -n "${{BUILD_WORKSPACE_DIRECTORY}}" ] || [ "${{BAZEL_TEST}}" = "1" ]; then
. {pre_setup_script_short}
else
. {pre_setup_script}
fi
{restore_outputs_cmd}
""".format(
pre_setup_script = pre_info.setup_script.path,
pre_setup_script_short = pre_info.setup_script.short_path,
restore_outputs_cmd = restore_outputs_cmd,
)
ctx.actions.write(
output = setup_script,
content = setup_script_cmd,
)
return KernelSerializedEnvInfo(
setup_script = setup_script,
inputs = depset(
[setup_script],
transitive = [
pre_info.inputs,
extra_inputs,
depset(outputs.keys()),
],
),
tools = pre_info.tools,
)
def _get_serialized_env_info_setup_restore_outputs_command(outputs, fake_system_map):
"""Returns the `restore_outputs` command for the environment to build kernel_module.
Args:
outputs: dictionary where
keys are `File`, and values are the relative paths under $OUT_DIR as the
destinastion
fake_system_map: Whether to create a fake `$OUT_DIR/System.map`
Returns:
the `restore_outputs` command for the environment to build kernel_module.
"""
cmd = ""
if outputs:
cmd += """
# Restore kernel build outputs
"""
for dep, relpath in outputs.items():
cmd += """
mkdir -p $(dirname ${{OUT_DIR}}/{relpath})
rsync -aL {dep} ${{OUT_DIR}}/{relpath}
""".format(
dep = dep.path,
relpath = relpath,
)
# If System.map does not already exist, create a fake System.map because
# `make modules` does not need it. For kernel_module(),
# make modules_install needs it, but we aren't running depmod in
# kernel_module, so a fake one is good enough.
if fake_system_map:
cmd += """
touch ${OUT_DIR}/System.map
"""
return cmd
def _create_infos(
ctx,
kbuild_mixed_tree_ret,
all_module_names,
main_action_ret,
modules_staging_archive,
kmi_strict_mode_out,
kmi_symbol_list_violations_check_out,
module_scripts_archive,
module_srcs):
"""Creates and returns a list of provided infos that the `kernel_build` target should return.
Args:
ctx: ctx
kbuild_mixed_tree_ret: from `_create_kbuild_mixed_tree`
all_module_names: `module_outs` + `module_implicit_outs`
main_action_ret: from `_build_main_action`
modules_staging_archive: from `_repack_modules_staging_archive`
kmi_strict_mode_out: from `_kmi_symbol_list_strict_mode`
kmi_symbol_list_violations_check_out: from `_kmi_symbol_list_violations_check`
module_srcs: from `kernel_utils.filter_module_srcs`
module_scripts_archive: from `_create_module_scripts_archive`
"""
base_kernel = base_kernel_utils.get_base_kernel(ctx)
all_output_files = main_action_ret.all_output_files
# outs and internal_outs are needed. implicit_outs are needed to
# build GKI's system_dlkm image to sign modules. Modules are not needed.
serialized_env_info_dependencies = list(all_output_files["outs"].values())
serialized_env_info_dependencies += all_output_files["internal_outs"].values()
serialized_env_info_dependencies += all_output_files["implicit_outs"].values()
serialized_env_info = create_serialized_env_info(
ctx = ctx,
setup_script_name = "{name}/{name}_setup.sh".format(name = ctx.attr.name),
pre_info = ctx.attr.config[KernelSerializedEnvInfo],
outputs = {
dep: paths.relativize(dep.path, main_action_ret.ruledir)
for dep in serialized_env_info_dependencies
},
fake_system_map = False,
extra_restore_outputs_cmd = kbuild_mixed_tree_ret.cmd,
extra_inputs = kbuild_mixed_tree_ret.inputs,
)
orig_env_info = ctx.attr.config[KernelBuildOriginalEnvInfo]
kernel_build_info = KernelBuildInfo(
out_dir_kernel_headers_tar = main_action_ret.out_dir_kernel_headers_tar,
outs = depset(all_output_files["outs"].values()),
base_kernel_files = kbuild_mixed_tree_ret.base_kernel_files,
)
kernel_build_uname_info = KernelBuildUnameInfo(
kernel_release = all_output_files["internal_outs"]["include/config/kernel.release"],
)
# For kernel_module()
ext_mod_serialized_env_info_deps = all_output_files["internal_outs"].values()
mod_min_env = create_serialized_env_info(
ctx = ctx,
setup_script_name = "{name}/{name}_mod_min_setup.sh".format(name = ctx.attr.name),
pre_info = ctx.attr.modules_prepare[KernelSerializedEnvInfo],
outputs = {
dep: paths.relativize(dep.path, main_action_ret.ruledir)
for dep in ext_mod_serialized_env_info_deps
},
fake_system_map = True,
extra_restore_outputs_cmd = "",
extra_inputs = depset(transitive = [module_srcs.module_scripts]),
)
# External modules do not need implicit_outs because they are unsigned.
ext_mod_full_serialized_env_info_dependencies = list(all_output_files["outs"].values())
ext_mod_full_serialized_env_info_dependencies += all_output_files["internal_outs"].values()
# For kernel_module() that require all kernel_build outputs and kernel_modules_install()
mod_full_env = create_serialized_env_info(
ctx = ctx,
setup_script_name = "{name}/{name}_mod_full_setup.sh".format(name = ctx.attr.name),
pre_info = ctx.attr.modules_prepare[KernelSerializedEnvInfo],
outputs = {
dep: paths.relativize(dep.path, main_action_ret.ruledir)
for dep in ext_mod_full_serialized_env_info_dependencies
},
fake_system_map = False,
extra_restore_outputs_cmd = kbuild_mixed_tree_ret.cmd,
extra_inputs = depset(transitive = [
kbuild_mixed_tree_ret.inputs,
module_srcs.module_scripts,
]),
)
# For ddk_config()
ddk_config_env = create_serialized_env_info(
ctx = ctx,
setup_script_name = "{name}/{name}_ddk_config_setup.sh".format(name = ctx.attr.name),
pre_info = ctx.attr.config[KernelSerializedEnvInfo],
outputs = {},
fake_system_map = False,
extra_restore_outputs_cmd = "",
extra_inputs = depset(transitive = [
module_srcs.module_scripts,
module_srcs.module_kconfig,
]),
)
ddk_module_defconfig_fragments = depset(transitive = [
target.files
for target in ctx.attr.ddk_module_defconfig_fragments
])
kernel_build_module_info = KernelBuildExtModuleInfo(
modules_staging_archive = modules_staging_archive,
module_hdrs = module_srcs.module_hdrs,
ddk_config_env = ddk_config_env,
mod_min_env = mod_min_env,
mod_full_env = mod_full_env,
modinst_env = mod_full_env,
collect_unstripped_modules = ctx.attr.collect_unstripped_modules,
strip_modules = ctx.attr.strip_modules,
ddk_module_defconfig_fragments = ddk_module_defconfig_fragments,
)
base_kernel_for_ddk_headers = base_kernel_utils.get_base_kernel_for_ddk_headers(ctx)
ddk_headers_info = ddk_headers_common_impl(
ctx.label,
# Because of left-to-right ordering, put DdkHeadersInfo from base_kernel
# at the end of the direct list so it has a lower priority.
ctx.attr.ddk_module_headers + ([base_kernel_for_ddk_headers] if base_kernel_for_ddk_headers else []),
[],
[],
)
kernel_uapi_depsets = []
if base_kernel:
kernel_uapi_depsets.append(base_kernel[KernelBuildUapiInfo].kernel_uapi_headers)
kernel_uapi_depsets.append(ctx.attr.kernel_uapi_headers.files)
kernel_uapi_headers_depset = depset(transitive = kernel_uapi_depsets, order = "postorder")
kernel_build_uapi_info = KernelBuildUapiInfo(
kernel_uapi_headers = kernel_uapi_headers_depset,
)
if ctx.files.combined_abi_symbollist:
if len(ctx.files.combined_abi_symbollist) > 1:
fail("{}: combined_abi_symbollist must only provide at most one file".format(ctx.label))
combined_abi_symbollist = ctx.files.combined_abi_symbollist[0]
else:
combined_abi_symbollist = None
kernel_build_abi_info = KernelBuildAbiInfo(
trim_nonlisted_kmi = trim_nonlisted_kmi_utils.get_value(ctx),
combined_abi_symbollist = combined_abi_symbollist,
modules_staging_archive = modules_staging_archive,
base_modules_staging_archive = base_kernel_utils.get_base_modules_staging_archive(ctx),
src_protected_exports_list = ctx.file.src_protected_exports_list,
src_protected_modules_list = ctx.file.src_protected_modules_list,
src_kmi_symbol_list = ctx.file.src_kmi_symbol_list,
kmi_strict_mode_out = kmi_strict_mode_out,
)
# Device modules takes precedence over base_kernel (GKI) modules.
unstripped_modules_depsets = []
if main_action_ret.unstripped_dir:
unstripped_modules_depsets.append(depset([main_action_ret.unstripped_dir]))
if base_kernel:
unstripped_modules_depsets.append(base_kernel[KernelUnstrippedModulesInfo].directories)
kernel_unstripped_modules_info = KernelUnstrippedModulesInfo(
directories = depset(transitive = unstripped_modules_depsets, order = "postorder"),
)
in_tree_modules_info = KernelBuildInTreeModulesInfo(
all_module_names = all_module_names,
)
images_info = KernelImagesInfo(
base_kernel_label = base_kernel.label if base_kernel else None,
outs = depset(all_output_files["outs"].values()),
base_kernel_files = kbuild_mixed_tree_ret.base_kernel_files,
)
gcov_info = GcovInfo(
gcno_mapping = main_action_ret.gcno_mapping,
gcno_dir = main_action_ret.gcno_dir,
)
rustavailable_out = rustavailable(
serialized_env_info = ctx.attr.config[KernelSerializedEnvInfo],
inputs = depset(
transitive =
[target.files for target in ctx.attr.srcs] + [target.files for target in ctx.attr.deps],
),
)
output_group_kwargs = {}
for d in all_output_files.values():
output_group_kwargs.update({name: depset([file]) for name, file in d.items()})
# TODO(b/291918087): Drop after common_kernels no longer use kernel_filegroup.
# These files should already be in kernel_filegroup_declaration.
output_group_kwargs["modules_staging_archive"] = depset([modules_staging_archive])
output_group_kwargs["rustavailable"] = depset([rustavailable_out])
output_group_info = OutputGroupInfo(**output_group_kwargs)
kbuild_mixed_tree_files = all_output_files["outs"].values() + all_output_files["module_outs"].values()
kbuild_mixed_tree_info = KernelBuildMixedTreeInfo(
files = depset(kbuild_mixed_tree_files),
)
cmds_info = KernelCmdsInfo(
srcs = depset([target.files for target in ctx.attr.srcs]),
directories = depset([main_action_ret.cmd_dir]),
)
compile_commands_info = CompileCommandsInfo(
infos = depset([CompileCommandsSingleInfo(
compile_commands_with_vars = main_action_ret.compile_commands_with_vars,
compile_commands_common_out_dir = main_action_ret.compile_commands_common_out_dir,
)]),
)
modules_prepare_archive = utils.find_file(
_MODULES_PREPARE_ARCHIVE,
ctx.files.modules_prepare,
what = ctx.label,
required = True,
)
filegroup_decl_info = KernelBuildFilegroupDeclInfo(
filegroup_srcs = depset(all_output_files["outs"].values() +
all_output_files["module_outs"].values()),
all_module_names = all_module_names,
modules_staging_archive = modules_staging_archive,
toolchain_version = ctx.attr.config[KernelToolchainInfo].toolchain_version,
kernel_release = all_output_files["internal_outs"]["include/config/kernel.release"],
modules_prepare_archive = modules_prepare_archive,
collect_unstripped_modules = ctx.attr.collect_unstripped_modules,
strip_modules = ctx.attr.strip_modules,
src_protected_modules_list = ctx.file.src_protected_modules_list,
ddk_module_defconfig_fragments = ddk_module_defconfig_fragments,
kernel_uapi_headers = kernel_uapi_headers_depset,
arch = ctx.attr.arch,
env_setup_script = ctx.attr.config[KernelConfigInfo].env_setup_script,
config_out_dir = ctx.file.config,
outs = depset(all_output_files["outs"].values()),
internal_outs = depset(all_output_files["internal_outs"].values()),
ruledir = main_action_ret.ruledir,
module_env_archive = module_scripts_archive,
has_base_kernel = base_kernel_utils.get_base_kernel(ctx) != None,
copy_module_symvers_outputs = main_action_ret.module_symvers_outputs,
)
default_info_files = all_output_files["outs"].values() + all_output_files["module_outs"].values()
if kmi_strict_mode_out:
default_info_files.append(kmi_strict_mode_out)
default_info_files.extend(main_action_ret.module_symvers_outputs)
if ctx.attr.keep_dot_config:
default_info_files.append(_get_dot_config(
config_out_dir = ctx.file.config,
hermetic_tools = hermetic_toolchain.get(ctx),
))
default_info_files.extend(main_action_ret.gcno_outputs)
if kmi_symbol_list_violations_check_out:
default_info_files.append(kmi_symbol_list_violations_check_out)
default_info = DefaultInfo(
files = depset(default_info_files),
# For kernel_build_test
runfiles = ctx.runfiles(files = default_info_files),
)
module_symvers_file_info = ModuleSymversFileInfo(
module_symvers = depset(main_action_ret.module_symvers_outputs),
)
return [
cmds_info,
ddk_headers_info,
serialized_env_info,
orig_env_info,
kbuild_mixed_tree_info,
kernel_build_info,
kernel_build_module_info,
kernel_build_uapi_info,
kernel_build_uname_info,
kernel_build_abi_info,
kernel_unstripped_modules_info,
in_tree_modules_info,
images_info,
gcov_info,
filegroup_decl_info,
compile_commands_info,
ctx.attr.config[KernelEnvAttrInfo],
ctx.attr.config[KernelToolchainInfo],
output_group_info,
default_info,
module_symvers_file_info,
]
def _kernel_build_impl(ctx):
kbuild_mixed_tree_ret = _create_kbuild_mixed_tree(ctx)
_kernel_build_check_toolchain(ctx)
all_output_names = _split_out_attrs(ctx)
# A file containing the basenames of the modules
all_module_basenames_file = _write_module_names_to_file(
ctx,
"all_module_basenames.txt",
[paths.basename(filename) for filename in all_output_names.modules],
)
main_action_ret = _build_main_action(
ctx = ctx,
kbuild_mixed_tree_ret = kbuild_mixed_tree_ret,
all_output_names = all_output_names,
all_module_basenames_file = all_module_basenames_file,
)
modules_staging_archive = _repack_modules_staging_archive(
ctx = ctx,
modules_staging_archive_self = main_action_ret.modules_staging_archive_self,
all_module_basenames_file = all_module_basenames_file,
)
kmi_strict_mode_out = _kmi_symbol_list_strict_mode(
ctx = ctx,
all_output_files = main_action_ret.all_output_files,
all_module_names = all_output_names.modules,
)
kmi_symbol_list_violations_check_out = _kmi_symbol_list_violations_check(ctx, modules_staging_archive)
module_srcs = kernel_utils.filter_module_srcs(ctx.files.srcs)
module_scripts_archive = _create_module_scripts_archive(
ctx = ctx,
module_srcs = module_srcs,
)
infos = _create_infos(
ctx = ctx,
kbuild_mixed_tree_ret = kbuild_mixed_tree_ret,
all_module_names = all_output_names.modules,
main_action_ret = main_action_ret,
modules_staging_archive = modules_staging_archive,
kmi_strict_mode_out = kmi_strict_mode_out,
kmi_symbol_list_violations_check_out = kmi_symbol_list_violations_check_out,
module_scripts_archive = module_scripts_archive,
module_srcs = module_srcs,
)
return infos
def _kernel_build_additional_attrs():
return dicts.add(
kernel_config_settings.of_kernel_build(),
trim_nonlisted_kmi_utils.attrs(),
base_kernel_utils.non_config_attrs(),
cache_dir.attrs(),
)
# Sync with kleaf/bazel.py
_kernel_build = rule(
implementation = _kernel_build_impl,
doc = "Defines a kernel build target.",
attrs = {
"config": attr.label(
mandatory = True,
providers = [
KernelSerializedEnvInfo,
KernelEnvAttrInfo,
KernelEnvMakeGoalsInfo,
KernelToolchainInfo,
],
doc = "the kernel_config target",
allow_single_file = True,
),
"keep_module_symvers": attr.bool(
doc = "If true, a copy of `Module.symvers` is kept, with the name `{name}_Module.symvers`",
),
"keep_dot_config": attr.bool(
doc = "If true, a copy of `.config` is kept, with the name `{name}_dot_config`",
),
"srcs": attr.label_list(mandatory = True, doc = "kernel sources", allow_files = True),
"outs": attr.string_list(),
"module_outs": attr.string_list(doc = "output *.ko files"),
"internal_outs": attr.string_list(doc = "Like `outs`, but not in dist"),
"module_implicit_outs": attr.string_list(doc = "Like `module_outs`, but not in dist"),
"implicit_outs": attr.string_list(doc = "Like `outs`, but not in dist"),
"_check_declared_output_list": attr.label(
default = Label("//build/kernel/kleaf:check_declared_output_list"),
cfg = "exec",
executable = True,
),
"_search_and_cp_output": attr.label(
default = Label("//build/kernel/kleaf:search_and_cp_output"),
cfg = "exec",
executable = True,
doc = "label referring to the script to process outputs",
),
"deps": attr.label_list(
allow_files = True,
),
"kmi_symbol_list_strict_mode": attr.bool(),
"raw_kmi_symbol_list": attr.label(
doc = "Label to abi_symbollist.raw. Must be 0 or 1 file.",
allow_files = True,
),
"collect_unstripped_modules": attr.bool(),
"_verify_ksymtab": attr.label(
default = "//build/kernel:abi_verify_ksymtab",
executable = True,
cfg = "exec",
),
"_check_symbol_protection": attr.label(
default = "//build/kernel:check_buildtime_symbol_protection",
executable = True,
cfg = "exec",
),
"_get_kmi_string": attr.label(
default = "//build/kernel/kleaf/impl:get_kmi_string",
executable = True,
cfg = "exec",
),
"_debug_print_scripts": attr.label(default = "//build/kernel/kleaf:debug_print_scripts"),
"_allow_undeclared_modules": attr.label(default = "//build/kernel/kleaf:allow_undeclared_modules"),
"_warn_undeclared_modules": attr.label(default = "//build/kernel/kleaf:warn_undeclared_modules"),
"_preserve_cmd": attr.label(default = "//build/kernel/kleaf/impl:preserve_cmd"),
"_kmi_symbol_list_violations_check": attr.label(default = "//build/kernel/kleaf:kmi_symbol_list_violations_check"),
# Though these rules are unrelated to the `_kernel_build` rule, they are added as fake
# dependencies so KernelBuildExtModuleInfo and KernelBuildUapiInfo works.
# There are no real dependencies. Bazel does not build these targets before building the
# `_kernel_build` target.
"modules_prepare": attr.label(providers = [KernelSerializedEnvInfo]),
"kernel_uapi_headers": attr.label(),
"combined_abi_symbollist": attr.label(
doc = """The **combined** `abi_symbollist` file, consist of `kmi_symbol_list` and
`additional_kmi_symbol_lists`. Must be 0 or 1 file.""",
allow_files = True,
),
"strip_modules": attr.bool(default = False, doc = "if set, debug information won't be kept for distributed modules. Note, modules will still be stripped when copied into the ramdisk."),
"src_protected_exports_list": attr.label(allow_single_file = True),
"src_protected_modules_list": attr.label(allow_single_file = True),
"src_kmi_symbol_list": attr.label(allow_single_file = True),
"pack_module_env": attr.bool(default = False, doc = "Create `<name>_module_scripts.tar.gz`."),
"sanitizers": attr.string_list(
allow_empty = False,
default = ["default"],
),
"ddk_module_defconfig_fragments": attr.label_list(
doc = "Additional defconfig fragments for dependant DDK modules.",
allow_empty = True,
allow_files = True,
),
"ddk_module_headers": attr.label_list(
doc = "Additional `ddk_headers` for dependant DDK modules.",
providers = [DdkHeadersInfo],
),
"arch": attr.string(),
} | _kernel_build_additional_attrs() | gcov_attrs(),
toolchains = [hermetic_toolchain.type],
subrules = [
_get_dot_config,
rustavailable,
],
)
def _kernel_build_check_toolchain(ctx):
"""Checks toolchain_version is the same as base_kernel at analysis phase."""
base_kernel = base_kernel_utils.get_base_kernel(ctx)
if not base_kernel:
return
this_toolchain = ctx.attr.config[KernelToolchainInfo].toolchain_version
base_toolchain = base_kernel[KernelToolchainInfo].toolchain_version
if this_toolchain != base_toolchain:
fail("""{this_label}:
ERROR: `toolchain_version` is "{this_toolchain}" for "{this_label}", but
`toolchain_version` is "{base_toolchain}" for "{base_kernel}" (`base_kernel`).
They must use the same `toolchain_version`.
Fix by setting `toolchain_version` of "{this_label}"
to be the one used by "{base_kernel}".
If "{base_kernel}" does not set `toolchain_version` explicitly, do not set
`toolchain_version` for "{this_label}" either.
""".format(
this_label = ctx.label,
this_toolchain = this_toolchain,
base_kernel = base_kernel.label,
base_toolchain = base_toolchain,
))
def _kmi_symbol_list_strict_mode(ctx, all_output_files, all_module_names):
"""Run for `KMI_SYMBOL_LIST_STRICT_MODE`.
"""
if not ctx.attr._use_kmi_symbol_list_strict_mode[BuildSettingInfo].value:
# buildifier: disable=print
print("\nWARNING: {this_label}: Attribute kmi_symbol_list_strict_mode\
IGNORED because --nokmi_symbol_list_strict_mode is set!".format(
this_label = ctx.label,
))
return None
if _skip_build_checks(ctx, what = "Attribute kmi_symbol_list_strict_mode"):
return None
if not ctx.attr.kmi_symbol_list_strict_mode:
return None
if not ctx.files.raw_kmi_symbol_list:
fail("{}: kmi_symbol_list_strict_mode requires kmi_symbol_list or additional_kmi_symbol_lists.")
if len(ctx.files.raw_kmi_symbol_list) > 1:
fail("{}: raw_kmi_symbol_list must only provide at most one file".format(ctx.label))
vmlinux = all_output_files["outs"].get("vmlinux")
if not vmlinux:
fail("{}: with kmi_symbol_list_strict_mode, outs does not contain vmlinux")
module_symvers = all_output_files["internal_outs"].get("Module.symvers")
if not module_symvers:
fail("{}: with kmi_symbol_list_strict_mode, outs does not contain module_symvers")
inputs = [
module_symvers,
]
inputs += ctx.files.raw_kmi_symbol_list # This is 0 or 1 file
transitive_inputs = [ctx.attr.config[KernelSerializedEnvInfo].inputs]
tools = [ctx.executable._verify_ksymtab]
transitive_tools = [ctx.attr.config[KernelSerializedEnvInfo].tools]
out = ctx.actions.declare_file("{}_kmi_strict_out/kmi_symbol_list_strict_mode_checked".format(ctx.attr.name))
command = kernel_utils.setup_serialized_env_cmd(
serialized_env_info = ctx.attr.config[KernelSerializedEnvInfo],
restore_out_dir_cmd = utils.get_check_sandbox_cmd(),
)
command += """
{verify_ksymtab} \\
--symvers-file {module_symvers} \\
--raw-kmi-symbol-list {raw_kmi_symbol_list} \\
--objects {vmlinux_base} {all_module_names}
touch {out}
""".format(
vmlinux_base = vmlinux.basename, # A fancy way of saying "vmlinux"
all_module_names = " ".join([m.removesuffix(".ko") for m in all_module_names]),
verify_ksymtab = ctx.executable._verify_ksymtab.path,
module_symvers = module_symvers.path,
raw_kmi_symbol_list = ctx.files.raw_kmi_symbol_list[0].path,
out = out.path,
)
debug.print_scripts(ctx, command, what = "kmi_symbol_list_strict_mode")
ctx.actions.run_shell(
mnemonic = "KernelBuildKmiSymbolListStrictMode",
inputs = depset(inputs, transitive = transitive_inputs),
tools = depset(tools, transitive = transitive_tools),
outputs = [out],
command = command,
progress_message = "Checking for kmi_symbol_list_strict_mode{}".format(_progress_message_suffix(ctx)),
)
return out
def _kmi_symbol_list_violations_check(ctx, modules_staging_archive):
"""Checks GKI modules' symbol violations at build time.
Args:
ctx: ctx
modules_staging_archive: The `modules_staging_archive` from `make`
in `_build_main_action`.
Returns:
Marker file `kmi_symbol_list_violations_checked` indicating the check
has been performed.
"""
if not ctx.attr._kmi_symbol_list_violations_check[BuildSettingInfo].value:
return None
if not ctx.files.raw_kmi_symbol_list:
return None
if len(ctx.files.raw_kmi_symbol_list) > 1:
fail("{}: raw_kmi_symbol_list must only provide at most one file".format(ctx.label))
if _skip_build_checks(ctx, what = "Symbol list violations check"):
return None
# Skip for sanitizer build as they are not valid GKI releasae configurations.
# Downstreams are expect to build kernel+modules+vendor modules locally
# and can disable the runtime symbol protection with CONFIG_SIG_PROTECT=n
# if required.
if ctx.attr.sanitizers[0] != "default":
return None
inputs = [
modules_staging_archive,
]
inputs += ctx.files.raw_kmi_symbol_list # This is 0 or 1 file
tools = [ctx.executable._check_symbol_protection]
# llvm-nm is needed to extract symbols.
# Use kernel_env as _hermetic_tools is not enough.
transitive_inputs = [ctx.attr.config[KernelBuildOriginalEnvInfo].env_info.inputs]
transitive_tools = [ctx.attr.config[KernelBuildOriginalEnvInfo].env_info.tools]
out = ctx.actions.declare_file(
"{}_kmi_symbol_list_violations/{}_kmi_symbol_list_violations_checked".format(
ctx.attr.name,
ctx.attr.name,
),
)
intermediates_dir = utils.intermediates_dir(ctx)
command = ctx.attr.config[KernelBuildOriginalEnvInfo].env_info.setup
command += """
mkdir -p {intermediates_dir}
tar xf {modules_staging_archive} -C {intermediates_dir}
{check_symbol_protection} \\
--abi-symbol-list {raw_kmi_symbol_list} \\
{intermediates_dir}
rm -rf {intermediates_dir}
touch {out}
""".format(
check_symbol_protection = ctx.executable._check_symbol_protection.path,
intermediates_dir = intermediates_dir,
modules_staging_archive = modules_staging_archive.path,
out = out.path,
raw_kmi_symbol_list = ctx.files.raw_kmi_symbol_list[0].path,
)
debug.print_scripts(ctx, command, what = "kmi_symbol_list_violations_check")
ctx.actions.run_shell(
mnemonic = "KernelBuildCheckSymbolViolations",
inputs = depset(inputs, transitive = transitive_inputs),
tools = depset(tools, transitive = transitive_tools),
outputs = [out],
command = command,
progress_message = "Checking for kmi_symbol_list_violations{}".format(_progress_message_suffix(ctx)),
)
return out
def _repack_modules_staging_archive(
ctx,
modules_staging_archive_self,
all_module_basenames_file):
"""Repackages `modules_staging_archive` to contain kernel modules from `base_kernel` as well.
Args:
ctx: ctx
modules_staging_archive_self: The `modules_staging_archive` from `make`
in `_build_main_action`.
all_module_basenames_file: Complete list of base names.
"""
hermetic_tools = hermetic_toolchain.get(ctx)
if not base_kernel_utils.get_base_kernel(ctx):
# No need to repack.
if not modules_staging_archive_self.basename == MODULES_STAGING_ARCHIVE:
fail("\nFATAL: {}: modules_staging_archive_self.basename == {}, but not {}".format(
ctx.label,
modules_staging_archive_self.basename,
MODULES_STAGING_ARCHIVE,
))
return modules_staging_archive_self
modules_staging_archive = ctx.actions.declare_file(
"{}/{}".format(ctx.label.name, MODULES_STAGING_ARCHIVE),
)
# Re-package module_staging_dir to also include the one from base_kernel.
# Pick ko files only from base_kernel, while keeping all depmod files from self.
modules_staging_dir = modules_staging_archive.dirname + "/staging"
cmd = hermetic_tools.setup + """
mkdir -p {modules_staging_dir}
tar xf {self_archive} -C {modules_staging_dir}
# Filter out device-customized modules that has the same name as GKI modules
base_modules=$(tar tf {base_archive} | grep '[.]ko$' || true)
for module in $(cat {all_module_basenames_file}); do
base_modules=$(echo "${{base_modules}}" | grep -v "${{module}}"'$' || true)
done
if [[ -n "${{base_modules}}" ]]; then
tar xf {base_archive} -C {modules_staging_dir} ${{base_modules}}
fi
tar czf {out_archive} -C {modules_staging_dir} .
rm -rf {modules_staging_dir}
""".format(
modules_staging_dir = modules_staging_dir,
self_archive = modules_staging_archive_self.path,
base_archive = base_kernel_utils.get_base_kernel(ctx)[KernelBuildExtModuleInfo].modules_staging_archive.path,
out_archive = modules_staging_archive.path,
all_module_basenames_file = all_module_basenames_file.path,
)
debug.print_scripts(ctx, cmd, what = "repackage_modules_staging_archive")
ctx.actions.run_shell(
mnemonic = "KernelBuildModuleStagingArchive",
inputs = [
modules_staging_archive_self,
base_kernel_utils.get_base_kernel(ctx)[KernelBuildExtModuleInfo].modules_staging_archive,
all_module_basenames_file,
],
outputs = [modules_staging_archive],
tools = hermetic_tools.deps,
progress_message = "Repackaging modules_staging_archive{}".format(_progress_message_suffix(ctx)),
command = cmd,
)
return modules_staging_archive
# TODO(b/291918087): Merge into filegroup_decl.tar.gz to flatten the archive.
def _create_module_scripts_archive(
ctx,
module_srcs):
"""Create `{name}_module_scripts.tar.gz`
Args:
ctx: ctx
module_srcs: from `kernel_utils.filter_module_srcs`
"""
if not ctx.attr.pack_module_env:
return None
hermetic_tools = hermetic_toolchain.get(ctx)
out = ctx.actions.declare_file("{name}/{name}{suffix}".format(
name = ctx.label.name,
suffix = MODULE_ENV_ARCHIVE_SUFFIX,
))
tar_srcs = depset(transitive = [
module_srcs.module_scripts,
module_srcs.module_kconfig,
])
cmd = hermetic_tools.setup + """
# Create archive of module_scripts/module_kconfig
tar cf {out} --dereference -T "$@"
""".format(
out = out.path,
)
args = ctx.actions.args()
args.use_param_file("%s", use_always = True)
# Uniquify for shorter script, and due to https://github.com/landley/toybox/issues/457
args.add_all(tar_srcs, uniquify = True)
ctx.actions.run_shell(
mnemonic = "KernelBuildModuleScriptsArchive",
inputs = depset(transitive = [
tar_srcs,
]),
outputs = [out],
tools = hermetic_tools.deps,
command = cmd,
arguments = [args],
progress_message = "Archiving scripts/kconfig for ext module{}".format(_progress_message_suffix(ctx)),
)
return out