#!/usr/bin/env sh # shellcheck disable=SC2030,SC2031,SC2059 # The above line must be directly after the shebang line. # Disables these warnings: # 2030 - Modification of var is local (to subshell caused by pipeline). # shell check 0.4.6 gets confused by the read -t 1 command and interprets # the '1' as $1 getting modified. # 2031 - var was modified in a subshell. That change might be lost. # caused by shell check bug with SC2030? This causes any $1 from that # point on to be flagged. # 2059 - Don't use variables in the printf format string. Use printf "..%s.." "$foo". # This is used for all of our color printing. # # SPDX-License-Identifier: GPL-2.0-only cd "$(dirname "$0")" || exit 1 if [ -z "$CROSSGCC_VERSION" ]; then CROSSGCC_VERSION="$(git log -n 1 --pretty=%cd --date=short .)_$(git log -n 1 --pretty=%h .)" fi # default settings PACKAGE=GCC TARGETDIR=$(pwd)/xgcc TARGETARCH=i386-elf DEFAULT_LANGUAGES=c LANGUAGES= DESTDIR= SAVETEMPS=0 BOOTSTRAP=0 THREADS=1 FETCH_ONLY=0 USE_COREBOOT_MIRROR=0 COREBOOT_MIRROR_URL="https://www.coreboot.org/releases/crossgcc-sources" # GCC toolchain version numbers GMP_VERSION=6.3.0 MPFR_VERSION=4.2.1 MPC_VERSION=1.3.1 GCC_VERSION=11.4.0 BINUTILS_VERSION=2.41 IASL_VERSION="R06_28_23" # CLANG version number CLANG_VERSION=16.0.6 CMAKE_VERSION=3.27.7 NASM_VERSION=2.16.01 # Filename for each package GMP_ARCHIVE="gmp-${GMP_VERSION}.tar.xz" MPFR_ARCHIVE="mpfr-${MPFR_VERSION}.tar.xz" MPC_ARCHIVE="mpc-${MPC_VERSION}.tar.gz" GCC_ARCHIVE="gcc-${GCC_VERSION}.tar.xz" BINUTILS_ARCHIVE="binutils-${BINUTILS_VERSION}.tar.xz" IASL_ARCHIVE="${IASL_VERSION}.tar.gz" # CLANG toolchain FILE locations LLVM_ARCHIVE="llvm-${CLANG_VERSION}.src.tar.xz" CLANG_ARCHIVE="clang-${CLANG_VERSION}.src.tar.xz" CRT_ARCHIVE="compiler-rt-${CLANG_VERSION}.src.tar.xz" CTE_ARCHIVE="clang-tools-extra-${CLANG_VERSION}.src.tar.xz" LLVMCMAKE_ARCHIVE="cmake-${CLANG_VERSION}.src.tar.xz" CMAKE_ARCHIVE="cmake-${CMAKE_VERSION}.tar.gz" NASM_ARCHIVE="nasm-${NASM_VERSION}.tar.bz2" # These URLs are sanitized by the jenkins toolchain test builder, so if # a completely new URL is added here, it probably needs to be added # to the jenkins build as well, or the builder won't download it. # GCC toolchain archive locations GMP_BASE_URL="https://ftpmirror.gnu.org/gmp" MPFR_BASE_URL="https://ftpmirror.gnu.org/mpfr" MPC_BASE_URL="https://ftpmirror.gnu.org/mpc" GCC_BASE_URL="https://ftpmirror.gnu.org/gcc/gcc-${GCC_VERSION}" BINUTILS_BASE_URL="https://ftpmirror.gnu.org/binutils" IASL_BASE_URL="https://github.com/acpica/acpica/archive/refs/tags" # CLANG toolchain archive locations LLVM_BASE_URL="https://github.com/llvm/llvm-project/releases/download/llvmorg-${CLANG_VERSION}" CLANG_BASE_URL="https://github.com/llvm/llvm-project/releases/download/llvmorg-${CLANG_VERSION}" CRT_BASE_URL="https://github.com/llvm/llvm-project/releases/download/llvmorg-${CLANG_VERSION}" CTE_BASE_URL="https://github.com/llvm/llvm-project/releases/download/llvmorg-${CLANG_VERSION}" LLVMCMAKE_BASE_URL="https://github.com/llvm/llvm-project/releases/download/llvmorg-${CLANG_VERSION}" CMAKE_BASE_URL="https://cmake.org/files/v${CMAKE_VERSION%.*}" NASM_BASE_URL="https://www.nasm.us/pub/nasm/releasebuilds/${NASM_VERSION}" ALL_ARCHIVES="$GMP_BASE_URL/$GMP_ARCHIVE $MPFR_BASE_URL/$MPFR_ARCHIVE $MPC_BASE_URL/$MPC_ARCHIVE \ $GCC_BASE_URL/$GCC_ARCHIVE $BINUTILS_BASE_URL/$BINUTILS_ARCHIVE $IASL_BASE_URL/$IASL_ARCHIVE \ $LLVM_BASE_URL/$LLVM_ARCHIVE $CLANG_BASE_URL/$CLANG_ARCHIVE $LLVMCMAKE_BASE_URL/$LLVMCMAKE_ARCHIVE \ $CRT_BASE_URL/$CRT_ARCHIVE $CTE_BASE_URL/$CTE_ARCHIVE $CMAKE_BASE_URL/$CMAKE_ARCHIVE $NASM_BASE_URL/$NASM_ARCHIVE" # GCC toolchain directories GMP_DIR="gmp-${GMP_VERSION}" MPFR_DIR="mpfr-${MPFR_VERSION}" MPC_DIR="mpc-${MPC_VERSION}" # shellcheck disable=SC2034 GCC_DIR="gcc-${GCC_VERSION}" # shellcheck disable=SC2034 BINUTILS_DIR="binutils-${BINUTILS_VERSION}" IASL_DIR="acpica-${IASL_VERSION}" # CLANG toolchain directories LLVM_DIR="llvm-${CLANG_VERSION}.src" CLANG_DIR="clang-${CLANG_VERSION}.src" CRT_DIR="compiler-rt-${CLANG_VERSION}.src" CTE_DIR="clang-tools-extra-${CLANG_VERSION}.src" LLVMCMAKE_DIR="cmake-${CLANG_VERSION}.src" CMAKE_DIR="cmake-${CMAKE_VERSION}" NASM_DIR="nasm-${NASM_VERSION}" unset MAKELEVEL MAKEFLAGS red='\033[0;31m' RED='\033[1;31m' green='\033[0;32m' GREEN='\033[1;32m' blue='\033[0;34m' CYAN='\033[1;36m' NC='\033[0m' # No Color UNAME=$(if uname | grep -iq cygwin; then echo Cygwin; else uname; fi) HALT_FOR_TOOLS=0 hostcc() { # $1 "host" or "target" if [ "$BOOTSTRAP" = 1 ] && [ "$1" = target ]; then echo "$DESTDIR$TARGETDIR/bin/gcc" else echo "$CC" fi } hostcxx() { # $1 "host" or "target" if [ "$BOOTSTRAP" = 1 ] && [ "$1" = target ]; then echo "$DESTDIR$TARGETDIR/bin/g++" else echo "$CXX" fi } normalize_dirs() { mkdir -p "$DESTDIR$TARGETDIR/lib" test -d "$DESTDIR$TARGETDIR/lib32" && mv "$DESTDIR$TARGETDIR"/lib32/* "$DESTDIR$TARGETDIR/lib" test -d "$DESTDIR$TARGETDIR/lib64" && mv "$DESTDIR$TARGETDIR"/lib64/* "$DESTDIR$TARGETDIR/lib" rm -rf "$DESTDIR$TARGETDIR/lib32" "$DESTDIR$TARGETDIR/lib64" perl -pi -e "s,/lib32,/lib," "$DESTDIR$TARGETDIR"/lib/*.la perl -pi -e "s,/lib64,/lib," "$DESTDIR$TARGETDIR"/lib/*.la } countdown() { tout=${1:-10} printf "\nPress Ctrl-C to abort, Enter to continue... %2ds" "$tout" while [ "$tout" -gt 0 ]; do sleep 1 tout=$((tout - 1)) printf "\b\b\b%2ds" $tout done printf "\n" } timeout() { tout=${1:-10} # Ignore SIGUSR1, should interrupt `read` though. trap false USR1 # Clean up in case the user aborts. trap 'kill $counter > /dev/null 2>&1' EXIT (countdown "$tout"; kill -USR1 $$)& counter=$! # Some shells with sh compatibility mode (e.g. zsh, mksh) only # let us interrupt `read` if a non-standard -t parameter is given. # shellcheck disable=SC2034,SC2039,SC2162 if echo | read -t 1 foo 2>/dev/null; then read -t $((tout + 1)) foo else read foo fi kill $counter > /dev/null 2>&1 trap - USR1 EXIT } please_install() { HALT_FOR_TOOLS=1 # shellcheck disable=SC1091 test -r /etc/os-release && . /etc/os-release # vanilla debian doesn't define `ID_LIKE`, just `ID` if [ -z "${ID_LIKE}" ] && [ -n "${ID}" ]; then ID_LIKE=${ID} fi case "$ID_LIKE" in debian) solution="sudo apt-get install $1" ;; suse) solution="sudo zypper install $1" ;; *) solution="using your OS packaging system" ;; esac printf "${RED}ERROR:${red} Missing tool: Please install '$1'. (eg $solution)${NC}\n" >&2 if [ -n "$2" ]; then printf "${RED}ERROR:${red} or install '$2'.${NC}\n" >&2 fi } searchtool() { # $1 short name # $2 search string # $3 soft fail if set # $4 alternative package to install on failure # result: file name of that tool on stdout # or no output if nothing suitable was found search=GNU if [ -n "$2" ]; then search="$2" fi for i in "$1" "g$1" "gnu$1"; do if [ -x "$(command -v "$i" 2>/dev/null)" ]; then if [ "$(cat /dev/null | $i --version 2>&1 | grep -c "$search")" \ -gt 0 ]; then echo "$i" return fi fi done # A workaround for OSX 10.9 and some BSDs, whose nongnu # patch and tar also work. if [ "$UNAME" = "Darwin" ] || [ "$UNAME" = "FreeBSD" ] || [ "$UNAME" = "NetBSD" ] || [ "$UNAME" = "OpenBSD" ]; then if [ "$1" = "patch" ] || [ "$1" = "tar" ]; then if [ -x "$(command -v "$1" 2>/dev/null)" ]; then echo "$1" return fi fi fi if echo "$1" | grep -q "sum" ; then algor=$(echo "$1" | sed -e 's,sum,,') if [ -x "$(command -v "$1" 2>/dev/null)" ]; then #xxxsum [file] echo "$1" return elif [ -x "$(command -v "$algor" 2>/dev/null)" ]; then #xxx [file] echo "$algor" return elif [ -x "$(command -v openssl 2>/dev/null)" ]; then #openssl xxx [file] echo openssl "$algor" return elif [ -x "$(command -v cksum 2>/dev/null)" ]; then #cksum -a xxx [file] #cksum has special options in NetBSD. Actually, NetBSD will use the second case above. echo "buildgcc" | cksum -a "$algor" > /dev/null 2>/dev/null && \ echo cksum -a "$algor" return fi fi [ -z "$3" ] && please_install "$1" "$4" false } # Run a compile check of the specified library option to see if it's installed check_for_library() { LIBRARY_FLAGS="$1" LIBRARY_PACKAGES="$2" LIBTEST_FILE=.libtest echo "int main(int argc, char **argv) { (void) argc; (void) argv; return 0; }" > "${LIBTEST_FILE}.c" # shellcheck disable=SC2086 "$CC" $CFLAGS $LIBRARY_FLAGS "${LIBTEST_FILE}.c" -o "${LIBTEST_FILE}" >/dev/null 2>&1 || \ please_install "$LIBRARY_PACKAGES" rm -rf "${LIBTEST_FILE}.c" "${LIBTEST_FILE}" } buildcc_major() { echo "${GCC_VERSION}" | cut -d. -f1 } buildcc_minor() { echo "${GCC_VERSION}" | cut -d. -f2 } buildcc_version() { echo "${GCC_VERSION}" | cut -d. -f1-2 } hostcc_major() { (echo __GNUC__ | ${CC} -E - 2>/dev/null || echo 0) | tail -1 } hostcc_minor() { (echo __GNUC_MINOR__ | ${CC} -E - 2>/dev/null || echo 0) | tail -1 } hostcc_version() { printf "%d.%d" "$(hostcc_major)" "$(hostcc_minor)" } hostcc_has_gnat1() { [ -x "$(${CC} -print-prog-name=gnat1)" ] } have_gnat() { hostcc_has_gnat1 && \ searchtool gnatbind "Free Software Foundation" nofail > /dev/null } ada_requested() { echo "${LANGUAGES}" | grep -q '\' } download() { package=$1 # shellcheck disable=SC2086 if [ "${USE_COREBOOT_MIRROR}" -eq 0 ]; then url="$(eval echo \$$package"_BASE_URL")" else url="${COREBOOT_MIRROR_URL}" fi file="$(eval echo \$$package"_ARCHIVE")" printf " * ${file} " if test -f "tarballs/${file}"; then printf "(cached)... " else printf "(downloading from ${url}/${file})" rm -f "tarballs/${file}" cd tarballs || exit 1 download_showing_percentage "${url}/${file}" cd .. fi if [ ! -f "tarballs/${file}" ]; then printf "${RED}Failed to download ${file}.${NC}\n" exit 1 fi } # Compute the hash of the package given in $1, and print it raw (just the # hexadecimal hash). compute_hash() { package=$1 # shellcheck disable=SC2086 file="$(eval echo \$$package"_ARCHIVE")" if test -z "$CHECKSUM"; then echo "${RED}\$CHECKSUM program missing. This is bad.${NC}" 1>&2 exit 1 fi $CHECKSUM "tarballs/$file" 2>/dev/null | sed -e 's@.*\([0-9a-f]\{40,\}\).*@\1@' } error_hash_missing() { package="$1" # shellcheck disable=SC2086 file="$(eval echo \$$package"_ARCHIVE")" fullhashfile="util/crossgcc/sum/$file.cksum" printf "${RED}hash file missing:${NC}\n\n" 1>&2 printf "Please verify util/crossgcc/tarball/$file carefully\n" 1>&2 printf "(using PGP if possible), and then rename\n" 1>&2 printf " ${CYAN}${fullhashfile}.calc${NC}\n" 1>&2 printf " to ${CYAN}${fullhashfile}${NC}\n\n" 1>&2 exit 1 } # Read the known hash file of the package given in $1, and print it raw. get_known_hash() { package=$1 # shellcheck disable=SC2086 file="$(eval echo \$$package"_ARCHIVE")" hashfile="sum/$file.cksum" if [ ! -f "$hashfile" ]; then calc_hash="$(compute_hash "$package")" || exit 1 echo "$calc_hash tarballs/$file" > "${hashfile}.calc" error_hash_missing "$package" exit 1 fi sed -e 's@.*\([0-9a-f]\{40,\}\).*@\1@' < "$hashfile" } error_hash_mismatch() { package=$1 known_hash="$2" computed_hash="$3" # shellcheck disable=SC2086 file="$(eval echo \$$package"_ARCHIVE")" printf "${RED}hash mismatch:${NC}\n\n" printf " expected (known) hash: $known_hash\n" printf "calculated hash of downloaded file: $computed_hash\n\n" printf "If you think this is due to a network error, please delete\n" printf " ${CYAN}util/crossgcc/tarballs/$file${NC}\n" printf "and try again. If the problem persists, it may be due to an\n" printf "administration error on the file server, or you might be\n" printf "subject to a Man-in-the-Middle attack\n\n" exit 1 } # verify_hash - Check that the hash of the file given in $1 matches the known # hash; Bail out on mismatch or missing hash file. verify_hash() { package=$1 known_hash="$(get_known_hash "$package")" || exit "$?" computed_hash="$(compute_hash "$package")" || exit "$?" if [ "$known_hash" != "$computed_hash" ]; then error_hash_mismatch "$package" "$known_hash" "$computed_hash" exit 1 fi printf "${GREEN}hash verified (${known_hash})${NC}\n" } unpack_and_patch() { package="$1" # shellcheck disable=SC2086 archive="$(eval echo \$$package"_ARCHIVE")" # shellcheck disable=SC2086 dir="$(eval echo \$$package"_DIR")" test -d "${dir}" && test -f "${dir}/.unpack_success" || ( printf " * "$archive"\n" FLAGS=zxf suffix=$(echo "$archive" | sed 's,.*\.,,') if [ "$suffix" = "gz" ] && [ -n "$PIGZ" ]; then FLAGS="-I pigz -xf" elif [ "$suffix" = "gz" ]; then FLAGS=zxf elif [ "$suffix" = "bz2" ] && [ -n "$LBZIP2" ]; then FLAGS="-I lbzip2 -xf" elif [ "$suffix" = "bz2" ]; then FLAGS=jxf elif [ "$suffix" = "xz" ]; then FLAGS="--xz -xf" elif [ "$suffix" = "lzma" ]; then FLAGS="--lzma -xf" fi # shellcheck disable=SC2086 $TAR $FLAGS "tarballs/$archive" for patch in patches/${dir}_*.patch; do test -r "$patch" || continue printf " o $(basename "$patch")\n" (cd "${dir}" || exit 1; $PATCH -s -N -p1 <"../${patch}") || { printf "\n${RED}Failed $patch.${NC}\n" exit 1 } done touch "${dir}/.unpack_success" ) } fn_exists() { # shellcheck disable=SC2039 type "$1" >/dev/null 2>&1 } is_package_enabled() { echo "$PACKAGES" |grep -q "\<$1\>" } package_uses_targetarch() { if [ "$1" = "GCC" ] || [ "$1" = "BINUTILS" ]; then true else false fi } generic_build() { package=$1 host_target=$2 builddir=$3 success=$4 version=$5 fn_exists "build_$package" || return mkdir -p "$builddir" if [ -f "$success" ]; then printf "Skipping $package v$version for $host_target as it is already built\n" else printf "Building $package v$version for $host_target ... " DIR="$PWD" cd "$builddir" || exit 1 rm -f .failed "build_${package}" "$host_target" > build.log 2>&1 cd "$DIR" || exit 1 if [ ! -f "$builddir/.failed" ]; then touch "$success"; else printf "${RED}failed${NC}. Check '$builddir/build.log'.\n" exit 1 fi printf "${green}ok${NC}\n" fi } build_for_host() { package="$1" # shellcheck disable=SC2086 version="$(eval echo \$$package"_VERSION")" generic_build "$package" host "build-$package" "${DESTDIR}${TARGETDIR}/.${package}.${version}.success" "$version" } build_for_target() { package="$1" # shellcheck disable=SC2086 version="$(eval echo \$$package"_VERSION")" generic_build "$package" target "build-${TARGETARCH}-$package" "${DESTDIR}${TARGETDIR}/.${TARGETARCH}-${package}.${version}.success" "$version" } build() { if package_uses_targetarch "$1"; then if [ $BOOTSTRAP -eq 1 ] && [ ! -f "${DESTDIR}${TARGETDIR}/.GCC.${GCC_VERSION}.success" ]; then build_for_host GCC fi build_for_target "$1" else build_for_host "$1" fi } exit_handler() { printf "${NC}Stop\n" exit 1 } cleanup() { if [ $SAVETEMPS -ne 0 ]; then printf "Leaving temporary files around... ${green}ok${NC}\n" return fi printf "Cleaning up temporary files... " for package in $PACKAGES; do # shellcheck disable=SC2086 rm -rf "build-${TARGETARCH}-$package" "build-$package" "$(eval echo \$$package"_DIR")" done rm -f getopt printf "${green}ok${NC}\n" } myhelp() { printf "Usage: $0 [-V] [-c] [-p ] [-d ] [-D ] [-C] [-G] [-S]\n" printf " $0 [-V|--version]\n" printf " $0 [-h|--help]\n\n" printf "Options:\n" printf " [-W|--print-version Print machine readable version\n" printf " [-V|--version] print version number and exit\n" printf " [-h|--help] print this help and exit\n" printf " [-c|--clean] remove temporary files before build\n" printf " [-t|--savetemps] don't remove temporary files after build\n" printf " [-y|--ccache] Use ccache when building cross compiler\n" printf " [-n|--nocolor] don't print color codes in output\n" printf " [-u|--urls] print the urls for all packages\n" printf " [-j|--jobs ] run jobs in parallel in make\n" printf " [-s]--supported print supported version of a tool\n" printf " [-d|--directory ] target directory to install cross compiler to\n" printf " (defaults to $TARGETDIR)\n\n" printf " [-D|--destdir ] destination directory to install cross compiler to\n" printf " (for RPM builds, default unset)\n" printf " [-f|--fetch] Download tarballs, but don't build anything\n" printf " [-P|--package ] Build a specific package: GCC, CLANG, IASL\n" printf " (defaults to $PACKAGE)\n" printf "GCC specific options:\n" printf " [-b|--bootstrap] bootstrap the host compiler before building\n" printf " the cross compiler\n" printf " [-p|--platform ] target platform to build cross compiler for\n" printf " (defaults to $TARGETARCH)\n" printf " [-l|--languages ] comma separated list of target languages\n" printf " (defaults to $DEFAULT_LANGUAGES)\n" printf "Platforms for GCC:\n" printf " x86_64 i386-elf i386-mingw32 riscv-elf arm aarch64\n" printf " powerpc64le-linux-gnu nds32le-elf\n\n" } printversion() { printf "${blue}Welcome to the ${red}coreboot${blue} cross toolchain builder v$CROSSGCC_VERSION ${NC}\n\n" } myversion() { printversion cat << EOF Copyright (C) 2008-2010 by coresystems GmbH Copyright (C) 2011 by Sage Electronic Engineering This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. EOF } have_hostcflags_from_gmp() { grep -q __GMP_CFLAGS "$DESTDIR$TARGETDIR/include/gmp.h" >/dev/null 2>&1 } set_hostcflags_from_gmp() { # Now set CFLAGS to match GMP CFLAGS but strip out -pedantic # as GCC 4.6.x fails if it's there. HOSTCFLAGS="$(grep __GMP_CFLAGS "$DESTDIR$TARGETDIR/include/gmp.h" |cut -d\" -f2 |\ sed s,-pedantic,,)" export HOSTCFLAGS } build_GMP() { # Check if GCC enables `-pie` by default (possible since GCC 6). # We need PIC in all static libraries then. if $CC -dumpspecs 2>/dev/null | grep -q '[{;][[:space:]]*:-pie\>' then OPTIONS="$OPTIONS --with-pic" fi # shellcheck disable=SC2086 CC="$(hostcc host)" CXX="$(hostcxx host)" \ ../${GMP_DIR}/configure \ --disable-shared \ --enable-fat \ --prefix="$TARGETDIR" \ $OPTIONS || touch .failed # shellcheck disable=SC2086 $MAKE $JOBS || touch .failed $MAKE install DESTDIR=$DESTDIR || touch .failed normalize_dirs set_hostcflags_from_gmp } build_MPFR() { test "$UNAME" = "Darwin" && CFLAGS="$CFLAGS -force_cpusubtype_ALL" CC="$(hostcc host)" CXX="$(hostcxx host)" \ ../${MPFR_DIR}/configure \ --disable-shared \ --prefix="$TARGETDIR" \ --infodir="$TARGETDIR/info" \ --with-gmp="$DESTDIR$TARGETDIR" \ CFLAGS="$HOSTCFLAGS" || touch .failed # shellcheck disable=SC2086 $MAKE $JOBS || touch .failed $MAKE install DESTDIR=$DESTDIR || touch .failed normalize_dirs # work around build problem of libgmp.la if [ "$DESTDIR" != "" ]; then perl -pi -e "s,$DESTDIR,," "$DESTDIR$TARGETDIR/lib/libgmp.la" fi } build_MPC() { CC="$(hostcc host)" CXX="$(hostcxx host)" \ ../${MPC_DIR}/configure \ --disable-shared \ --prefix="$TARGETDIR" \ --infodir="$TARGETDIR/info" \ --with-mpfr="$DESTDIR$TARGETDIR" \ --with-gmp="$DESTDIR$TARGETDIR" \ CFLAGS="$HOSTCFLAGS" || touch .failed # work around build problem of libmpfr.la if [ "$DESTDIR" != "" ]; then perl -pi -e "s,$TARGETDIR/lib/libgmp.la,$DESTDIR\$&," "$DESTDIR$TARGETDIR/lib/libmpfr.la" fi # shellcheck disable=SC2086 $MAKE $JOBS || touch .failed $MAKE install DESTDIR=$DESTDIR || touch .failed # work around build problem of libmpfr.la if [ "$DESTDIR" != "" ]; then perl -pi -e "s,$DESTDIR,," "$DESTDIR$TARGETDIR/lib/libmpfr.la" fi normalize_dirs } build_BINUTILS() { if [ $TARGETARCH = "x86_64-elf" ]; then ADDITIONALTARGET=",i386-elf" fi # shellcheck disable=SC2086 CC="$(hostcc target)" CXX="$(hostcxx target)" \ ../binutils-${BINUTILS_VERSION}/configure \ --prefix="$TARGETDIR" \ --target=${TARGETARCH} \ --enable-targets=${TARGETARCH}${ADDITIONALTARGET} \ --disable-werror \ --disable-nls \ --enable-lto \ --enable-gold \ --enable-multilib \ --disable-docs \ --disable-texinfo \ ${BINUTILS_OPTIONS} \ CFLAGS="$HOSTCFLAGS" \ CXXFLAGS="$HOSTCFLAGS" || touch .failed # shellcheck disable=SC2086 $MAKE $JOBS || touch .failed $MAKE install DESTDIR=$DESTDIR || touch .failed } bootstrap_GCC() { # shellcheck disable=SC2086 CC="$(hostcc host)" CXX="$(hostcxx host)" \ CFLAGS="$HOSTCFLAGS" \ CFLAGS_FOR_BUILD="$HOSTCFLAGS" \ CFLAGS_FOR_TARGET="$HOSTCFLAGS -fPIC" \ CXXFLAGS="$HOSTCFLAGS" \ CXXFLAGS_FOR_BUILD="$HOSTCFLAGS" \ CXXFLAGS_FOR_TARGET="$HOSTCFLAGS -fPIC" \ ../gcc-${GCC_VERSION}/configure \ --prefix="$TARGETDIR" \ --libexecdir="$TARGETDIR/lib" \ --enable-bootstrap \ --disable-werror \ --disable-nls \ --disable-shared \ --disable-multilib \ --disable-libssp \ --disable-libquadmath \ --disable-libcc1 \ --disable-libsanitizer \ ${GCC_OPTIONS} \ --enable-languages="${LANGUAGES}" \ --with-gmp="$DESTDIR$TARGETDIR" \ --with-mpfr="$DESTDIR$TARGETDIR" \ --with-mpc="$DESTDIR$TARGETDIR" \ --with-pkgversion="coreboot bootstrap v$CROSSGCC_VERSION" \ && \ # shellcheck disable=SC2086 $MAKE $JOBS BOOT_CFLAGS="$HOSTCFLAGS" BUILD_CONFIG="" bootstrap && \ $MAKE install-gcc \ install-target-libgcc \ maybe-install-target-libada \ maybe-install-target-libstdc++-v3 \ DESTDIR=$DESTDIR || touch .failed } build_cross_GCC() { # Work around crazy code generator in GCC that confuses CLANG. $CC --version | grep clang >/dev/null 2>&1 && \ CLANGFLAGS="-fbracket-depth=1024" [ -n "$CXX" ] && $CXX --version | grep clang >/dev/null 2>&1 && \ CLANGCXXFLAGS="-fbracket-depth=1024" # GCC does not honor HOSTCFLAGS at all. CFLAGS are used for # both target and host object files. # There's a work-around called CFLAGS_FOR_BUILD and CFLAGS_FOR_TARGET # but it does not seem to work properly. At least the host library # libiberty is not compiled with CFLAGS_FOR_BUILD. # Also set the CXX version of the flags because GCC is now compiled # using C++. # shellcheck disable=SC2086 CC="$(hostcc target)" CXX="$(hostcxx target)" \ CFLAGS_FOR_TARGET="-O2 -Dinhibit_libc" \ CFLAGS="$HOSTCFLAGS $CLANGFLAGS" \ CFLAGS_FOR_BUILD="$HOSTCFLAGS $CLANGFLAGS" \ CXXFLAGS="$HOSTCFLAGS $CLANGCXXFLAGS" \ CXXFLAGS_FOR_BUILD="$HOSTCFLAGS $CLANGCXXFLAGS" \ ../gcc-${GCC_VERSION}/configure \ --prefix="$TARGETDIR" \ --libexecdir="$TARGETDIR/lib" \ --target=${TARGETARCH} \ --disable-werror \ --disable-shared \ --enable-lto \ --enable-plugins \ --enable-gold \ --enable-ld=default \ --disable-libssp \ --disable-bootstrap \ --disable-nls \ --disable-libquadmath \ --without-headers \ --disable-threads \ --enable-interwork \ --enable-multilib \ --enable-targets=all \ --disable-libatomic \ --disable-libcc1 \ --disable-decimal-float \ ${GCC_OPTIONS} \ --enable-languages="${LANGUAGES}" \ --with-system-zlib \ --with-gmp="$DESTDIR$TARGETDIR" \ --with-mpfr="$DESTDIR$TARGETDIR" \ --with-mpc="$DESTDIR$TARGETDIR" \ --with-gnu-as \ --with-gnu-ld \ --with-pkgversion="coreboot toolchain v$CROSSGCC_VERSION" \ && \ mkdir -p gcc/$TARGETARCH && \ rm -f "gcc/$TARGETARCH/$GCC_VERSION" && \ ln -s "$DESTDIR$TARGETDIR/$TARGETARCH/bin" "gcc/$TARGETARCH/$GCC_VERSION" && \ $MAKE $JOBS CFLAGS_FOR_BUILD="$HOSTCFLAGS" all-gcc && \ $MAKE install-gcc DESTDIR="$DESTDIR" || touch .failed if [ ! -f .failed ] && [ "$(echo $TARGETARCH | grep -c -- -mingw32)" -eq 0 ]; then # shellcheck disable=SC2086 $MAKE $JOBS CFLAGS_FOR_BUILD="$HOSTCFLAGS" all-target-libgcc && \ $MAKE install-target-libgcc DESTDIR=$DESTDIR || touch .failed fi } build_GCC() { if [ "$1" = host ]; then bootstrap_GCC "$1" else build_cross_GCC "$1" fi } build_IASL() { RDIR=$PWD cd ../$IASL_DIR/generate/unix || exit 1 CFLAGS="$HOSTCFLAGS" HOST="_LINUX" test "$UNAME" = "Darwin" && HOST="_APPLE" test "$UNAME" = "FreeBSD" && HOST="_FreeBSD" test "$UNAME" = "Cygwin" && HOST="_CYGWIN" HOST="$HOST" CFLAGS="$CFLAGS" \ OPT_CFLAGS="-O -D_FORTIFY_SOURCE=2 -D COREBOOT_TOOLCHAIN_VERSION='\"coreboot toolchain v$CROSSGCC_VERSION\"' " \ $MAKE $JOBS CC="$(hostcc host)" iasl acpibin acpidump acpiexec acpihelp acpisrc acpixtract mkdir -p "$DESTDIR$TARGETDIR/bin/" (cd "$DESTDIR$TARGETDIR/bin" && rm -f iasl acpibin acpidump acpiexec acpihelp acpisrc acpixtract) || touch "$RDIR/.failed" (cd bin && cp iasl acpibin acpidump acpiexec acpihelp acpisrc acpixtract "$DESTDIR$TARGETDIR/bin") || touch "$RDIR/.failed" } build_LLVM() { ln -nsf "$LLVM_DIR" ../llvm ln -nsf "$CLANG_DIR" ../clang ln -nsf "$CTE_DIR" ../clang-tools-extra ln -nsf "$CRT_DIR" ../compiler-rt ln -nsf "$LLVMCMAKE_DIR" ../cmake $CMAKE -G "Unix Makefiles" \ -DCMAKE_INSTALL_PREFIX="$DESTDIR$TARGETDIR" \ -DCLANG_VENDOR="coreboot toolchain v$CROSSGCC_VERSION - " \ -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra;compiler-rt" \ -DLLVM_INCLUDE_BENCHMARKS="OFF" \ -DLLVM_INCLUDE_TESTS="OFF" \ -DLLVM_INCLUDE_EXAMPLES="OFF" \ -DCMAKE_BUILD_TYPE=Release \ -DLLVM_TARGETS_TO_BUILD="AArch64;ARM;PowerPC;RISCV;X86" \ ../llvm || touch .failed # shellcheck disable=SC2086 $MAKE $JOBS || touch .failed $MAKE install || touch .failed rm -f ../llvm ../clang ../clang-tools-extra ../compiler-rt ../cmake cp -a ../$CLANG_DIR/tools/scan-build/* "$DESTDIR$TARGETDIR/bin" cp -a ../$CLANG_DIR/tools/scan-view/* "$DESTDIR$TARGETDIR/bin" } build_CMAKE() { CC="$(hostcc host)" CXX="$(hostcxx host)" CFLAGS="$HOSTCFLAGS" \ ../${CMAKE_DIR}/configure \ --parallel=${THREADS} \ --prefix="$TARGETDIR" || touch .failed # shellcheck disable=SC2086 $MAKE $JOBS || touch .failed $MAKE install DESTDIR=$DESTDIR || touch .failed } build_NASM() { CC="$(hostcc host)" CXX="$(hostcxx host)" CFLAGS="$HOSTCFLAGS" \ ../${NASM_DIR}/configure \ --prefix="$TARGETDIR" || touch .failed # shellcheck disable=SC2086 $MAKE $JOBS || touch .failed $MAKE install DESTDIR=$DESTDIR || touch .failed normalize_dirs } print_supported() { case "$PRINTSUPPORTED" in BINUTILS|binutils) printf "%s\n" "$BINUTILS_VERSION";; CLANG|clang) printf "%s\n" "$CLANG_VERSION";; GCC|gcc) printf "%s\n" "$GCC_VERSION";; GMP|gmp) printf "%s\n" "$GMP_VERSION";; IASL|iasl) printf "%s\n" "$IASL_VERSION";; MPC|mpc) printf "%s\n" "$MPC_VERSION";; MPFR|mpfr) printf "%s\n" "$MPFR_VERSION";; NASM|nasm) printf "%s\n" "${NASM_VERSION}";; *) printf "Unknown tool %s\n" "$PRINTSUPPORTED";; esac } trap exit_handler 1 2 3 15 # Look if we have getopt. If not, build it. export PATH=$PATH:. getopt - > /dev/null 2>/dev/null || gcc -o getopt getopt.c # parse parameters.. try to find out whether we're running GNU getopt getoptbrand="$(getopt -V 2>/dev/null | sed -e '1!d' -e 's,^\(......\).*,\1,')" if [ "${getoptbrand}" = "getopt" ]; then # Detected GNU getopt that supports long options. args=$(getopt -l print-version,version,help,clean,directory:,bootstrap,bootstrap-only,platform:,languages:,package:,jobs:,destdir:,savetemps,scripting,ccache,supported:,urls,nocolor,mirror,fetch -o WVhcd:bBp:l:P:j:D:tSys:unmf -- "$@") getopt_ret=$? eval set -- "$args" else # Detected non-GNU getopt args=$(getopt WVhcd:bBp:l:P:j:D:tSys:unm $*) getopt_ret=$? # shellcheck disable=SC2086 set -- $args fi if [ $getopt_ret != 0 ]; then myhelp exit 1 fi while true ; do case "$1" in -W|--print-version) shift; echo $CROSSGCC_VERSION; exit 0;; -V|--version) shift; myversion; exit 0;; -h|--help) shift; myhelp; exit 0;; -c|--clean) shift; clean=1;; -t|--savetemps) shift; SAVETEMPS=1;; -d|--directory) shift; TARGETDIR="$1"; shift;; -b|--bootstrap) shift; BOOTSTRAP=1;; -B|--bootstrap-only) shift; BOOTSTRAPONLY=1; BOOTSTRAP=1;; -p|--platform) shift; TARGETARCH="$1"; shift;; -l|--languages) shift; LANGUAGES="$1"; shift;; -m|--mirror) shift; USE_COREBOOT_MIRROR=1;; -D|--destdir) shift; DESTDIR="$1"; shift;; -f|--fetch) shift; FETCH_ONLY=1;; -j|--jobs) shift; THREADS="$1"; JOBS="-j $1"; shift;; -P|--package) shift; PACKAGE="$1"; shift;; -y|--ccache) shift; USECCACHE=1;; -s|--supported) shift; PRINTSUPPORTED="$1"; shift;; -u|--urls) shift; printf "%s\n" "$ALL_ARCHIVES"; exit 0;; -n|--nocolor) shift; \ unset red RED green GREEN blue BLUE cyan CYAN NC;; --) shift; break;; *) break;; esac done if [ $# -gt 0 ]; then printf "Excessive arguments: $*\n" myhelp exit 1 fi if [ -n "$PRINTSUPPORTED" ]; then print_supported exit 0 fi #print toolchain builder version string as the header printversion printf "Building toolchain using %d thread(s).\n\n" "$THREADS" case "$TARGETARCH" in x86_64-elf) ;; x86_64*) TARGETARCH=x86_64-elf;; i386-elf) ;; i386-mingw32) ;; riscv-elf) TARGETARCH=riscv64-elf GCC_OPTIONS="$GCC_OPTIONS --with-isa-spec=20191213" BINUTILS_OPTIONS="$BINUTILS_OPTIONS --with-isa-spec=20191213";; powerpc64*-linux*) ;; i386*) TARGETARCH=i386-elf;; arm*) TARGETARCH=arm-eabi;; aarch64*) TARGETARCH=aarch64-elf;; nds32le-elf) ;; *) printf "${red}WARNING: Unsupported architecture $TARGETARCH.${NC}\n\n"; ;; esac # Figure out which packages to build case "$PACKAGE" in GCC|gcc) echo "Target architecture is $TARGETARCH" NAME="${TARGETARCH} cross GCC" PACKAGES="GMP MPFR MPC BINUTILS GCC" ;; CLANG|clang) NAME="LLVM clang" LLVM_VERSION=${CLANG_VERSION} PACKAGES="CMAKE LLVM CLANG CRT CTE LLVMCMAKE" CMAKE=${DESTDIR}${TARGETDIR}/bin/cmake ;; IASL|iasl) NAME="IASL ACPI compiler" PACKAGES=IASL ;; CMAKE|cmake) NAME="CMake" PACKAGES=CMAKE ;; NASM|nasm) NAME="NASM" PACKAGES=NASM ;; *) printf "${red}ERROR: Unsupported package $PACKAGE. (Supported packages are GCC, CLANG, IASL, and NASM)${NC}\n\n"; exit 1 ;; esac # Find all the required tools: TAR=$(searchtool tar) || exit $? PATCH=$(searchtool patch) || exit $? MAKE=$(searchtool make) || exit $? SHA1SUM=$(searchtool sha1sum) #SHA512SUM=$(searchtool sha512sum) #MD5SUM=$(searchtool md5sum) CHECKSUM=$SHA1SUM LBZIP2=$(searchtool lbzip2 "" nofail) PIGZ=$(searchtool pigz "" nofail) searchtool m4 > /dev/null searchtool bison > /dev/null searchtool flex flex > /dev/null searchtool bzip2 "bzip2," > /dev/null searchtool xz "XZ Utils" "" "xz-utils" > /dev/null if searchtool wget "GNU" nofail > /dev/null; then download_showing_percentage() { url=$1 printf "... ${red} 0%%" wget --tries=3 "$url" 2>&1 | while read -r line; do echo "$line" | grep -o "[0-9]\+%" | awk '{printf("\b\b\b\b%4s", $1)}' done printf "${NC}... " } elif searchtool curl "^curl " > /dev/null; then download_showing_percentage() { url=$1 echo curl -O --progress-bar --location --retry 3 "$url" } fi # Allow $CC override from the environment. if [ -n "$CC" ]; then if [ ! -x "$(command -v "$CC" 2>/dev/null)" ]; then printf "${RED}ERROR:${red} CC is set to '%s' but wasn't found.${NC}\n" "$CC" HALT_FOR_TOOLS=1 fi else if searchtool gnatgcc "Free Software Foundation" nofail > /dev/null; then # gnatgcc is deprecated and in recent GCC releases its purpose is # fulfilled by the gcc binary. In case of a deprecated gnatgcc # version is installed, it doesn't provide the expected output and # hostcc_has_gnat1() fails. In this case, just set the value of CC # to gcc. # TODO: Remove this whole branch when time is appropriate as the # second branch fulfills our needs. CC=gnatgcc if ! hostcc_has_gnat1; then CC=gcc fi elif searchtool gcc "Free Software Foundation" nofail > /dev/null; then CC=gcc else searchtool cc '^' nofail > /dev/null || please_install gcc CC=cc fi fi # We can leave $CXX empty if it's not set since *buildgcc* never # calls it directly. This way configure scripts can search for # themselves and we still override it when a bootstrapped g++ is # to be used (cf. hostcxx()). if [ -n "$CXX" ]; then if [ ! -x "$(command -v "$CXX" 2>/dev/null)" ]; then printf "${RED}ERROR:${red} CXX is set to '%s' but wasn't found.${NC}\n" "$CXX" HALT_FOR_TOOLS=1 fi else searchtool g++ "Free Software Foundation" nofail > /dev/null || \ searchtool clang "clang version" nofail > /dev/null || \ searchtool clang "LLVM" "" "g++" > /dev/null fi check_for_library "-lz" "zlib (zlib1g-dev or zlib-devel)" if [ "$HALT_FOR_TOOLS" -ne 0 ]; then exit 1 fi # This initial cleanup is useful when updating the toolchain script. if [ "$clean" = "1" ]; then cleanup fi # Set up host compiler and flags needed for various OSes if is_package_enabled "GCC"; then # sane preset: let the configure script figure out things by itself # more importantly, avoid any values that might already linger in the variable OPTIONS="ABI=" if [ "$UNAME" = "Darwin" ]; then #GCC_OPTIONS="$GCC_OPTIONS --enable-threads=posix" # generally the OS X compiler can create x64 binaries. # Per default it generated i386 binaries in 10.5 and x64 # binaries in 10.6 (even if the kernel is 32bit) # For some weird reason, 10.5 autodetects an ABI=64 though # so we're setting the ABI explicitly here. if [ "$(sysctl -n hw.optional.x86_64 2>/dev/null)" -eq 1 ] 2>/dev/null; then OPTIONS="ABI=64" else OPTIONS="ABI=32" fi # In Xcode 4.5.2 the default compiler is clang. # However, this compiler fails to compile gcc 4.7.x. As a # workaround it's possible to compile gcc with llvm-gcc. if $CC -v 2>&1 | grep -q LLVM; then CC=llvm-gcc fi elif [ "$UNAME" = "Linux" ] || [ "$UNAME" = "Cygwin" ]; then # gmp is overeager with detecting 64bit CPUs even if they run # a 32bit kernel and userland. if [ "$(uname -m 2>/dev/null)" = "i686" ]; then OPTIONS="ABI=32" fi elif [ "$UNAME" = "NetBSD" ]; then # same for NetBSD but this one reports an i386 if [ "$(uname -m 2>/dev/null)" = "i386" ]; then OPTIONS="ABI=32" fi fi if [ -z "${LANGUAGES}" ]; then if have_gnat; then printf "\nFound compatible Ada compiler, enabling Ada support by default.\n\n" LANGUAGES="ada,${DEFAULT_LANGUAGES}" else printf "\n${red}WARNING${NC}\n" printf "No compatible Ada compiler (GNAT) found. You can continue without\n" printf "Ada support, but this will limit the features of ${blue}coreboot${NC} (e.g.\n" printf "native graphics initialization won't be available on most Intel\n" printf "boards).\n\n" printf "Usually, you can install GNAT with your package management system\n" printf "(the package is called \`gnat\` or \`gcc-ada\`). It has to match the\n" printf "\`gcc\` package in version. If there are multiple versions of GCC in-\n" printf "stalled, you can point this script to the matching version through\n" printf "the \`CC\` and \`CXX\` environment variables.\n\n" printf "e.g. on Ubuntu 14.04, if \`gcc\` is \`gcc-4.8\`:\n" printf " apt-get install gnat-4.8 && make crossgcc\n\n" printf "on Ubuntu 16.04, if \`gcc\` is \`gcc-5\`:\n" printf " apt-get install gnat-5 && make crossgcc\n" timeout 30 LANGUAGES="${DEFAULT_LANGUAGES}" fi fi if [ "$BOOTSTRAP" != 1 ] && \ { [ "$(hostcc_major)" -lt 4 ] || \ { [ "$(hostcc_major)" -eq 4 ] && \ [ "$(hostcc_minor)" -lt 9 ] ; } ; } then printf "\n${red}WARNING${NC}\n" printf "Building coreboot requires a host compiler newer than 4.9.x while\n" printf "yours is $(hostcc_version).\n" printf "Enabling bootstrapping to provide a sufficiently new compiler:\n" printf "This will take significantly longer than a usual build.\n" printf "Alternatively you can abort now and update your host compiler.\n" timeout 15 BOOTSTRAP=1 fi if ada_requested; then if ! have_gnat; then please_install gnat gcc-ada exit 1 fi fi fi # GCC export HOSTCFLAGS="-Os" if have_hostcflags_from_gmp; then set_hostcflags_from_gmp fi if [ "$USECCACHE" = 1 ]; then CC="ccache $CC" fi # Prepare target directory for building GCC # (dependencies must be in the PATH) mkdir -p "$DESTDIR$TARGETDIR/bin" mkdir -p "$DESTDIR$TARGETDIR/share" export PATH=$DESTDIR$TARGETDIR/bin:$PATH # Download, unpack, patch and build all packages printf "Downloading and verifying tarballs ...\n" mkdir -p tarballs for P in $PACKAGES; do download "$P" || exit "$?" verify_hash "$P" || exit "$?" done printf "Downloaded tarballs ... ${green}ok${NC}\n" if [ ${FETCH_ONLY} -eq 1 ]; then exit 0 fi printf "Unpacking and patching ...\n" for P in $PACKAGES; do unpack_and_patch $P || exit 1 done printf "Unpacked and patched ... ${green}ok${NC}\n" if [ -n "$BOOTSTRAPONLY" ]; then printf "Building bootstrap compiler only ...\n" for pkg in GMP MPFR MPC GCC; do build_for_host $pkg done exit 0 fi printf "Building packages ...\n" for package in $PACKAGES; do build $package done printf "Packages built ... ${green}ok${NC}\n" # Adding git information of current tree to target directory # for reproducibility PROGNAME=$(basename "$0") rm -f "$DESTDIR$TARGETDIR/share/$PROGNAME-*" cp "$PROGNAME" "$DESTDIR$TARGETDIR/share/$PROGNAME-$CROSSGCC_VERSION" # Adding edk2 tools template mkdir -p "$DESTDIR$TARGETDIR/share/edk2config" sed -e "s,@@PREFIX@@,$TARGETDIR,g" edk2tools.txt > "$DESTDIR$TARGETDIR/share/edk2config/tools_def.txt" printf "Copied EDK2 tools template ... ${green}ok${NC}\n" cleanup printf "\n${green}You can now run $NAME from $TARGETDIR.${NC}\n"