#!/usr/bin/env bash set -eo pipefail function extract() { echo "Extracting ${1}..." case $1 in *.tar.bz2) tar xjf "$1" ;; *.tar.xz) tar xJf "$1" ;; *.tar.gz) tar xzf "$1" ;; *) >&2 echo "don't know how to extract '$1'..." exit 1 esac } function unpack() { mkdir -p "${ARCHIVE_DIR}" cd "${ARCHIVE_DIR}" || exit 2 local -r URL=$1 local -r RELATIVE_DIR=$2 local -r DESTINATION="${ARCHIVE_DIR}/${RELATIVE_DIR}" if [[ ! -d "${DESTINATION}" ]] ; then echo "Downloading ${URL}..." local -r ARCHIVE_NAME=$(basename "${URL}") [[ -f "${ARCHIVE_NAME}" ]] || wget --no-verbose "${URL}" extract "${ARCHIVE_NAME}" rm -f "${ARCHIVE_NAME}" fi } function install_qemu() { if [[ "${QEMU_ARCH}" == "DISABLED" ]]; then >&2 echo 'QEMU is disabled !' return 0 fi local -r QEMU_VERSION=${QEMU_VERSION:=9.0.4} local -r QEMU_TARGET=${QEMU_ARCH}-linux-user if echo "${QEMU_VERSION} ${QEMU_TARGET}" | cmp --silent "${QEMU_INSTALL}/.build" -; then echo "qemu ${QEMU_VERSION} up to date!" return 0 fi echo "QEMU_VERSION: ${QEMU_VERSION}" echo "QEMU_TARGET: ${QEMU_TARGET}" rm -rf "${QEMU_INSTALL}" # Checking for a tarball before downloading makes testing easier :-) local -r QEMU_URL="https://download.qemu.org/qemu-${QEMU_VERSION}.tar.xz" local -r QEMU_DIR="qemu-${QEMU_VERSION}" unpack "${QEMU_URL}" "${QEMU_DIR}" cd "${QEMU_DIR}" || exit 2 # Qemu (meson based build) depends on: pkgconf, libglib2.0, python3, ninja ./configure \ --prefix="${QEMU_INSTALL}" \ --target-list="${QEMU_TARGET}" \ --audio-drv-list= \ --disable-brlapi \ --disable-curl \ --disable-curses \ --disable-docs \ --disable-gcrypt \ --disable-gnutls \ --disable-gtk \ --disable-libnfs \ --disable-libssh \ --disable-nettle \ --disable-opengl \ --disable-sdl \ --disable-virglrenderer \ --disable-vte # wrapper on ninja make -j8 make install echo "$QEMU_VERSION $QEMU_TARGET" > "${QEMU_INSTALL}/.build" } function assert_defined(){ if [[ -z "${!1}" ]]; then >&2 echo "Variable '${1}' must be defined" exit 1 fi } function clean_build() { # Cleanup previous build rm -rf "${BUILD_DIR}" mkdir -p "${BUILD_DIR}" } function expand_bootlin_config() { # ref: https://toolchains.bootlin.com/ local -r GCC_DIR=${ARCHIVE_DIR}/${GCC_RELATIVE_DIR} case "${TARGET}" in "arm" | "armv7-eabihf") local -r TOOLCHAIN_URL="https://toolchains.bootlin.com/downloads/releases/toolchains/armv7-eabihf/tarballs/armv7-eabihf--glibc--stable-2024.05-1.tar.xz" local -r GCC_PREFIX="arm" local -r GCC_SUFFIX="eabihf" ;; "armeb" | "armebv7-eabihf") local -r TOOLCHAIN_URL="https://toolchains.bootlin.com/downloads/releases/toolchains/armebv7-eabihf/tarballs/armebv7-eabihf--glibc--stable-2024.05-1.tar.xz" local -r GCC_PREFIX="armeb" local -r GCC_SUFFIX="eabihf" ;; "arm64" | "aarch64") local -r TOOLCHAIN_URL="https://toolchains.bootlin.com/downloads/releases/toolchains/aarch64/tarballs/aarch64--glibc--stable-2024.05-1.tar.xz" local -r GCC_PREFIX="aarch64" local -r GCC_SUFFIX="" ;; "arm64be" | "aarch64be") local -r TOOLCHAIN_URL="https://toolchains.bootlin.com/downloads/releases/toolchains/aarch64be/tarballs/aarch64be--glibc--stable-2024.05-1.tar.xz" local -r GCC_PREFIX="aarch64_be" local -r GCC_SUFFIX="" ;; "ppc" | "ppc-440fp") local -r TOOLCHAIN_URL="https://toolchains.bootlin.com/downloads/releases/toolchains/powerpc-440fp/tarballs/powerpc-440fp--glibc--stable-2024.05-1.tar.xz" local -r GCC_PREFIX="powerpc" local -r GCC_SUFFIX="" QEMU_ARGS+=( -cpu "440epx" ) ;; "ppc-e500mc") local -r TOOLCHAIN_URL="https://toolchains.bootlin.com/downloads/releases/toolchains/powerpc-e500mc/tarballs/powerpc-e500mc--glibc--stable-2024.05-1.tar.xz" local -r GCC_PREFIX="powerpc" local -r GCC_SUFFIX="" QEMU_ARGS+=( -cpu "e500mc" ) ;; "ppc64" | "ppc64-power8") local -r TOOLCHAIN_URL="https://toolchains.bootlin.com/downloads/releases/toolchains/powerpc64-power8/tarballs/powerpc64-power8--glibc--stable-2024.05-1.tar.xz" local -r GCC_PREFIX="powerpc64" local -r GCC_SUFFIX="" ;; "ppc64le" | "ppc64le-power8") local -r TOOLCHAIN_URL="https://toolchains.bootlin.com/downloads/releases/toolchains/powerpc64le-power8/tarballs/powerpc64le-power8--glibc--stable-2024.05-1.tar.xz" local -r GCC_PREFIX="powerpc64le" local -r GCC_SUFFIX="" ;; "riscv32") local -r TOOLCHAIN_URL="https://toolchains.bootlin.com/downloads/releases/toolchains/riscv32-ilp32d/tarballs/riscv32-ilp32d--glibc--bleeding-edge-2024.05-1.tar.xz" local -r GCC_PREFIX="riscv32" local -r GCC_SUFFIX="" ;; "riscv64") local -r TOOLCHAIN_URL="https://toolchains.bootlin.com/downloads/releases/toolchains/riscv64-lp64d/tarballs/riscv64-lp64d--glibc--stable-2024.05-1.tar.xz" local -r GCC_PREFIX="riscv64" local -r GCC_SUFFIX="" ;; "s390x") local -r TOOLCHAIN_URL="https://toolchains.bootlin.com/downloads/releases/toolchains/s390x-z13/tarballs/s390x-z13--glibc--stable-2024.05-1.tar.xz" local -r GCC_PREFIX="s390x" local -r GCC_SUFFIX="" ;; *) >&2 echo 'unknown power platform' exit 1 ;; esac local -r TOOLCHAIN_RELATIVE_DIR="${TARGET}" unpack "${TOOLCHAIN_URL}" "${TOOLCHAIN_RELATIVE_DIR}" local -r EXTRACT_DIR="${ARCHIVE_DIR}/$(basename ${TOOLCHAIN_URL%.tar.xz})" local -r TOOLCHAIN_DIR=${ARCHIVE_DIR}/${TOOLCHAIN_RELATIVE_DIR} if [[ -d "${EXTRACT_DIR}" ]]; then mv "${EXTRACT_DIR}" "${TOOLCHAIN_DIR}" fi local -r SYSROOT_DIR="${TOOLCHAIN_DIR}/${GCC_PREFIX}-buildroot-linux-gnu${GCC_SUFFIX}/sysroot" #local -r STAGING_DIR=${SYSROOT_DIR}-stage # Write a Toolchain file # note: This is manadatory to use a file in order to have the CMake variable # 'CMAKE_CROSSCOMPILING' set to TRUE. # ref: https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling-for-linux cat >"${TOOLCHAIN_FILE}" <&2 echo 'unknown mips platform' exit 1 ;; esac local -r SYSROOT_DIR=${GCC_DIR}/sysroot local -r STAGING_DIR=${SYSROOT_DIR}-stage # Write a Toolchain file # note: This is manadatory to use a file in order to have the CMake variable # 'CMAKE_CROSSCOMPILING' set to TRUE. # ref: https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling-for-linux cat >"${TOOLCHAIN_FILE}" <&2 echo "QEMU is disabled for ${TARGET}" return fi install_qemu RUN_CMD="${QEMU_INSTALL}/bin/qemu-${QEMU_ARCH} ${QEMU_ARGS[*]}" cd "${BUILD_DIR}" || exit 2 declare -a TEST_BINARIES=() TEST_BINARIES+=($(find "${BUILD_DIR}"/test -executable -type f)) TEST_BINARIES+=($(find "${BUILD_DIR}" -maxdepth 1 -executable -type f)) set -x set -e for test_binary in ${TEST_BINARIES[*]} ; do ${RUN_CMD} "${test_binary}" done set +e set +x } function usage() { local -r NAME=$(basename "$0") echo -e "$NAME - Build using a cross toolchain. SYNOPSIS \t$NAME [-h|--help] [toolchain|build|qemu|test|all] DESCRIPTION \tCross compile using a cross toolchain. \tYou MUST define the following variables before running this script: \t* TARGET: \t\tx86_64 \t\tarmv7-eabihf(arm) armebv7-eabihf(armeb) (bootlin) \t\taarch64(arm64) aarch64be(arm64be) (bootlin) \t\tmips32 mips32el (codespace) \t\tmips64 mips64el (codespace) \t\tppc-440fp(ppc) ppc-e500mc (bootlin) \t\tppc64 ppc64le (bootlin) \t\triscv32 riscv64 (bootlin) \t\ts390x (bootlin) OPTIONS \t-h --help: show this help text \ttoolchain: download, unpack toolchain and generate CMake toolchain file \tbuild: toolchain + build the project using the toolchain file (note: remove previous build dir) \tqemu: download, unpack and build qemu \ttest: qemu + run all executable using qemu (note: don't build !) \tall: build + test (default) EXAMPLES * Using export: export TARGET=aarch64-linux-gnu $0 * One-liner: TARGET=aarch64-linux-gnu $0" } # Main function main() { case ${1} in -h | --help) usage; exit ;; esac assert_defined TARGET declare -r PROJECT_DIR="$(cd -P -- "$(dirname -- "$0")/.." && pwd -P)" declare -r ARCHIVE_DIR="${PROJECT_DIR}/build_cross/archives" declare -r BUILD_DIR="${PROJECT_DIR}/build_cross/${TARGET}" declare -r TOOLCHAIN_FILE=${ARCHIVE_DIR}/toolchain_${TARGET}.cmake echo "Target: '${TARGET}'" echo "Project dir: '${PROJECT_DIR}'" echo "Archive dir: '${ARCHIVE_DIR}'" echo "Build dir: '${BUILD_DIR}'" echo "toolchain file: '${TOOLCHAIN_FILE}'" declare -a CMAKE_DEFAULT_ARGS=( -G ${CMAKE_GENERATOR:-"Ninja"} ) declare -a CMAKE_ADDITIONAL_ARGS=() declare -a QEMU_ARGS=() case ${TARGET} in x86_64) declare -r QEMU_ARCH=x86_64 ;; arm | armv7-eabihf) expand_bootlin_config declare -r QEMU_ARCH=arm ;; armeb | armebv7-eabihf) expand_bootlin_config declare -r QEMU_ARCH=DISABLED ;; arm64 | aarch64) expand_bootlin_config declare -r QEMU_ARCH=aarch64 ;; arm64be | aarch64be) expand_bootlin_config declare -r QEMU_ARCH=aarch64_be ;; mips32) expand_codescape_config declare -r QEMU_ARCH=mips ;; mips32el) expand_codescape_config declare -r QEMU_ARCH=mipsel ;; mips64) expand_codescape_config declare -r QEMU_ARCH=mips64 ;; mips64el) expand_codescape_config declare -r QEMU_ARCH=mips64el ;; ppc | ppc-440fp | ppc-e500mc ) expand_bootlin_config declare -r QEMU_ARCH=ppc ;; ppc64 | ppc64-power8) expand_bootlin_config declare -r QEMU_ARCH=ppc64 ;; ppc64le | ppc64le-power8) expand_bootlin_config declare -r QEMU_ARCH=ppc64le ;; riscv32) expand_bootlin_config declare -r QEMU_ARCH=riscv32 ;; riscv64) expand_bootlin_config declare -r QEMU_ARCH=riscv64 ;; s390x) expand_bootlin_config declare -r QEMU_ARCH=s390x ;; *) >&2 echo "Unknown TARGET '${TARGET}'..." exit 1 ;; esac declare -r QEMU_INSTALL=${ARCHIVE_DIR}/qemu-${QEMU_ARCH} case ${1} in toolchain) exit ;; build) build ;; qemu) install_qemu ;; test) run_test ;; *) build run_test ;; esac } main "${1:-all}"