blob: 48843de3b8b07b1c09767c6a5b815471742337bc [file] [log] [blame]
# Copyright 2022 The Chromium OS Authors. All rights reserved.
# Distributed under the terms of the GNU General Public License v2
# @ECLASS: cros-rustc.eclass
# The Chromium OS Toolchain Team <>
# Please report bugs via
# @BLURB: Eclass for building CrOS' Rust toolchain.
# This eclass is used to build both dev-lang/rust-host and dev-lang/rust.
# dev-lang/rust-host is an ebuild that provides all artifacts necessary for
# building Rust for the host. dev-lang/rust supplements this with libraries for
# cross-compiling. We maintain this split because we need to build Rust
# binaries for the host prior to cross-* libraries being available.
# An important concept when building dev-lang/rust-host and dev-lang/rust is
# continuity: these packages are expected to be built from _identical_ Rust
# sources. This assumption:
# - doesn't restrict us in any meaningful way,
# - keeps us more consistent with upstream flows for building `rustc`, and
# - allows us to significantly cut down on the build time of dev-lang/rust,
# since dev-lang/rust can skip unpacking sources, configuring them, and
# rebuilding LLVM + stage0 + stage1.
# Moreover, if you want to make meaningful changes to Rust, you'll need to
# always reemerge _both_ dev-lang/rust-host and dev-lang/rust. dev-lang/rust
# assumes that the sources unpacked by dev-lang/rust-host, if present, are
# identical to the ones it will build. dev-lang/rust-host always starts with a
# clean slate.
if [[ -z ${_ECLASS_CROS_RUSTC} ]]; then
# Check for EAPI 7+.
case "${EAPI:-0}" in
[0123456]) die "unsupported EAPI (${EAPI}) in eclass (${ECLASS})" ;;
EXPORT_FUNCTIONS pkg_setup src_unpack src_prepare src_configure src_compile
PYTHON_COMPAT=( python3_{6..9} )
inherit python-any-r1 toolchain-funcs
# This is the list of target triples for rustc as they appear in the cros_sdk.
# cros-rust_src_configure instructs cros-rust_src_compile to use
# "${triple}-clang" when building each one of these.
# These are the triples we use GCC with. `*-cros-*` triples should not be
# included here.
# Set to a nonempty value if we want to build a nonstandard set of sources
# (this is intended mostly to power bisection of rustc itself).
# This should never be set to anything in production.
# If you want to set this as a user, each `emerge` of `dev-lang/rust-host` or
# `dev-lang/rust` assumes the following:
# 1. A full Rust checkout is available under `_CROS_RUSTC_RAW_SOURCES_ROOT`.
# 2. You've ensured that all submodules under `_CROS_RUSTC_RAW_SOURCES_ROOT` are
# up-to-date with your currently checked out revision.
# 3. You've ensured that the appropriate bootstrap compiler is cached under
# 4. You've run `cargo vendor` under `_CROS_RUSTC_RAW_SOURCES_ROOT`
# 5. The sources under `_CROS_RUSTC_RAW_SOURCES_ROOT` are the exact sources you
# want to apply `${PATCHES}` to.
# 6. You are OK with this script modifying your rustc sources at
# `_CROS_RUSTC_RAW_SOURCES_ROOT` (by applying patches to them).
# Step 2 can be done with
# `dev-lang/rust/files/bisect-scripts/`. Steps 3 and
# 4 can be accomplished with
# `dev-lang/rust/files/bisect-scripts/`.
# There's a fair amount of direct commonality between dev-lang/rust and
# dev-lang/rust-host. Capture that here.
ABI_VER="$(ver_cut 1-2)"
# The version of rust-bootstrap that we're using to build our current Rust
# toolchain. This is generally the version released prior to the current one,
# since Rust uses the beta compiler to build the nightly compiler.
# If `CROS_RUSTC_BUILD_RAW_SOURCES` is nonempty, a full Rust source tree is
# expected to be available here.
if [[ -z "${CROS_RUSTC_BUILD_RAW_SOURCES}" ]]; then
SRC_URI="${SRC} -> rustc-${PV}-src.tar.gz"
# If a bisection is happening, we use the bootstrap compiler that upstream prefers.
# Clear this so we don't accidentally use it below.
LICENSE="|| ( MIT Apache-2.0 ) BSD-1 BSD-2 BSD-4 UoI-NCSA"
RESTRICT="binchecks strip"
if [[ -z "${CROS_RUSTC_BUILD_RAW_SOURCES}" ]]; then
# Locations where we cache our build/src dirs.
# @FUNCTION: cros-rustc_has_existing_checkout
# Tests whether we have a properly src_prepare'd checkout in ${CROS_RUSTC_DIR}.
cros-rustc_has_existing_checkout() {
# @FUNCTION: cros-rustc_has_existing_stage1_build
# Tests whether ${CROS_RUSTC_BUILD_DIR} has a valid stage1 toolchain available.
cros-rustc_has_existing_stage1_build() {
cros-rustc_pkg_setup() {
if [[ ${MERGE_TYPE} != "binary" ]]; then
addwrite "${CROS_RUSTC_DIR}"
# Disable warnings about 755 only applying to the deepest directory; that's
# fine.
# shellcheck disable=SC2174
mkdir -p -m 755 "${CROS_RUSTC_DIR}"
if [[ -n "${CROS_RUSTC_BUILD_RAW_SOURCES}" ]]; then
ewarn "cros-rustc.eclass is using raw sources. This feature is for debugging only."
# Sets up a cargo config.toml that instructs our bootstrap rustc to use
# the correct linker. `rust-bootstrap` can be made to work around this since
# we have local patches, but bootstrap compilers downloaded from upstream
# (e.g., during bisection) cannot. This should be called during src_unpack
# if you opt out of calling `cros-rustc_src_unpack`. Otherwise,
# `cros-rustc_src_unpack` will take care of this.
cros-rustc_setup_cargo_home() {
export CARGO_HOME="${T}/cargo_home"
mkdir -p "${CARGO_HOME}" || die
cat >> "${CARGO_HOME}/config.toml" <<EOF || die
linker = "${CHOST}-clang"
linker = "${CHOST}-clang"
cros-rustc_src_unpack() {
if [[ -n "${CROS_RUSTC_BUILD_RAW_SOURCES}" ]]; then
if [[ ! -d "${_CROS_RUSTC_RAW_SOURCES_ROOT}" ]]; then
eerror "You must have a full Rust checkout in _CROS_RUSTC_RAW_SOURCES_ROOT."
if [[ -e "${S}" && ! -L "${S}" ]]; then
rm -rf "${S}" || die
ln -sf "$(readlink -m "${_CROS_RUSTC_RAW_SOURCES_ROOT}")" "${S}" || die
local dirs=( "${CROS_RUSTC_BUILD_DIR}" "${CROS_RUSTC_SRC_DIR}" )
if [[ -e "${CROS_RUSTC_SRC_DIR}" || -e "${CROS_RUSTC_BUILD_DIR}" ]]; then
einfo "Removing old source/build directories..."
rm -rf "${dirs[@]}"
# Disable warnings about 755 only applying to the deepest directory; that's
# fine.
# shellcheck disable=SC2174
mkdir -p -m 755 "${dirs[@]}"
(cd "${CROS_RUSTC_SRC_DIR}" && default)
cros-rustc_src_prepare() {
if [[ -n "${CROS_RUSTC_BUILD_RAW_SOURCES}" ]]; then
einfo "Synchronizing bootstrap compiler caches ..."
cp -avu "${_CROS_RUSTC_RAW_SOURCES_ROOT}/build/cache" "${CROS_RUSTC_BUILD_DIR}" || die
# Copy "unknown" vendor targets to create cros_sdk target triple
# variants as referred to in 0001-add-cros-targets.patch and
# RUSTC_TARGET_TRIPLES. armv7a is treated specially because the cros
# toolchain differs in more than just the vendor part of the target
# triple. The arch is armv7a in cros versus armv7.
pushd compiler/rustc_target/src/spec || die
sed -e 's:"unknown":"pc":g' > || die
sed -e 's:"unknown":"cros":g' > || die
sed -e 's:"unknown":"cros":g' > || die
sed -e 's:"unknown":"cros":g' > || die
popd || die
# The miri tool is built because of 'extended = true' in
# cros-config.toml, but the build is busted. See the upstream issue:
# [ lang/rust/issues/56576]. Because miri isn't
# installed or needed, this sed script eradicates the command that
# builds it during the bootstrap script.
pushd src/bootstrap || die
sed -i 's@tool::Miri,@@g'
popd || die
# Tsk. Tsk. The rust makefile for LLVM's compiler-rt uses -ffreestanding
# but one of the files includes <stdlib.h> causing occasional problems
# with MB_LEN_MAX. See for the thrilling details. This
# line patches over the problematic include.
sed -e 's:#include <stdlib.h>:void abort(void);:g' \
-i "${ECONF_SOURCE:-.}"/src/llvm-project/compiler-rt/lib/builtins/int_util.c || die
# For the rustc_llvm module, the build will link with -nodefaultlibs and
# manually choose the std C++ library. For x86_64 Linux, the build
# script always chooses libstdc++ which will not work if LLVM was built
# with USE="default-libcxx". This snippet changes that choice to libc++
# in the case that clang++ defaults to libc++.
if "${CXX}" -### -x c++ - < /dev/null 2>&1 | grep -q -e '-lc++'; then
sed -i 's:"stdc++":"c++":g' compiler/rustc_llvm/ || die
cros-rustc_src_configure() {
tc-export CC PKG_CONFIG
# If FEATURES=ccache is set, we can cache LLVM builds. We could set this to
# true unconditionally, but invoking `ccache` to just have it `exec` the
# compiler costs ~10secs of wall time on rust-host builds. No point in
# wasting the cycles.
local use_ccache=false
[[ -z "${CCACHE_DISABLE:-}" ]] && use_ccache=true
local targets=""
local tt
# These variables are defined by users of this eclass; their use here is safe.
# shellcheck disable=SC2154
targets+="\"${tt}\", "
local bootstrap_compiler_info
if [[ -z "${CROS_RUSTC_BUILD_RAW_SOURCES}" ]]; then
cargo = \"/opt/rust-bootstrap-${BOOTSTRAP_VERSION}/bin/cargo\"
rustc = \"/opt/rust-bootstrap-${BOOTSTRAP_VERSION}/bin/rustc\""
local config=cros-config.toml
cat > "${config}" <<EOF
target = [${targets}]
docs = false
submodules = false
python = "${EPYTHON}"
vendor = true
extended = true
tools = ["cargo", "rustfmt", "clippy", "cargofmt"]
sanitizers = true
profiler = true
build-dir = "${CROS_RUSTC_BUILD_DIR}"
ccache = ${use_ccache}
ninja = true
targets = "AArch64;ARM;X86"
prefix = "${ED}usr"
libdir = "$(get_libdir)"
mandir = "share/man"
default-linker = "${CBUILD}-clang"
description = "${PF}"
channel = "nightly"
codegen-units = 0
llvm-libunwind = 'in-tree'
codegen-tests = false
new-symbol-mangling = true
for tt in "${RUSTC_TARGET_TRIPLES[@]}" ; do
cat >> "${config}" <<EOF
cc = "${tt}-clang"
cxx = "${tt}-clang++"
linker = "${tt}-clang++"
cros-rustc_src_compile() {
${EPYTHON} build --stage 2 --config cros-config.toml "$@" || die
# Remove the src/rust symlink which will be dangling after sources are
# removed, and the containing src directory.
rm "${CROS_RUSTC_BUILD_DIR}/x86_64-unknown-linux-gnu/stage2/lib/rustlib/src/rust" || die
rmdir "${CROS_RUSTC_BUILD_DIR}/x86_64-unknown-linux-gnu/stage2/lib/rustlib/src" || die
# Since we always build for stage2, we're guaranteed that stage1 exists
# at this point.