|
| 1 | +#!/bin/bash |
| 2 | +# |
| 3 | +# zephyr-test.sh - Build and test wolfSSL Zephyr samples in a Docker container |
| 4 | +# |
| 5 | +# Usage: |
| 6 | +# ./zephyr-test.sh [options] |
| 7 | +# |
| 8 | +# Options: |
| 9 | +# -r, --repo <url> wolfSSL git repo URL |
| 10 | +# -b, --branch <branch> wolfSSL branch/revision |
| 11 | +# -z, --zephyr <version> Zephyr version tag |
| 12 | +# -t, --target <board> Board target |
| 13 | +# -s, --sample <name> Sample to build |
| 14 | +# -v, --verbose Verbose compile output (show full compiler commands) |
| 15 | +# -W, --werror Build with -Werror (treat warnings as errors) |
| 16 | +# --commit <sha> Checkout specific commit after fetching branch |
| 17 | +# -c, --cmake-args <str> Extra CMake args passed after -- to west build |
| 18 | +# --extra-conf <file> Extra Kconfig overlay (copied into container) |
| 19 | +# -i, --interactive Drop into interactive shell |
| 20 | +# -h, --help Show this help |
| 21 | +# |
| 22 | +# Examples: |
| 23 | +# # Test master against Zephyr 4.1.0 for native_sim |
| 24 | +# ./zephyr-test.sh |
| 25 | +# |
| 26 | +# # Test a specific branch for frdm_rw612 on Zephyr 4.3.0 |
| 27 | +# ./zephyr-test.sh -b zephyr-4_3_0-posix-fix -z v4.3.0 -t frdm_rw612/rw612 |
| 28 | +# |
| 29 | +# # Test with Zephyr 4.3.0 on native_sim |
| 30 | +# ./zephyr-test.sh -z v4.3.0 |
| 31 | +# |
| 32 | +# # Interactive shell to debug |
| 33 | +# ./zephyr-test.sh -z v4.3.0 -i |
| 34 | +# |
| 35 | +# # Test a fork |
| 36 | +# ./zephyr-test.sh -r https://github.com/myuser/wolfssl -b my-fix -z v4.3.0 |
| 37 | +# |
| 38 | +# # Build with -Werror like customer |
| 39 | +# ./zephyr-test.sh -z v4.3.0 -W |
| 40 | +# |
| 41 | +# # Pass extra cmake args |
| 42 | +# ./zephyr-test.sh -z v4.3.0 -c "-DCMAKE_C_FLAGS=-U_POSIX_C_SOURCE" |
| 43 | + |
| 44 | +set -euo pipefail |
| 45 | + |
| 46 | +# Defaults |
| 47 | +WOLFSSL_REPO="https://github.com/wolfSSL/wolfssl" |
| 48 | +WOLFSSL_BRANCH="master" |
| 49 | +ZEPHYR_VERSION="v4.1.0" |
| 50 | +BOARD_TARGET="native_sim" |
| 51 | +SAMPLE_NAME="wolfssl_tls_sock" |
| 52 | +INTERACTIVE=0 |
| 53 | +VERBOSE=0 |
| 54 | +WERROR=0 |
| 55 | +WOLFSSL_COMMIT="" |
| 56 | +CMAKE_EXTRA="" |
| 57 | +EXTRA_CONF="" |
| 58 | +CONTAINER_NAME="" # set dynamically after arg parsing |
| 59 | + |
| 60 | +GHCR="ghcr.io/zephyrproject-rtos/zephyr-build" |
| 61 | + |
| 62 | +usage() { |
| 63 | + sed -n '3,/^$/s/^# \?//p' "$0" |
| 64 | + exit 0 |
| 65 | +} |
| 66 | + |
| 67 | +select_docker_image() { |
| 68 | + local ver="${1#v}" |
| 69 | + local major="${ver%%.*}" |
| 70 | + local minor="${ver#*.}" |
| 71 | + minor="${minor%%.*}" |
| 72 | + |
| 73 | + if [[ "$major" -ge 4 && "$minor" -ge 2 ]]; then |
| 74 | + # Zephyr 4.2+ needs SDK 0.17.x (v0.28.7 image) |
| 75 | + echo "${GHCR}:v0.28.7" |
| 76 | + elif [[ "$major" -ge 4 ]]; then |
| 77 | + # Zephyr 4.0-4.1 needs SDK 0.17.0 (v0.27.4 image; v0.26.18/v0.28.7 picolibc is incompatible) |
| 78 | + echo "${GHCR}:v0.27.4" |
| 79 | + else |
| 80 | + # Zephyr 3.x and older |
| 81 | + echo "${GHCR}:v0.26.18" |
| 82 | + fi |
| 83 | +} |
| 84 | + |
| 85 | +while [[ $# -gt 0 ]]; do |
| 86 | + case "$1" in |
| 87 | + -r|--repo) WOLFSSL_REPO="$2"; shift 2 ;; |
| 88 | + -b|--branch) WOLFSSL_BRANCH="$2"; shift 2 ;; |
| 89 | + -z|--zephyr) ZEPHYR_VERSION="$2"; shift 2 ;; |
| 90 | + -t|--target) BOARD_TARGET="$2"; shift 2 ;; |
| 91 | + -s|--sample) SAMPLE_NAME="$2"; shift 2 ;; |
| 92 | + -v|--verbose) VERBOSE=1; shift ;; |
| 93 | + -W|--werror) WERROR=1; shift ;; |
| 94 | + --commit) WOLFSSL_COMMIT="$2"; shift 2 ;; |
| 95 | + -c|--cmake-args) CMAKE_EXTRA="$2"; shift 2 ;; |
| 96 | + --extra-conf) EXTRA_CONF="$2"; shift 2 ;; |
| 97 | + -i|--interactive) INTERACTIVE=1; shift ;; |
| 98 | + -h|--help) usage ;; |
| 99 | + *) echo "Unknown option: $1"; usage ;; |
| 100 | + esac |
| 101 | +done |
| 102 | + |
| 103 | +DOCKER_IMAGE=$(select_docker_image "$ZEPHYR_VERSION") |
| 104 | + |
| 105 | +# Build a unique container name from version, board, sample, and PID to avoid collisions |
| 106 | +ZVER_SLUG="${ZEPHYR_VERSION#v}" |
| 107 | +BOARD_SLUG="${BOARD_TARGET//\//-}" |
| 108 | +CONTAINER_NAME="wolfssl-zephyr-${ZVER_SLUG}-${BOARD_SLUG}-${SAMPLE_NAME}-$$" |
| 109 | + |
| 110 | +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" |
| 111 | +LOG_DIR="${SCRIPT_DIR}/logs" |
| 112 | +mkdir -p "${LOG_DIR}" |
| 113 | +TIMESTAMP=$(date +%Y%m%d_%H%M%S) |
| 114 | +ZVER="${ZEPHYR_VERSION#v}" |
| 115 | +LOG_FILE="${LOG_DIR}/${ZVER}_${WOLFSSL_BRANCH}_${BOARD_TARGET//\//-}_${TIMESTAMP}.log" |
| 116 | + |
| 117 | +echo "==> wolfSSL repo: ${WOLFSSL_REPO}" |
| 118 | +echo "==> wolfSSL branch: ${WOLFSSL_BRANCH}" |
| 119 | +echo "==> Zephyr version: ${ZEPHYR_VERSION}" |
| 120 | +echo "==> Board target: ${BOARD_TARGET}" |
| 121 | +echo "==> Sample: ${SAMPLE_NAME}" |
| 122 | +echo "==> Docker image: ${DOCKER_IMAGE}" |
| 123 | +[[ -n "$WOLFSSL_COMMIT" ]] && echo "==> Commit: ${WOLFSSL_COMMIT}" |
| 124 | +[[ "$WERROR" == "1" ]] && echo "==> Werror: enabled" |
| 125 | +[[ -n "$CMAKE_EXTRA" ]] && echo "==> CMake args: ${CMAKE_EXTRA}" |
| 126 | +[[ -n "$EXTRA_CONF" ]] && echo "==> Extra conf: ${EXTRA_CONF}" |
| 127 | +echo "==> Log file: ${LOG_FILE}" |
| 128 | +echo "" |
| 129 | + |
| 130 | +# Pull the Docker image |
| 131 | +echo "==> Pulling Docker image..." |
| 132 | +docker pull "${DOCKER_IMAGE}" |
| 133 | + |
| 134 | +# Build the script that runs inside the container |
| 135 | +BUILD_SCRIPT=$(cat <<'INNER_SCRIPT' |
| 136 | +#!/bin/bash |
| 137 | +set -euo pipefail |
| 138 | +
|
| 139 | +ZEPHYR_VERSION="__ZEPHYR_VERSION__" |
| 140 | +BOARD_TARGET="__BOARD_TARGET__" |
| 141 | +SAMPLE_NAME="__SAMPLE_NAME__" |
| 142 | +WOLFSSL_REPO="__WOLFSSL_REPO__" |
| 143 | +WOLFSSL_BRANCH="__WOLFSSL_BRANCH__" |
| 144 | +WOLFSSL_COMMIT="__WOLFSSL_COMMIT__" |
| 145 | +INTERACTIVE="__INTERACTIVE__" |
| 146 | +VERBOSE="__VERBOSE__" |
| 147 | +WERROR="__WERROR__" |
| 148 | +CMAKE_EXTRA="__CMAKE_EXTRA__" |
| 149 | +EXTRA_CONF="__EXTRA_CONF__" |
| 150 | +EXTRA_CONF_CONTENT="__EXTRA_CONF_CONTENT__" |
| 151 | +
|
| 152 | +WORKDIR="/workdir" |
| 153 | +cd "$WORKDIR" |
| 154 | +
|
| 155 | +# --- 1. Initialize Zephyr workspace --- |
| 156 | +echo "==> [container] Initializing Zephyr workspace (${ZEPHYR_VERSION})..." |
| 157 | +west init --mr "${ZEPHYR_VERSION}" zephyrproject |
| 158 | +cd zephyrproject |
| 159 | +
|
| 160 | +# --- 2. Add wolfSSL to the west manifest --- |
| 161 | +echo "==> [container] Adding wolfSSL to west.yml (${WOLFSSL_REPO}@${WOLFSSL_BRANCH})..." |
| 162 | +cd zephyr |
| 163 | +
|
| 164 | +# Use sed to inject wolfSSL remote and project into west.yml, |
| 165 | +# same approach as the wolfSSL CI workflow |
| 166 | +REPO_BASE=$(echo "${WOLFSSL_REPO}" | sed 's|/[^/]*$||') |
| 167 | +REF=$(echo "${WOLFSSL_BRANCH}" | sed 's/\//\\\//g') |
| 168 | +
|
| 169 | +sed -i "s|remotes:|remotes:\n - name: wolfssl\n url-base: ${REPO_BASE}|" west.yml |
| 170 | +sed -i "s|projects:|projects:\n - name: wolfssl\n path: modules/crypto/wolfssl\n remote: wolfssl\n revision: ${REF}|" west.yml |
| 171 | +
|
| 172 | +echo "==> [container] Updated west.yml:" |
| 173 | +grep -A2 "wolfssl" west.yml |
| 174 | +cd .. |
| 175 | +
|
| 176 | +# --- 3. Update all modules (including wolfSSL) --- |
| 177 | +echo "==> [container] Running west update..." |
| 178 | +west update -n -o=--depth=1 |
| 179 | +
|
| 180 | +# --- 3b. Checkout specific commit if requested --- |
| 181 | +if [[ -n "$WOLFSSL_COMMIT" ]]; then |
| 182 | + echo "==> [container] Checking out commit ${WOLFSSL_COMMIT}..." |
| 183 | + cd modules/crypto/wolfssl |
| 184 | + git fetch --unshallow "${WOLFSSL_REPO}" "${WOLFSSL_BRANCH}" |
| 185 | + git checkout "${WOLFSSL_COMMIT}" |
| 186 | + cd "${WORKDIR}/zephyrproject" |
| 187 | +fi |
| 188 | +
|
| 189 | +# --- 4. Export and install deps --- |
| 190 | +echo "==> [container] Exporting Zephyr..." |
| 191 | +west zephyr-export |
| 192 | +
|
| 193 | +echo "==> [container] Installing host packages (newlib, python3-venv)..." |
| 194 | +sudo apt-get update -qq && sudo apt-get install -y -qq python3-venv libnewlib-dev >/dev/null 2>&1 || true |
| 195 | +python3 -m venv .venv |
| 196 | +source .venv/bin/activate |
| 197 | +pip3 install west |
| 198 | +echo "==> [container] Installing Python dependencies..." |
| 199 | +pip3 install -r zephyr/scripts/requirements.txt |
| 200 | +
|
| 201 | +export ZEPHYR_BASE="${WORKDIR}/zephyrproject/zephyr" |
| 202 | +
|
| 203 | +# Ensure Zephyr SDK is found (enables newlib for native_sim) |
| 204 | +if [[ -z "${ZEPHYR_SDK_INSTALL_DIR:-}" ]]; then |
| 205 | + SDK_DIR=$(find /opt -maxdepth 2 -name "zephyr-sdk-*" -type d 2>/dev/null | head -1) |
| 206 | + if [[ -n "$SDK_DIR" ]]; then |
| 207 | + export ZEPHYR_SDK_INSTALL_DIR="$SDK_DIR" |
| 208 | + echo "==> [container] Found SDK: ${ZEPHYR_SDK_INSTALL_DIR}" |
| 209 | + fi |
| 210 | +fi |
| 211 | +
|
| 212 | +# --- 5. Build or interactive --- |
| 213 | +if [[ "$INTERACTIVE" == "1" ]]; then |
| 214 | + echo "" |
| 215 | + echo "==========================================" |
| 216 | + echo " Interactive mode - workspace is ready" |
| 217 | + echo " Workspace: ${WORKDIR}/zephyrproject" |
| 218 | + echo " wolfSSL: modules/crypto/wolfssl" |
| 219 | + echo "" |
| 220 | + echo " Example build commands:" |
| 221 | + echo " west build -p always -b ${BOARD_TARGET} modules/crypto/wolfssl/zephyr/samples/${SAMPLE_NAME}" |
| 222 | + echo " west build -t run" |
| 223 | + echo "" |
| 224 | + echo " To run twister tests:" |
| 225 | + echo " ./zephyr/scripts/twister -T modules/crypto/wolfssl/zephyr/samples/${SAMPLE_NAME} -vvv" |
| 226 | + echo "==========================================" |
| 227 | + echo "" |
| 228 | + exec /bin/bash |
| 229 | +else |
| 230 | + if [[ -n "$EXTRA_CONF" ]]; then |
| 231 | + echo "$EXTRA_CONF_CONTENT" > /workdir/zephyrproject/extra.conf |
| 232 | + echo "==> [container] Extra conf written to /workdir/zephyrproject/extra.conf" |
| 233 | + cat /workdir/zephyrproject/extra.conf |
| 234 | + fi |
| 235 | +
|
| 236 | + echo "==> [container] Building sample: ${SAMPLE_NAME} for ${BOARD_TARGET}..." |
| 237 | + CMAKE_ARGS="" |
| 238 | + if [[ "$VERBOSE" == "1" ]]; then |
| 239 | + CMAKE_ARGS="${CMAKE_ARGS} -DCMAKE_VERBOSE_MAKEFILE=ON" |
| 240 | + fi |
| 241 | + if [[ "$WERROR" == "1" ]]; then |
| 242 | + CMAKE_ARGS="${CMAKE_ARGS} -DCMAKE_C_FLAGS=-Werror" |
| 243 | + fi |
| 244 | + if [[ -n "$CMAKE_EXTRA" ]]; then |
| 245 | + CMAKE_ARGS="${CMAKE_ARGS} ${CMAKE_EXTRA}" |
| 246 | + fi |
| 247 | +
|
| 248 | + if [[ -n "$EXTRA_CONF" ]]; then |
| 249 | + CMAKE_ARGS="${CMAKE_ARGS} -DOVERLAY_CONFIG=/workdir/zephyrproject/extra.conf" |
| 250 | + fi |
| 251 | +
|
| 252 | + west build -p always -b "${BOARD_TARGET}" \ |
| 253 | + "modules/crypto/wolfssl/zephyr/samples/${SAMPLE_NAME}" \ |
| 254 | + ${CMAKE_ARGS:+-- $CMAKE_ARGS} |
| 255 | +
|
| 256 | + echo "" |
| 257 | + echo "==> [container] Build succeeded!" |
| 258 | +
|
| 259 | + # Run the app for emulator targets and watch for completion |
| 260 | + case "${BOARD_TARGET}" in |
| 261 | + native_sim*|qemu_*) |
| 262 | + echo "==> [container] Running sample on ${BOARD_TARGET}..." |
| 263 | + RUN_TIMEOUT=300 # 5 minutes |
| 264 | + RUN_LOG="/tmp/run_output.log" |
| 265 | + APP_RC=1 |
| 266 | +
|
| 267 | + west build -t run > "${RUN_LOG}" 2>&1 & |
| 268 | + RUN_PID=$! |
| 269 | +
|
| 270 | + ELAPSED=0 |
| 271 | + while kill -0 "${RUN_PID}" 2>/dev/null; do |
| 272 | + if [[ $ELAPSED -ge $RUN_TIMEOUT ]]; then |
| 273 | + echo "==> [container] TIMEOUT: app did not complete within ${RUN_TIMEOUT}s" |
| 274 | + kill "${RUN_PID}" 2>/dev/null || true |
| 275 | + wait "${RUN_PID}" 2>/dev/null || true |
| 276 | + cat "${RUN_LOG}" |
| 277 | + exit 1 |
| 278 | + fi |
| 279 | + # Check for success strings |
| 280 | + if grep -q "Benchmark complete\|Test complete\|Client Return: 0" "${RUN_LOG}" 2>/dev/null; then |
| 281 | + echo "==> [container] App completed successfully!" |
| 282 | + APP_RC=0 |
| 283 | + kill "${RUN_PID}" 2>/dev/null || true |
| 284 | + wait "${RUN_PID}" 2>/dev/null || true |
| 285 | + break |
| 286 | + fi |
| 287 | + sleep 2 |
| 288 | + ELAPSED=$((ELAPSED + 2)) |
| 289 | + done |
| 290 | +
|
| 291 | + cat "${RUN_LOG}" |
| 292 | +
|
| 293 | + if [[ $APP_RC -ne 0 ]]; then |
| 294 | + # Process exited on its own - check if it printed a success string |
| 295 | + if grep -q "Benchmark complete\|Test complete\|Client Return: 0" "${RUN_LOG}" 2>/dev/null; then |
| 296 | + APP_RC=0 |
| 297 | + else |
| 298 | + echo "==> [container] App exited without a success string" |
| 299 | + exit 1 |
| 300 | + fi |
| 301 | + fi |
| 302 | + ;; |
| 303 | + *) |
| 304 | + echo "==> [container] Board '${BOARD_TARGET}' is not an emulator target, skipping run." |
| 305 | + echo " Build artifacts are in: ${WORKDIR}/zephyrproject/build/zephyr/" |
| 306 | + ;; |
| 307 | + esac |
| 308 | +fi |
| 309 | +INNER_SCRIPT |
| 310 | +) |
| 311 | + |
| 312 | +# Substitute variables into the inner script |
| 313 | +BUILD_SCRIPT="${BUILD_SCRIPT//__ZEPHYR_VERSION__/$ZEPHYR_VERSION}" |
| 314 | +BUILD_SCRIPT="${BUILD_SCRIPT//__BOARD_TARGET__/$BOARD_TARGET}" |
| 315 | +BUILD_SCRIPT="${BUILD_SCRIPT//__SAMPLE_NAME__/$SAMPLE_NAME}" |
| 316 | +BUILD_SCRIPT="${BUILD_SCRIPT//__WOLFSSL_REPO__/$WOLFSSL_REPO}" |
| 317 | +BUILD_SCRIPT="${BUILD_SCRIPT//__WOLFSSL_BRANCH__/$WOLFSSL_BRANCH}" |
| 318 | +BUILD_SCRIPT="${BUILD_SCRIPT//__WOLFSSL_COMMIT__/$WOLFSSL_COMMIT}" |
| 319 | +BUILD_SCRIPT="${BUILD_SCRIPT//__INTERACTIVE__/$INTERACTIVE}" |
| 320 | +BUILD_SCRIPT="${BUILD_SCRIPT//__VERBOSE__/$VERBOSE}" |
| 321 | +BUILD_SCRIPT="${BUILD_SCRIPT//__WERROR__/$WERROR}" |
| 322 | +BUILD_SCRIPT="${BUILD_SCRIPT//__CMAKE_EXTRA__/$CMAKE_EXTRA}" |
| 323 | +if [[ -n "$EXTRA_CONF" ]]; then |
| 324 | + EXTRA_CONF_CONTENT=$(cat "$EXTRA_CONF") |
| 325 | + BUILD_SCRIPT="${BUILD_SCRIPT//__EXTRA_CONF__/yes}" |
| 326 | + BUILD_SCRIPT="${BUILD_SCRIPT//__EXTRA_CONF_CONTENT__/$EXTRA_CONF_CONTENT}" |
| 327 | +else |
| 328 | + BUILD_SCRIPT="${BUILD_SCRIPT//__EXTRA_CONF__/}" |
| 329 | + BUILD_SCRIPT="${BUILD_SCRIPT//__EXTRA_CONF_CONTENT__/}" |
| 330 | +fi |
| 331 | + |
| 332 | +# Clean up container on exit (covers crashes, interrupts, and normal exit) |
| 333 | +cleanup() { |
| 334 | + docker rm -f "${CONTAINER_NAME}" 2>/dev/null || true |
| 335 | +} |
| 336 | +trap cleanup EXIT |
| 337 | + |
| 338 | +# Stop any existing container with the same name |
| 339 | +docker rm -f "${CONTAINER_NAME}" 2>/dev/null || true |
| 340 | + |
| 341 | +# Build docker run args |
| 342 | +DOCKER_ARGS=( |
| 343 | + --name "${CONTAINER_NAME}" |
| 344 | + --rm |
| 345 | +) |
| 346 | + |
| 347 | +if [[ "$INTERACTIVE" == "1" ]]; then |
| 348 | + DOCKER_ARGS+=(-it) |
| 349 | +fi |
| 350 | + |
| 351 | +echo "==> Starting container..." |
| 352 | +echo "" |
| 353 | + |
| 354 | +if [[ "$INTERACTIVE" == "1" ]]; then |
| 355 | + docker run "${DOCKER_ARGS[@]}" \ |
| 356 | + "${DOCKER_IMAGE}" \ |
| 357 | + bash -c "${BUILD_SCRIPT}" |
| 358 | +else |
| 359 | + { |
| 360 | + echo "===== wolfSSL Zephyr Test Log =====" |
| 361 | + echo "Date: $(date)" |
| 362 | + echo "Zephyr: ${ZEPHYR_VERSION}" |
| 363 | + echo "Repo: ${WOLFSSL_REPO}" |
| 364 | + echo "Branch: ${WOLFSSL_BRANCH}" |
| 365 | + echo "Board: ${BOARD_TARGET}" |
| 366 | + echo "Sample: ${SAMPLE_NAME}" |
| 367 | + echo "Docker: ${DOCKER_IMAGE}" |
| 368 | + echo "===================================" |
| 369 | + echo "" |
| 370 | + } > "${LOG_FILE}" |
| 371 | + |
| 372 | + set +e |
| 373 | + set -o pipefail |
| 374 | + docker run "${DOCKER_ARGS[@]}" \ |
| 375 | + "${DOCKER_IMAGE}" \ |
| 376 | + bash -c "${BUILD_SCRIPT}" 2>&1 \ |
| 377 | + | tee -a "${LOG_FILE}" |
| 378 | + RC=$? |
| 379 | + set -e |
| 380 | + |
| 381 | + echo "" |
| 382 | + echo "${LOG_FILE}" |
| 383 | + exit $RC |
| 384 | +fi |
0 commit comments