#! /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