Implement cross-architectural and native testing on Gitlab

This change implements, on gitlab.com/racket/racket CI pipelines,
cross architectural and native testing using virtualized hardware
using user mode qemu.

It also fixes use of caches/artifacts to deal with passing of the qemu
build and llvm build through stages.

Testing added for Racket:
Native: armv7l (running on rpi3), x86_64
Emulated: arm64, armel, armhf, i386, mips, mips64el, mipsel, ppc64el,
s390x

Testing added for Racket CS:
Native: x86_64
Emulation: i386
This commit is contained in:
Paulo Matos 2019-04-02 09:17:42 +02:00
commit 35d269c29e
2 changed files with 428 additions and 20 deletions

View File

@ -2,6 +2,7 @@ stages:
- prepare
- test
# ---------------------------------------------------------------------------------------------------
# Using debian:stable-slim to perform an llvm build with Z3 support
# The reason this is necessary is that we need Z3 support in scan-build
# to be able to crosscheck errors with z3 which decreases the false positives
@ -9,12 +10,13 @@ stages:
#
# Also, this should only be performed once. Once in cache llvm won't be build anymore
# until we force it.
prepare-cache:
prepare-cache:llvm:
image: debian:stable-slim
stage: prepare
tags:
- linux
- x86_64
- shared-cache
variables:
INSTALL_DIR: $CI_PROJECT_DIR/install
script:
@ -25,16 +27,22 @@ prepare-cache:
- mkdir $INSTALL_DIR
- mv z3-4.8.4.d6df51951f4c-x64-debian-8.11/bin z3-4.8.4.d6df51951f4c-x64-debian-8.11/include $INSTALL_DIR
- export PATH=$INSTALL_DIR/bin:$PATH
- git clone https://github.com/llvm/llvm-project.git
- git clone -b release/8.x https://github.com/llvm/llvm-project.git
- cd llvm-project
- mkdir build
- cd build
- cmake -DCMAKE_INSTALL_PREFIX=$INSTALL_DIR -DCLANG_ANALYZER_ENABLE_Z3_SOLVER=ON -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_ENABLE_PROJECTS=clang -DZ3_INCLUDE_DIR=$INSTALL_DIR/include/ ../llvm/
- cmake -DCMAKE_INSTALL_PREFIX=$INSTALL_DIR -DCLANG_ANALYZER_ENABLE_Z3_SOLVER=ON -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_ENABLE_PROJECTS=clang -DZ3_INCLUDE_DIR=$INSTALL_DIR/include/ -DCMAKE_BUILD_TYPE=MinSizeRel ../llvm/
- make -j5
- make -j5 install
cache:
key: llvm-8x-HEAD
paths:
- $INSTALL_DIR
artifacts:
name: "llvm-8x-HEAD"
paths:
- $INSTALL_DIR
expire_in: 1 week
# Build racket with scan-build
scan-build:racket:
@ -42,6 +50,7 @@ scan-build:racket:
tags:
- linux
- x86_64
- shared-cache
variables:
INSTALL_DIR: $CI_PROJECT_DIR/install
before_script:
@ -51,13 +60,11 @@ scan-build:racket:
- export PATH=$INSTALL_DIR/bin:$PATH
- export LD_LIBRARY_PATH=$INSTALL_DIR/bin:$LD_LIBRARY_PATH
- scan-build -o scan-report_cc -analyzer-config 'crosscheck-with-z3=true' make PKGS="" CPUS=5 CONFIGURE_ARGS_qq='CFLAGS="-O0 -g -DMZ_DECLARE_NORETURN" --disable-strip'
dependencies:
- prepare-cache:llvm
artifacts:
paths:
- scan-report_cc/
cache:
policy: pull
paths:
- $INSTALL_DIR
# Build racketcs with scan-build
scan-build:racketcs:
@ -65,6 +72,7 @@ scan-build:racketcs:
tags:
- linux
- x86_64
- shared-cache
variables:
INSTALL_DIR: $CI_PROJECT_DIR/install
before_script:
@ -74,14 +82,13 @@ scan-build:racketcs:
- export PATH=$INSTALL_DIR/bin:$PATH
- export LD_LIBRARY_PATH=$INSTALL_DIR/bin:$LD_LIBRARY_PATH
- scan-build -o scan-report-cs_cc -analyzer-config 'crosscheck-with-z3=true' make PKGS="" CPUS=5 CONFIGURE_ARGS_qq='CFLAGS="-O0 -g -DMZ_DECLARE_NORETURN" --disable-strip' cs
dependencies:
- prepare-cache:llvm
artifacts:
paths:
- scan-report-cs_cc/
cache:
policy: pull
paths:
- $INSTALL_DIR
# ---------------------------------------------------------------------------------------------------
##
## The following jobs build/test racket and racketcs with ubsan enabled
##
@ -94,15 +101,6 @@ scan-build:racketcs:
- update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 800 --slave /usr/bin/g++ g++ /usr/bin/g++-8
- export PATH=$PWD/racket/bin:$PATH
# This is just used to provide some environment information for debugging purposes
envinfo:
extends: .prepare
script:
- cat /proc/cpuinfo
- lsb_release -a
- gcc -v
- export
test:ubsan:
extends: .prepare
script:
@ -161,3 +159,175 @@ test:ubsan:cs:
paths:
- cs-logs/
- runtime-errors.log
# ---------------------------------------------------------------------------------------------------
# Runs a cross compilation and testing using qemu chrooted into the correct architecture
# Each of the test:<arch>: will extend .preparearch which run the proper script
# Currently testing:
# * arm64
# * armel
# * armhf
# * i386
# * mips
# * mipsel
# * mips64el
# * s390x
# * ppc64el
prepare-cache:qemu:
image: ubuntu:18.04
stage: prepare
tags:
- linux
- x86_64
- privileged
variables:
INSTALL_DIR: $CI_PROJECT_DIR/install
script:
- if [ -d $INSTALL_DIR ]; then exit 0; fi
- apt-get update && apt-get install -y gcc make bison flex git wget xz-utils python pkg-config libglib2.0-dev libpixman-1-dev
- wget https://download.qemu.org/qemu-3.1.0.tar.xz
- tar -xvJf qemu-3.1.0.tar.xz
- mkdir qemu-build
- cd qemu-build
- ../qemu-3.1.0/configure --static --disable-kvm --disable-xen --disable-spice --target-list='i386-linux-user aarch64-linux-user arm-linux-user mips-linux-user mipsel-linux-user mips64el-linux-user s390x-linux-user ppc64le-linux-user riscv64-linux-user' --prefix=$INSTALL_DIR
- make -j5
- make -j5 install
cache:
key: qemu-3.1.0
paths:
- $INSTALL_DIR
artifacts:
name: "qemu-3.1.0"
paths:
- $INSTALL_DIR
expire_in: 1 week
.preparearch:
image: ubuntu:18.04
stage: test
tags:
- x86_64
- privileged
- linux
before_script:
- ls -la
- find $INSTALL_DIR -type f
- export PATH=$INSTALL_DIR/bin:$PATH
- apt-get update
script:
- .gitlab/build-test.sh --jobs ${JOBS} --with-arch ${ARCH} --with-debian stretch --with-debian-mirror http://ftp.de.debian.org/debian/ --with-project-path ${CI_PROJECT_DIR} --with-chroot-path /tmp/racket-${ARCH}-${CI_COMMIT_SHORT_SHA}-chroot --with-qemu-path $INSTALL_DIR
dependencies:
- prepare-cache:qemu
test:native:x86_64:
extends: .preparearch
variables:
ARCH: "x86_64"
JOBS: 6
INSTALL_DIR: $CI_PROJECT_DIR/install
test:native:armv7l:
extends: .preparearch
variables:
ARCH: "armv7l"
JOBS: 5
INSTALL_DIR: $CI_PROJECT_DIR/install
tags:
- armv7l
- linux
test:emulation:arm64:
extends: .preparearch
variables:
ARCH: "arm64"
JOBS: 6
INSTALL_DIR: $CI_PROJECT_DIR/install
test:emulation:armel:
extends: .preparearch
variables:
ARCH: "armel"
JOBS: 6
INSTALL_DIR: $CI_PROJECT_DIR/install
test:emulation:armhf:
extends: .preparearch
variables:
ARCH: "armhf"
JOBS: 6
INSTALL_DIR: $CI_PROJECT_DIR/install
test:emulation:i386:
extends: .preparearch
variables:
ARCH: "i386"
JOBS: 6
INSTALL_DIR: $CI_PROJECT_DIR/install
test:emulation:mips:
extends: .preparearch
variables:
ARCH: "mips"
JOBS: 6
INSTALL_DIR: $CI_PROJECT_DIR/install
test:emulation:mipsel:
extends: .preparearch
variables:
ARCH: "mipsel"
JOBS: 6
INSTALL_DIR: $CI_PROJECT_DIR/install
test:emulation:mips64el:
extends: .preparearch
variables:
ARCH: "mips64el"
JOBS: 6
INSTALL_DIR: $CI_PROJECT_DIR/install
test:emulation:s390x:
extends: .preparearch
variables:
ARCH: "s390x"
JOBS: 6
INSTALL_DIR: $CI_PROJECT_DIR/install
test:emulation:ppc64el:
extends: .preparearch
variables:
ARCH: "ppc64el"
JOBS: 6
INSTALL_DIR: $CI_PROJECT_DIR/install
.preparearch:cs:
image: ubuntu:18.04
stage: test
tags:
- x86_64
- privileged
- linux
before_script:
- export PATH=$INSTALL_DIR:$PATH
- apt-get update
script:
- .gitlab/build-test.sh --jobs ${JOBS} --with-arch ${ARCH} --with-debian stretch --with-debian-mirror http://ftp.de.debian.org/debian/ --with-project-path ${CI_PROJECT_DIR} --with-chroot-path /tmp/racket-${ARCH}-${CI_COMMIT_SHORT_SHA}-chroot --enable-cs --with-qemu-path $INSTALL_DIR
dependencies:
- prepare-cache:qemu
test:native:x86_64:cs:
extends: .preparearch:cs
allow_failure: true
variables:
ARCH: "x86_64"
JOBS: 6
INSTALL_DIR: $CI_PROJECT_DIR/install
test:native:i386:cs:
extends: .preparearch:cs
variables:
ARCH: "i386"
JOBS: 6
INSTALL_DIR: $CI_PROJECT_DIR/install

