# Contributor: Rasmus Thomsen <oss@cogitri.dev>
# Contributor: Jakub Jirutka <jakub@jirutka.cz>
# Contributor: Shiz <hi@shiz.me>
# Contributor: Jeizsm <jeizsm@gmail.com>
# Contributor: Ariadne Conill <ariadne@dereferenced.org>
# Contributor: Celeste <cielesti@protonmail.com>
# Contributor: omni <omni+alpine@hack.org>
# Maintainer: team/rust <alpine@ptrcnull.me>
pkgname=rust
pkgver=1.87.0
_llvmver=20
_bootver=1.86.0
pkgrel=0
# Source tarballs for beta releases are listed here:
# https://static.rust-lang.org/dist/channel-rust-beta.toml
pkgdesc="Rust Programming Language toolchain"
url="https://www.rust-lang.org/"
arch="all"
license="Apache-2.0 OR MIT"

# 'cc' is needed at runtime just for linking. Someday rustc might invoke
# the linker directly, and then we'll only need 'ld'.
# See: https://github.com/rust-lang/rust/issues/11937
depends="gcc musl-dev"

# * Rust is self-hosted, so you need rustc (and cargo) to build rustc...
#   The last revision of this abuild that does not depend on itself (uses
#   prebuilt rustc and cargo) is 2e6769eb39eaff3029d8298fc02856623c563cd8.
# * Don't add libgit2-dev into make dependencies. There's no compatibility
#   guarantees between libgit2-sys and arbitrary system libgit2 versions;
#   when system libgit2 is upgraded to a new minor version, it may break
#   cargo and since we need cargo to rebuild cargo... this already happened
#   several times. For this reason we build cargo with vendored libgit2.
makedepends_build="
	!libgit2-dev
	cargo-bootstrap>=$_bootver
	clang$_llvmver
	coreutils
	curl-dev
	file
	libssh2-dev
	llvm$_llvmver-dev
	llvm$_llvmver-test-utils
	openssl-dev
	python3
	rust-bootstrap>=$_bootver
	tar
	wasi-libc
	xz-dev
	zlib-dev
	"
makedepends_host="
	curl-dev
	libssh2-dev
	llvm$_llvmver-dev
	openssl-dev
	zlib-dev
	"

# XXX: This is a hack to allow this abuild to depend on itself. Adding "rust"
# to makedepends would not work, because abuild implicitly removes $pkgname
# and $subpackages from the abuild's dependencies.
provides="rust-bootstrap=$pkgver-r$pkgrel"
# !archcheck: needed for -src that contains some testing binaries.
# net: crates from crate.io
options="!archcheck net"

case "$CARCH" in
arm*)
	# +crt-static still broken here, last checked with rust 1.82.0
	options="$options !check"
	;;
*)
	;;
esac

# Only cargo is needed for bootstrapping.
subpackages="cargo"
[ -z "$BOOTSTRAP" ] && subpackages="
	$pkgname-dbg
	$subpackages
	$pkgname-doc
	$pkgname-clippy
	$pkgname-gdb::noarch
	$pkgname-lldb::noarch
	$pkgname-src::noarch
	$pkgname-wasm
	cargo-bash-completions:_cargo_bashcomp:noarch
	cargo-zsh-completion:_cargo_zshcomp:noarch
	cargo-doc:_cargo_doc:noarch
	rustc-dev:_rustc_dev
	rustfmt
	"
[ -z "$BOOTSTRAP" ] && case "$CHOST" in
	aarch64*|x86_64*)
	subpackages="$subpackages $pkgname-uefi"
	;;
esac
source="https://static.rust-lang.org/dist/rustc-$pkgver-src.tar.xz
	alpine-move-py-scripts-to-share.patch
	alpine-target.patch
	do-not-install-libunwind-source.patch
	gcc-eh-libunwind.patch
	install-template-shebang.patch
	musl-fix-linux_musl_base.patch
	need-rpath.patch
	need-ssp_nonshared.patch
	no-export-ld-library-path.patch
	system-wasm-ld.patch
	system-uefi-ld.patch
	check-rustc
	"
