Fathi Boudra | 422bf77 | 2019-12-02 11:10:16 +0200 | [diff] [blame^] | 1 | #!/bin/bash |
| 2 | # |
| 3 | # Copyright (c) 2019, Arm Limited. All rights reserved. |
| 4 | # |
| 5 | # SPDX-License-Identifier: BSD-3-Clause |
| 6 | # |
| 7 | |
| 8 | set -e |
| 9 | |
| 10 | in_red() { |
| 11 | echo "$(tput setaf 1)${1:?}$(tput sgr0)" |
| 12 | } |
| 13 | export -f in_red |
| 14 | |
| 15 | in_green() { |
| 16 | echo "$(tput setaf 2)${1:?}$(tput sgr0)" |
| 17 | } |
| 18 | export -f in_green |
| 19 | |
| 20 | in_yellow() { |
| 21 | echo "$(tput setaf 3)${1:?}$(tput sgr0)" |
| 22 | } |
| 23 | export -f in_yellow |
| 24 | |
| 25 | print_success() { |
| 26 | in_green "$1: SUCCESS" |
| 27 | } |
| 28 | export -f print_success |
| 29 | |
| 30 | print_failure() { |
| 31 | in_red "$1: FAILURE" |
| 32 | } |
| 33 | export -f print_failure |
| 34 | |
| 35 | print_unstable() { |
| 36 | in_yellow "$1: UNSTABLE" |
| 37 | } |
| 38 | export -f print_unstable |
| 39 | |
| 40 | gen_makefile() { |
| 41 | local num="$(find -name "*.test" -type f | wc -l)" |
| 42 | local i=0 |
| 43 | |
| 44 | cat <<EOF >Makefile |
| 45 | SHELL=/bin/bash |
| 46 | |
| 47 | all: |
| 48 | |
| 49 | EOF |
| 50 | |
| 51 | # If we're using local checkouts for either TF or TFTF, we must |
| 52 | # serialise builds |
| 53 | while [ "$i" -lt "$num" ]; do |
| 54 | { |
| 55 | printf "all: %03d_run %03d_build\n" "$i" "$i" |
| 56 | if upon "$serialize_builds" && [ "$i" -gt 0 ]; then |
| 57 | printf "%03d_build: %03d_build\n" "$i" "$((i - 1))" |
| 58 | fi |
| 59 | echo |
| 60 | } >>Makefile |
| 61 | let "++i" |
| 62 | done |
| 63 | |
| 64 | cat <<EOF >>Makefile |
| 65 | |
| 66 | %_run: %_build |
| 67 | @run_one_test "\$@" |
| 68 | |
| 69 | %_build: |
| 70 | @run_one_test "\$@" |
| 71 | EOF |
| 72 | } |
| 73 | |
| 74 | # This function is invoked from the Makefile. Descriptor 5 points to the active |
| 75 | # terminal. |
| 76 | run_one_test() { |
| 77 | id="${1%%_*}" |
| 78 | action="${1##*_}" |
| 79 | test_file="$(find -name "$id*.test" -printf "%f\n")" |
| 80 | |
| 81 | mkdir -p "$id" |
| 82 | |
| 83 | # Copy the test_file into the workspace directory with the name |
| 84 | # TEST_DESC, just like Jenkins would. |
| 85 | export TEST_DESC="$(basename "$test_file")" |
| 86 | cp "$test_file" "$id/TEST_DESC" |
| 87 | |
| 88 | workspace="$id" test_desc="$test_file" "$ci_root/script/parse_test.sh" |
| 89 | |
| 90 | set -a |
| 91 | source "$id/env" |
| 92 | set +a |
| 93 | |
| 94 | # Makefiles don't like commas and colons in file names. We therefore |
| 95 | # replace them with _ |
| 96 | config_subst="$(echo "$TEST_CONFIG" | tr ',:' '_')" |
| 97 | config_string="$id: $TEST_GROUP/$TEST_CONFIG" |
| 98 | workspace="$workspace/$TEST_GROUP/$config_subst" |
| 99 | mkdir -p "$workspace" |
| 100 | |
| 101 | log_file="$workspace/artefacts/build.log" |
| 102 | if [ "$parallel" -gt 1 ]; then |
| 103 | console_file="$workspace/console.log" |
| 104 | exec 6>>"$console_file" |
| 105 | else |
| 106 | exec 6>&5 |
| 107 | fi |
| 108 | |
| 109 | # Unset make flags for build script |
| 110 | MAKEFLAGS= |
| 111 | |
| 112 | case "$action" in |
| 113 | "build") |
| 114 | echo "building: $config_string" >&5 |
| 115 | if ! bash $minus_x "$ci_root/script/build_package.sh" \ |
| 116 | >&6 2>&1; then |
| 117 | { |
| 118 | print_failure "$config_string (build)" |
| 119 | if [ "$console_file" ]; then |
| 120 | echo " see $console_file" |
| 121 | fi |
| 122 | } >&5 |
| 123 | exit 1 |
| 124 | fi |
| 125 | ;; |
| 126 | |
| 127 | "run") |
| 128 | # Local runs only for FVP unless asked not to |
| 129 | if echo "$RUN_CONFIG" | grep -q "^fvp" && \ |
| 130 | not_upon "$skip_runs"; then |
| 131 | echo "running: $config_string" >&5 |
| 132 | if bash $minus_x "$ci_root/script/run_package.sh" \ |
| 133 | >&6 2>&1; then |
| 134 | if grep -q -e "--BUILD UNSTABLE--" \ |
| 135 | "$log_file"; then |
| 136 | print_unstable "$config_string" >&5 |
| 137 | else |
| 138 | print_success "$config_string" >&5 |
| 139 | fi |
| 140 | exit 0 |
| 141 | else |
| 142 | { |
| 143 | print_failure "$config_string (run)" |
| 144 | if [ "$console_file" ]; then |
| 145 | echo " see $console_file" |
| 146 | fi |
| 147 | } >&5 |
| 148 | exit 1 |
| 149 | fi |
| 150 | else |
| 151 | if grep -q -e "--BUILD UNSTABLE--" \ |
| 152 | "$log_file"; then |
| 153 | print_unstable "$config_string (not run)" >&5 |
| 154 | else |
| 155 | print_success "$config_string (not run)" >&5 |
| 156 | fi |
| 157 | exit 0 |
| 158 | fi |
| 159 | ;; |
| 160 | |
| 161 | *) |
| 162 | in_red "Invalid action: $action!" >&5 |
| 163 | exit 1 |
| 164 | ;; |
| 165 | esac |
| 166 | } |
| 167 | export -f run_one_test |
| 168 | |
| 169 | workspace="${workspace:?}" |
| 170 | ci_root="$(readlink -f "$(dirname "$0")/..")" |
| 171 | |
| 172 | # If this script was invoked with bash -x, have subsequent build/run invocations |
| 173 | # to use -x as well. |
| 174 | if echo "$-" | grep -q "x"; then |
| 175 | export minus_x="-x" |
| 176 | fi |
| 177 | |
| 178 | # For a local run, when some variables as specified as "?", launch zenity to |
| 179 | # prompt for test config via. GUI. If it's "??", then choose a directory. |
| 180 | if [ "$test_groups" = "?" -o "$test_groups" = "??" ]; then |
| 181 | zenity_opts=( |
| 182 | --file-selection |
| 183 | --filename="$ci_root/group/README" |
| 184 | --multiple |
| 185 | --title "Choose test config" |
| 186 | ) |
| 187 | |
| 188 | if [ "$test_groups" = "??" ]; then |
| 189 | zenity_opts+=("--directory") |
| 190 | fi |
| 191 | |
| 192 | # In case of multiple selections, zenity returns absolute paths of files |
| 193 | # separated by '|'. We remove the pipe characters, and make the paths |
| 194 | # relative to the group directory. |
| 195 | selections="$(cd "$ci_root"; zenity ${zenity_opts[*]})" |
| 196 | test_groups="$(echo "$selections" | tr '|' ' ')" |
| 197 | test_groups="$(echo "$test_groups" | sed "s#$ci_root/group/##g")" |
| 198 | fi |
| 199 | |
| 200 | test_groups="${test_groups:?}" |
| 201 | local_count=0 |
| 202 | |
| 203 | if [ -z "$tf_root" ]; then |
| 204 | in_red "NOTE: NOT using local work tree for TF" |
| 205 | else |
| 206 | tf_root="$(readlink -f $tf_root)" |
| 207 | tf_refspec= |
| 208 | in_green "Using local work tree for TF" |
| 209 | let "++local_count" |
| 210 | fi |
| 211 | |
| 212 | if [ -z "$tftf_root" ]; then |
| 213 | in_red "NOTE: NOT using local work tree for TFTF" |
| 214 | tforg_user="${tforg_user:?}" |
| 215 | else |
| 216 | tftf_root="$(readlink -f $tftf_root)" |
| 217 | tf_refspec= |
| 218 | in_green "Using local work tree for TFTF" |
| 219 | let "++local_count" |
| 220 | fi |
| 221 | |
| 222 | if [ -z "$scp_root" ]; then |
| 223 | in_red "NOTE: NOT using local work tree for SCP" |
| 224 | else |
| 225 | scp_root="$(readlink -f $scp_root)" |
| 226 | scp_refspec= |
| 227 | in_green "Using local work tree for SCP" |
| 228 | let "++local_count" |
| 229 | fi |
| 230 | |
| 231 | # User preferences |
| 232 | user_test_run="$test_run" |
| 233 | user_dont_clean="$dont_clean" |
| 234 | user_keep_going="$keep_going" |
| 235 | user_primary_live="$primary_live" |
| 236 | |
| 237 | export ci_root |
| 238 | export dont_clean=0 |
| 239 | export local_ci=1 |
| 240 | export parallel |
| 241 | export test_run=0 |
| 242 | export primary_live=0 |
| 243 | |
| 244 | rm -rf "$workspace" |
| 245 | mkdir -p "$workspace" |
| 246 | |
| 247 | source "$ci_root/utils.sh" |
| 248 | |
| 249 | # SCP is not cloned by default |
| 250 | export clone_scp |
| 251 | export scp_root |
| 252 | if not_upon "$scp_root" && upon "$clone_scp"; then |
| 253 | clone_scp=1 |
| 254 | else |
| 255 | clone_scp=0 |
| 256 | fi |
| 257 | |
| 258 | # Use clone_repos.sh to clone and share repositores that aren't local. |
| 259 | no_tf="$tf_root" no_tftf="$tftf_root" no_ci="$ci_root" \ |
| 260 | bash $minus_x "$ci_root/script/clone_repos.sh" |
| 261 | |
| 262 | set -a |
| 263 | source "$workspace/env" |
| 264 | set +a |
| 265 | |
| 266 | if [ "$local_count" -gt 0 ]; then |
| 267 | # At least one repository is local |
| 268 | serialize_builds=1 |
| 269 | else |
| 270 | dont_clean=0 |
| 271 | fi |
| 272 | |
| 273 | export -f upon not_upon |
| 274 | |
| 275 | # Generate test descriptions |
| 276 | "$ci_root/script/gen_test_desc.py" |
| 277 | |
| 278 | # Iterate through test files in workspace |
| 279 | pushd "$workspace" |
| 280 | |
| 281 | if not_upon "$parallel" || echo "$parallel" | grep -vq "[0-9]"; then |
| 282 | parallel=1 |
| 283 | test_run="$user_test_run" |
| 284 | dont_clean="$user_dont_clean" |
| 285 | primary_live="$user_primary_live" |
| 286 | fi |
| 287 | |
| 288 | if [ "$parallel" -gt 1 ]; then |
| 289 | msg="Running at most $parallel jobs in parallel" |
| 290 | if upon "$serialize_builds"; then |
| 291 | msg+=" (builds serialized)" |
| 292 | fi |
| 293 | msg+="..." |
| 294 | fi |
| 295 | |
| 296 | # Generate Makefile |
| 297 | gen_makefile |
| 298 | |
| 299 | if upon "$msg"; then |
| 300 | echo "$msg" |
| 301 | echo |
| 302 | fi |
| 303 | |
| 304 | keep_going="${user_keep_going:-1}" |
| 305 | if not_upon "$keep_going"; then |
| 306 | keep_going= |
| 307 | fi |
| 308 | |
| 309 | MAKEFLAGS= make -r -j "$parallel" ${keep_going+-k} 5>&1 &>"make.log" |