238
.gitlab/build-test.sh Executable file
View File

@ -0,0 +1,238 @@
#! /bin/bash
# This script shows no error on shellcheck:
# https://github.com/koalaman/shellcheck
set -e
# ---------------------------------------------------------------------------------------------------
# Script called by jobs in .gitlab-ci.yml to build and test racket,
# possibly in a cross environment.
last_command=
current_command=
# keep track of the last executed command
trap 'last_command=$current_command; current_command=$BASH_COMMAND' DEBUG
# echo an error message before exiting
trap 'echo "\"${last_command}\" command failed with exit code $?."' EXIT
# ---------------------------------------------------------------------------------------------------
function usage () {
MSG=$1
echo "${MSG}"
echo
echo "Usage: ./build-test.sh [--jobs <count>]"
echo " [--single-thread]"
echo " [--with-arch <arch>]"
echo " [--with-debian <debian>]"
echo " [--with-debian-mirror <debian-mirror>]"
echo " [--with-configure-args <configure-args>]"
echo " [--enable-cs]"
echo " [--with-project-path <project-path>]"
echo " [--with-chroot-path <chroot-path>]"
echo " [--with-qemu-path <qemu-path>]"
exit 1
}
DEBIAN=
DEBIAN_MIRROR=
JOBS=
RACKET_CONFIGURE_ARGS=
ARCH="$(uname -m)"
BUILD_DIR=${CI_PROJECT_DIR}
MAKE_TARGET="in-place"
CHROOT_DIR="/tmp/racket-chroot"
QEMU_PATH=
# Parse options
until
opt=$1
case ${opt} in
--jobs)
shift
JOBS=$1
;;
--single-thread)
JOBS=1
;;
--with-arch)
shift
ARCH=$1
;;
--with-debian)
shift
DEBIAN=$1
;;
--with-debian-mirror)
shift
DEBIAN_MIRROR=$1
;;
--with-configure-args)
shift
RACKET_CONFIGURE_ARGS=$1
;;
--enable-cs)
MAKE_TARGET="cs"
;;
--with-project-path)
shift
BUILD_DIR=$1
;;
--with-chroot-path)
shift
CHROOT_DIR=$1
;;
--with-qemu-path)
shift
QEMU_PATH=$1
;;
?*)
usage "Unknown argument $1"
;;
*)
;;
esac
[ "x${opt}" = "x" ]
do
shift
done
set -eu
# ---------------------------------------------------------------------------------------------------
# Set QEMU ARCH which depends on ARCH
if [ ! -e "/.chroot_is_done" ]
then
QEMU_ARCH=
case ${ARCH} in
"amd64")
QEMU_ARCH="x86_64"
;;
"arm64")
QEMU_ARCH="aarch64"
;;
"armel"|"armhf"|"armv7l")
QEMU_ARCH="arm"
;;
"i386"|"mips"|"mipsel"|"mips64el"|"s390x"|"x86_64")
QEMU_ARCH=${ARCH}
;;
"ppc64el")
QEMU_ARCH="ppc64le"
;;
*)
echo "Unknown architecture ${ARCH}"
echo "Available archs: amd64, arm64, armel, armhf, armv7l, i386, mips, mipsel, mips64el, s390x, ppc64el"
echo "These are the official names for the debian ports available listed at:"
echo "https://www.debian.org/ports/"
echo "NOTE: we also accept x86_64 as an alias for amd64"
exit 1
;;
esac
fi
# ---------------------------------------------------------------------------------------------------
# Packages to install on the HOST
HOST_DEPENDENCIES="debootstrap binfmt-support sbuild rsync"
# Packages to install on the GUEST
GUEST_DEPENDENCIES="devscripts build-essential git m4 sudo python libfontconfig1-dev make gcc libpango1.0-dev libcairo2-dev openssl emacs25-nox libturbojpeg0-dev uuid-dev"
function setup_chroot {
# Host dependencies
echo "Installing host dependencies"
apt-get install -y ${HOST_DEPENDENCIES}
# Create chrooted environment
echo "Creating chroot environment"
mkdir "${CHROOT_DIR}"
debootstrap --foreign --no-check-gpg --include=fakeroot,build-essential \
--arch="${ARCH}" "${DEBIAN}" "${CHROOT_DIR}" "${DEBIAN_MIRROR}"
cp ${QEMU_PATH}/bin/qemu-${QEMU_ARCH} "${CHROOT_DIR}"/usr/bin/qemu-${QEMU_ARCH}-static
chroot "${CHROOT_DIR}" ./debootstrap/debootstrap --second-stage
sbuild-createchroot --arch="${ARCH}" --foreign --setup-only \
"${DEBIAN}" "${CHROOT_DIR}" "${DEBIAN_MIRROR}"
# Install dependencies inside chroot
echo "Installing guest dependencies"
chroot "${CHROOT_DIR}" apt-get update
chroot "${CHROOT_DIR}" apt-get --allow-unauthenticated install \
-y ${GUEST_DEPENDENCIES}
# Create build dir and copy travis build files to our chroot environment
echo "Copying into chroot: ${BUILD_DIR}/ -> ${CHROOT_DIR}/${BUILD_DIR}/"
mkdir -p "${CHROOT_DIR}"/"${BUILD_DIR}"
rsync -av "${BUILD_DIR}"/ "${CHROOT_DIR}"/"${BUILD_DIR}"/
# Indicate chroot environment has been set up
touch "${CHROOT_DIR}"/.chroot_is_done
# Call ourselves again which will cause tests to run
echo "Recursively calling script"
if [ ${MAKE_TARGET} = "cs" ]; then
chroot "${CHROOT_DIR}" bash -c "cd ${BUILD_DIR} && ./.gitlab/build-test.sh --jobs ${JOBS} --with-arch ${ARCH} --with-project-path ${BUILD_DIR} --enable-cs"
else
chroot "${CHROOT_DIR}" bash -c "cd ${BUILD_DIR} && ./.gitlab/build-test.sh --jobs ${JOBS} --with-arch ${ARCH} --with-project-path ${BUILD_DIR}"
fi
}
# Information about environment
echo "Environment information"
echo "======================="
echo " Machine : $(uname -m)"
echo " Jobs : ${JOBS}"
echo " Target Arch : ${ARCH}"
echo " chroot Path : ${CHROOT_DIR}"
echo " Build Path : ${BUILD_DIR}"
echo " Make Target : ${MAKE_TARGET}"
echo " Debian : ${DEBIAN}"
echo " Debian Mirror : ${DEBIAN_MIRROR}"
echo "Racket Configure Args : ${RACKET_CONFIGURE_ARGS}"
if [ ! -e "/.chroot_is_done" ]; then
echo " QEMU Arch : ${QEMU_ARCH}"
fi
if [ ! -e "/.chroot_is_done" ]; then
if [ "${ARCH}" != "$(uname -m)" ]; then
# test run, need to set up chrooted environment first
echo "Setting up chrooted ${ARCH} environment"
setup_chroot
else # We are compiling and running tests natively
apt-get install -y ${GUEST_DEPENDENCIES}
fi
fi
echo "Compiling"
echo "Environment: $(uname -a)"
annotate-output make CPUS=${JOBS} \
PKGS="racket-test db-test unstable-flonum-lib net-test" \
CONFIGURE_ARGS_qq="${RACKET_CONFIGURE_ARGS}" \
${MAKE_TARGET}
echo "Running tests"
echo "Environment: $(uname -a)"
export PATH=${BUILD_DIR}/racket/bin:$PATH
command -v racket
racket -v
annotate-output raco test -l tests/racket/test
annotate-output racket -l tests/racket/contract/all
annotate-output raco test -l tests/json/json
annotate-output raco test -l tests/file/main
annotate-output raco test -l tests/net/head
annotate-output raco test -l tests/net/uri-codec
annotate-output raco test -l tests/net/url
annotate-output raco test -l tests/net/url-port
annotate-output raco test -l tests/net/encoders
annotate-output raco test -l tests/openssl/basic
annotate-output raco test -l tests/openssl/https
annotate-output raco test -l tests/match/main
annotate-output raco test -l tests/zo-path
annotate-output raco test -l tests/xml/test
annotate-output raco test -l tests/db/all-tests
annotate-output raco test -c tests/stxparse
echo "DONE!"