• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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