#!/bin/sh : ${TEST_BOARDS_AVAILABLE:="samr21-xpro"} : ${TEST_BOARDS_LLVM_COMPILE:="iotlab-m3 native nrf52dk mulle nucleo-f401re samr21-xpro slstk3402a"} export RIOT_CI_BUILD=1 export STATIC_TESTS=${STATIC_TESTS:-1} export CFLAGS_DBG="" export DLCACHE_DIR=${DLCACHE_DIR:-~/.dlcache} NIGHTLY=${NIGHTLY:-0} RUN_TESTS=${RUN_TESTS:-${NIGHTLY}} DWQ_ENV="-E BOARDS -E APPS -E NIGHTLY -E RUN_TESTS" check_label() { local label="${1}" [ -z "${CI_PULL_LABELS}" ] && return 1 echo "${CI_PULL_LABELS}" | grep -q "${label}" return $? } [ "$RUN_TESTS" != "1" ] && { check_label "CI: run tests" && RUN_TESTS=1 } error() { echo "$@" exit 1 } # if MURDOCK_HOOK is set, this function will execute it and pass on all it's # parameters. should the hook script exit with negative exit code, hook() makes # this script exit with error, too. # hook() will be called from different locations of this script. # currently, the only caller is "run_test", which calls "hook run_test_pre". # More hooks will be added as needed. hook() { if [ -n "${MURDOCK_HOOK}" ]; then echo "- executing hook $1" "${MURDOCK_HOOK}" "$@" || { error "$0: hook \"${MURDOCK_HOOK} $@\" failed!" } echo "- hook $1 finished" fi } # true if word "$1" is in list of words "$2", false otherwise # uses grep -w, thus only alphanum and "_" count as word bounderies # (word "def" matches "abc-def") is_in_list() { [ $# -ne 2 ] && return 1 local needle="$1" local haystack="$2" echo "$haystack" | grep -q -w "$needle" } _greplist() { if [ $# -eq 0 ]; then echo cat else echo -n "grep -E ($1" shift for i in $*; do echo -n "|$i" done echo ")" fi } # get list of all app directories get_apps() { make -f makefiles/app_dirs.inc.mk info-applications \ | $(_greplist $APPS) | sort } # take app dir as parameter, print all boards that are supported # Only print for boards in $BOARDS. get_supported_boards() { local appdir=$1 local boards="$(make --no-print-directory -C$appdir info-boards-supported 2>/dev/null || echo broken)" if [ "$boards" = broken ]; then echo "makefile_broken" return fi for board in $boards do echo $board done | $(_greplist $BOARDS) } get_supported_toolchains() { local appdir=$1 local board=$2 local toolchains="gnu" if is_in_list "${board}" "${TEST_BOARDS_LLVM_COMPILE}"; then toolchains="$(make -s --no-print-directory -C${appdir} BOARD=${board} \ info-toolchains-supported 2> /dev/null | grep -o -e "llvm" -e "gnu")" fi echo "${toolchains}" } # given an app dir as parameter, print "$appdir $board:$toolchain" for each # supported board and toolchain. Only print for boards in $BOARDS. get_app_board_toolchain_pairs() { local appdir=$1 for board in $(get_supported_boards $appdir) do for toolchain in $(get_supported_toolchains $appdir $board) do echo $appdir $board:$toolchain done done | $(_greplist $BOARDS) } # use dwqc to create full "appdir board toolchain" compile job list get_compile_jobs() { get_apps | \ dwqc ${DWQ_ENV} -s \ "$0 get_app_board_toolchain_pairs \${1}" \ | xargs '-d\n' -n 1 echo $0 compile } print_worker() { [ -n "$DWQ_WORKER" ] && \ echo "-- running on worker ${DWQ_WORKER} thread ${DWQ_WORKER_THREAD}, build number $DWQ_WORKER_BUILDNUM." } # compile one app for one board with one toolchain. delete intermediates. compile() { local appdir=$1 local board=$(echo $2 | cut -f 1 -d':') local toolchain=$(echo $2 | cut -f 2 -d':') [ "$board" = "makefile_broken" ] && error "$0: Makefile in \"$appdir\" seems to be broken!" # set build directory. CI ensures only one build at a time in $(pwd). export BINDIR="$(pwd)/build" export PKGDIRBASE="${BINDIR}/pkg" # Pre-build cleanup rm -rf ${BINDIR} print_worker # sanity checks [ $# -ne 2 ] && error "$0: compile: invalid parameters (expected \$appdir \$board:\$toolchain)" [ ! -d "$appdir" ] && error "$0: compile: error: application directory \"$appdir\" doesn't exist" [ ! -d "boards/$board" ] && error "$0: compile: error: board directory \"boards/$board\" doesn't exist" # compile CCACHE_BASEDIR="$(pwd)" BOARD=$board TOOLCHAIN=$toolchain RIOT_CI_BUILD=1 \ make -C${appdir} clean all -j${JOBS:-4} RES=$? # run tests if [ $RES -eq 0 ]; then if [ $RUN_TESTS -eq 1 -o "$board" = "native" ]; then if [ -f "${BINDIR}/.test" ]; then if [ "$board" = "native" ]; then BOARD=$board make -C${appdir} test RES=$? elif is_in_list "$board" "$TEST_BOARDS_AVAILABLE"; then BOARD=$board TOOLCHAIN=$toolchain make -C${appdir} test-murdock RES=$? fi fi fi fi if [ -d ${BINDIR} ] then echo "-- build directory size: $(du -sh ${BINDIR} | cut -f1)" # cleanup rm -rf ${BINDIR} fi return $RES } test_job() { local appdir=$1 local board=$(echo $2 | cut -f 1 -d':') local toolchain=$(echo $2 | cut -f 2 -d':') local flashfile="$3" [ ! -f "$flashfile" ] && { echo "$0: _test(): flashfile \"$flashfile\" doesn't exist!" return 1 } dwqc \ ${DWQ_ENV} \ ${DWQ_JOBID:+--subjob} \ --file $flashfile:$appdir/bin/${board}/$(basename $flashfile) \ --queue ${TEST_QUEUE:-$board} \ --maxfail 1 \ "./.murdock run_test $appdir $board:$toolchain" } run_test() { local appdir=$1 local board=$(echo $2 | cut -f 1 -d':') local toolchain=$(echo $2 | cut -f 2 -d':') print_worker echo "-- executing tests for $appdir on $board (compiled with $toolchain toolchain):" hook run_test_pre BOARD=$board TOOLCHAIN=${toolchain} make -C$appdir flash-only test } # execute static tests static_tests() { local repo=${CI_BASE_REPO:-https://github.com/RIOT-OS/RIOT} local branch=${CI_BASE_BRANCH:-master} print_worker OUT="$(git remote add upstream $repo 2>&1 && git fetch upstream ${branch}:${branch} 2>&1)" RES=$? if [ $RES -ne 0 ]; then echo "$OUT" exit 1 fi BUILDTEST_MCU_GROUP=static-tests ./dist/tools/ci/build_and_test.sh } get_jobs() { [ "$STATIC_TESTS" = "1" ] && \ echo "$0 static_tests###{ \"jobdir\" : \"exclusive\" }" get_compile_jobs } $*