blob: 59a5378254f464882762eaa420d6bfc42ed4ecc2 [file] [log] [blame]
# Copyright 2019 The Chromium OS Authors. All rights reserved.
# Distributed under the terms of the GNU General Public License v2
# @ECLASS: cros-bazel.eclass
# @MAINTAINER:
# Michael Martis <martis@chromium.org>
# @DESCRIPTION:
# A utility eclass for Chromium OS-specific additions to the Bazel eclass. In
# particular, functions supporting cross-compilation are provided.
if [[ ! ${_CROS_BAZEL_ECLASS} ]]; then
inherit bazel toolchain-funcs
# @ECLASS-VARIABLE: BAZEL_BAZELRC
# @DESCRIPTION:
# The location of the resource file used to provide Portage's build
# configuration details to Bazel. Must be kept in sync with the Bazel eclass.
BAZEL_BAZELRC="${T}/bazelrc"
# @ECLASS-VARIABLE: BAZEL_CC_BAZELRC
# @INTERNAL
# @DESCRIPTION:
# The location of the resource file specifying build configuration details for
# cross compilation (if setup). Is 'sourced' by BAZEL_BAZELRC.
BAZEL_CC_BAZELRC="${T}/cc_bazelrc"
# @ECLASS-VARIABLE: BAZEL_PORTAGE_PACKAGE_DIR
# @INTERNAL
# @DESCRIPTION:
# The directory used to store generated configuration targets (e.g. toolchain
# targets for cross compilation).
BAZEL_PORTAGE_PACKAGE_DIR="${T}/portage_packages/"
# @ECLASS-VARIABLE: BAZEL_CC_CONFIG_DIR
# @INTERNAL
# @DESCRIPTION:
# The directory (relative to BAZEL_PORTAGE_PACKAGE_DIR) in which "host" and
# "target" toolchain targets are generated for cross compilation.
BAZEL_CC_CONFIG_DIR="ebazel_cc_config"
# @ECLASS-VARIABLE: BAZEL_CC_BUILD
# @INTERNAL
# @DESCRIPTION:
# A template (with Bash-style variable placeholders) used to populate build
# files for both the "host" and "target" toolchain targets.
# shellcheck disable=SC2016
BAZEL_CC_BUILD='package(default_visibility = ["//visibility:public"])
cc_toolchain_suite(
name = "toolchain",
toolchains = {
"${cpu_str}|local": "portage_toolchain",
},
)
filegroup(name = "empty")
cc_toolchain(
name = "portage_toolchain",
all_files = ":empty",
compiler_files = ":empty",
cpu = "${cpu_str}",
dwp_files = ":empty",
linker_files = ":empty",
objcopy_files = ":empty",
strip_files = ":empty",
supports_param_files = 0,
)
'
# @ECLASS-VARIABLE: BAZEL_CC_TOOLCHAIN
# @INTERNAL
# @DESCRIPTION:
# A template (with Bash-style variable placeholders) used to populate CROSSTOOL
# files for both the "host" and "target" architectures. Specifies the details
# of the compiler and default flags / settings to use when compiling C/C++
# code.
# shellcheck disable=SC2016
BAZEL_CC_CROSSTOOL='major_version: "local"
minor_version: ""
default_target_cpu: "same_as_host"
default_toolchain {
cpu: "${cpu_str}"
toolchain_identifier: "portage_toolchain"
}
toolchain {
abi_version: "local"
abi_libc_version: "local"
compiler: "local"
host_system_name: "local"
needsPic: true
target_libc: "local"
target_cpu: "${cpu_str}"
target_system_name: "local"
toolchain_identifier: "portage_toolchain"
feature {
name: "determinism"
flag_set {
action: "c-compile"
action: "c++-compile"
flag_group {
# Make C++ compilation deterministic. Use linkstamping instead of these
# compiler symbols.
flag: "-Wno-builtin-macro-redefined"
flag: "-D__DATE__=\"redacted\""
flag: "-D__TIMESTAMP__=\"redacted\""
flag: "-D__TIME__=\"redacted\""
}
}
}
feature {
name: "pic"
flag_set {
action: "c-compile"
action: "c++-compile"
flag_group {
expand_if_all_available: "pic"
flag: "-fPIC"
}
flag_group {
expand_if_none_available: "pic"
flag: "-fPIE"
}
}
}
# Security hardening on by default.
feature {
name: "hardening"
flag_set {
action: "c-compile"
action: "c++-compile"
flag_group {
# Conservative choice; -D_FORTIFY_SOURCE=2 may be unsafe in some cases.
# We need to undef it before redefining it as some distributions now
# have it enabled by default.
flag: "-U_FORTIFY_SOURCE"
flag: "-D_FORTIFY_SOURCE=1"
flag: "-fstack-protector"
}
}
flag_set {
action: "c++-link-dynamic-library"
action: "c++-link-nodeps-dynamic-library"
flag_group {
flag: "-Wl,-z,relro,-z,now"
}
}
flag_set {
action: "c++-link-executable"
flag_group {
flag: "-pie"
flag: "-Wl,-z,relro,-z,now"
}
}
}
feature {
name: "warnings"
flag_set {
action: "c-compile"
action: "c++-compile"
flag_group {
# All warnings are enabled. Maybe enable -Werror as well?
flag: "-Wall"
# Add another warning that is not part of -Wall.
flag: "-Wunused-but-set-parameter"
# But disable some that are problematic.
flag: "-Wno-free-nonheap-object" # has false positives
}
}
}
# Anticipated future default.
feature {
name: "no-canonical-prefixes"
flag_set {
action: "c-compile"
action: "c++-compile"
action: "c++-link-executable"
action: "c++-link-dynamic-library"
action: "c++-link-nodeps-dynamic-library"
flag_group {
flag: "-no-canonical-prefixes"
}
}
}
feature {
name: "disable-assertions"
flag_set {
action: "c-compile"
action: "c++-compile"
flag_group {
flag: "-DNDEBUG"
}
}
}
feature {
name: "linker-bin-path"
flag_set {
action: "c++-link-executable"
action: "c++-link-dynamic-library"
action: "c++-link-nodeps-dynamic-library"
flag_group {
flag: "-B/usr/bin/"
}
}
}
feature {
name: "common"
implies: "determinism"
implies: "pic"
implies: "hardening"
implies: "warnings"
implies: "no-canonical-prefixes"
implies: "linker-bin-path"
}
feature {
name: "opt"
implies: "common"
implies: "disable-assertions"
flag_set {
action: "c-compile"
action: "c++-compile"
flag_group {
# No debug symbols.
# Maybe we should enable https://gcc.gnu.org/wiki/DebugFission for opt
# or even generally? However, that cant happen here, as it requires
# special handling in Bazel.
flag: "-g0"
# Conservative choice for -O
# -O3 can increase binary size and even slow down the resulting binaries.
# Profile first and / or use FDO if you need better performance than this.
flag: "-O2"
# Removal of unused code and data at link time (can this increase binary size in some cases?).
flag: "-ffunction-sections"
flag: "-fdata-sections"
}
}
flag_set {
action: "c++-link-dynamic-library"
action: "c++-link-nodeps-dynamic-library"
action: "c++-link-executable"
flag_group {
flag: "-Wl,--gc-sections"
}
}
}
feature {
name: "fastbuild"
implies: "common"
}
feature {
name: "dbg"
implies: "common"
flag_set {
action: "c-compile"
action: "c++-compile"
flag_group {
flag: "-g"
}
}
}
tool_path { name: "gcc" path: "${env_cc}" }
tool_path { name: "ar" path: "${env_ar}" }
tool_path { name: "compat-ld" path: "${env_ld}" }
tool_path { name: "cpp" path: "${env_cpp}" }
tool_path { name: "dwp" path: "${env_dwp}" }
tool_path { name: "gcov" path: "${env_gcov}" }
tool_path { name: "ld" path: "${env_ld}" }
tool_path { name: "nm" path: "${env_nm}" }
tool_path { name: "objcopy" path: "${env_objcopy}" }
tool_path { name: "objdump" path: "${env_objdump}" }
tool_path { name: "strip" path: "${env_strip}" }
# Enabled dynamic linking.
linking_mode_flags { mode: DYNAMIC }
${builtin_include_dirs}
builtin_sysroot: "${env_sysroot}"
}
'
# @FUNCTION: bazel_get_builtin_include_dirs
# @USAGE: <compiler binary>
# @RETURN:
# A list of the directories that are searched by default on invocation of the
# given compiler's preprocessor. These directories are normalized (e.g.
# parsing "..") and formatted as cxx_builtin_include_directory fields of the
# CToolchain proto message.
# @MAINTAINER:
# Michael Martis <martis@chromium.org>
# @INTERNAL
bazel_get_builtin_include_dirs() {
# Constants that demarcate default include dir information.
local match_head="#include <...> search starts here:"
local match_foot="End of search list."
local comp="${1}"
# Get preprocessor output (which contains searched include dirs).
local preproc_output
preproc_output="$("${comp}" -E -xc++ -Wp,-v - 2>&1 <<< "int main() { return 0; }" || die)"
# Keep only the include dirs (which are between two known markers).
local include_dirs
include_dirs="$(sed "1,/${match_head}/d;/${match_foot}/,\$d" <<< "${preproc_output}" || die)"
# For each include dir...
while read -r include_dir; do
# Normalize (e.g. process '..' sequences in) the path.
local norm_dir
# shellcheck disable=SC2015
norm_dir="$(cd "${include_dir}" && pwd || die)"
# Print the normalized path as a proto field.
echo " cxx_builtin_include_directory: \"${norm_dir}\""
done <<< "${include_dirs}"
}
# @FUNCTION: bazel_populate_crosstool_target
# @USAGE: <sysroot> <prefix> <cpu string> <output directory>
# @MAINTAINER:
# Michael Martis <martis@chromium.org>
# @INTERNAL
# @DESCRIPTION:
# Accepts an environment sysroot, environment prefix (used to locate correct
# binaries for the environment) and environment CPU string (either '' or
# 'BUILD_'), and populates Bazel toolchain targets for the specified
# environment in the given output directory.
bazel_populate_crosstool_target() {
local env_sysroot="${1}"
local env_prefix="${2}"
local cpu_str="${3}"
local output_dir="${4}"
# Query compiler type (gcc / clang) from environment variables.
local comp_type
comp_type="$("tc-get-${env_prefix}compiler-type" || die)"
# Get actual compiler binary.
local comp
comp="$("tc-get${env_prefix}CC" || die)"
# Write out the BUILD file for this configuration.
cpu_str="${cpu_str}" \
envsubst <<< "${BAZEL_CC_BUILD}" > "${output_dir}/BUILD" || die
# Write out the CROSSTOOL file for this configuration, including formatted
# include directories and compiler / linker flags from the environment.
#
# We call tc-getPROG directly for cpp, since we require a program that directly
# performs preprocessing (i.e. takes no flags), whereas tc-getCPP returns an
# invocation of the compiler for preprocessing (which uses flags).
cpu_str="${cpu_str}" \
builtin_include_dirs="$(bazel_get_builtin_include_dirs "${comp}" || die)" \
env_sysroot="${env_sysroot}" \
env_cc="$(command -v "${comp}" || die)" \
env_ar="$(command -v "$("tc-get${env_prefix}AR")" || die)" \
env_ld="$(command -v "$("tc-get${env_prefix}LD")" || die)" \
env_cpp="$(command -v "$("tc-get${env_prefix}PROG" CPP cpp)" || die)" \
env_dwp="$(command -v "$("tc-get${env_prefix}DWP")" || die)" \
env_gcov="$(command -v "$("tc-get${env_prefix}GCOV")" || die)" \
env_nm="$(command -v "$("tc-get${env_prefix}NM")" || die)" \
env_objcopy="$(command -v "$("tc-get${env_prefix}OBJCOPY")" || die)" \
env_objdump="$(command -v "$("tc-get${env_prefix}OBJDUMP")" || die)" \
env_strip="$(command -v "$("tc-get${env_prefix}STRIP")" || die)" \
envsubst <<< "${BAZEL_CC_CROSSTOOL}" > "${output_dir}/CROSSTOOL" || die
}
# @FUNCTION: bazel_get_stdlib_linkflag
# @USAGE: <compiler type>
# @RETURN: The correct stdlib linking flag for the given compiler type.
# @MAINTAINER:
# Michael Martis <martis@chromium.org>
# @INTERNAL
bazel_get_stdlib_linkflag() {
case "${1}" in
clang) echo "-lc++";;
gcc) echo "-lstdc++";;
*) die "Unsupported compiler type '${comp_type}'."
esac
}
# @FUNCTION: bazel_setup_crosstool
# @USAGE: <host cpu string> <target cpu string>
# @MAINTAINER:
# Michael Martis <martis@chromium.org>
# @DESCRIPTION:
# Accepts Bazel "host" and "target" CPU strings, and creates Bazel targets
# (under ${T}) that can be used to configure Bazel C++ compilation based on
# Portage environment variables.
#
# Also updates the bazelrc to specify the new crosstool targets by default.
#
# Should only be called once; subsequent calls will have no effect.
bazel_setup_crosstool() {
if [[ -f "${BAZEL_CC_BAZELRC}" ]]; then
return
fi
bazel_setup_bazelrc
local host_cpu_str="${1}"
if [[ -z "${host_cpu_str}" ]]; then
die "Must specify host CPU string when generating Bazel CROSSTOOL targets."
fi
local target_cpu_str="${2}"
if [[ -z "${target_cpu_str}" ]]; then
die "Must specify target CPU string when generating Bazel CROSSTOOL targets."
fi
# Populate host toolchain targets.
local host_crosstool_dir="${BAZEL_PORTAGE_PACKAGE_DIR}/${BAZEL_CC_CONFIG_DIR}/host"
mkdir -p "${host_crosstool_dir}" || die
bazel_populate_crosstool_target / BUILD_ "${host_cpu_str}" "${host_crosstool_dir}"
# Populate target toolchain targets.
local target_crosstool_dir="${BAZEL_PORTAGE_PACKAGE_DIR}/${BAZEL_CC_CONFIG_DIR}/target"
mkdir -p "${target_crosstool_dir}" || die
bazel_populate_crosstool_target "${PORTAGE_CONFIGROOT}" "" "${target_cpu_str}" "${target_crosstool_dir}"
# Create a bazelrc specifying the new toolchain targets by default.
cat > "${BAZEL_CC_BAZELRC}" <<-EOF || die
# Make Bazel respect Portage C/C++ configuration.
build --package_path="%workspace%:${BAZEL_PORTAGE_PACKAGE_DIR}"
build --host_crosstool_top="//${BAZEL_CC_CONFIG_DIR}/host:toolchain" --crosstool_top="//${BAZEL_CC_CONFIG_DIR}/target:toolchain"
build --host_cpu="${host_cpu_str}" --cpu="${target_cpu_str}" --compiler=local --host_compiler=local
# Add correct standard library link flags.
build --linkopt="$(bazel_get_stdlib_linkflag "$(tc-get-compiler-type)" || die)"
build --host_linkopt="$(bazel_get_stdlib_linkflag "$(tc-get-BUILD_compiler-type)" || die)"
# Some compiler scripts require SYSROOT defined.
build --action_env SYSROOT="${PORTAGE_CONFIGROOT}"
# In case another config has disabled cross-compilation, re-enable it here.
build --distinct_host_configuration
EOF
echo "import ${BAZEL_CC_BAZELRC}" >> "${BAZEL_BAZELRC}" || die
}
_CROS_BAZEL_ECLASS=1
fi