Artifact Content

Artifact 9d634b0c86de7d3fc4276052fd3c751293031b78:


#! /bin/bash

# build-cc @@VERS@@

if [ "$1" = "--version" ]; then
	echo 'build-cc @@VERS@@'

	exit 0
fi

# Start in the same working directory as our script
OURSCP="$(which "$0" 2>/dev/null)"
if [ -f "${OURSCP}" ]; then
	TOPDIR="$(cd "$(dirname "${OURSCP}")" && pwd)" || exit 1
fi
export TOPDIR

cd "${TOPDIR}" || exit 1

# Load configuration
## Provide defaults
### Root for cross-compiler toolchains
CCROOT="${HOME}/root/cross-compilers"

### Path to platform files
if [ -z "${BUILD_CC_PLATFORMDIR}" ]; then
	BUILD_CC_PLATFORMDIR="$(pwd)/platform"
fi

## Load configuration file
if [ -f "build-cc.conf" ]; then
	. "build-cc.conf"
fi

# Tool versions
## SHA1 may be left blank to disable hashing
if [ -z "${BINUTILS_VERS}" ]; then
	BINUTILS_VERS='2.29.1'
	BINUTILS_SHA1='5156099a6c50bd330c3d4c8fc56a9bf725ccaf08'
fi
if [ -z "${GCC_VERS}" ]; then
	GCC_VERS='7.3.0'
	GCC_SHA1='9689b9cae7b2886fdaa08449a26701f095c04e48'
fi
if [ -z "${GMP_VERS}" ]; then
	GMP_VERS='6.1.2'
	GMP_SHA1='366ded6a44cd108ba6b3f5b9a252eab3f3a95cdf'
fi
if [ -z "${MPFR_VERS}" ]; then
	MPFR_VERS='4.0.0'
	MPFR_SHA1='799245347044c8f0da9e513f86bb5e4c07974931'
fi
if [ -z "${MPC_VERS}" ]; then
	MPC_VERS='1.1.0'
	MPC_SHA1='b019d9e1d27ec5fb99497159d43a3164995de2d0'
fi

# Start of script
## Initialize default values
use_multilib="${BUILD_CC_MULTILIB_DEFAULT:-1}"
use_gnu_ld='1'
use_gnu_as='1'

## Parse arguments
### Determine list of platforms
platforms=(
	armel-generic-linux-uclibc
	mipsel-generic-linux-uclibc
	mips-generic-linux-musl
	x86_64-generic-linux-musl
	x86_64-generic-linux-gnu
)

untested_platforms=(
	powerpc-generic-linux-gnu
	powerpc-generic-linux-uclibc
)

for platformfile in platform/*.sh; do
	platform="$(echo "${platformfile}" | sed 's@^.*/@@;s@\.sh$@@')"

	if grep '^# Tested' "${platformfile}" >/dev/null 2>/dev/null; then
		platforms=("${platforms[@]}" "${platform}")
	else
		untested_platforms=("${untested_platforms[@]}" "${platform}")
	fi
done

# Verify platform
if [ "${BUILD_CC_USE_UNTESTED}" = '1' ]; then
	platforms=("${platforms[@]}" "${untested_platforms[@]}")
fi