builddir="$srcdir/rustc-$pkgver-src"

# secfixes:
#   1.71.1-r0:
#     - CVE-2023-38497
#   1.66.1-r0:
#     - CVE-2022-46176
#   1.56.1-r0:
#     - CVE-2021-42574
#   1.52.1-r1:
#     - CVE-2021-29922
#   1.51.0-r2:
#     - CVE-2020-36323
#     - CVE-2021-31162
#   1.34.2-r0:
#     - CVE-2019-12083
#   1.26.0-r0:
#     - CVE-2019-16760

if [ -z "$BOOTSTRAP" ]; then
	makedepends_build="
		$makedepends_build
		patchelf
		"
	makedepends_host="
		$makedepends_host
		scudo-malloc
		"
fi

# We have to add new architectures in multiple steps:
# 1. Compile with the upstream triplets, compiling alpine's triplets in
# 2. Compile again, now with our triplets selected as build/target, now that
#    rustc knows about them
_build="$CBUILD"
_target="$CHOST"

_rlibdir="usr/lib/rustlib/$_target/lib"
_sharedir="usr/share/rust"

ldpath="/$_rlibdir"

export OPENSSL_NO_VENDOR=1
export RUST_BACKTRACE=1

export gztool="$(command -v pigz || echo gzip)"

# this is useful for when we have to patch vendored projects
_clear_vendor_checksums() {
	sed -i 's/\("files":{\)[^}]*/\1/' vendor/$1/.cargo-checksum.json
}

_unpack_build_dist() {
	local distname="$1" destdir="$2" tarball=

	case "$distname" in
	rust-src)
		tarball="$builddir"/build/dist/rust-src-$pkgver.tar.gz
		;;
	rust-std-*)
		tarball="$builddir"/build/dist/rust-std-$pkgver-${distname#rust-std-*}.tar.gz
		;;
	*)
		tarball="$builddir"/build/dist/$distname-$pkgver-$_target.tar.gz
		;;
	esac

	tar -I "$gztool -d" -xf "$tarball" \
		-C "$destdir" --strip-components=2 \
		--exclude=manifest.in --no-same-owner
}

