rust
Source
FROM scratch AS build
ARG VERSION
ARG TARGETARCH
ARG MRUSTC_VERSION
ENV BOOTSTRAP_RUST_VERSION="1.90"
COPY --from=stagex/core-filesystem . /
COPY --from=stagex/core-busybox . /
# As `/bin/bash` is required by `mrustc`, wrap BusyBox's Bourne-inspired shell (`hush`) as `bash`
RUN --network=none printf "%s\n%s\n" '#!/bin/sh' 'hush "$@"' > /usr/bin/bash && chmod +x /usr/bin/bash
COPY --from=stagex/core-make . /
COPY --from=stagex/core-pkgconf . /
COPY --from=stagex/core-python . /
COPY --from=stagex/core-onetbb . /
COPY --from=stagex/core-mold . /
COPY --from=stagex/core-zlib . /
COPY --from=stagex/core-libzstd . /
COPY --from=stagex/core-llvm . /
COPY --from=stagex/core-llvm-libgcc . /
COPY --from=stagex/core-musl . /
COPY --from=stagex/core-ca-certificates . /
COPY --from=stagex/core-curl . /
COPY <<-'EOF' /etc/profile
set -eux
if [ "$TARGETARCH" = "amd64" ]; then
export ARCH="x86_64"
fi
if [ "$TARGETARCH" = "arm64" ]; then
export ARCH="aarch64"
fi
export TARGET=${ARCH}-unknown-linux-musl
export MAKEFLAGS="-j$(nproc)"
export LLVM_ROOT="/usr/lib/llvm${LLVM_VERSION}"
if [ "$LLVM_VERSION" = "system" ]; then
export LLVM_ROOT="/usr"
fi
export LLVM_BINDIR=${LLVM_ROOT}/bin
export LLVM_CONFIG=${LLVM_BINDIR}/llvm-config
export CC=${LLVM_BINDIR}/clang
export CXX=clang++
export CCC_OVERRIDE_OPTIONS="${CCC_OVERRIDE_OPTIONS:-}"
export CLANG_LIB=/usr/lib/clang/$($CC -dumpversion | cut -d'.' -f1)/lib/${TARGET}
EOF
SHELL ["/bin/sh","-l","-c"]
# Rust defaults to static linking for `musl` targets when we want dynamic linking.
ENV RUSTFLAGS="-Ctarget-feature=-crt-static"
# Don't use the `openssl` vendored within the Rust source tarball
COPY --from=stagex/core-openssl . /
ENV OPENSSL_NO_VENDOR=1
ADD fetch/mrustc-${MRUSTC_VERSION}.tar.gz .
ADD fetch/*.patch .
ADD *.patch .
WORKDIR /mrustc-${MRUSTC_VERSION}
COPY fetch/rustc-${BOOTSTRAP_RUST_VERSION}.0-src.tar.gz .
ENV LLVM_VERSION="system"
# We start by applying our set of patches to `mrustc` and the bootstrapped Rust source
RUN --network=none <<-EOF
set -eux
export RUSTC_VERSION="${BOOTSTRAP_RUST_VERSION}.0"
# Patch a segfault in type inference, fixed after the release was tagged
patch -p1 -i ../aea331a4f60535eb6c146b82c50255334e4abf3a.patch
# Patch undefined behavior `mrustc` relies on, `g++` graces, but `clang++` doesn't
patch -p1 -i ../mrustc-undefined-behavior.patch
# `inline` a `const static` so its value may be used from distinct objects which don't inherently
# have visibility. Weirdly, `clang++` only requires this with `-O0` and not `-O1`. Presumably,
# with even a minimal amount of optimizations, this will already be inlined as necessary.
sed -i s/"static const unsigned PTR_BASE"/"inline static const unsigned PTR_BASE"/ src/hir/encoded_literal.hpp
# Patch the patch file `mrustc` will apply itself, as it isn't accepted by `busybox` as-is due to misc oddities
patch -p1 -i ../patch.patch
# Apply `mrustc`'s patches to the `rustc` source so it'll build with `mrustc`
make RUSTCSRC
RUSTC_SRC=rustc-${BOOTSTRAP_RUST_VERSION}.0-src
# Remove binaries shipped within the source tarball
for file in $(find $RUSTC_SRC -type f | grep -E "\.(o|a|so|lib|dll|exe)$"); do
rm $file
done
# Strip executable bits from everything marked executable
# This doesn't erase such files outright as a variety of scripts are marked executable
# This does stop outright execution of any compiled artifacts included within the source tarball
for executable in $(find $RUSTC_SRC -type f -type f -executable); do
chmod -x $executable
done
# `mrustc` wants to build the LLVM toolchain vendored within Rust. We don't
# Shim the build directory to our existing LLVM toolchain
ln -s $LLVM_ROOT $RUSTC_SRC/build
# Stub a `Makefile` so `mrustc`'s `Makefile` thinks the target was created and it can itself build LLVM
echo "all:" > $RUSTC_SRC/build/Makefile
# `rustix`'s ASM (as transpiled by `mrustc`) is rejected by `clang`, so use the `libc` backend
for rustix in $(ls $RUSTC_SRC/vendor | grep rustix); do
for toml in $(find $RUSTC_SRC/vendor/$rustix -name "Cargo.toml"); do
sed -i s/'default = \['/'default = \["use-libc",'/ $toml
done
done
# Build a dynamically-linked `rustc`
sed -i s/'RUSTFLAGS="'/'RUSTFLAGS="-Ctarget-feature=-crt-static '/ run_rustc/Makefile
EOF
# We now prepare our working tree by defining what will be Rust's self-contained libraries
RUN --network=none <<-EOF
set -eux
mkdir self-contained
for file in rcrt1.o crti.o crtn.o; do
cp /usr/lib/${file} self-contained/
done
for file in crtbegin crtend; do
cp $CLANG_LIB/clang_rt.${file}.o self-contained/${file}S.o
done
# Populate each prefix `mrustc` will build with the self-contained toolchain
for prefix in prefix-s prefix-2 prefix; do
PREFIX_LIB=run_rustc/output/$prefix/lib/rustlib/$TARGET/lib
mkdir -p $PREFIX_LIB
ln -s $(realpath ./self-contained) $PREFIX_LIB/self-contained
done
EOF
# Build `mrustc`, `minicargo`
RUN --network=none <<-EOF
set -eux
# TODO: Revisit why this requires disabling `libc++`'s assertions
make CXXFLAGS="-g0 -O2 -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_NONE"
make -f minicargo.mk bin/minicargo
EOF
# Build `rustc`, `cargo` with `mrustc`, `minicargo`
RUN --network=none <<-EOF
set -eux
# Because the following `Makefile`s don't observe `$LD`, install `mold` as `ld`
ln -sf /usr/bin/mold /usr/bin/ld
export RUSTC_TARGET="${TARGET}"
export RUSTC_VERSION="${BOOTSTRAP_RUST_VERSION}.0"
export RUSTC_INSTALL_BINDIR="bin"
export OUTDIR_SUF=
export MRUSTC_TARGET_VER="${BOOTSTRAP_RUST_VERSION}"
# Don't compile any debug information
export CCC_OVERRIDE_OPTIONS="$CCC_OVERRIDE_OPTIONS +-g0"
# Remove `gcc`-specific flags since we're using `clang`
# Limit optimizations due to `clang`-compiled `mrustc` output seemingly having undefined behavior
export CCC_OVERRIDE_OPTIONS="$CCC_OVERRIDE_OPTIONS x-fno-tree-sra O2 +-fno-strict-return +-fno-delete-null-pointer-checks"
# Explicitly disable usage of `rustix`'s' 'raw' (assembly) backend, which `mrustc` compiles to
# an inline `__asm__` which `gcc` accepts yet `clang` does not. While we disable some vendored
# libraries with `ENV`, we only do this here as it's a bootstrap-only limitation, not a
# preference.
export CARGO_CFG_RUSTIX_NO_LINUX_RAW=1
# Compile `rustc`, `cargo`
make -f minicargo.mk output/rustc
make -f minicargo.mk output/cargo
EOF
RUN --network=none <<-EOF
set -eux
export RUSTC_TARGET="${TARGET}"
export RUSTC_VERSION="${BOOTSTRAP_RUST_VERSION}.0"
export RUSTC_INSTALL_BINDIR="bin"
export OUTDIR_SUF=
export CCC_OVERRIDE_OPTIONS="$CCC_OVERRIDE_OPTIONS +-g0"
make -C run_rustc
# Move the sysroot `mrustc` created to the format expected by the rest of this process
mkdir /rust-${RUSTC_VERSION}
cp -R run_rustc/output/prefix /rust-${RUSTC_VERSION}/usr
EOF
WORKDIR /
ENV CONFIGURE_FLAGS=
COPY --chmod=0755 <<-'EOF' build
set -eux
TARGET_VERSION=${1}
BUILD_VERSION=${2}
TOOLS=${3:-cargo}
PREFIX=/rust-${TARGET_VERSION}/usr
BUILD_PREFIX=/rust-${BUILD_VERSION}/usr
export AS=${LLVM_BINDIR}/llvm-as
export AR=${LLVM_BINDIR}/llvm-ar
export NM=${LLVM_BINDIR}/llvm-nm
export DWP=${LLVM_BINDIR}/llvm-dwp
export RANLIB=${LLVM_BINDIR}/llvm-ranlib
export READELF=${LLVM_BINDIR}/llvm-readelf
export STRIP=${LLVM_BINDIR}/llvm-strip
export OBJCOPY=${LLVM_BINDIR}/llvm-objcopy
export OBJDUMP=${LLVM_BINDIR}/llvm-objdump
export SIZE=${LLVM_BINDIR}/llvm-size
export LIBCC="$CLANG_LIB/libclang_rt.builtins.a"
ln -sf ${LLVM_BINDIR}/clang++ /usr/bin/c++
ln -sf ${LLVM_BINDIR}/clang /usr/bin/cc
ln -sf /usr/bin/mold /usr/bin/ld
cd rustc-${TARGET_VERSION}-src
# Remove binaries shipped within the source tarball
for file in $(find . -type f | grep -E "\.(o|a|so|lib|dll|exe)$"); do
rm $file
done
# Strip executable bits from everything marked executable
# This doesn't erase such files outright as a variety of scripts are marked executable
# This does stop outright execution of any compiled artifacts included within the source tarball
for executable in $(find . -type f -type f -executable); do
chmod -x $executable
done
# Remove all outstanding `cargo-checksum` files as we removed files they may or may not commit to
for crate in $(ls ./vendor); do
if [ -f "./vendor/$crate/.cargo-checksum.json" ]; then
# Outright removal of this file will cause an error
# It being a stub with no actual metadata won't however
echo '{ "files": {} }' > "./vendor/$crate/.cargo-checksum.json"
fi
done
# Remove all checksums from existing lockfiles as they've now been invalidated.
for file in $(find . -type f -name "Cargo.lock"); do
WITHOUT_CHECKSUMS=$(grep -v '^checksum = "' "$file")
echo "$WITHOUT_CHECKSUMS" > $file
done
# Remove specification of the `--frozen` argument, saying not to use the exact existing lockfiles,
# with the `--offline` argument, saying not to use the internet to decide the lockfile.
for file in $(find ./src/bootstrap -type f); do
sed -i s/'"--frozen"'/'"--offline"'/ "$file"
done
export RUSTFLAGS="${RUSTFLAGS:-}"
# Tune how `rustc` is built for a faster build process.
export RUSTFLAGS="-Clinker=clang -Clink-arg=-Wl,-fuse-ld=mold $RUSTFLAGS"
# For intermediary compilers, which aren't part of the committed output, we're more aggressive.
if [ ! "$TARGET_VERSION" = "$VERSION" ]; then
# Disable generating/embedding LLVM bitcode within the objects as it won't be used
export RUSTFLAGS="-Cembed-bitcode=false $RUSTFLAGS"
# Enable native codegen as this intermediary step doesn't have to be reproducible
export RUSTFLAGS="-Ctarget-cpu=native $RUSTFLAGS"
# But handle the edge-case https://github.com/rust-lang/rust/pull/148841 presents
if [ $(printf "%s" "$BUILD_VERSION" | cut -d'.' -f2) -lt 93 ]; then
export RUSTFLAGS="$RUSTFLAGS -Ctarget-feature=-avx512f"
fi
# Set `codegen-units` to `nproc`, which will break reproducible builds as Rust builds are only
# deterministic with a consistent amount of units.
export RUSTFLAGS="-Ccodegen-units=$(nproc) $RUSTFLAGS"
# Also, if the compiler is sufficiently modern, enable the threaded frontend. This was available
# under nightly as soon ~1.75, but we delay enabling it until it was more tested and stable.
# Note the usage of threads may produce a non-deterministic output without flags such as
# `-Zcodegen-source-order` to sort the outputs after their compilation.
if [ $(printf "%s" "$BUILD_VERSION" | cut -d'.' -f2) -ge 85 ]; then
THREADS_TO_USE=8
if [ $(nproc) -lt $THREADS_TO_USE ]; then
THREADS_TO_USE=$(nproc)
fi
export RUSTFLAGS="-Zthreads=$THREADS_TO_USE $RUSTFLAGS"
fi
fi
python3 ./src/bootstrap/configure.py \
--build="${TARGET}" \
--host="${TARGET}" \
--target="${TARGET}" \
--local-rust-root="${BUILD_PREFIX}" \
--tools="${TOOLS}" \
--llvm-root="${LLVM_ROOT}" \
--llvm-libunwind="system" \
--enable-local-rust \
--enable-clang \
--enable-option-checking \
--enable-vendor \
--dist-compression-formats=gz \
--disable-docs \
--python="python3" \
--prefix="${PREFIX}/usr" \
--sysconfdir="${PREFIX}/etc" \
--release-channel="stable" \
--set="install.prefix=${PREFIX}" \
--set="target.${TARGET}.crt-static=false" \
--set="target.${TARGET}.musl-root=/usr" \
--set="target.${TARGET}.llvm-config=${LLVM_BINDIR}/llvm-config" \
--set="rust.optimize=2" \
--set="rust.lld=false" \
--set="rust.llvm-tools=false" \
$CONFIGURE_FLAGS
if [ ! "${TARGET_VERSION}" = "${VERSION}" ]; then
# We only build the first-stage Rust compiler, as sufficient for further bootstrapping.
#
# We specifically choose `library`, as in, the Rust standard library, as what to build up to.
#
# For more context on the changes made, see
# https:/blog.rust-lang.org/inside-rust/2025/05/29/redesigning-the-initial-bootstrap-sequence.
python3 x.py build --stage 1 library
STAGE_FOR_CARGO=1
if [ $(printf "%s" "$TARGET_VERSION" | cut -d'.' -f2) -lt 90 ]; then
# We specify to build `cargo` with the original toolchain, as acceptable, as building with the
# just-built toolchain causes Rust's bootstrap to try to flush it out _much_ more.
STAGE_FOR_CARGO=0
fi
# We also build `cargo`, as required to continue the bootstrap.
python3 x.py build --stage $STAGE_FOR_CARGO src/tools/cargo
# Manually 'install' it
mkdir -p ${PREFIX}
mv build/host/stage1/* ${PREFIX}/
mv $(find build/host/ -type f -name "cargo" | head -n1) ${PREFIX}/bin/
else
python3 x.py install
fi
cd /
[ "${TARGET_VERSION}" == "${VERSION}" ] || rm -rf rustc-${TARGET_VERSION}-src /rust-${BUILD_VERSION}
EOF
ADD fetch/rustc-1.91.1-src.tar.gz .
RUN --network=none ./build 1.91.1 1.90.0 ""
ADD fetch/rustc-1.92.0-src.tar.gz .
RUN --network=none ./build 1.92.0 1.91.1 ""
ADD fetch/rustc-1.93.1-src.tar.gz .
RUN --network=none ./build 1.93.1 1.92.0 ""
ADD fetch/rustc-1.94.0-src.tar.gz .
# Patch the default for `musl` targets to be dynamically, not statically, linked
RUN --network=none <<-EOF
set -eux
for file in $(find ./rustc-${VERSION}-src/compiler/rustc_target/src/spec/targets/ -type f | grep "musl"); do
sed -i s/"crt_static_default = true"/"crt_static_default = false"/ $file
done
EOF
RUN --network=none ./build ${VERSION} 1.93.1 cargo,clippy,rustdoc,rustfmt,rust-demangler,src
RUN --network=none <<-EOF
set -eux
mv /rust-${VERSION} /rootfs
find /rootfs -type f -name "*.log" -delete
find /rootfs -type f -name "*.a" -delete
for manifest in $(find /rootfs -type f -name "manifest-*"); do
sort -o $manifest $manifest
done
EOF
FROM stagex/core-filesystem AS package-rust
COPY --from=build /rootfs/ /Copied to clipboard!