idx="${#platforms[@]}"
for platform in "${BUILD_CC_PLATFORMDIR}"/*-platform.tar.bz2; do
	if [ ! -f "${platform}" ]; then
		continue
	fi

	platform="$(basename "${platform}" | sed 's@-platform.tar.bz2$@@')"

	platforms[${idx}]="${platform}"
	idx=$[${idx} + 1]
done

if [ -z "$1" ]; then
	set -- list
fi

if [ "$1" = "list" ]; then
	echo 'Available Targets:'
	for platform in "${platforms[@]}"; do
		echo "  ${platform}"
	done

	exit 0
elif [ "$1" = "clean" -o "$1" = "distclean" ]; then
	true
else
	found='0'
	for platform in "${platforms[@]}"; do
		if [ "$1" = "${platform}" ]; then
			found='1'

			break
		fi
	done

	if [ "${found}" = '0' ]; then
		echo "Unknown target: $1, aborting." >&2

		if [ "${BUILD_CC_FORCE_PLATFORM}" != '1' ]; then
			exit 1
		else
			echo "Proceeding anyway due to BUILD_CC_FORCE_PLATFORM" >&2
		fi
	fi
fi

## Load user-supplied profiles
. "${TOPDIR}/platform/${platform}.sh"

## URLs
### Binutils
if [ -z "${BINUTILS_URL}" ]; then
	BINUTILS_URL="http://ftp.gnu.org/gnu/binutils/binutils-${BINUTILS_VERS}.tar.bz2"
fi
BINUTILS_TARBALL="src/binutils-${BINUTILS_VERS}.tar.bz2"
BINUTILS_DIR="binutils-${BINUTILS_VERS}"

### GCC
if [ -z "${GCC_URL}" ]; then
	GCC_URL="http://mirrors.kernel.org/gnu/gcc/gcc-${GCC_VERS}/gcc-${GCC_VERS}.tar.xz"
fi
GCC_TARBALL="src/gcc-${GCC_VERS}.tar.xz"
GCC_DIR="gcc-${GCC_VERS}"

### GMP
if [ -z "${GMP_URL}" ]; then
	GMP_URL="http://ftp.gnu.org/gnu/gmp/gmp-${GMP_VERS}.tar.bz2"
fi
GMP_TARBALL="src/gmp-${GMP_VERS}.tar.bz2"
GMP_DIR="gmp-$(echo "${GMP_VERS}" | sed 's@[a-zA-Z]*$@@')"

### MPFR
if [ -z "${MPFR_URL}" ]; then
	MPFR_URL="http://www.mpfr.org/mpfr-${MPFR_VERS}/mpfr-${MPFR_VERS}.tar.bz2"
fi
MPFR_TARBALL="src/mpfr-${MPFR_VERS}.tar.bz2"
MPFR_DIR="mpfr-${MPFR_VERS}"

### MPC
if [ -z "${MPC_URL}" ]; then
	MPC_URL="https://ftp.gnu.org/gnu/mpc/mpc-${MPC_VERS}.tar.gz"
fi
MPC_TARBALL="src/mpc-${MPC_VERS}.tar.gz"
MPC_DIR="mpc-${MPC_VERS}"

## Clean-up
if [ "$1" = "clean" -o "$1" = "distclean" ]; then
	rm -rf "${BINUTILS_DIR}" "${GCC_DIR}" "${GMP_DIR}" "${MPFR_DIR}" "${MPC_DIR}"

	for platform in "${platforms[@]}"; do
		rm -rf "gcc-${platform}"
		rm -rf "gcc-${GCC_VERS}-${platform}"
		rm -rf "binutils-${platform}"
	done

	for appscript in scripts/* scripts/pre/* scripts/post/*; do
		"${appscript}" "clean" >/dev/null 2>/dev/null
	done

	if [ "$1" = "clean" ]; then
		exit 0
	fi
fi

if [ "$1" = "distclean" ]; then
	rm -f "${BINUTILS_TARBALL}" "${GCC_TARBALL}" "${GMP_TARBALL}" "${MPFR_TARBALL}" "${MPC_TARBALL}"

	for appscript in scripts/* scripts/pre/* scripts/post/*; do
		"${appscript}" "distclean" >/dev/null 2>/dev/null
	done

	rmdir src >/dev/null 2>/dev/null

	exit 0
fi

## Tools
MAKE="${MAKE:-make}"
BUILD_CC_MAKE_FLAGS_SINGLE="$(echo " ${BUILD_CC_MAKE_FLAGS} " | sed 's@ -j *[0-9][0-9]* @ @g;s@^  *@@g;s@  *$@@g')"
export MAKE BUILD_CC_MAKE_FLAGS_SINGLE BUILD_CC_MAKE_FLAGS

## Functions
. 'scripts/common'

## Determine path for this cross-compiler
CCNAME="$1"
CCDIR="${CCROOT}/${CCNAME}"

## Rename GCC directory since it may be patched based on the platform
GCC_DIR="${GCC_DIR}-${CCNAME}"

## Determine attributes for this compiler
### Per CPU
case "${CCNAME}" in
	hppa64-*|hppa1*-)
		BUILD_CC_GCC_CONFIGURE_EXTRA="${BUILD_CC_GCC_CONFIGURE_EXTRA} --disable-libquadmath"
		;;
esac

### Per OS
case "${CCNAME}" in
	*-hpux*)
		#### Default to disabling multilib, will be re-enabled by target if supported
		use_multilib='0'
		;;
	*-freebsd*)
		#### Default to disabling multilib, will be re-enabled by target if supported
		use_multilib='0'
		;;
	*-darwin*)
		use_gnu_ld='0'
		use_gnu_as='0'
		use_ld64='0'

		if echo "${CCNAME}" | egrep '^(x86_64|ppc64)-' >/dev/null; then
			use_ld64='0'
		fi

		if [ "${use_ld64}" = '0' ]; then
			ldcmd='ld'
		else
			ldcmd='ld64'
		fi

		BUILD_CC_GCC_CONFIGURE_EXTRA="${BUILD_CC_GCC_CONFIGURE_EXTRA} --with-ld=${CCDIR}/${CCNAME}/bin/${ldcmd} --with-as=${CCDIR}/${CCNAME}/bin/as"

		unset ldcmd
		;;
esac

if [ "${use_gnu_ld}" = "1" ]; then
	BUILD_CC_GCC_CONFIGURE_EXTRA="${BUILD_CC_GCC_CONFIGURE_EXTRA} --with-gnu-ld --with-ld=${CCDIR}/bin/${CCNAME}-ld"
fi

if [ "${use_gnu_as}" = "1" ]; then
	BUILD_CC_GCC_CONFIGURE_EXTRA="${BUILD_CC_GCC_CONFIGURE_EXTRA} --with-gnu-as --with-as=${CCDIR}/bin/${CCNAME}-as"
fi

## Determine platform file tarball
PLATFORM_TARBALL="${BUILD_CC_PLATFORMDIR}/${CCNAME}-platform.tar.bz2"
ADDONS_TARBALL="${BUILD_CC_PLATFORMDIR}/${CCNAME}-addons.tar.bz2"

### Install platform files (needed for libgcc)
(
	mkdir -p "${CCDIR}/${CCNAME}"
	cd "${CCDIR}/${CCNAME}" || exit 1

	if [ ! -e 'usr' ]; then
		#### Create symlink so things like "./usr/include" work.
		ln -s . usr
	fi

	if [ -f "${PLATFORM_TARBALL}" ]; then
		bzip2 -dc "${PLATFORM_TARBALL}" | "${TAR:-tar}" --keep-old-files -xf - >/dev/null 2>/dev/null
	fi

	if [ -f "${ADDONS_TARBALL}" ]; then
		bzip2 -dc "${ADDONS_TARBALL}" | "${TAR:-tar}" --keep-old-files -xf - >/dev/null 2>/dev/null
	fi
)

### Set use_multilib authoritatively if possible
if [ -f "${CCDIR}/${CCNAME}/multilib" ]; then
	use_multilib="$(cat "${CCDIR}/${CCNAME}/multilib")"
	rm -f "${CCDIR}/${CCNAME}/multilib"
fi

### Set C compiler flags from tarball
if [ -f "${CCDIR}/${CCNAME}/BUILD_CC_GCC_CONFIGURE_EXTRA" ]; then
	add="$(cat "${CCDIR}/${CCNAME}/BUILD_CC_GCC_CONFIGURE_EXTRA")"

	#### Ensure these flags aren't malicious
	##### XXX: TODO: Build the extra flags as an array for safety
	if ! echo "${add}" | grep '[;|<>]' >/dev/null; then
		BUILD_CC_GCC_CONFIGURE_EXTRA="${BUILD_CC_GCC_CONFIGURE_EXTRA} ${add}"
	fi
	
	rm -f "${CCDIR}/${CCNAME}/BUILD_CC_GCC_CONFIGURE_EXTRA"
fi

## Determine stage of build process
STAGE="$2"

### If we have a platform tarball and a stage has not been specified, assume we are at stage 2
if [ -z "${STAGE}" ]; then
	if [ -f "${PLATFORM_TARBALL}" ]; then
		STAGE="stage2"
		STAGE_INIT='stage2'
	else
		STAGE="stage1"
		STAGE_INIT='stage1'
	fi
else
	STAGE_INIT='stage1'
fi

### Normalize stage names
if [ "${STAGE}" != "stage1" ]; then
	STAGE="stage2"
fi
export STAGE

# Determine flags for this build, to pass to sub-scripts
FLAGS=''
if [ "${use_multilib}" = '1' ]; then
	FLAGS="${FLAGS} multilib"
fi

if [ "${use_ld64}" = '1' ]; then
	FLAGS="${FLAGS} ld64"
fi

if [ "${STAGE_INIT}" = 'stage1' ]; then
	FLAGS="${FLAGS} from_stage1"
else
	FLAGS="${FLAGS} from_stage2"
fi

## Stage 1 platforms that require particular configuration
if [ "${STAGE_INIT}" = 'stage1' ]; then
	case "${platform}" in
		*-linux-musl)
			# MUSL libc platforms do not support GCC sanitizer (which relies on glibc features)
			BUILD_CC_GCC_CONFIGURE_EXTRA="${BUILD_CC_GCC_CONFIGURE_EXTRA} --disable-libsanitizer --disable-target-libsanitizer"

			# MUSL libc platforms do not support GCC mudflap (which relies on glibc features)
			BUILD_CC_GCC_CONFIGURE_EXTRA="${BUILD_CC_GCC_CONFIGURE_EXTRA} --disable-mudflap --disable-libmudflap"
			;;
	esac
fi

### Do pre-compilation steps
for prescript in scripts/pre/*; do
	if [ ! -x "${prescript}" ]; then
		continue
	fi

	sourcefile="${TMPDIR:-/tmp}/build-cc-sourcefile-$$${RANDOM}${RANDOM}${RANDOM}"
	rm -f "${sourcefile}"

	"${prescript}" "${CCNAME}" "${CCDIR}" "${CCDIR}/${CCNAME}" "${STAGE}" "${sourcefile}" "${FLAGS}" || exit 1

	if [ -f "${sourcefile}" ]; then
		. "${sourcefile}"
		rm -f "${sourcefile}"
	fi
done

## Compile binutils for this platform if needed
(
	if [ -f "${CCDIR}/bin/${CCNAME}-ar" ]; then
		if [ ! -f "${CCDIR}/bin/${CCNAME}-rebuild-binutils" ]; then
			exit 0
		fi
	fi

	echo " * Building GNU Binutils version ${BINUTILS_VERS} for ${CCNAME}"

	if [ ! -d "${BINUTILS_DIR}" ]; then
		download "${BINUTILS_URL}" "${BINUTILS_TARBALL}" "${BINUTILS_SHA1}" || exit 1

		bzip2 -dc "${BINUTILS_TARBALL}" | tar -xf -

		# Apply patches
		## Apply patch files
		for patchfile in "$(pwd)/patches/binutils"/*.diff; do
			(
				echo " * Applying patch ${patchfile}"

				cd "${BINUTILS_DIR}" || exit 1

				"${PATCH:-patch}" -p1 < "${patchfile}" || exit 1
			) || exit 1
		done
	fi

	rm -rf "binutils-${CCNAME}"
	mkdir "binutils-${CCNAME}"
	cd "binutils-${CCNAME}" || exit -1

	"../${BINUTILS_DIR}/configure" --enable-gold=no --enable-ld=default --target="${CCNAME}" --prefix="${CCDIR}" --disable-nls --with-sysroot="${CCDIR}/${CCNAME}" --with-build-sysroot="${CCDIR}/${CCNAME}" --program-prefix="${CCNAME}-"

	${MAKE} ${BUILD_CC_MAKE_FLAGS} || exit 1

	${MAKE} ${BUILD_CC_MAKE_FLAGS} install || exit 1

	rm -f "${CCDIR}/bin/${CCNAME}-rebuild-binutils"
) || exit 1
rm -rf "binutils-${CCNAME}"

## Compile C compiler (GCC) if needed
### Prepare GCC fix-ups (stage1)
if [ "${STAGE}" = "stage1" ]; then
	### XXX: GCC produces a broken compiler -- compiles require libgcc_eh.a, which it doesn't emit
	libgcceha="${CCDIR}/lib/gcc/${CCNAME}/${GCC_VERS}/libgcc_eh.a"
	if [ -h "${libgcceha}" ]; then
		rm -f "${libgcceha}"
	fi
fi

### If multilib was not requested, disable it
if [ "${use_multilib}" = "0" ]; then
	# Disable multilib
	BUILD_CC_GCC_CONFIGURE_EXTRA="--disable-multilib ${BUILD_CC_GCC_CONFIGURE_EXTRA}"
elif [ "${use_multilib}" = '2' ]; then
	# Force use of the "--enable-multilib" option
	BUILD_CC_GCC_CONFIGURE_EXTRA="--enable-multilib ${BUILD_CC_GCC_CONFIGURE_EXTRA}"
fi

## Compile
(
	### Determine if compilation is needed
	if [ -f "${CCDIR}/bin/${CCNAME}-gcc" ]; then
		if find "${CCDIR}/lib/gcc" -type f -name 'libgcc.a' | grep libgcc >/dev/null; then
			if [ "${STAGE}" = "stage1" ]; then
				#### A stage1 compiler is available
				exit 2
			else
				#### Determine if a stage2 compiler has been built
				if find "${CCDIR}" -type f -name 'libgcc_s.*' | grep libgcc_s >/dev/null; then
					exit 2
				fi
			fi
		fi
	fi

	echo " * Building GNU C Compiler version ${GCC_VERS} for ${CCNAME}, ${STAGE}"

	if [ ! -d "${GCC_DIR}" ]; then
		# Sanity
		rm -rf "gcc-${GCC_VERS}"

		# Download sources required
		download "${GCC_URL}" "${GCC_TARBALL}" "${GCC_SHA1}" || exit 1
		download "${GMP_URL}" "${GMP_TARBALL}" "${GMP_SHA1}" || exit 1
		download "${MPFR_URL}" "${MPFR_TARBALL}" "${MPFR_SHA1}" || exit 1
		download "${MPC_URL}" "${MPC_TARBALL}" "${MPC_SHA1}" || exit 1

		# Extract sources

		xz -dc "${GCC_TARBALL}" | "${TAR:-tar}" -xf -
		bzip2 -dc "${GMP_TARBALL}" | "${TAR:-tar}" -xf -
		bzip2 -dc "${MPFR_TARBALL}" | "${TAR:-tar}" -xf -
		gzip -dc "${MPC_TARBALL}" | "${TAR:-tar}" -xf -

		# Rename GCC dir to the name we need
		rm -rf "${GCC_DIR}"
		mv "gcc-${GCC_VERS}" "${GCC_DIR}"

		# Arrange sources as needed
		rm -rf "${GCC_DIR}/gmp"
		rm -rf "${GCC_DIR}/mpfr"
		rm -rf "${GCC_DIR}/mpc"

		mv "${GMP_DIR}" "${GCC_DIR}/gmp"
		mv "${MPFR_DIR}" "${GCC_DIR}/mpfr"
		mv "${MPC_DIR}" "${GCC_DIR}/mpc"

		# Apply patches
		## Apply patch files
		for patchfile in "$(pwd)/patches/gcc"/*.diff; do
			patchfile_checkscript="$(echo "${patchfile}" | sed 's@\.diff$@.sh@')"
			if [ ! -f "${patchfile}" ]; then
				continue
			fi

			if [ -x "${patchfile_checkscript}" ]; then
				if ! "${patchfile_checkscript}" "${GCC_VERS}" "${CCNAME}"; then
					continue
				fi
			fi

			(
				echo " * Applying patch ${patchfile}"

				cd "${GCC_DIR}" || exit 1

				"${PATCH:-patch}" -p1 < "${patchfile}" || exit 1
			) || exit 1
		done

		## Apply patch scripts
		export GCC_DIR
		for gccscript in scripts/gcc/*; do
			if [ ! -x "${gccscript}" ]; then
				continue
			fi

			sourcefile="${TMPDIR:-/tmp}/build-cc-sourcefile-$$${RANDOM}${RANDOM}${RANDOM}"
			rm -f "${sourcefile}"

			"${gccscript}" "${CCNAME}" "${CCDIR}" "${CCDIR}/${CCNAME}" "${STAGE}" "${sourcefile}" || exit 1

			if [ -f "${sourcefile}" ]; then
				. "${sourcefile}"
				rm -f "${sourcefile}"
			fi
		done
	fi

	# Build GCC with shared object support
	## Create build directory
	rm -rf "gcc-${CCNAME}"
	mkdir "gcc-${CCNAME}"
	cd "gcc-${CCNAME}" || exit -1

	## Unset "CPPFLAGS" since GOMP/OpenMP does not properly sanitize it
	unset CPPFLAGS

	## Build GCC
	(
		PATH="${PATH}:${CCDIR}/bin"
		export PATH

		if [ "${STAGE}" = "stage1" ]; then
			### Stage 1 -- just GCC and libgcc, no more will build until we get some headers
			"../${GCC_DIR}/configure" --target="${CCNAME}" --prefix="${CCDIR}" --with-sysroot="${CCDIR}/${CCNAME}" --with-build-sysroot="${CCDIR}/${CCNAME}" --program-prefix="${CCNAME}-" \
				--enable-languages='c' \
				--disable-nls --without-headers --disable-threads --disable-shared --with-newlib \
				--disable-decimal-float --disable-threads --disable-libatomic --disable-libgomp \
				--disable-libitm --disable-libquadmath --disable-libsanitizer --disable-libssp \
				--disable-libvtv --disable-libcilkrts --with-system-zlib ${BUILD_CC_GCC_CONFIGURE_EXTRA}
			${MAKE} ${BUILD_CC_MAKE_FLAGS} all-gcc || exit 1
			${MAKE} ${BUILD_CC_MAKE_FLAGS} install-gcc || exit 1
			${MAKE} ${BUILD_CC_MAKE_FLAGS} all-target-libgcc || exit 1
			${MAKE} ${BUILD_CC_MAKE_FLAGS} install-target-libgcc || exit 1
		else
			if [ -n "${BUILD_CC_EXTRA_LANGUAGES}" ]; then
				BUILD_CC_EXTRA_LANGUAGES=",${BUILD_CC_EXTRA_LANGUAGES}"
			fi

			### Stage 2 -- the full compiler suite
			"../${GCC_DIR}/configure" --target="${CCNAME}" --prefix="${CCDIR}" --with-headers="${CCDIR}/${CCNAME}/include" --disable-nls --enable-languages="c,c++${BUILD_CC_EXTRA_LANGUAGES}" --with-sysroot="${CCDIR}/${CCNAME}" --with-build-sysroot="${CCDIR}/${CCNAME}" --program-prefix="${CCNAME}-" --with-system-zlib ${BUILD_CC_GCC_CONFIGURE_EXTRA}

			${MAKE} ${BUILD_CC_MAKE_FLAGS} || exit 1
			${MAKE} ${BUILD_CC_MAKE_FLAGS} install || exit 1
		fi
	) || exit 1
)
retval="$?"

### XXX: Create symlink for libgcc_eh.a since GCC references
### it without building it for static (disable-shared) builds
if [ "${STAGE}" = "stage1" ]; then
	if [ -h "${libgcceha}" ]; then
		rm -f "${libgcceha}"
	fi

	if [ ! -f "${libgcceha}" ]; then
		ln -s libgcc.a "${libgcceha}"
	fi
fi

### If we exited with failure above, abort
if [ "${retval}" != "2" -a "${retval}" != "0" ]; then
	exit 1
fi
rm -rf "gcc-${CCNAME}"

## Install libraries for this platform
for appscript in scripts/* scripts/post/*; do
	if echo "${appscripts}" | grep '/common$' >/dev/null; then
		continue
	fi

	if [ ! -f "${appscript}" -o ! -x "${appscript}" ]; then
		continue
	fi

	(
		PATH="${PATH}:${CCDIR}/bin"
		CC="${CCNAME}-gcc"
		LD="${CCNAME}-ld"
		CXX="${CCNAME}-g++"
		AR="${CCNAME}-ar"
		STRIP="${CCNAME}-strip"
		RANLIB="${CCNAME}-ranlib"
		export PATH CC LD CXX AR STRIP RANLIB

		"${appscript}" "${CCNAME}" "${CCDIR}" "${CCDIR}/${CCNAME}" "${STAGE}" "${FLAGS}" || exit 1
	) || exit 1
done

## Do it all again for the next stage
if [ "${STAGE}" = "stage1" ]; then
	"$0" "${CCNAME}" "stage2" || exit "$?"
fi

## Clean up
exit 0