Source

FROM scratch AS build

ARG VERSION
ARG TARGETARCH
ARG MRUSTC_VERSION
ARG BSTR_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-llvm / /
COPY --from=stagex/core-llvm-libgcc / /

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-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 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 LD=mold
	export CC=clang
	export CXX=clang++
	export CCC_OVERRIDE_OPTIONS="${CCC_OVERRIDE_OPTIONS:-} +-fuse-ld=mold"

	export LLVM_LIB=${LLVM_ROOT}/lib/${TARGET}
	export LIBCC="$LLVM_LIB/libclang_rt.builtins.a"
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

# We begin ratcheting Rust from a version which requires LLVM 21
COPY --from=stagex/core-llvm21 . /
ENV LLVM_VERSION="21"

# mrustc expects libatomic.
COPY --from=stagex/core-libatomic-stub . /

ADD fetch/bstr-${BSTR_VERSION}.tar.gz .
RUN --network=none <<-'EOF'
	mkdir /bstr
	cd /bstr-${BSTR_VERSION}/scripts/regex/
	# Extract the scripts used to generate the regex patterns `bstr` needs
	mv grapheme.sh sentence.sh word.sh /bstr/
	cd /
	rm -rf /bstr-${BSTR_VERSION}
EOF

ADD fetch/mrustc-${MRUSTC_VERSION}.tar.gz .
ADD fetch/*.patch .
ADD patches/*.patch .
WORKDIR /mrustc-${MRUSTC_VERSION}
COPY fetch/rustc-${BOOTSTRAP_RUST_VERSION}.0-src.tar.gz .

COPY --chmod=0755 <<-'EOF' /sanitize_rust-src
	set -eux

	cd "$1"

	# Remove non-source files shipped within the source tarball
	# `.inc` is used within LLVM for some C source files which are included
	# `.in` corresponds to input for template files, though greater specificity is used than `*.in`
	# The Rust build system is orchestrated with Python, contains a hand-written `configure`, and a pair of scripts named `x`
	# The `stage0` file contains metadata on the stage-0 (bootstrap) toolchain used to build Rust
	# The `version`, `channel` files contains version information for the Rust present within the tree
	# The `.gitmodules` file contains a list of what would be Git submodules
	# The `.ftl` file is a resource for Fluent: https://projectfluent.org
	# `xdg-open` corresponds to a shell script contained with the Rust crate `opener`
	# `.el` is the extension for configuration files for Eglot, as used with Emacs
	# `rustdoc` and `cargo` contain web files and image assets
	# Rust ships some wrappers for `gdb`/`lldb` which are shell scripts
	# The `gix` assets are text files used to instantiate Git repositories and so on
	# TODO: `_cargo` is unidentified and seemingly not present within the Git repository
	# TODO: '*.rs.data' are auditable Rust source files in `icu-list-data`, generated by
	# `icu-provider-baked`, which should be handled ourselves
	find . -type f \
		! -name "*.toml" ! -name "*.lock" ! -name "*.json" \
		! -name "*.ld" ! -iname "*.s" ! -name "*.asm" ! -name "*.h" ! -name "*.c" ! -name "*.hpp" ! -name "*.cpp" ! -name "*.inc" \
		! -name "*.h.in" ! -name "*.pc.in" ! -name "Makefile.in" ! -name "*.rc.in" \
		! -name "*.rs" ! -name "*.py" ! -name "*.sh" ! -name "configure" ! -name "x" ! -name "x.ps1" \
		! -name "stage0" ! -name "version" ! -name "channel" ! -name ".gitmodules" \
		! -name "*.ftl" ! -name "xdg-open" ! -name "*.el" \
		! -name "*.html" ! -name "*.html.template" ! -name "*.css" ! -name "*.js" ! -name "*.png" ! -name "*.svg" \
		! -name "rust-gdb" ! -name "rust-gdbgui" ! -name "rust-lldb" ! -name "lldb_commands" ! -name "_cargo" \
		! -iname "*.txt" ! -name "*.md" ! -name "COPY*" ! -name "LICENSE*" \
		! -path "*vendor/gix-*/src/assets/*" \
		! -name "*.rs.data" \
		-delete
	# While we allowed `configure` as Rust itself has a handwritten, small `configure` in its root,
	# we want to erase all others as they're frequently generated via `autoconf`
	find . -mindepth 2 -type f -name "configure" -delete
	# We also erase such `x` files
	find . -mindepth 2 -type f -name "x" -delete

	# Sanity check these files are ASCII by checking no bytes are greater than 128
	check_ascii() {
		if [ $(cat "$0" | od -An -tx1 | grep " [8-9a-f]" | wc -c) -ne 0 ]; then
			echo "\`$0\` was not ASCII"
			exit 1
		fi
	}
	# This does not include all exceptional files, solely the ones more likely to raise concerns which should be ASCII
	for exceptional in "*.inc" "*.in" "configure" "x" "x.ps1" "stage0" "version" "channel" ".gitmodules" "*.ftl" "xdg-open" "*.el" "*.rs.data" "rust-gdb" "rust-gdbgui" "rust-lldb" "lldb_commands" "_cargo"; do
		find . -type f -name "$exceptional" | while IFS="\n" read -r file; do
			check_ascii "$file"
		done
	done
	find . -type f -path "*vendor/gix-*/src/assets/*" | while IFS="\n" read -r file; do
		check_ascii "$file"
	done

	# Satisfy how `bstr` wants pre-compiled state machines for its regexes

	# The `fsm` directory we're patching has been effectively unchanged for years, but we explicitly
	# remove historical versions (pre-mid-2023) for which this patch wouldn't apply. We then replace
	# them with the latest version which will still be compatible as they still have the same major
	# version and follow SemVer.
	rm -rf ./vendor/bstr-0.*
	# Update the `1.y.z` series to `1.12.0`, which we assume is the latest vendored `bstr`
	# TODO: Automatically detect the latest `bstr`
	ls ./vendor/ | grep "^bstr-1\.[0-5]\.[0-9]*$" | while IFS="\n" read -r old_bstr; do
		rm -rf "./vendor/${old_bstr}"
		cp -r "./vendor/bstr-1.12.0" "./vendor/${old_bstr}"
		sed -i s/1.12.0/$(printf "%s" "${old_bstr}" | cut -d'-' -f2)/ "./vendor/${old_bstr}/Cargo.toml"
	done

	ls ./vendor | grep "^bstr-" | while IFS="\n" read -r bstr; do
		cd ./vendor/"${bstr}"

		# We first provide the raw regex patterns in an uncompiled state
		sh /bstr/grapheme.sh > src/unicode/fsm/grapheme_break.txt
		sh /bstr/sentence.sh > src/unicode/fsm/sentence_break.txt
		sh /bstr/word.sh > src/unicode/fsm/word_break.txt

		# We then patch `bstr` to not expect precompiled regex patterns but to instead perform the
		# compilation itself. While these are lazy and cached, this will cause the latency of the first
		# use to potentially be minutes. Unfortunately, to produce the state machines ourselves would
		# require compiling `regex-cli`, a non-trivial Rust binary, or possibly `ucd-generate` (another
		# Rust binary though one which should be quite feasible to package), or another tool which
		# supports this input and outputs into an amenable format.
		# TODO: Add a build script to `bstr` to compile the expected state machines
		sed -i s/'features = \["dfa-search"\]'/'features = \["dfa-search", "syntax", "dfa-build"\]'/ ./Cargo.toml
		patch -p1 -i /bstr.patch

		cd ../..
	done
EOF

# 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"

	# Whitelist the allowed files within `mrustc` to remove any potential non-source files
	find . -type f \
		! -name "Makefile" ! -name "*.mk" ! -name "*.sh" \
		! -name "*.h" ! -name "*.hpp" ! -name "*.cpp" \
		! -name "*.toml" ! -name "*.rs" ! -name "*.patch" ! -name "*.txt" \
		! -name "rustc-${BOOTSTRAP_RUST_VERSION}.0-src.tar.gz" \
		-delete

	tar -xf rustc-${BOOTSTRAP_RUST_VERSION}.0-src.tar.gz
	# Replace the `.tar` so it isn't extracted again, restoring/overwriting any files
	rm rustc-${BOOTSTRAP_RUST_VERSION}.0-src.tar.gz
	touch rustc-${BOOTSTRAP_RUST_VERSION}.0-src.tar.gz

	RUSTC_SRC=rustc-${BOOTSTRAP_RUST_VERSION}.0-src
	/sanitize_rust-src "$RUSTC_SRC"
	# Generate `mrustc`'s marker that this is an extracted tarball
	touch $RUSTC_SRC/extracted

	# Even with the `mrustc`, `rustc` imports, all files should be restricted to the above extensions
	# at this time and it shouldn't be possible to recreate any other files (such as by re-extraction
	# from the tarball or restoring a cached version), ensuring the following is from source so long
	# as no non-source files masqueraded under a source file extension

	# 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

	# `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
	touch $RUSTC_SRC/src/llvm-project/llvm/CMakeLists.txt
	echo "all:" > $RUSTC_SRC/build/Makefile

	# Patch a segfault in `mrustc`'s type inference, fixed after the release was tagged
	patch -p1 -i ../aea331a4f60535eb6c146b82c50255334e4abf3a.patch
	# Patch undefined behavior `mrustc` relies on, which `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

	# `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 $LLVM_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

	# `mrustc` wants to compile in version information obtained with `git`, which we don't care for
	# and don't bother adding a dependency upon `git` to enable
	STUBBED_GIT=0
	if [ ! -f /usr/bin/git ]; then
		echo 'int main() { return 0; }' > git.c
		$CC git.c -o /usr/bin/git
		rm git.c
		STUBBED_GIT=1
	fi

	# TODO: Revisit why this requires disabling `libc++`'s assertions
	make CXXFLAGS="-g0 -O2 -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_NONE"

	if [ $STUBBED_GIT -eq 1 ]; then
		rm /usr/bin/git
	fi

	make -f minicargo.mk bin/minicargo
EOF

# Build `rustc`, `cargo` with `mrustc`, `minicargo`
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 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

# Rust, when linking the C++ standard library, does so based off of the declared `llvm-config`. Our
# `LLVM_CONFIG` lacks `libc++` (pointing to a historical LLVM intending to be solely the LLVM IR),
# but our system does have (and only has) `libc++`, so we explicitly direct Rust to use `libc++`.
ENV LLVM_USE_LIBCXX=1

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"

	# Building the standard library is cheap (only taking a few minutes), but has been
	# observed to sporadically fail. Since it's such a cheap operation, to limit requiring
	# human intervention, this will re-attempt it a limited amount of times.
	# TODO: Identify why this sometimes fails and fix the underlying root cause
	STD_ATTEMPTS=5
	while [ $STD_ATTEMPTS -ne 0 ]; do
		make -C run_rustc output/prefix-s/lib/rustlib/$ARCH-unknown-linux-musl/lib/libstd.rlib || true
		STD_ATTEMPTS=$(( $STD_ATTEMPTS - 1 ))
	done

	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
	ln -sf /usr/bin/clang /usr/bin/cc
	ln -sf /usr/bin/clang++ /usr/bin/c++
	cd rustc-${TARGET_VERSION}-src

	/sanitize_rust-src .

	# 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
		cp "$file" "${file}.bak"
		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

	# Restore the original lockfiles into the installed sources
	if [ -d ${PREFIX}/lib/rustlib/src/rust ]; then
		cd ${PREFIX}/lib/rustlib/src/rust
		find . -type f -name "Cargo.lock" | while IFS="\n" read -r lockfile; do
			mv /rustc-${TARGET_VERSION}-src/${lockfile}.bak $lockfile
		done
	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.1-src.tar.gz .
RUN --network=none ./build 1.94.1 1.93.1 ""

ENV LLVM_VERSION="system"

ADD fetch/rustc-1.95.0-src.tar.gz .
RUN --network=none ./build 1.95.0 1.94.1 ""

ADD fetch/rustc-1.96.0-src.tar.gz .

# Patch the default for `musl` targets to be dynamically, not statically, linked
RUN --network=none <<-EOF
	set -eux
	find ./rustc-${VERSION}-src/compiler/rustc_target/src/spec/targets/ -type f | grep "musl" | while IFS="\n" read -r file; do
		sed -i s/"crt_static_default = true"/"crt_static_default = false"/ "$file"
	done
EOF

RUN --network=none ./build ${VERSION} 1.95.0 cargo,clippy,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
	find /rootfs -type f -name "manifest-*" | while IFS="\n" read -r manifest; do
		sort -o "$manifest" "$manifest"
	done
EOF

FROM stagex/core-filesystem AS package
COPY --from=build /rootfs/ /
Copied to clipboard!