blob: c60f287d2a3949c6d0729d429bd3306927f89605 [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.
"""Creates proper .config and others for kernel_build."""
load("@bazel_skylib//lib:dicts.bzl", "dicts")
load("@bazel_skylib//lib:shell.bzl", "shell")
load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
load(":abi/trim_nonlisted_kmi_utils.bzl", "trim_nonlisted_kmi_utils")
load(":cache_dir.bzl", "cache_dir")
load(
":common_providers.bzl",
"DefconfigInfo",
"KernelBuildOriginalEnvInfo",
"KernelConfigInfo",
"KernelEnvAttrInfo",
"KernelEnvInfo",
"KernelEnvMakeGoalsInfo",
"KernelSerializedEnvInfo",
"KernelToolchainInfo",
"StepInfo",
)
load(":config_utils.bzl", "config_utils")
load(":debug.bzl", "debug")
load(":hermetic_toolchain.bzl", "hermetic_toolchain")
load(":kernel_config_settings.bzl", "kernel_config_settings")
load(":kgdb.bzl", "kgdb")
load(":scripts_config_arg_builder.bzl", _config = "scripts_config_arg_builder")
load(":stamp.bzl", "stamp")
load(":utils.bzl", "kernel_utils", "utils")
visibility("//build/kernel/kleaf/...")
# Name of raw symbol list under $OUT_DIR
_RAW_KMI_SYMBOL_LIST_BELOW_OUT_DIR = "abi_symbollist.raw"
def _check_defconfig_minimized_impl(
subrule_ctx,
defconfig_info,
pre_defconfig_fragment_files,
check_defconfig_attr_value):
"""Checks that defconfig matches the result of savedefconfig. """
if check_defconfig_attr_value != "minimized":
return StepInfo(
inputs = depset(),
cmd = "",
outputs = [],
tools = [],
)
if not defconfig_info or not defconfig_info.file or pre_defconfig_fragment_files:
# A good string repr of kernel_build.defconfig
defconfig = None
if defconfig_info:
defconfig = defconfig_info.file.path if defconfig_info.file else defconfig_info.make_target
fail("""{kernel_build_label}: check_defconfig="minimized" requires the following:
- defconfig is set and not phony_defconfig (but it is {defconfig})
- pre_defconfig_fragments is not set (but it is {pre_defconfig_fragment_files})
""".format(
kernel_build_label = subrule_ctx.label.name.removesuffix("_config"),
defconfig = defconfig,
pre_defconfig_fragment_files = [file.path for file in pre_defconfig_fragment_files],
))
cmd = """
if [[ "${{POST_DEFCONFIG_CMDS}}" =~ check_defconfig ]]; then
echo "ERROR: Please delete check_defconfig from POST_DEFCONFIG_CMDS." >&2
exit 1
fi
kleaf_internal_check_defconfig_minimized {}
""".format(defconfig_info.file.path)
return StepInfo(
inputs = depset(),
cmd = cmd,
outputs = [],
tools = [],
)
_check_defconfig_minimized = subrule(
implementation = _check_defconfig_minimized_impl,
)
def _config_trim_impl(subrule_ctx, trim_attr_value, raw_kmi_symbol_list_file):
"""Return configs for trimming.
Args:
subrule_ctx: subrule_ctx
trim_attr_value: value of trim_nonlisted_kmi_utils.get_value(ctx)
raw_kmi_symbol_list_file: the raw_kmi_symbol_list file
Returns:
a list of arguments to `scripts/config`
"""
if trim_attr_value and not raw_kmi_symbol_list_file:
fail("{}: trim_nonlisted_kmi is set but raw_kmi_symbol_list is empty.".format(subrule_ctx.label))
return []
_config_trim = subrule(
implementation = _config_trim_impl,
)
def _config_symbol_list_impl(_subrule_ctx, raw_kmi_symbol_list_file):
"""Return configs for `raw_symbol_list`.
Args:
_subrule_ctx: subrule_ctx
raw_kmi_symbol_list_file: the raw_kmi_symbol_list file
Returns:
a list of arguments to `scripts/config`
"""
if not raw_kmi_symbol_list_file:
return []
return [
_config.set_str(
"UNUSED_KSYMS_WHITELIST",
_RAW_KMI_SYMBOL_LIST_BELOW_OUT_DIR,
),
]
_config_symbol_list = subrule(implementation = _config_symbol_list_impl)
def _config_keys_impl(_subrule_ctx, module_signing_key_file, system_trusted_key_file):
"""Return configs for module signing keys and system trusted keys.
Note: by embedding the system path into the binary, the resulting build
becomes non-deterministic and the path leaks into the binary. It can be
discovered with `strings` or even by inspecting the kernel config from the
binary.
Args:
_subrule_ctx: subrule_ctx
module_signing_key_file: file of module_signing_key
system_trusted_key_file: file of system_trusted_key
Returns:
a list of arguments to `scripts/config`
"""
configs = []
if module_signing_key_file:
configs.append(_config.set_str(
"MODULE_SIG_KEY",
module_signing_key_file.basename,
))
if system_trusted_key_file:
configs.append(_config.set_str(
"SYSTEM_TRUSTED_KEYS",
system_trusted_key_file.basename,
))
return configs
_config_keys = subrule(implementation = _config_keys_impl)
def _check_trimming_disabled_impl(subrule_ctx, trim_attr_value, **kwargs):
"""Checks that trimming is disabled if --k*san is set
Args:
subrule_ctx: subrule_ctx
trim_attr_value: value of trim_nonlisted_kmi_utils.get_value(ctx)
**kwargs: must contain all k*san attrs
"""
if not trim_attr_value:
return
for attr_name in (
"_kasan",
"_kasan_sw_tags",
"_kasan_generic",
"_kcov",
"_kcsan",
):
if kwargs[attr_name][BuildSettingInfo].value:
fail("{}: --{} requires trimming to be disabled".format(subrule_ctx.label, attr_name))
_check_trimming_disabled = subrule(
implementation = _check_trimming_disabled_impl,
attrs = {
"_kasan": attr.label(default = "//build/kernel/kleaf:kasan"),
"_kasan_sw_tags": attr.label(default = "//build/kernel/kleaf:kasan_sw_tags"),
"_kasan_generic": attr.label(default = "//build/kernel/kleaf:kasan_generic"),
"_kcov": attr.label(default = "//build/kernel/kleaf:kcov"),
"_kcsan": attr.label(default = "//build/kernel/kleaf:kcsan"),
},
)
def _reconfig_impl(
_subrule_ctx,
trim_attr_value,
raw_kmi_symbol_list_file,
module_signing_key_file,
system_trusted_key_file,
post_defconfig_fragment_files):
"""Return a command and extra inputs to re-configure `.config` file.
Args:
_subrule_ctx: subrule_ctx
trim_attr_value: value of trim_nonlisted_kmi_utils.get_value(ctx)
raw_kmi_symbol_list_file: the raw_kmi_symbol_list file
module_signing_key_file: file of module_signing_key
system_trusted_key_file: file of system_trusted_key
post_defconfig_fragment_files: files of post_defconfig_fragments
"""
_check_trimming_disabled(trim_attr_value = trim_attr_value)
configs = []
apply_post_defconfig_fragments_cmd = ""
configs += _config_trim(
trim_attr_value = trim_attr_value,
raw_kmi_symbol_list_file = raw_kmi_symbol_list_file,
)
configs += _config_symbol_list(
raw_kmi_symbol_list_file = raw_kmi_symbol_list_file,
)
configs += _config_keys(
module_signing_key_file = module_signing_key_file,
system_trusted_key_file = system_trusted_key_file,
)
configs += kgdb.get_scripts_config_args()
if post_defconfig_fragment_files:
post_defconfig_fragments_paths = [f.path for f in post_defconfig_fragment_files]
apply_post_defconfig_fragments_cmd = config_utils.create_merge_config_cmd(
base_expr = "${OUT_DIR}/.config",
defconfig_fragments_paths_expr = " ".join(post_defconfig_fragments_paths),
)
apply_post_defconfig_fragments_cmd += """
need_olddefconfig=1
"""
cmd = """
(
need_olddefconfig=
configs_to_apply=$(echo {configs})
# There could be reconfigurations based on configs which can lead to
# an empty `configs_to_apply` even when `configs` is not empty,
# for that reason it is better to check it is not empty before using it.
if [ -n "${{configs_to_apply}}" ]; then
${{KERNEL_DIR}}/scripts/config --file ${{OUT_DIR}}/.config ${{configs_to_apply}}
need_olddefconfig=1
fi
{apply_post_defconfig_fragments_cmd}
if [[ -n "${{need_olddefconfig}}" ]]; then
make -C ${{KERNEL_DIR}} ${{TOOL_ARGS}} O=${{OUT_DIR}} olddefconfig
fi
)
""".format(
configs = " ".join(configs),
apply_post_defconfig_fragments_cmd = apply_post_defconfig_fragments_cmd,
)
return StepInfo(
cmd = cmd,
inputs = depset(post_defconfig_fragment_files),
outputs = [],
tools = [],
)
_reconfig = subrule(
implementation = _reconfig_impl,
subrules = [
_check_trimming_disabled,
_config_trim,
_config_symbol_list,
_config_keys,
kgdb.get_scripts_config_args,
],
)
def _set_up_defconfig_impl(subrule_ctx, defconfig_info, is_run_env):
"""Puts defconfig in $OUT_DIR."""
if not defconfig_info:
return StepInfo(inputs = depset(), cmd = "", outputs = [], tools = [])
if not defconfig_info.file:
if not defconfig_info.make_target:
fail("{}: Unreconized defconfig!".format(subrule_ctx.label.name))
cmd = """
if [[ -n "${{DEFCONFIG}}" ]]; then
echo "ERROR: DEFCONFIG cannot be set in build configs if kernel_build.defconfig is set." >&2
echo " DEFCONFIG=${{DEFCONFIG}}" >&2
echo " kernel_build.defconfig={defconfig_make_target}" >&2
exit 1
fi
DEFCONFIG={defconfig_make_target}
""".format(
defconfig_make_target = defconfig_info.make_target,
)
return StepInfo(inputs = depset(), cmd = cmd, outputs = [], tools = [])
cmd = """
if [[ -n "${{DEFCONFIG}}" ]]; then
echo "ERROR: DEFCONFIG cannot be set in build configs if kernel_build.defconfig is set." >&2
echo " DEFCONFIG=${{DEFCONFIG}}" >&2
echo " kernel_build.defconfig={defconfig_file}" >&2
exit 1
fi
DEFCONFIG=kleaf_internal_{kernel_build_name}_defconfig
(
{set_src_arch_cmd}
if [[ -f "${{KERNEL_DIR}}/arch/${{SRCARCH}}/configs/${{DEFCONFIG}}" ]]; then
echo "ERROR: Please delete ${{KERNEL_DIR}}/arch/${{SRCARCH}}/configs/${{DEFCONFIG}} and try again." >&2
exit 1
fi
mkdir -p "${{OUT_DIR}}/arch/${{SRCARCH}}/configs/"
cp -L {defconfig_file} "${{OUT_DIR}}/arch/${{SRCARCH}}/configs/${{DEFCONFIG}}"
)
""".format(
set_src_arch_cmd = kernel_utils.set_src_arch_cmd(),
kernel_build_name = subrule_ctx.label.name.removesuffix("_config"),
defconfig_file = defconfig_info.file.short_path if is_run_env else defconfig_info.file.path,
)
return StepInfo(
inputs = depset([defconfig_info.file]),
cmd = cmd,
outputs = [],
tools = [],
)
_set_up_defconfig = subrule(
implementation = _set_up_defconfig_impl,
)
def _pre_defconfig_impl(_subrule_ctx, pre_defconfig_fragment_files, is_run_env):
cmd = ""
if pre_defconfig_fragment_files:
cmd += """
if [[ -n "${{PRE_DEFCONFIG_CMDS}}" ]]; then
echo "ERROR: PRE_DEFCONFIG_CMDS must not be set if kernel_build.pre_defconfig_fragments is set!" >&2
echo " PRE_DEFCONFIG_CMDS=${{PRE_DEFCONFIG_CMDS}}" >&2
echo " kernel_build.pre_defconfig_fragments={fragments}" >&2
exit 1
fi
""".format(
fragments = " ".join([(file.short_path if is_run_env else file.path) for file in pre_defconfig_fragment_files]),
)
cmd += """
# Pre-defconfig commands
eval ${PRE_DEFCONFIG_CMDS}
"""
if pre_defconfig_fragment_files:
apply_pre_defconfig_fragments_cmd = config_utils.create_merge_config_cmd(
base_expr = "${OUT_DIR}/arch/${SRCARCH}/configs/${DEFCONFIG}",
defconfig_fragments_paths_expr = " ".join([(file.short_path if is_run_env else file.path) for file in pre_defconfig_fragment_files]),
quiet = True,
)
cmd += """
(
{set_src_arch_cmd}
if ! [[ -f ${{OUT_DIR}}/arch/${{SRCARCH}}/configs/${{DEFCONFIG}} ]]; then
echo "ERROR: No base defconfig to apply pre defconfig fragment on!" >&2
exit 1
fi
# Apply pre_defconfig_fragments
{apply_pre_defconfig_fragments_cmd}
)
""".format(
set_src_arch_cmd = kernel_utils.set_src_arch_cmd(),
apply_pre_defconfig_fragments_cmd = apply_pre_defconfig_fragments_cmd,
)
return StepInfo(
inputs = depset(pre_defconfig_fragment_files),
cmd = cmd,
outputs = [],
tools = [],
)
_pre_defconfig = subrule(
implementation = _pre_defconfig_impl,
)
def _make_defconfig_impl(_subrule_ctx):
cmd = """
# Actual defconfig
make -C ${KERNEL_DIR} ${TOOL_ARGS} O=${OUT_DIR} ${DEFCONFIG}
"""
return StepInfo(
inputs = depset(),
cmd = cmd,
outputs = [],
tools = [],
)
_make_defconfig = subrule(
implementation = _make_defconfig_impl,
)
def _post_defconfig_impl(
_subrule_ctx,
trim_attr_value,
raw_kmi_symbol_list_file,
module_signing_key_file,
system_trusted_key_file,
post_defconfig_fragment_files):
"""Handle post defconfig step
Args:
_subrule_ctx: subrule_ctx
trim_attr_value: value of trim_nonlisted_kmi_utils.get_value(ctx)
raw_kmi_symbol_list_file: the raw_kmi_symbol_list file
module_signing_key_file: file of module_signing_key
system_trusted_key_file: file of system_trusted_key
post_defconfig_fragment_files: files of post_defconfig_fragments
"""
cmd = """
# Post-defconfig commands
eval ${POST_DEFCONFIG_CMDS}
"""
reconfig_ret = _reconfig(
trim_attr_value = trim_attr_value,
raw_kmi_symbol_list_file = raw_kmi_symbol_list_file,
module_signing_key_file = module_signing_key_file,
system_trusted_key_file = system_trusted_key_file,
post_defconfig_fragment_files = post_defconfig_fragment_files,
)
cmd += reconfig_ret.cmd
return StepInfo(
inputs = reconfig_ret.inputs,
cmd = cmd,
outputs = reconfig_ret.outputs,
tools = reconfig_ret.tools,
)
_post_defconfig = subrule(
implementation = _post_defconfig_impl,
subrules = [_reconfig],
)
def _check_dot_config_against_defconfig_impl(
_subrule_ctx,
check_defconfig_attr_value,
defconfig_info,
pre_defconfig_fragment_files,
post_defconfig_fragment_files):
"""Checks .config against defconfig and fragments."""
if check_defconfig_attr_value == "disabled":
return StepInfo(
inputs = depset(),
cmd = "",
outputs = [],
tools = [],
)
check_defconfig_step = None
transitive_inputs = []
tools = []
outputs = []
if (defconfig_info and defconfig_info.file) or pre_defconfig_fragment_files or post_defconfig_fragment_files:
check_defconfig_step = config_utils.create_check_defconfig_step(
defconfig = defconfig_info.file if defconfig_info else None,
pre_defconfig_fragments = pre_defconfig_fragment_files,
post_defconfig_fragments = post_defconfig_fragment_files,
)
transitive_inputs.append(check_defconfig_step.inputs)
tools += check_defconfig_step.tools
outputs += check_defconfig_step.outputs
return StepInfo(
cmd = check_defconfig_step.cmd if check_defconfig_step else "",
inputs = depset(post_defconfig_fragment_files, transitive = transitive_inputs),
outputs = outputs,
tools = tools,
)
_check_dot_config_against_defconfig = subrule(
implementation = _check_dot_config_against_defconfig_impl,
subrules = [config_utils.create_check_defconfig_step],
)
def _kernel_config_impl(ctx):
localversion_file = stamp.write_localversion(ctx)
inputs = [
s
for s in ctx.files.srcs
if any([token in s.path for token in [
"Kbuild",
"Kconfig",
"Makefile",
"configs/",
"scripts/",
".fragment",
]])
]
transitive_inputs = []
tools = []
out_dir = ctx.actions.declare_directory(ctx.attr.name + "/out_dir")
outputs = [out_dir]
defconfig_info = None
if ctx.attr.defconfig:
if DefconfigInfo in ctx.attr.defconfig:
defconfig_info = ctx.attr.defconfig[DefconfigInfo]
elif len(ctx.files.defconfig) == 1:
defconfig_info = DefconfigInfo(file = ctx.files.defconfig[0], make_target = None)
else:
fail("{}: defconfig {} must provide exactly one file".format(ctx.label, ctx.attr.defconfig.label))
if ctx.attr.pre_defconfig_fragments and (not defconfig_info or not defconfig_info.file):
fail("{}: Must also set defconfig to a non phony_defconfig target if using pre_defconfig_fragments".format(ctx.label.name.removesuffix("_config")))
step_returns = [
_set_up_defconfig(
is_run_env = False,
defconfig_info = defconfig_info,
),
_pre_defconfig(
is_run_env = False,
pre_defconfig_fragment_files = ctx.files.pre_defconfig_fragments,
),
_make_defconfig(),
]
check_defconfig_minimized_ret = _check_defconfig_minimized(
check_defconfig_attr_value = ctx.attr.check_defconfig,
defconfig_info = defconfig_info,
pre_defconfig_fragment_files = ctx.files.pre_defconfig_fragments,
)
step_returns.append(check_defconfig_minimized_ret)
# If we already checked .config against `make savedefconfig`, we don't need to check
# .config against defconfig/pre_defconfig_fragments again. Otherwise, check
# .config against defconfig/pre_defconfig_fragments before applying post_defconfig_fragments.
if not check_defconfig_minimized_ret.cmd:
step_returns.append(
_check_dot_config_against_defconfig(
check_defconfig_attr_value = ctx.attr.check_defconfig,
defconfig_info = defconfig_info,
pre_defconfig_fragment_files = ctx.files.pre_defconfig_fragments,
post_defconfig_fragment_files = [],
),
)
step_returns += [
_post_defconfig(
trim_attr_value = trim_nonlisted_kmi_utils.get_value(ctx),
raw_kmi_symbol_list_file = utils.optional_file(ctx.files.raw_kmi_symbol_list),
module_signing_key_file = ctx.file.module_signing_key,
system_trusted_key_file = ctx.file.system_trusted_key,
post_defconfig_fragment_files = ctx.files.post_defconfig_fragments,
),
_check_dot_config_against_defconfig(
check_defconfig_attr_value = ctx.attr.check_defconfig,
defconfig_info = DefconfigInfo(file = None, make_target = None),
pre_defconfig_fragment_files = [],
post_defconfig_fragment_files = ctx.files.post_defconfig_fragments,
),
]
transitive_inputs += [step_return.inputs for step_return in step_returns]
outputs += [out for step_return in step_returns for out in step_return.outputs]
tools += [tool for step_return in step_returns for tool in step_return.tools]
transitive_inputs.append(ctx.attr.env[KernelEnvInfo].inputs)
transitive_tools = [ctx.attr.env[KernelEnvInfo].tools]
cache_dir_step = cache_dir.get_step(
ctx = ctx,
common_config_tags = ctx.attr.env[KernelEnvAttrInfo].common_config_tags,
symlink_name = "config",
)
inputs += cache_dir_step.inputs
outputs += cache_dir_step.outputs
tools += cache_dir_step.tools
inputs.append(localversion_file)
sync_raw_kmi_symbol_list_cmd = ""
if ctx.files.raw_kmi_symbol_list:
sync_raw_kmi_symbol_list_cmd = """
rsync -aL {raw_kmi_symbol_list} {out_dir}/{raw_kmi_symbol_list_below_out_dir}
""".format(
out_dir = out_dir.path,
raw_kmi_symbol_list = ctx.files.raw_kmi_symbol_list[0].path,
raw_kmi_symbol_list_below_out_dir = _RAW_KMI_SYMBOL_LIST_BELOW_OUT_DIR,
)
inputs += ctx.files.raw_kmi_symbol_list
# exclude keys in out_dir to avoid accidentally including them
# in the distribution.
command = ctx.attr.env[KernelEnvInfo].setup + """
{cache_dir_cmd}
{defconfig_cmd}
# HACK: run syncconfig to avoid re-triggerring kernel_build
make -C ${{KERNEL_DIR}} ${{TOOL_ARGS}} O=${{OUT_DIR}} syncconfig
# Grab outputs
rsync -aL ${{OUT_DIR}}/.config {out_dir}/.config
rsync -aL ${{OUT_DIR}}/include/ {out_dir}/include/
rsync -aL {localversion_file} {out_dir}/localversion
{sync_raw_kmi_symbol_list_cmd}
# Ensure reproducibility. The value of the real $ROOT_DIR is replaced in the setup script.
sed -i'' -e 's:'"${{ROOT_DIR}}"':${{ROOT_DIR}}:g' {out_dir}/include/config/auto.conf.cmd
# HACK: Ensure we always SYNC auto.conf. This ensures binaries like fixdep are always
# re-built. See b/263415662
echo "include/config/auto.conf: FORCE" >> {out_dir}/include/config/auto.conf.cmd
{cache_dir_post_cmd}
""".format(
out_dir = out_dir.path,
cache_dir_cmd = cache_dir_step.cmd,
cache_dir_post_cmd = cache_dir_step.post_cmd,
defconfig_cmd = "\n".join([step_return.cmd for step_return in step_returns]),
localversion_file = localversion_file.path,
sync_raw_kmi_symbol_list_cmd = sync_raw_kmi_symbol_list_cmd,
)
debug.print_scripts(ctx, command)
ctx.actions.run_shell(
mnemonic = "KernelConfig",
inputs = depset(inputs, transitive = transitive_inputs),
outputs = outputs,
tools = tools + [depset(transitive = transitive_tools)],
progress_message = "Creating kernel config{} %{{label}}".format(
ctx.attr.env[KernelEnvAttrInfo].progress_message_note,
),
command = command,
execution_requirements = kernel_utils.local_exec_requirements(ctx),
)
post_setup_deps = [out_dir]
extra_restore_outputs_cmd = ""
for file in (ctx.file.module_signing_key, ctx.file.system_trusted_key):
if not file:
continue
extra_restore_outputs_cmd += """
if [ -n "${{BUILD_WORKSPACE_DIRECTORY}}" ] || [ "${{BAZEL_TEST}}" = "1" ] ; then
rsync -aL {file_short} ${{OUT_DIR}}/{basename}
else
rsync -aL {file} ${{OUT_DIR}}/{basename}
fi
""".format(
file = file.path,
file_short = file.short_path,
basename = file.basename,
)
post_setup_deps.append(file)
# <kernel_build>_config_setup.sh
serialized_env_info_setup_script = ctx.actions.declare_file("{name}/{name}_setup.sh".format(name = ctx.attr.name))
ctx.actions.write(
output = serialized_env_info_setup_script,
content = get_config_setup_command(
env_setup_command = ctx.attr.env[KernelEnvInfo].setup,
out_dir = out_dir,
extra_restore_outputs_cmd = extra_restore_outputs_cmd,
),
)
serialized_env_info = KernelSerializedEnvInfo(
setup_script = serialized_env_info_setup_script,
tools = ctx.attr.env[KernelEnvInfo].tools,
inputs = depset(post_setup_deps + [
serialized_env_info_setup_script,
], transitive = transitive_inputs),
)
config_script_ret = _get_config_script(
env_info = ctx.attr.env[KernelEnvInfo],
defconfig_info = defconfig_info,
pre_defconfig_fragment_files = ctx.files.pre_defconfig_fragments,
)
config_script_runfiles = ctx.runfiles(
files = inputs,
transitive_files = depset(transitive = transitive_inputs + [
ctx.attr.env[KernelEnvInfo].inputs,
ctx.attr.env[KernelEnvInfo].tools,
config_script_ret.inputs,
]),
)
return [
serialized_env_info,
ctx.attr.env[KernelEnvAttrInfo],
ctx.attr.env[KernelEnvMakeGoalsInfo],
ctx.attr.env[KernelToolchainInfo],
KernelBuildOriginalEnvInfo(
env_info = ctx.attr.env[KernelEnvInfo],
),
DefaultInfo(
files = depset([out_dir]),
executable = config_script_ret.executable,
runfiles = config_script_runfiles,
),
KernelConfigInfo(
env_setup_script = ctx.file.env,
),
]
def _get_config_script_impl(
subrule_ctx,
env_info,
defconfig_info,
pre_defconfig_fragment_files):
"""Handles config.sh.
Args:
subrule_ctx: subrule_ctx
env_info: kernel_env[KernelEnvInfo]
defconfig_info: the DefconfigInfo from attr defconfig
pre_defconfig_fragment_files: list of files of pre_defconfig_fragments
"""
executable = subrule_ctx.actions.declare_file("{}/config.sh".format(subrule_ctx.label.name))
step_returns = [
_set_up_defconfig(
is_run_env = True,
defconfig_info = defconfig_info,
),
_pre_defconfig(
is_run_env = True,
pre_defconfig_fragment_files = pre_defconfig_fragment_files,
),
_make_defconfig(),
]
# We can't handle outputs because this is a `run` command not a `build` command.
if [out for step_return in step_returns for out in step_return.outputs]:
fail("ERROR: None of the defconfig steps should produce outputs! {}".format(
[step_return.outputs for step_return in step_returns],
))
# We can't handle tools yet because it may contain FilesToRunProvider, which can't be placed
# in runfiles directly.
if [tool for step_return in step_returns for tool in step_return.tools]:
fail("ERROR: None of the defconfig steps should require tools! {}".format(
[step_return.tools for step_return in step_returns],
))
script = env_info.setup
# TODO(b/254348147): Support ncurses for hermetic tools
script += """
export HOSTCFLAGS="${HOSTCFLAGS} --sysroot="
export HOSTLDFLAGS="${HOSTLDFLAGS} --sysroot="
"""
script += kernel_utils.set_src_arch_cmd()
script += """
menucommand="${1:-savedefconfig}"
if ! [[ "${menucommand}" =~ .*config ]]; then
echo "Invalid command $menucommand. Must be *config." >&2
exit 1
fi
"""
script += "\n".join([step_return.cmd for step_return in step_returns])
inputs = []
if defconfig_info and defconfig_info.make_target:
# defconfig = some phony_defconfig
script += """
echo "ERROR: With defconfig set to a phony_defconfig, menuconfig etc. is not supported." >&2
exit 1
"""
elif not defconfig_info or not defconfig_info.file:
# defconfig = None
# Legacy code path.
# TODO(b/368119551): Clean up once kernel_build.defconfig is required.
script += """
# Show UI
menuconfig ${menucommand}
"""
else:
# defconfig = some file
inputs.append(defconfig_info.file)
script += """
(
orig_config=$(mktemp)
changed_config=$(mktemp)
new_fragment=$(mktemp)
trap "rm -f ${orig_config} ${changed_config} ${new_fragment}" EXIT
new_config="${OUT_DIR}/.config"
cp "${OUT_DIR}/.config" ${orig_config}
make -C ${KERNEL_DIR} ${TOOL_ARGS} O=${OUT_DIR} ${MAKE_ARGS} ${menucommand}
"""
if pre_defconfig_fragment_files:
script += """
${{KERNEL_DIR}}/scripts/diffconfig -m ${{orig_config}} ${{new_config}} > ${{changed_config}}
KCONFIG_CONFIG=${{new_fragment}} ${{ROOT_DIR}}/${{KERNEL_DIR}}/scripts/kconfig/merge_config.sh -m {fragments} ${{changed_config}}
""".format(
fragments = " ".join([file.short_path for file in pre_defconfig_fragment_files]),
)
if len(pre_defconfig_fragment_files) == 1:
script += """
sort_config ${{new_fragment}} > $(realpath {fragment})
echo "Updated $(realpath {fragment})"
""".format(
fragment = pre_defconfig_fragment_files[0].short_path,
)
else:
script += """
sorted_new_fragment=$(mktemp)
sort_config ${{new_fragment}} > ${{sorted_new_fragment}}
echo "ERROR: Unable to update any file because there are multiple pre_defconfig_fragments." >&2
echo " Please manually update the following files:" >&2
echo " "{quoted_indented_fragments} >&2
echo " ... with the content of ..." >&2
echo " ${{sorted_new_fragment}}" >&2
# Intentionally not delete sorted_new_fragment
exit 1
""".format(
quoted_indented_fragments = shell.quote("\n ".join([file.short_path for file in pre_defconfig_fragment_files])),
)
else:
script += """
make -C ${{KERNEL_DIR}} ${{TOOL_ARGS}} O=${{OUT_DIR}} ${{MAKE_ARGS}} savedefconfig
mv ${{OUT_DIR}}/defconfig $(realpath {defconfig})
echo "Updated $(realpath {defconfig})"
""".format(
defconfig = defconfig_info.file.short_path,
)
script += """
)
"""
script += """
# Post-defconfig commands
eval ${POST_DEFCONFIG_CMDS}
"""
# Do not apply any post_defconfig_fragments because:
# - They have no effect; we already saved necessary defconfig and fragments.
# - They usually refer to variants of the build, controlled by attributes, flag values, etc.
# See kernel_build.defconfig_fragments for details.
subrule_ctx.actions.write(
output = executable,
content = script,
is_executable = True,
)
return struct(
executable = executable,
inputs = depset(inputs, transitive = [step_return.inputs for step_return in step_returns]),
)
_get_config_script = subrule(
implementation = _get_config_script_impl,
subrules = [
_set_up_defconfig,
_pre_defconfig,
_make_defconfig,
],
)
def get_config_setup_command(
env_setup_command,
out_dir,
extra_restore_outputs_cmd):
"""Returns the content of `<kernel_build>_config_setup.sh`, given the parameters.
Args:
env_setup_command: command to set up environment from `kernel_env`
out_dir: output directory from `kernel_config`
extra_restore_outputs_cmd: Extra CMD to restore outputs
Returns:
the command to setup the environment like after `make defconfig`.
"""
cmd = """
{env_setup_command}
{eval_restore_out_dir_cmd}
[ -z ${{OUT_DIR}} ] && echo "FATAL: configs post_env_info setup run without OUT_DIR set!" >&2 && exit 1
# Restore kernel config inputs
mkdir -p ${{OUT_DIR}}/include/
if [ -n "${{BUILD_WORKSPACE_DIRECTORY}}" ] || [ "${{BAZEL_TEST}}" = "1" ]; then
rule_out_dir={out_dir_short}
else
rule_out_dir={out_dir}
fi
rsync -aL ${{rule_out_dir}}/.config ${{OUT_DIR}}/.config
if [[ "${{kleaf_do_not_rsync_out_dir_include}}" == "1" ]]; then
export kleaf_out_dir_include_candidate="${{rule_out_dir}}/include/"
else
rsync -aL --chmod=D+w ${{rule_out_dir}}/include/ ${{OUT_DIR}}/include/
fi
rsync -aL --chmod=F+w ${{rule_out_dir}}/localversion ${{OUT_DIR}}/localversion
if [[ -f ${{rule_out_dir}}/{raw_kmi_symbol_list_below_out_dir} ]]; then
rsync -aL --chmod=F+w \\
${{rule_out_dir}}/{raw_kmi_symbol_list_below_out_dir} ${{OUT_DIR}}/
fi
unset rule_out_dir
# Restore real value of $ROOT_DIR in auto.conf.cmd
if [[ "${{kleaf_do_not_rsync_out_dir_include}}" != "1" ]]; then
# Restore real value of $ROOT_DIR in auto.conf.cmd
sed -i'' -e 's:${{ROOT_DIR}}:'"${{ROOT_DIR}}"':g' ${{OUT_DIR}}/include/config/auto.conf.cmd
fi
""".format(
env_setup_command = env_setup_command,
eval_restore_out_dir_cmd = kernel_utils.eval_restore_out_dir_cmd(),
out_dir = out_dir.path,
out_dir_short = out_dir.short_path,
raw_kmi_symbol_list_below_out_dir = _RAW_KMI_SYMBOL_LIST_BELOW_OUT_DIR,
)
cmd += extra_restore_outputs_cmd
return cmd
def _kernel_config_additional_attrs():
return dicts.add(
kernel_config_settings.of_kernel_config(),
trim_nonlisted_kmi_utils.attrs(),
cache_dir.attrs(),
)
kernel_config = rule(
implementation = _kernel_config_impl,
doc = """Defines a kernel config target.
- When `bazel build <target>`, this target runs `make defconfig` etc. during the build.
- When `bazel run <target> -- Xconfig`, this target runs `make Xconfig`.
""",
attrs = {
"env": attr.label(
mandatory = True,
providers = [
KernelEnvInfo,
KernelEnvAttrInfo,
KernelEnvMakeGoalsInfo,
KernelToolchainInfo,
],
doc = "environment target that defines the kernel build environment",
allow_single_file = True,
),
"srcs": attr.label_list(mandatory = True, doc = "kernel sources", allow_files = True),
"raw_kmi_symbol_list": attr.label(
doc = "Label to abi_symbollist.raw. Must be 0 or 1 file.",
allow_files = True,
),
"module_signing_key": attr.label(
doc = "Label to module signing key.",
allow_single_file = True,
),
"system_trusted_key": attr.label(
doc = "Label to trusted system key.",
allow_single_file = True,
),
"defconfig": attr.label(allow_files = True),
"pre_defconfig_fragments": attr.label_list(
doc = "**pre** defconfig fragments",
allow_files = True,
),
"post_defconfig_fragments": attr.label_list(
doc = "**post** defconfig fragments",
allow_files = True,
),
"check_defconfig": attr.string(
doc = "minimized: Checks defconfig against savedefconfig. match: check values only.",
default = "match",
values = ["disabled", "minimized", "match"],
),
"_config_is_stamp": attr.label(default = "//build/kernel/kleaf:config_stamp"),
"_debug_print_scripts": attr.label(default = "//build/kernel/kleaf:debug_print_scripts"),
} | _kernel_config_additional_attrs(),
executable = True,
toolchains = [hermetic_toolchain.type],
subrules = [
_set_up_defconfig,
_pre_defconfig,
_make_defconfig,
_check_defconfig_minimized,
_post_defconfig,
_check_dot_config_against_defconfig,
_get_config_script,
],
)