_bootstrap_install() {
	local destprefix="$1"

	cd "$builddir"/build/$_build

	install -Dvm755 stage1-rustc/$_target/release/rustc-main \
		"$destprefix"/bin/rustc
	install -Dvm755 stage2-tools-bin/cargo \
		-t "$destprefix"/bin/

	cd "$builddir"/build/$_build/stage2/lib/rustlib/$_target/lib

	install -Dvm644 librustc_driver-*.so libstd-*.so \
		-t "$destprefix"/lib/

	install -Dvm644 ./*.rlib -t "$destprefix"/lib/rustlib/$_target/lib/
	cp -Rv self-contained "$destprefix"/lib/rustlib/$_target/lib/
}

build() {
	if [ "$_build" != "$_target" ]; then
		export PKG_CONFIG_ALLOW_CROSS=1

		# Do not carry over toolchain (flags) from $CBUILD.
		unset RUSTFLAGS CC CXX
	fi
	if [ "$pkgver" = "$_bootver" ]; then
		# Skip stage0 when `rust-bootstrap` is of the
		# same version as the Rust we are building.
		local local_rebuild=--set="build.local-rebuild=true"
	fi
	if [ -z "$BOOTSTRAP" ]; then
		# Skip LTO to allow for faster bootstrapping.
		case "$CARCH" in
		riscv64|s390x|arm*)
			;;
		*)
			local lto=--set="rust.lto=thin"
			;;
		esac
	fi

	export PATH="/usr/lib/llvm$_llvmver/bin:$PATH"

	# absolutely do not set these here, rust "knows" what it's doing
	unset CARGO_PROFILE_RELEASE_LTO
	unset CARGO_PROFILE_RELEASE_OPT_LEVEL
	unset CARGO_PROFILE_RELEASE_PANIC
	unset CARGO_PROFILE_RELEASE_CODEGEN_UNITS

	export CFLAGS="$CFLAGS -O2"
	export CXXFLAGS="$CXXFLAGS -O2"

	case "$CARCH" in
	aarch64|ppc64le|x86_64|loongarch64)
		# this doesn't build on s390x, but since it's only useful with rust-analyzer,
		# and we enable it on these arches only, just do that
		local rust_analyzer="rust-analyzer-proc-macro-srv,"
		;;
	*)
		local rust_analyzer=""
		;;
	esac

	local targets="$_target" extra_conf_opts=
	if subpackage_types_has wasm; then
		targets="$targets,wasm32-wasip1,wasm32-wasip1-threads,wasm32-wasip2,wasm32-unknown-unknown"
		extra_conf_opts='
			--set=target.wasm32-unknown-unknown.sanitizers=false
			--set=target.wasm32-unknown-unknown.profiler=false
			--set=target.wasm32-unknown-unknown.linker=lld
			--set=target.wasm32-wasip1.sanitizers=false
			--set=target.wasm32-wasip1.profiler=false
			--set=target.wasm32-wasip1.wasi-root=/usr/share/wasi-sysroot
			--set=target.wasm32-wasip1-threads.sanitizers=false
			--set=target.wasm32-wasip1-threads.profiler=false
			--set=target.wasm32-wasip1-threads.wasi-root=/usr/share/wasi-sysroot
			--set=target.wasm32-wasip2.sanitizers=false
			--set=target.wasm32-wasip2.profiler=false
			--set=target.wasm32-wasip2.wasi-root=/usr/share/wasi-sysroot
			'
	fi
	local _uefi_target=${CHOST%%-*}-unknown-uefi
	if subpackage_types_has uefi; then
		targets="$targets,$_uefi_target"
		# Too many jobs uses too much memory and fails with OOM error.
		# This compounds when building this extra target.
		cores=$(nproc)
		JOBS=$((cores / 3 + 1))
	fi

	# ./configure will not run when config.toml exists,
	# so assume we are restarting the build with ./x.py
	[ -f "config.toml" ] || ./configure \
		--build="$_build" \
		--host="$_target" \
		--target="$targets" \
		--prefix="/usr" \
		--release-channel="stable" \
		--enable-local-rust \
		--local-rust-root="/usr" \
		--llvm-root="/usr/lib/llvm$_llvmver" \
		--disable-docs \
		--enable-extended \
		--tools="cargo,clippy,src,${rust_analyzer}rustdoc,rustfmt,rust-demangler" \
		--enable-llvm-link-shared \
		--enable-option-checking \
		--enable-locked-deps \
		--enable-profiler \
		--enable-vendor \
		--dist-compression-formats=gz \
		--python="python3" \
		--set="change-id=138986" \
		--set="build.description=Alpine Linux $pkgver-r$pkgrel" \
		--set="rust.musl-root=/usr" \
		--set="rust.backtrace-on-ice=true" \
		--set="rust.codegen-units=1" \
		--set="rust.codegen-units-std=1" \
		--set="rust.deny-warnings=false" \
		--set="rust.remap-debuginfo=true" \
		--set="rust.lld=false" \
		$lto \
		$local_rebuild \
		--set="target.$_target.llvm-config=/usr/lib/llvm$_llvmver/bin/llvm-config" \
		--set="target.$_target.musl-root=/usr" \
		--set="target.$_target.crt-static=false" \
		--set="target.$_target.cc=${CROSS_COMPILE}cc" \
		--set="target.$_target.cxx=${CROSS_COMPILE}c++" \
		--set="target.$_target.ar=${CROSS_COMPILE}ar" \
		--set="target.$_target.linker=${CROSS_COMPILE}cc" \
		--set="target.$_uefi_target.crt-static=true" \
		--set="target.$_uefi_target.cc=clang-$_llvmver" \
		--set="target.$_uefi_target.profiler=false" \
		--set="target.$_uefi_target.optimized-compiler-builtins=false" \
		--set="target.$_build.musl-root=/usr" \
		--set="target.$_build.crt-static=false" \
		--set="target.$_build.cc=cc" \
		--set="target.$_build.cxx=c++" \
		--set="target.$_build.ar=ar" \
		--set="target.$_build.linker=cc" \
		$extra_conf_opts

	if [ -z "$BOOTSTRAP" ]; then
		python3 ./x.py dist --jobs ${JOBS:-2}
	else
		python3 ./x.py build --jobs ${JOBS:-2} \
			--host $_target --target $targets \
			--stage=2 --incremental src/tools/cargo
	fi
}

check() {
	# At this moment lib/rustlib/$CHOST/lib does not contain a complete
	# copy of the .so libs from lib (they will be copied there during
	# 'x.py install'). Thus we must set LD_LIBRARY_PATH for tests to work.

	msg "Setting up test env..."

	mkdir -p "$builddir/test-rustc"

	if [ -z "$BOOTSTRAP" ]; then
		_unpack_build_dist rustc "$builddir/test-rustc" &
		_unpack_build_dist rust-std "$builddir/test-rustc" &
		wait
	else
		_bootstrap_install "$builddir/test-rustc"
	fi

	export LD_LIBRARY_PATH="$builddir/test-rustc/lib/rustlib/$CHOST/lib"

	msg "Sanity checks..."
	"$srcdir"/check-rustc "$builddir"/test-rustc/bin/rustc

	# XXX: There's some problem with these tests, we will figure it out later.
	#	make check \
	#		LD_LIBRARY_PATH="$_stage0dir/lib" \
	#		VERBOSE=1

	# these rebuild the whole compiler which takes a long time
	# msg "Running tests for cargo..."
	# CFG_DISABLE_CROSS_TESTS=1 python3 ./x.py test --no-fail-fast src/tools/cargo

	unset LD_LIBRARY_PATH
}

package() {
	# merged into main package
	provides="$provides rust-stdlib=$pkgver-r$pkgrel"
	replaces="rust-stdlib"

	mkdir -p "$pkgdir/usr"

	if [ -n "$BOOTSTRAP" ]; then
		_bootstrap_install "$pkgdir/usr"

	 	if subpackage_types_has wasm; then
	 		cp -Rv build/$_build/stage2/lib/rustlib/wasm32-* \
	 			"$pkgdir"/usr/lib/rustlib/
	 	fi

		return 0
	fi

	cd "$pkgdir"

	_unpack_build_dist rustc "$pkgdir/usr" &
	_unpack_build_dist rust-std "$pkgdir/usr" &
	_unpack_build_dist rustc-dev "$pkgdir/usr" &
	_unpack_build_dist rust-src "$pkgdir/usr" &
	wait
	_unpack_build_dist cargo "$pkgdir/usr" &
	_unpack_build_dist clippy "$pkgdir/usr" &
	_unpack_build_dist rustfmt "$pkgdir/usr" &
	if subpackage_types_has wasm; then
		_unpack_build_dist rust-std-wasm32-unknown-unknown "$pkgdir/usr" &
		_unpack_build_dist rust-std-wasm32-wasip1 "$pkgdir/usr" &
		_unpack_build_dist rust-std-wasm32-wasip1-threads "$pkgdir/usr" &
		_unpack_build_dist rust-std-wasm32-wasip2 "$pkgdir/usr" &
	fi
	if subpackage_types_has uefi; then
		_unpack_build_dist rust-std-${_target%%-*}-unknown-uefi "$pkgdir/usr" &
	fi
	wait

	# Prevent rustc-dev from providing the same *.so files,
	# which causes it to conflict with the main rust package.
	local libpath; for libpath in usr/lib/*.so; do
		rm -v "$_rlibdir/${libpath##*/}"
		ln -sv "/$libpath" $_rlibdir/
	done

	# We don't want to include stuff like rust-llvm-* tools, as we will use the
	# system tools.
	rm -f usr/lib/rustlib/"$_target"/bin/rust-llvm-*

	# Python scripts are noarch, so move them to /usr/share.
	# Requires move-py-scripts-to-share.patch to be applied.
	_mv usr/lib/rustlib/etc/*.py $_sharedir/etc/
	rmdir -p usr/lib/rustlib/etc 2>/dev/null || true

	_mv usr/etc/bash_completion.d/cargo usr/share/bash-completion/completions/
	rmdir -p usr/etc/bash_completion.d 2>/dev/null || true

	_mv usr/lib/rustlib/src/rust usr/src/
	ln -s ../../../src/rust usr/lib/rustlib/src/rust
	mv usr/lib/rustlib/rustc-src/rust usr/src/rustc
	ln -s ../../../src/rustc usr/lib/rustlib/rustc-src/rust

	# Remove some clutter.
	cd "$pkgdir"/usr/lib/rustlib
	rm -f components install.log manifest-* rust-installer-version uninstall.sh
	if [ "$_build" != "$_target" ]; then
		rm -rf "$pkgdir"/usr/lib/rustlib/$_build
	fi

	# for some reason the tar archive gets some root-only files for rlibs
	find "$pkgdir"/usr/lib/rustlib -type f -perm -600 -exec chmod 0644 {} \+
	find "$pkgdir"/usr/lib/rustlib -type f -perm -700 -exec chmod 0755 {} \+

	if [ -z "$BOOTSTRAP" ]; then
		# link scudo to tools by default on architectures where it works well
		# this is roughly 20+% faster with no downside except slightly higher
		# memory use, but this is a compiler that already uses a ton of memory
		case "$CARCH" in
		arm*) ;;
		*)
			patchelf --add-needed libscudo.so \
				"$pkgdir"/usr/bin/cargo \
				"$pkgdir"/usr/bin/rustc \
				"$pkgdir"/usr/bin/rustdoc
			;;
		esac
	fi
}

_rustc_dev() {
	pkgdesc="$pkgdesc (compiler libraries)"

	amove $_rlibdir/lib*.rmeta \
		$_rlibdir/lib*.so
}

wasm() {
	pkgdesc="WebAssembly targets for rust"
	depends="rust=$pkgver-r$pkgrel lld wasi-libc"

	amove usr/lib/rustlib/wasm32-*
}

uefi() {
	pkgdesc="UEFI targets for rust"
	depends="rust=$pkgver-r$pkgrel lld"

	amove usr/lib/rustlib/*-uefi
}

analysis() {
	pkgdesc="Compiler analysis data for the Rust standard library"
	depends="$pkgname=$pkgver-r$pkgrel"

	amove $_rlibdir/../analysis
}

gdb() {
	pkgdesc="GDB pretty printers for Rust"
	depends="$pkgname=$pkgver-r$pkgrel gdb"

	amove usr/bin/rust-gdb*
	amove $_sharedir/etc/gdb_*.py
}

lldb() {
	pkgdesc="LLDB pretty printers for Rust"
	depends="$pkgname=$pkgver-r$pkgrel lldb py3-lldb"

	amove usr/bin/rust-lldb
	amove $_sharedir/etc/lldb_*.py
	amove usr/lib/rustlib/etc/lldb_commands
}

clippy() {
	pkgdesc="Lints to catch common mistakes and improve your Rust code"
	depends="$pkgname=$pkgver-r$pkgrel"

	amove usr/bin/cargo-clippy
	amove usr/bin/clippy-driver
}

src() {
	pkgdesc="$pkgdesc (source code)"
	depends="$pkgname=$pkgver-r$pkgrel"
	license="($license) AND OFL-1.1 AND GPL-3.0-or-later AND GPL-3.0-with-GCC-exception AND CC-BY-SA-3.0 LGPL-3.0"

	amove usr/src \
		usr/lib/rustlib/src \
		usr/lib/rustlib/rustc-src
}

cargo() {
	pkgdesc="The Rust package manager"
	license="(Apache-2.0 OR MIT) AND UNLICENSE"
	depends="$pkgname=$pkgver-r$pkgrel"
	# XXX: See comment on top-level provides=.
	provides="cargo-bootstrap=$pkgver-r$pkgrel"

	amove usr/bin/cargo
}

rustfmt() {
	pkgdesc="Rust Code Formatter"
	license="Apache-2.0"
	depends="cargo=$pkgver-r$pkgrel"

	amove usr/bin/rustfmt
	amove usr/bin/cargo-fmt
}

_cargo_bashcomp() {
	default_bashcomp

	pkgdesc="Bash completions for cargo"
	install_if="cargo=$pkgver-r$pkgrel bash-completion"
}

_cargo_zshcomp() {
	default_zshcomp

	pkgdesc="ZSH completions for cargo"
	install_if="cargo=$pkgver-r$pkgrel zsh"
}

_cargo_doc() {
	pkgdesc="The Rust package manager (documentation)"
	install_if="docs cargo=$pkgver-r$pkgrel"

	# XXX: This is hackish!
	cd "$pkgdir"/../$pkgname-doc
	_mv usr/share/man/man1/cargo* "$subpkgdir"/usr/share/man/man1/
}

_mv() {
	local dest; for dest; do true; done  # get last argument
	mkdir -p "$dest"
	mv "$@"
}

sha512sums="
2ef08c9be45434401f916d54b3739f52d255f8a3a4ce05a717499250c2333eeaf601f1d18085b878c027c609f44da74d0151f5bfc2c9ae1e01166919a91a1d2b  rustc-1.87.0-src.tar.xz
9de9d7ad05584e3cafddedcac409a4605a1d67e82260220deb3efd965603ee3148062b081c0cd736e50d82daad3a723fb6a7493fdf4dddfe36288645425a7946  alpine-move-py-scripts-to-share.patch
43fba7aa9d9491e4b46ad44061f12990ce0dc8858a330c0f35e1fd6a16c9fd09b2e51ca44ca9b60889540d7f047111129bd4ce8bcbfba068fb44fd581dee8d8b  alpine-target.patch
aafaf76a3969666bbc5f8423cdbb6fdd62080298a5c9dd018cb157fcfa4a0ea9e7f9cf9313a70ad3f9453990ba3cbcae7b066c851a94b5608c0f0236dbd4937a  do-not-install-libunwind-source.patch
71f9af2281deb4bf869446b4639f15b51f8671896157214be2ff92b6d84885a97f76f39e90896972a27d78d1ccddf4fd825b1fed9fdfba7eba578efa5618c687  gcc-eh-libunwind.patch
b283d31cc8d6559aa639d44f776e97c2a1e0f23c717b6784e549861b881555bd467fa6023180866aae81a9a58c5f83e306940ee645f3c6733a1bdef6130036c9  install-template-shebang.patch
9139d55df968eac0d80b582f9809633f8ed62882b08da06da67d841b23fbd2163b9723ccee916f4543ad84fb216d2e033a448fb8802620b58e125e77f20ea7eb  musl-fix-linux_musl_base.patch
0eb49827ec812e6ba093aa274d7cd4e9e79fb67846d90bdaa946db8ba3fb6083785692b150e0b36d6d97ba13f3c08afa30f77035492db5891e233f5e61b44976  need-rpath.patch
7c8644f5aa9138d72ef8d9d5bddc90d2767df171f92518bb5376f8ee676a62305533cb6ac045e323d6191d808f43c43cd741e8d2f09f70b41f1ef48283817e17  need-ssp_nonshared.patch
5f2aca58a64eaa24fbcf894f8e6532fad69ba25c9e3e915fa5045de179b72053c12c7f1e87213b8bcde61acb94fa35ec79e45429cf444a62b5de81dc4c239123  no-export-ld-library-path.patch
0a6ac7d297a49973f420c8bb678def4ceb69f74d16c542d654d37d93d55e3349acf0143a99b9aa4c39c546a5fcc92be9fead6b94e668254cdb834048f54af75a  system-wasm-ld.patch
f3ddb29ef6055eae2112836242123eb2d2643662827a38c789d608526dc15201fe6b2e1f32a4023b86a89831497045910ca5b0f0b831e78d080169c3ef60abd5  system-uefi-ld.patch
c31fdfe8a9b3411576c75da46645cf0465b9053000a2ab49cf9b2f2733f679d6d33acbf236d67a20e14935d094a685453b7f1840180249f39d610fd0902c3125  check-rustc
"
