1#!/usr/bin/env bash 2set -eo pipefail 3 4function extract() { 5 echo "Extracting ${1}..." 6 case $1 in 7 *.tar.bz2) tar xjf "$1" ;; 8 *.tar.xz) tar xJf "$1" ;; 9 *.tar.gz) tar xzf "$1" ;; 10 *) 11 >&2 echo "don't know how to extract '$1'..." 12 exit 1 13 esac 14} 15 16function unpack() { 17 mkdir -p "${ARCHIVE_DIR}" 18 cd "${ARCHIVE_DIR}" || exit 2 19 local -r URL=$1 20 local -r RELATIVE_DIR=$2 21 local -r DESTINATION="${ARCHIVE_DIR}/${RELATIVE_DIR}" 22 if [[ ! -d "${DESTINATION}" ]] ; then 23 echo "Downloading ${URL}..." 24 local -r ARCHIVE_NAME=$(basename "${URL}") 25 test -f "${ARCHIVE_NAME}" || wget --no-verbose "${URL}" 26 extract "${ARCHIVE_NAME}" 27 rm -f "${ARCHIVE_NAME}" 28 fi 29} 30 31function install_qemu() { 32 if [[ "${QEMU_ARCH}" == "DISABLED" ]]; then 33 >&2 echo 'QEMU is disabled !' 34 return 0 35 fi 36 local -r QEMU_VERSION=${QEMU_VERSION:=5.2.0} 37 local -r QEMU_TARGET=${QEMU_ARCH}-linux-user 38 39 if echo "${QEMU_VERSION} ${QEMU_TARGET}" | cmp --silent "${QEMU_INSTALL}/.build" -; then 40 echo "qemu ${QEMU_VERSION} up to date!" 41 return 0 42 fi 43 44 echo "QEMU_VERSION: ${QEMU_VERSION}" 45 echo "QEMU_TARGET: ${QEMU_TARGET}" 46 47 rm -rf "${QEMU_INSTALL}" 48 49 # Checking for a tarball before downloading makes testing easier :-) 50 local -r QEMU_URL="http://wiki.qemu-project.org/download/qemu-${QEMU_VERSION}.tar.xz" 51 local -r QEMU_DIR="qemu-${QEMU_VERSION}" 52 unpack ${QEMU_URL} ${QEMU_DIR} 53 cd ${QEMU_DIR} || exit 2 54 55 # Qemu (meson based build) depends on: pkgconf, libglib2.0, python3, ninja 56 ./configure \ 57 --prefix="${QEMU_INSTALL}" \ 58 --target-list="${QEMU_TARGET}" \ 59 --audio-drv-list= \ 60 --disable-brlapi \ 61 --disable-curl \ 62 --disable-curses \ 63 --disable-docs \ 64 --disable-gcrypt \ 65 --disable-gnutls \ 66 --disable-gtk \ 67 --disable-libnfs \ 68 --disable-libssh \ 69 --disable-nettle \ 70 --disable-opengl \ 71 --disable-sdl \ 72 --disable-virglrenderer \ 73 --disable-vte \ 74 --enable-modules 75 76 # --static Not supported on Archlinux 77 # so we use --enable-modules 78 79 # wrapper on ninja 80 make -j8 81 make install 82 83 echo "$QEMU_VERSION $QEMU_TARGET" > "${QEMU_INSTALL}/.build" 84} 85 86function assert_defined(){ 87 if [[ -z "${!1}" ]]; then 88 >&2 echo "Variable '${1}' must be defined" 89 exit 1 90 fi 91} 92 93function clean_build() { 94 # Cleanup previous build 95 rm -rf "${BUILD_DIR}" 96 mkdir -p "${BUILD_DIR}" 97} 98 99function expand_linaro_config() { 100 #ref: https://releases.linaro.org/components/toolchain/binaries/ 101 local -r LINARO_VERSION=7.5-2019.12 102 local -r LINARO_ROOT_URL=https://releases.linaro.org/components/toolchain/binaries/${LINARO_VERSION} 103 104 local -r GCC_VERSION=7.5.0-2019.12 105 local -r GCC_URL=${LINARO_ROOT_URL}/${TARGET}/gcc-linaro-${GCC_VERSION}-x86_64_${TARGET}.tar.xz 106 local -r GCC_RELATIVE_DIR="gcc-linaro-${GCC_VERSION}-x86_64_${TARGET}" 107 unpack "${GCC_URL}" "${GCC_RELATIVE_DIR}" 108 109 local -r SYSROOT_VERSION=2.25-2019.12 110 local -r SYSROOT_URL=${LINARO_ROOT_URL}/${TARGET}/sysroot-glibc-linaro-${SYSROOT_VERSION}-${TARGET}.tar.xz 111 local -r SYSROOT_RELATIVE_DIR=sysroot-glibc-linaro-${SYSROOT_VERSION}-${TARGET} 112 unpack "${SYSROOT_URL}" "${SYSROOT_RELATIVE_DIR}" 113 114 local -r SYSROOT_DIR=${ARCHIVE_DIR}/${SYSROOT_RELATIVE_DIR} 115 local -r STAGING_DIR=${ARCHIVE_DIR}/${SYSROOT_RELATIVE_DIR}-stage 116 local -r GCC_DIR=${ARCHIVE_DIR}/${GCC_RELATIVE_DIR} 117 118 # Write a Toolchain file 119 # note: This is manadatory to use a file in order to have the CMake variable 120 # 'CMAKE_CROSSCOMPILING' set to TRUE. 121 # ref: https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling-for-linux 122 cat >"$TOOLCHAIN_FILE" <<EOL 123set(CMAKE_SYSTEM_NAME Linux) 124set(CMAKE_SYSTEM_PROCESSOR ${TARGET}) 125 126set(CMAKE_SYSROOT ${SYSROOT_DIR}) 127set(CMAKE_STAGING_PREFIX ${STAGING_DIR}) 128 129set(tools ${GCC_DIR}) 130set(CMAKE_C_COMPILER \${tools}/bin/${TARGET}-gcc) 131set(CMAKE_CXX_COMPILER \${tools}/bin/${TARGET}-g++) 132 133set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 134set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 135set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 136set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) 137EOL 138CMAKE_ADDITIONAL_ARGS+=( -DCMAKE_TOOLCHAIN_FILE="${TOOLCHAIN_FILE}" ) 139QEMU_ARGS+=( -L "${SYSROOT_DIR}" ) 140QEMU_ARGS+=( -E LD_LIBRARY_PATH=/lib ) 141} 142 143function expand_codescape_config() { 144 # ref: https://codescape.mips.com/components/toolchain/2020.06-01/downloads.html 145 # ref: https://codescape.mips.com/components/toolchain/2019.02-04/downloads.html 146 local -r DATE=2020.06-01 147 #local -r DATE=2019.02-04 148 local -r CODESCAPE_URL=https://codescape.mips.com/components/toolchain/${DATE}/Codescape.GNU.Tools.Package.${DATE}.for.MIPS.MTI.Linux.CentOS-6.x86_64.tar.gz 149 #local -r CODESCAPE_URL=https://codescape.mips.com/components/toolchain/${DATE}/Codescape.GNU.Tools.Package.${DATE}.for.MIPS.IMG.Linux.CentOS-6.x86_64.tar.gz 150 local -r GCC_URL=${CODESCAPE_URL} 151 local -r GCC_RELATIVE_DIR="mips-mti-linux-gnu/${DATE}" 152 #local -r GCC_RELATIVE_DIR="mips-img-linux-gnu/${DATE}" 153 unpack "${GCC_URL}" "${GCC_RELATIVE_DIR}" 154 155 local -r GCC_DIR=${ARCHIVE_DIR}/${GCC_RELATIVE_DIR} 156 local MIPS_FLAGS="" 157 local LIBC_DIR_SUFFIX="" 158 local FLAVOUR="" 159 case "${TARGET}" in 160 "mips32") 161 MIPS_FLAGS="-EB -mips32r6 -mabi=32" 162 FLAVOUR="mips-r6-hard" 163 #MIPS_FLAGS="-EB -mips32r2 -mabi=32" 164 #FLAVOUR="mips-r2-hard" 165 LIBC_DIR_SUFFIX="lib" 166 ;; 167 "mips32el") 168 MIPS_FLAGS="-EL -mips32r6 -mabi=32" 169 FLAVOUR="mipsel-r6-hard" 170 #MIPS_FLAGS="-EL -mips32r2 -mabi=32" 171 #FLAVOUR="mipsel-r2-hard" 172 LIBC_DIR_SUFFIX="lib" 173 ;; 174 "mips64") 175 MIPS_FLAGS="-EB -mips64r6 -mabi=64" 176 FLAVOUR="mips-r6-hard" 177 #FLAVOUR="mips-r2-hard" 178 LIBC_DIR_SUFFIX="lib64" 179 ;; 180 "mips64el") 181 MIPS_FLAGS="-EL -mips64r6 -mabi=64" 182 FLAVOUR="mipsel-r6-hard" 183 #FLAVOUR="mipsel-r2-hard" 184 LIBC_DIR_SUFFIX="lib64" 185 ;; 186 *) 187 >&2 echo 'unknown mips platform' 188 exit 1 ;; 189 esac 190 local -r SYSROOT_DIR=${GCC_DIR}/sysroot 191 local -r STAGING_DIR=${SYSROOT_DIR}-stage 192 193 # Write a Toolchain file 194 # note: This is manadatory to use a file in order to have the CMake variable 195 # 'CMAKE_CROSSCOMPILING' set to TRUE. 196 # ref: https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling-for-linux 197 cat >"${TOOLCHAIN_FILE}" <<EOL 198set(CMAKE_SYSTEM_NAME Linux) 199set(CMAKE_SYSTEM_PROCESSOR ${TARGET}) 200 201set(CMAKE_SYSROOT ${SYSROOT_DIR}) 202set(CMAKE_STAGING_PREFIX ${STAGING_DIR}) 203 204set(tools ${GCC_DIR}) 205 206set(CMAKE_C_COMPILER \${tools}/bin/mips-mti-linux-gnu-gcc) 207#set(CMAKE_C_COMPILER \${tools}/bin/mips-img-linux-gnu-gcc) 208set(CMAKE_C_FLAGS "${MIPS_FLAGS}") 209 210set(CMAKE_CXX_COMPILER \${tools}/bin/mips-mti-linux-gnu-g++) 211#set(CMAKE_CXX_COMPILER \${tools}/bin/mips-img-linux-gnu-g++) 212set(CMAKE_CXX_FLAGS "${MIPS_FLAGS}") 213 214set(CMAKE_FIND_ROOT_PATH ${GCC_DIR}) 215set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 216set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 217set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 218set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) 219EOL 220 221CMAKE_ADDITIONAL_ARGS+=( -DCMAKE_TOOLCHAIN_FILE="${TOOLCHAIN_FILE}" ) 222QEMU_ARGS+=( -L "${SYSROOT_DIR}/${FLAVOUR}" ) 223local -r LIBC_DIR=${GCC_DIR}/mips-mti-linux-gnu/lib/${FLAVOUR}/${LIBC_DIR_SUFFIX} 224#local -r LIBC_DIR=${GCC_DIR}/mips-img-linux-gnu/lib/${FLAVOUR}/${LIBC_DIR_SUFFIX} 225QEMU_ARGS+=( -E LD_PRELOAD="${LIBC_DIR}/libstdc++.so.6:${LIBC_DIR}/libgcc_s.so.1" ) 226} 227 228function build() { 229 cd "${PROJECT_DIR}" || exit 2 230 set -x 231 clean_build 232 cmake -S. -B"${BUILD_DIR}" "${CMAKE_DEFAULT_ARGS[@]}" "${CMAKE_ADDITIONAL_ARGS[@]}" 233 cmake --build "${BUILD_DIR}" --target all -j8 -v 234 set +x 235} 236 237function run_test() { 238 assert_defined QEMU_ARCH 239 if [[ "${QEMU_ARCH}" == "DISABLED" ]]; then 240 >&2 echo "QEMU is disabled for ${TARGET}" 241 return 242 fi 243 install_qemu 244 RUN_CMD="${QEMU_INSTALL}/bin/qemu-${QEMU_ARCH} ${QEMU_ARGS[*]}" 245 246 cd "${BUILD_DIR}" || exit 2 247 set -x 248 for test_binary in "${BUILD_DIR}"/list_cpu_feature* ; do 249 ${RUN_CMD} "${test_binary}" 250 done 251 set +x 252} 253 254function usage() { 255 local -r NAME=$(basename "$0") 256 echo -e "$NAME - Build using a cross toolchain. 257 258SYNOPSIS 259\t$NAME [-h|--help] [toolchain|build|qemu|test|all] 260 261DESCRIPTION 262\tCross compile using a cross toolchain. 263 264\tYou MUST define the following variables before running this script: 265\t* TARGET: 266\t\tx86_64 267\t\taarch64-linux-gnu aarch64_be-linux-gnu 268\t\tarm-linux-gnueabihf armv8l-linux-gnueabihf arm-linux-gnueabi 269\t\tarmeb-linux-gnueabihf armeb-linux-gnueabi 270\t\tmips32 mips32el 271\t\tmips64 mips64el 272 273OPTIONS 274\t-h --help: show this help text 275\ttoolchain: download, unpack toolchain and generate CMake toolchain file 276\tbuild: toolchain + build the project using the toolchain file (note: remove previous build dir) 277\tqemu: download, unpack and build qemu 278\ttest: qemu + run all executable using qemu (note: don't build !) 279\tall: build + test (default) 280 281EXAMPLES 282* Using export: 283export TARGET=aarch64-linux-gnu 284$0 285 286* One-liner: 287TARGET=aarch64-linux-gnu $0" 288} 289 290# Main 291function main() { 292 case ${1} in 293 -h | --help) 294 usage; exit ;; 295 esac 296 297 assert_defined TARGET 298 299 declare -r PROJECT_DIR="$(cd -P -- "$(dirname -- "$0")/.." && pwd -P)" 300 declare -r ARCHIVE_DIR="${PROJECT_DIR}/build_cross/archives" 301 declare -r BUILD_DIR="${PROJECT_DIR}/build_cross/${TARGET}" 302 declare -r TOOLCHAIN_FILE=${ARCHIVE_DIR}/toolchain_${TARGET}.cmake 303 304 echo "Target: '${TARGET}'" 305 306 echo "Project dir: '${PROJECT_DIR}'" 307 echo "Archive dir: '${ARCHIVE_DIR}'" 308 echo "Build dir: '${BUILD_DIR}'" 309 echo "toolchain file: '${TOOLCHAIN_FILE}'" 310 311 declare -a CMAKE_DEFAULT_ARGS=( -G ${CMAKE_GENERATOR:-"Ninja"} ) 312 declare -a CMAKE_ADDITIONAL_ARGS=() 313 314 declare -a QEMU_ARGS=() 315 case ${TARGET} in 316 x86_64) 317 declare -r QEMU_ARCH=x86_64 ;; 318 arm-linux-gnueabihf | armv8l-linux-gnueabihf | arm-linux-gnueabi) 319 expand_linaro_config 320 declare -r QEMU_ARCH=arm ;; 321 armeb-linux-gnueabihf | armeb-linux-gnueabi) 322 expand_linaro_config 323 declare -r QEMU_ARCH=DISABLED ;; 324 aarch64-linux-gnu) 325 expand_linaro_config 326 declare -r QEMU_ARCH=aarch64 ;; 327 aarch64_be-linux-gnu) 328 expand_linaro_config 329 declare -r QEMU_ARCH=DISABLED ;; 330 mips32) 331 expand_codescape_config 332 declare -r QEMU_ARCH=mips ;; 333 mips32el) 334 expand_codescape_config 335 declare -r QEMU_ARCH=mipsel ;; 336 mips64) 337 expand_codescape_config 338 declare -r QEMU_ARCH=mips64 ;; 339 mips64el) 340 expand_codescape_config 341 declare -r QEMU_ARCH=mips64el ;; 342 *) 343 >&2 echo "Unknown TARGET '${TARGET}'..." 344 exit 1 ;; 345 esac 346 declare -r QEMU_INSTALL=${ARCHIVE_DIR}/qemu-${QEMU_ARCH} 347 348 case ${1} in 349 toolchain) 350 exit ;; 351 build) 352 build ;; 353 qemu) 354 install_qemu ;; 355 test) 356 run_test ;; 357 *) 358 build 359 run_test ;; 360 esac 361} 362 363main "${1:-all}" 364