blob: 7979d8defd595cf1b35d856b62bcc4845a1c434d [file] [log] [blame]
# Copyright (C) 2023 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Helper for `kernel_env` to get toolchains for different platforms."""
load("@bazel_skylib//lib:shell.bzl", "shell")
load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
load("@kernel_toolchain_info//:dict.bzl", "VARS")
load("//prebuilts/clang/host/linux-x86/kleaf:versions.bzl", _CLANG_VERSIONS = "VERSIONS")
load(
":common_providers.bzl",
"KernelEnvToolchainsInfo",
"KernelPlatformToolchainInfo",
)
load(":utils.bzl", "utils")
visibility("//build/kernel/kleaf/...")
_RustEnvInfo = provider(
"return value of rust env subrule",
fields = {
"cmd": "command line",
"inputs": "depset of files",
},
)
def _quote_prepend_cwd(value):
"""Prepends $PWD to value.
Returns:
quoted shell value
"""
if not value.startswith("/"):
return "${PWD}/" + shell.quote(value)
return shell.quote(value)
def _get_declared_toolchain_version(ctx):
declared_toolchain_version = None
for version in _CLANG_VERSIONS:
attr = getattr(ctx.attr, "_clang_version_{}".format(version))
if ctx.target_platform_has_constraint(attr[platform_common.ConstraintValueInfo]):
declared_toolchain_version = version
return declared_toolchain_version
def _check_toolchain_version(ctx, resolved_toolchain_info, declared_toolchain_version, platform_name):
if declared_toolchain_version == None:
# kernel_build does not declare toolchain_version. Use default CLANG_VERSION from toolchain
# resolution.
return
if resolved_toolchain_info.compiler_version != declared_toolchain_version:
if resolved_toolchain_info.compiler_version == "kleaf_user_clang_toolchain_skip_version_check":
# buildifier: disable=print
print("\nWARNING: kernel_build.toolchain_version = {}, but overriding with --user_clang_toolchain".format(
declared_toolchain_version,
))
else:
fail("{}: Resolved to incorrect toolchain for {} platform. Expected: {}, actual: {}".format(
ctx.label,
platform_name,
declared_toolchain_version,
resolved_toolchain_info.compiler_version,
))
def _get_target_arch(ctx):
if ctx.target_platform_has_constraint(ctx.attr._platform_cpu_arm[platform_common.ConstraintValueInfo]):
return "arm"
if ctx.target_platform_has_constraint(ctx.attr._platform_cpu_arm64[platform_common.ConstraintValueInfo]):
return "arm64"
if ctx.target_platform_has_constraint(ctx.attr._platform_cpu_i386[platform_common.ConstraintValueInfo]):
return "i386"
if ctx.target_platform_has_constraint(ctx.attr._platform_cpu_riscv64[platform_common.ConstraintValueInfo]):
return "riscv64"
if ctx.target_platform_has_constraint(ctx.attr._platform_cpu_x86_64[platform_common.ConstraintValueInfo]):
return "x86_64"
fail("{}: Cannot determine target platform.".format(ctx.label))
def _quote_sanitize_flags(flags):
"""Turns paths into ones relative to $PWD for each flag.
Kbuild executes the compiler in subdirectories, hence an absolute path is needed.
Returns:
quoted shell value
"""
result_quoted_flags = []
long_flags = [
"--sysroot",
"-iquote",
"-isystem",
]
short_flags = [
"-I",
"-L",
]
prev = None
for _index, flag in enumerate(flags):
if prev in long_flags or prev in short_flags:
result_quoted_flags.append(_quote_prepend_cwd(flag))
elif any([flag.startswith(long_flag + "=") for long_flag in long_flags]):
key, value = flag.split("=", 2)
result_quoted_flags.append("{}={}".format(key, _quote_prepend_cwd(value)))
elif any([flag.startswith(short_flag) for short_flag in short_flags]):
key, value = flag[:2], flag[2:]
result_quoted_flags.append("{}{}".format(key, _quote_prepend_cwd(value)))
else:
result_quoted_flags.append(shell.quote(flag))
prev = flag
return "' '".join(result_quoted_flags)
def _kernel_toolchains_impl(ctx):
exec = ctx.attr.exec_toolchain[KernelPlatformToolchainInfo]
target = ctx.attr.target_toolchain[KernelPlatformToolchainInfo]
# The toolchain_version declared in kernel_build. May be None to use
# default toolchain version.
declared_toolchain_version = _get_declared_toolchain_version(ctx)
# Check that
# declared_toolchain_version == None or exec.compiler_version == declared_toolchain_version
_check_toolchain_version(ctx, exec, declared_toolchain_version, "exec")
# Check that
# declared_toolchain_version == None or target.compiler_version == declared_toolchain_version
_check_toolchain_version(ctx, target, declared_toolchain_version, "target")
# If declared_toolchain_version == None, ensures that the resolved toolchain
# for the two platforms equal.
if target.compiler_version != exec.compiler_version:
fail("{}: Target platform has compiler version {} but exec platform has {}".format(
ctx.label,
target.compiler_version,
exec.compiler_version,
))
actual_toolchain_version = target.compiler_version
all_files_transitive = [exec.all_files, target.all_files]
target_arch = _get_target_arch(ctx)
quoted_bin_paths = [
_quote_prepend_cwd(exec.bin_path),
_quote_prepend_cwd(target.bin_path),
]
setup_env_var_cmd = """
export PATH={quoted_bin_paths}:${{PATH}}
""".format(
quoted_bin_paths = ":".join(quoted_bin_paths),
)
kernel_setup_env_var_cmd = setup_env_var_cmd
if ctx.attr._kernel_use_resolved_toolchains[BuildSettingInfo].value:
# RUNPATH_EXECROOT: A heuristic path to execroot expressed relative to $ORIGIN.
# RUNPATH_EXECROOT assumes that all binaries built by Kbuild are 1~3 levels
# below OUT_DIR,
# e.g. $OUT_DIR/scripts/sign-file, $OUT_DIR/tools/bpf/resolve_btfids/resolve_btfids
# If this ever changes, edit kleaf_internal_eval_ldflags and add more levels.
kernel_setup_env_var_cmd += """
export HOSTCFLAGS={quoted_hostcflags}
export USERCFLAGS={quoted_usercflags}
export HOSTLDFLAGS={quoted_hostldflags}
export USERLDFLAGS={quoted_userldflags}
mkdir -p ${{OUT_DIR}}
# Append to *LDFLAGS based on the current settings of $OUT_DIR.
function kleaf_internal_append_one_ldflags() {{
local backtrack_relative=$1
local RUNPATH_EXECROOT='$$$$\\{{ORIGIN\\}}/'"${{backtrack_relative}}$(realpath ${{ROOT_DIR}} --relative-to ${{OUT_DIR}})"
export HOSTLDFLAGS="${{HOSTLDFLAGS}} "{hostldexpr}
export USERLDFLAGS="${{USERLDFLAGS}} "{userldexpr}
}}
export -f kleaf_internal_append_one_ldflags
function kleaf_internal_eval_ldflags() {{
kleaf_internal_append_one_ldflags ../
kleaf_internal_append_one_ldflags ../../
kleaf_internal_append_one_ldflags ../../../
kleaf_internal_append_one_ldflags ../../../../
kleaf_internal_append_one_ldflags ../../../../../
kleaf_internal_append_one_ldflags ../../../../../../
}}
export -f kleaf_internal_eval_ldflags
kleaf_internal_eval_ldflags
""".format(
quoted_hostcflags = _quote_sanitize_flags(exec.cflags),
quoted_usercflags = _quote_sanitize_flags(target.cflags),
quoted_hostldflags = _quote_sanitize_flags(exec.ldflags),
hostldexpr = exec.ldexpr,
quoted_userldflags = _quote_sanitize_flags(target.ldflags),
userldexpr = target.ldexpr,
)
rust_env = _get_rust_env(
rust_tools = ctx.attr._rust_tools,
host_libc = exec.libc,
exec_glibc_info = ctx.attr.exec_glibc_toolchain[KernelPlatformToolchainInfo],
)
kernel_setup_env_var_cmd += rust_env.cmd
all_files_transitive.append(rust_env.inputs)
# Kleaf clang bins are under kleaf/parent, so CLANG_PREBUILT_BIN in
# build.config.common is incorrect. Manually set additional PATH's.
return KernelEnvToolchainsInfo(
all_files = depset(transitive = all_files_transitive),
target_arch = target_arch,
setup_env_var_cmd = setup_env_var_cmd,
kernel_setup_env_var_cmd = kernel_setup_env_var_cmd,
compiler_version = actual_toolchain_version,
host_runpaths = exec.runpaths,
host_sysroot = exec.sysroot,
)
def _get_rust_env_impl(_subrule_ctx, rust_tools, host_libc, exec_glibc_info):
if not rust_tools:
return _RustEnvInfo(
inputs = depset(),
# Always declare this function so we can use it unconditionally when handling --cache_dir
cmd = """
function kleaf_internal_eval_rust_flags() { :; }
export -f kleaf_internal_eval_rust_flags
""",
)
rust_files_depset = depset(transitive = [target.files for target in rust_tools])
# Note: There are only 2 files in this depset, so using to_list() is okay
rust_files_list = rust_files_depset.to_list()
rustc = utils.find_file("rustc", rust_files_list, "rust tools", required = True)
bindgen = utils.find_file("bindgen", rust_files_list, "rust tools", required = True)
if host_libc == "musl":
target = "x86_64-unknown-linux-musl"
elif host_libc == "glibc":
target = "x86_64-unknown-linux-gnu"
else:
fail("Unknown libc {}".format(target))
# RUNPATH_EXECROOT: A heuristic path to execroot expressed relative to $ORIGIN.
# RUNPATH_EXECROOT assumes that all binaries built by Kbuild are several levels
# below OUT_DIR,
# e.g. $OUT_DIR/scripts/generate_rust_targets
# If this ever changes, edit kleaf_internal_eval_rust_flags and add more levels.
cmd = """
export PATH="${{PATH}}:${{ROOT_DIR}}/"{quoted_rust_bin}":${{ROOT_DIR}}/"{quoted_clangtools_bin}
export HOSTRUSTFLAGS="--target {target}"
export PROCMACROLDFLAGS={quoted_proc_macro_ldflags}
function kleaf_internal_append_one_rust_flags() {{
local backtrack_relative=$1
local RUNPATH_EXECROOT='$$$$\\{{ORIGIN\\}}/'"${{backtrack_relative}}$(realpath ${{ROOT_DIR}} --relative-to ${{OUT_DIR}})"
local RUNPATH_EXECROOT_LESSQUOTE='$$$$ORIGIN/'"${{backtrack_relative}}$(realpath ${{ROOT_DIR}} --relative-to ${{OUT_DIR}})"
export HOSTRUSTFLAGS="${{HOSTRUSTFLAGS}} "-Clink-args=-Wl,-rpath,${{RUNPATH_EXECROOT}}/{quoted_rust_bin}/../lib64
export PROCMACROLDFLAGS="${{PROCMACROLDFLAGS}} "-Wl,-rpath,${{RUNPATH_EXECROOT_LESSQUOTE}}/{quoted_rust_bin}/../lib64
}}
export -f kleaf_internal_append_one_rust_flags
function kleaf_internal_eval_rust_flags() {{
kleaf_internal_append_one_rust_flags ../
}}
export -f kleaf_internal_eval_rust_flags
kleaf_internal_eval_rust_flags
""".format(
target = target,
quoted_rust_bin = shell.quote(rustc.dirname),
quoted_clangtools_bin = shell.quote(bindgen.dirname),
quoted_proc_macro_ldflags = _quote_sanitize_flags(exec_glibc_info.ldflags),
)
return _RustEnvInfo(
inputs = depset(transitive = [rust_files_depset, exec_glibc_info.all_files]),
cmd = cmd,
)
_get_rust_env = subrule(
implementation = _get_rust_env_impl,
)
def _get_rust_tools(rust_toolchain_version):
if not rust_toolchain_version:
return []
rust_binaries = "//prebuilts/rust/linux-x86/%s:binaries" % rust_toolchain_version
bindgen = "//prebuilts/clang-tools:linux-x86/bin/bindgen"
return [Label(rust_binaries), Label(bindgen)]
kernel_toolchains = rule(
doc = """Helper for `kernel_env` to get toolchains for different platforms.""",
implementation = _kernel_toolchains_impl,
attrs = {
"exec_toolchain": attr.label(
cfg = "exec",
providers = [KernelPlatformToolchainInfo],
),
"exec_glibc_toolchain": attr.label(
cfg = "exec",
providers = [KernelPlatformToolchainInfo],
),
"target_toolchain": attr.label(
providers = [KernelPlatformToolchainInfo],
),
# TODO(b/284390729): Use toolchain resolution
"rust_toolchain_version": attr.string(
doc = "the version of the rust toolchain to use for this environment",
default = VARS.get("RUSTC_VERSION", ""),
),
"_rust_tools": attr.label_list(default = _get_rust_tools, allow_files = True),
"_kernel_use_resolved_toolchains": attr.label(
default = "//build/kernel/kleaf:incompatible_kernel_use_resolved_toolchains",
),
"_platform_cpu_arm": attr.label(default = "@platforms//cpu:arm"),
"_platform_cpu_arm64": attr.label(default = "@platforms//cpu:arm64"),
"_platform_cpu_i386": attr.label(default = "@platforms//cpu:i386"),
"_platform_cpu_riscv64": attr.label(default = "@platforms//cpu:riscv64"),
"_platform_cpu_x86_64": attr.label(default = "@platforms//cpu:x86_64"),
} | {
"_clang_version_{}".format(version): attr.label(default = "//prebuilts/clang/host/linux-x86/kleaf:{}".format(version))
for version in _CLANG_VERSIONS
},
subrules = [_get_rust_env],
)