#!/bin/sh export TEST_BOARDS_AVAILABLE=${TEST_BOARDS_AVAILABLE:-"samr21-xpro"} 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() { find tests/ examples/ \ -mindepth 2 -maxdepth 2 -name Makefile -type f \ | xargs dirname | $(_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) } # given an app dir as parameter, print "$appdir board" for each supported # board. Only print for boards in $BOARDS. get_app_board_pairs() { local appdir=$1 for board in $(get_supported_boards $appdir) do echo $appdir $board done | $(_greplist $BOARDS) } # use dwqc to create full "appdir board" compile job list get_compile_jobs() { get_apps | \ dwqc ${DWQ_ENV} -s \ "$0 get_app_board_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. delete intermediates. compile() { local appdir=$1 local board=$2 [ "$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)" [ ! -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 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 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=$2 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" } run_test() { local appdir=$1 local board=$2 print_worker echo "-- executing tests for $appdir on $board:" hook run_test_pre BOARD=$board 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 } $*