Merge pull request #654 from sysprog21/ci-arch-test #3797
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: [push, pull_request] | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| detect-code-related-file-changes: | |
| runs-on: ubuntu-24.04 | |
| outputs: | |
| has_code_related_changes: ${{ steps.set_has_code_related_changes.outputs.has_code_related_changes }} | |
| steps: | |
| - name: Check out the repo | |
| uses: actions/checkout@v6 | |
| - name: Test changed files | |
| id: changed-files | |
| uses: tj-actions/changed-files@v47 | |
| with: | |
| files: | | |
| .ci/** | |
| build/** | |
| mk/** | |
| src/** | |
| tests/** | |
| tools/** | |
| .clang-format | |
| Dockerfile | |
| Makefile | |
| - name: Set has_code_related_changes | |
| id: set_has_code_related_changes | |
| run: | | |
| if [[ ${{ steps.changed-files.outputs.any_changed }} == true ]]; then | |
| echo "has_code_related_changes=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "has_code_related_changes=false" >> $GITHUB_OUTPUT | |
| fi | |
| host-x64: | |
| needs: [detect-code-related-file-changes] | |
| if: needs.detect-code-related-file-changes.outputs.has_code_related_changes == 'true' | |
| timeout-minutes: 60 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| compiler: [gcc, clang] | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| submodules: 'true' | |
| - name: Read LLVM Version | |
| id: llvm-version | |
| run: echo "version=$(cat .ci/llvm-version)" >> $GITHUB_OUTPUT | |
| - name: Cache LLVM | |
| id: cache-llvm | |
| uses: actions/cache@v5 | |
| with: | |
| path: /usr/lib/llvm-${{ steps.llvm-version.outputs.version }} | |
| key: ${{ runner.os }}-${{ runner.arch }}-llvm-${{ steps.llvm-version.outputs.version }} | |
| - name: Cache RISC-V Toolchain | |
| id: cache-toolchain | |
| uses: actions/cache@v5 | |
| with: | |
| path: ${{ github.workspace }}/toolchain | |
| key: ${{ runner.os }}-${{ runner.arch }}-riscv-toolchain-${{ hashFiles('.ci/riscv-toolchain-install.sh') }} | |
| - name: Install dependencies | |
| run: | | |
| set +e # Disable errexit | |
| # Configure apt to tolerate mirror sync failures | |
| sudo tee /etc/apt/apt.conf.d/99-ci-reliability > /dev/null << 'EOF' | |
| APT::Update::Error-Mode "any"; | |
| Acquire::IndexTargets::deb::DEP-11::DefaultEnabled "false"; | |
| Acquire::IndexTargets::deb::DEP-11-icons::DefaultEnabled "false"; | |
| Acquire::IndexTargets::deb::DEP-11-icons-hidpi::DefaultEnabled "false"; | |
| Acquire::Retries "3"; | |
| Acquire::Check-Valid-Until "false"; | |
| EOF | |
| # Update with error tolerance | |
| sudo apt-get update -q=2 2>&1 | |
| UPDATE_EXIT=$? | |
| if [ $UPDATE_EXIT -ne 0 ]; then | |
| echo "WARNING: apt update exited with code $UPDATE_EXIT, continuing..." | |
| fi | |
| sudo apt-get install -q=2 curl libsdl2-dev libsdl2-mixer-dev device-tree-compiler expect bc p7zip-full | |
| set -e # Re-enable errexit | |
| shell: bash | |
| - name: Install RISC-V Toolchain | |
| if: steps.cache-toolchain.outputs.cache-hit != 'true' | |
| run: | | |
| .ci/riscv-toolchain-install.sh | |
| - name: Setup RISC-V Toolchain PATH | |
| run: echo "${{ github.workspace }}/toolchain/bin" >> $GITHUB_PATH | |
| - name: Install LLVM | |
| if: steps.cache-llvm.outputs.cache-hit != 'true' | |
| run: | | |
| .ci/fetch.sh -q -o llvm.sh https://apt.llvm.org/llvm.sh | |
| chmod +x ./llvm.sh | |
| sudo ./llvm.sh ${{ steps.llvm-version.outputs.version }} | |
| - name: Setup LLVM PATH | |
| run: echo "/usr/lib/llvm-${{ steps.llvm-version.outputs.version }}/bin" >> $GITHUB_PATH | |
| - name: Install compiler | |
| id: install_cc | |
| uses: rlalik/setup-cpp-compiler@master | |
| with: | |
| compiler: ${{ matrix.compiler }} | |
| - name: Setup emsdk | |
| uses: mymindstorm/setup-emsdk@v14 | |
| with: | |
| version: 3.1.51 | |
| actions-cache-folder: 'emsdk-cache' | |
| - name: Set parallel jobs variable | |
| run: | | |
| echo "PARALLEL=-j$(nproc)" >> "$GITHUB_ENV" | |
| echo "BOOT_LINUX_TEST=TMP_FILE=\$(mktemp "$RUNNER_TEMP/tmpfile.XXXXXX"); \ | |
| sudo env TMP_FILE=\${TMP_FILE} .ci/boot-linux-prepare.sh setup; \ | |
| . \${TMP_FILE}; \ | |
| .ci/boot-linux.sh; \ | |
| EXIT_CODE=\$?; \ | |
| sudo env TMP_FILE=\${TMP_FILE} BLK_DEV_EXT4=\${BLK_DEV_EXT4} \ | |
| BLK_DEV_SIMPLEFS=\${BLK_DEV_SIMPLEFS} .ci/boot-linux-prepare.sh cleanup; \ | |
| exit \${EXIT_CODE};" >> "$GITHUB_ENV" | |
| - name: Cache build artifacts | |
| uses: actions/cache@v5 | |
| with: | |
| path: build/ | |
| key: rv32emu-artifacts-${{ runner.os }}-${{ hashFiles('mk/artifact.mk', 'mk/external.mk') }} | |
| restore-keys: | | |
| rv32emu-artifacts-${{ runner.os }}- | |
| - name: Fetch artifacts | |
| env: | |
| CC: ${{ steps.install_cc.outputs.cc }} | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| . .ci/common.sh | |
| fetch_artifact ELF | |
| fetch_artifact Linux-Image ENABLE_SYSTEM=1 | |
| rm -f build/.config # Clean config after ENABLE_SYSTEM=1 to prevent contamination | |
| fetch_artifact sail ENABLE_ARCH_TEST=1 | |
| # get from rv32emu-prebuilt | |
| .ci/fetch.sh -o build/shareware_doom_iwad.zip "https://raw.githubusercontent.com/sysprog21/rv32emu-prebuilt/doom-artifact/shareware_doom_iwad.zip" | |
| unzip -o -d build/ build/shareware_doom_iwad.zip | |
| # emcc builds run on x64 only - WebAssembly output is platform-independent | |
| # This validates Emscripten compatibility without redundant builds on macOS | |
| - name: default build using emcc | |
| if: success() | |
| run: | | |
| make distclean | |
| make CC=emcc ENABLE_JIT=0 $PARALLEL | |
| - name: default build for system emulation using emcc | |
| if: success() | |
| run: | | |
| make distclean | |
| make CC=emcc ENABLE_SYSTEM=1 ENABLE_JIT=0 $PARALLEL | |
| make distclean ENABLE_SYSTEM=1 | |
| - name: Build with various optimization levels | |
| if: success() | |
| env: | |
| CC: ${{ steps.install_cc.outputs.cc }} | |
| run: | | |
| set -euo pipefail | |
| # Test boundary cases: -O0 (unoptimized), -O2 (standard), -Ofast (aggressive) | |
| for opt_level in -O0 -O2 -Ofast; do | |
| echo "Building with OPT_LEVEL=$opt_level" | |
| if ! (make distclean && make OPT_LEVEL=$opt_level $PARALLEL); then | |
| echo "ERROR: Build failed with OPT_LEVEL=$opt_level" | |
| exit 1 | |
| fi | |
| done | |
| - name: Build system emulation with various optimization levels | |
| if: success() | |
| env: | |
| CC: ${{ steps.install_cc.outputs.cc }} | |
| run: | | |
| set -euo pipefail | |
| # Test boundary cases: -O0 (unoptimized), -O2 (standard), -Ofast (aggressive) | |
| for opt_level in -O0 -O2 -Ofast; do | |
| echo "Building system emulation with OPT_LEVEL=$opt_level" | |
| if ! (make distclean && make OPT_LEVEL=$opt_level ENABLE_SYSTEM=1 $PARALLEL); then | |
| echo "ERROR: System emulation build failed with OPT_LEVEL=$opt_level" | |
| exit 1 | |
| fi | |
| done | |
| - name: check + tests | |
| if: success() | |
| env: | |
| CC: ${{ steps.install_cc.outputs.cc }} | |
| run: | | |
| make distclean | |
| make check $PARALLEL | |
| make tests $PARALLEL | |
| make misalign $PARALLEL | |
| make tool $PARALLEL | |
| - name: core extension disable tests (interpreter + JIT) | |
| if: success() | |
| env: | |
| CC: ${{ steps.install_cc.outputs.cc }} | |
| run: | | |
| set -euo pipefail | |
| # Core extensions + bit manipulation + CSR/fence + performance features | |
| # Tests both interpreter and JIT modes (12 × 2 = 24 builds) | |
| # Consolidated from 25 separate builds (13 interpreter + 12 JIT) | |
| for ext in ENABLE_EXT_M ENABLE_EXT_A ENABLE_EXT_F ENABLE_EXT_C \ | |
| ENABLE_Zba ENABLE_Zbb ENABLE_Zbc ENABLE_Zbs \ | |
| ENABLE_Zicsr ENABLE_Zifencei \ | |
| ENABLE_MOP_FUSION ENABLE_BLOCK_CHAINING; do | |
| echo "Testing ${ext}=0 (interpreter)" | |
| if ! (make distclean && make ${ext}=0 check $PARALLEL); then | |
| echo "ERROR: Interpreter test failed with ${ext}=0" | |
| exit 1 | |
| fi | |
| echo "Testing ${ext}=0 (JIT)" | |
| if ! (make ENABLE_JIT=1 clean && make ${ext}=0 ENABLE_JIT=1 check $PARALLEL); then | |
| echo "ERROR: JIT test failed with ${ext}=0" | |
| exit 1 | |
| fi | |
| done | |
| - name: SDL disable test | |
| if: success() | |
| env: | |
| CC: ${{ steps.install_cc.outputs.cc }} | |
| run: | | |
| # SDL is graphics subsystem, interpreter-only test sufficient | |
| make distclean && make ENABLE_SDL=0 check $PARALLEL | |
| - name: misalignment test in block emulation | |
| if: success() | |
| env: | |
| CC: ${{ steps.install_cc.outputs.cc }} | |
| run: | | |
| make -C tests/system/alignment/ | |
| make distclean && make ENABLE_ELF_LOADER=1 ENABLE_EXT_C=0 ENABLE_SYSTEM=1 misalign-in-blk-emu $PARALLEL | |
| - name: MMU test | |
| if: success() | |
| env: | |
| CC: ${{ steps.install_cc.outputs.cc }} | |
| run: | | |
| make -C tests/system/mmu/ | |
| make distclean && make ENABLE_ELF_LOADER=1 ENABLE_SYSTEM=1 mmu-test $PARALLEL | |
| - name: gdbstub test | |
| if: success() | |
| env: | |
| CC: ${{ steps.install_cc.outputs.cc }} | |
| run: | | |
| make distclean && make ENABLE_GDBSTUB=1 gdbstub-test $PARALLEL | |
| - name: JIT base test | |
| if: success() | |
| env: | |
| CC: ${{ steps.install_cc.outputs.cc }} | |
| run: | | |
| # Base JIT test (extension disable tests handled in consolidated step above) | |
| make ENABLE_JIT=1 clean && make ENABLE_JIT=1 check $PARALLEL | |
| - name: undefined behavior test | |
| if: success() || failure() | |
| run: | | |
| # Clear cached config from previous steps to avoid ENABLE_SYSTEM=1 contamination | |
| rm -f build/.config | |
| make distclean && make ENABLE_UBSAN=1 check $PARALLEL | |
| make ENABLE_JIT=1 clean && make ENABLE_JIT=1 ENABLE_UBSAN=1 check $PARALLEL | |
| - name: boot Linux kernel test | |
| if: success() | |
| env: | |
| CC: ${{ steps.install_cc.outputs.cc }} | |
| run: | | |
| make distclean && make INITRD_SIZE=32 ENABLE_SYSTEM=1 $PARALLEL && make ENABLE_SYSTEM=1 artifact $PARALLEL | |
| bash -c "${BOOT_LINUX_TEST}" | |
| make ENABLE_SYSTEM=1 clean | |
| - name: boot Linux kernel test (JIT) | |
| if: success() | |
| env: | |
| CC: ${{ steps.install_cc.outputs.cc }} | |
| run: | | |
| make distclean && make INITRD_SIZE=32 ENABLE_SYSTEM=1 ENABLE_JIT=1 ENABLE_MOP_FUSION=0 $PARALLEL && make ENABLE_SYSTEM=1 artifact $PARALLEL | |
| bash -c "${BOOT_LINUX_TEST}" | |
| make ENABLE_SYSTEM=1 ENABLE_JIT=1 ENABLE_MOP_FUSION=0 clean | |
| - name: Architecture test | |
| if: success() | |
| env: | |
| CC: ${{ steps.install_cc.outputs.cc }} | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| . .ci/common.sh | |
| export LATEST_RELEASE=$(download_with_headers "https://api.github.com/repos/sysprog21/rv32emu-prebuilt/releases" \ | |
| "Authorization: Bearer ${GH_TOKEN}" \ | |
| | grep '"tag_name"' \ | |
| | grep "sail" \ | |
| | head -n 1 \ | |
| | sed -E 's/.*"tag_name": "([^"]+)".*/\1/') | |
| .ci/riscv-tests.sh | |
| # Native AArch64 on GitHub ARM runners - fast, no QEMU overhead | |
| host-arm64: | |
| needs: [detect-code-related-file-changes] | |
| if: needs.detect-code-related-file-changes.outputs.has_code_related_changes == 'true' | |
| timeout-minutes: 30 | |
| runs-on: ubuntu-24.04-arm | |
| steps: | |
| - name: checkout code | |
| uses: actions/checkout@v6 | |
| with: | |
| submodules: 'true' | |
| - name: Set LLVM version | |
| run: echo "LLVM_VERSION=$(cat .ci/llvm-version)" >> $GITHUB_ENV | |
| - name: Install dependencies | |
| run: | | |
| # Configure apt to tolerate ARM mirror sync failures | |
| sudo tee /etc/apt/apt.conf.d/99-ci-reliability > /dev/null << 'EOF' | |
| APT::Update::Error-Mode "any"; | |
| Acquire::IndexTargets::deb::DEP-11::DefaultEnabled "false"; | |
| Acquire::IndexTargets::deb::DEP-11-icons::DefaultEnabled "false"; | |
| Acquire::IndexTargets::deb::DEP-11-icons-hidpi::DefaultEnabled "false"; | |
| Acquire::Retries "3"; | |
| Acquire::Check-Valid-Until "false"; | |
| EOF | |
| # Update with error tolerance | |
| sudo apt-get update -q=2 || { | |
| echo "WARNING: apt update failed, continuing with cached indexes..." | |
| } | |
| # Install packages with retry for critical ones | |
| sudo apt-get install -q=2 -y make curl wget libsdl2-dev libsdl2-mixer-dev \ | |
| lsb-release software-properties-common gnupg bc || { | |
| echo "WARNING: Some packages failed, retrying critical ones..." | |
| sudo apt-get install -q=2 -y --no-download make curl wget || true | |
| } | |
| - name: Install LLVM | |
| run: | | |
| .ci/fetch.sh -q -o llvm.sh https://apt.llvm.org/llvm.sh | |
| chmod +x ./llvm.sh | |
| sudo ./llvm.sh $LLVM_VERSION | |
| - name: Setup LLVM PATH | |
| run: echo "/usr/lib/llvm-${LLVM_VERSION}/bin" >> $GITHUB_PATH | |
| - name: Set parallel jobs variable | |
| run: echo "PARALLEL=-j$(nproc)" >> "$GITHUB_ENV" | |
| - name: Fetch artifacts | |
| env: | |
| CC: clang-${{ env.LLVM_VERSION }} | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| . .ci/common.sh | |
| LATEST_RELEASE=$(download_with_headers "https://api.github.com/repos/sysprog21/rv32emu-prebuilt/releases" \ | |
| "Authorization: Bearer ${GH_TOKEN}" \ | |
| | grep '"tag_name"' \ | |
| | grep "ELF" \ | |
| | head -n 1 \ | |
| | sed -E 's/.*"tag_name": "([^"]+)".*/\1/') | |
| if [ -z "$LATEST_RELEASE" ]; then | |
| echo "Error: Failed to fetch LATEST_RELEASE from GitHub API" | |
| exit 1 | |
| fi | |
| make LATEST_RELEASE=$LATEST_RELEASE artifact | |
| # FIXME: gcc build fails on AArch64/Linux hosts | |
| - name: check + tests | |
| env: | |
| CC: clang-${{ env.LLVM_VERSION }} | |
| run: | | |
| make $PARALLEL | |
| make check $PARALLEL | |
| - name: JIT tests | |
| env: | |
| CC: clang-${{ env.LLVM_VERSION }} | |
| run: | | |
| set -euo pipefail | |
| make ENABLE_JIT=1 clean && make ENABLE_JIT=1 check $PARALLEL | |
| for ext in ENABLE_EXT_A ENABLE_EXT_F ENABLE_EXT_C; do | |
| echo "JIT test with ${ext}=0" | |
| make ENABLE_JIT=1 clean && make ${ext}=0 ENABLE_JIT=1 check $PARALLEL | |
| done | |
| macOS-arm64: | |
| needs: [detect-code-related-file-changes] | |
| if: needs.detect-code-related-file-changes.outputs.has_code_related_changes == 'true' | |
| timeout-minutes: 60 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| compiler: [gcc-15, clang] | |
| runs-on: macos-latest # M1 chip | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| submodules: 'true' | |
| - name: Read LLVM Version | |
| id: llvm-version | |
| run: echo "version=$(cat .ci/llvm-version)" >> $GITHUB_OUTPUT | |
| - name: Cache RISC-V Toolchain | |
| id: cache-toolchain | |
| uses: actions/cache@v5 | |
| with: | |
| path: ${{ github.workspace }}/toolchain | |
| key: ${{ runner.os }}-${{ runner.arch }}-riscv-toolchain-${{ hashFiles('.ci/riscv-toolchain-install.sh') }} | |
| - name: Cache Homebrew packages | |
| id: cache-brew | |
| uses: actions/cache@v5 | |
| with: | |
| path: | | |
| /opt/homebrew/Cellar/llvm@${{ steps.llvm-version.outputs.version }} | |
| /opt/homebrew/Cellar/sdl2 | |
| /opt/homebrew/Cellar/dtc | |
| key: ${{ runner.os }}-${{ runner.arch }}-brew-llvm${{ steps.llvm-version.outputs.version }}-${{ hashFiles('.github/workflows/main.yml') }} | |
| - name: Install dependencies | |
| run: | | |
| brew install make dtc expect sdl2 bc e2fsprogs p7zip llvm@${{ steps.llvm-version.outputs.version }} dcfldd | |
| brew install sdl2_mixer || echo "Warning: sdl2_mixer installation failed, continuing without SDL_MIXER support" | |
| # Ensure symlinks exist after cache restore (cached Cellar may lack /opt/homebrew/bin links) | |
| brew link dtc --overwrite 2>/dev/null || true | |
| command -v dtc || { echo "ERROR: dtc not found in PATH after install"; exit 1; } | |
| - name: Install RISC-V Toolchain | |
| if: steps.cache-toolchain.outputs.cache-hit != 'true' | |
| run: | | |
| .ci/riscv-toolchain-install.sh | |
| - name: Setup toolchain paths | |
| run: | | |
| echo "${{ github.workspace }}/toolchain/bin" >> $GITHUB_PATH | |
| echo "$(brew --prefix llvm@${{ steps.llvm-version.outputs.version }})/bin" >> $GITHUB_PATH | |
| - name: Set up Python 3.12 | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: '3.12' | |
| - name: Install compiler | |
| id: install_cc | |
| uses: rlalik/setup-cpp-compiler@master | |
| with: | |
| compiler: ${{ matrix.compiler }} | |
| # NOTE: emsdk removed - emcc builds run on host-x64 only (WebAssembly is platform-independent) | |
| - name: Set parallel jobs variable | |
| run: | | |
| echo "PARALLEL=-j$(sysctl -n hw.logicalcpu)" >> "$GITHUB_ENV" | |
| echo "BOOT_LINUX_TEST=TMP_FILE=\$(mktemp "$RUNNER_TEMP/tmpfile.XXXXXX"); \ | |
| sudo env TMP_FILE=\${TMP_FILE} .ci/boot-linux-prepare.sh setup; \ | |
| . \${TMP_FILE}; \ | |
| .ci/boot-linux.sh; \ | |
| EXIT_CODE=\$?; \ | |
| sudo env TMP_FILE=\${TMP_FILE} BLK_DEV_EXT4=\${BLK_DEV_EXT4} .ci/boot-linux-prepare.sh cleanup; \ | |
| exit \${EXIT_CODE};" >> "$GITHUB_ENV" | |
| - name: Symlink gcc-15 due to the default /usr/local/bin/gcc links to system's clang | |
| run: | | |
| ln -s /opt/homebrew/opt/gcc/bin/gcc-15 /usr/local/bin/gcc-15 | |
| - name: fetch artifact first to reduce HTTP requests | |
| env: | |
| CC: ${{ steps.install_cc.outputs.cc }} | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| . .ci/common.sh | |
| fetch_artifact ELF | |
| fetch_artifact Linux-Image ENABLE_SYSTEM=1 | |
| rm -f build/.config # Clean config after ENABLE_SYSTEM=1 to prevent contamination | |
| fetch_artifact sail ENABLE_ARCH_TEST=1 | |
| # get from rv32emu-prebuilt | |
| .ci/fetch.sh -o build/shareware_doom_iwad.zip "https://raw.githubusercontent.com/sysprog21/rv32emu-prebuilt/doom-artifact/shareware_doom_iwad.zip" | |
| unzip -o -d build/ build/shareware_doom_iwad.zip | |
| # NOTE: emcc builds removed - they run on host-x64 only since WebAssembly output is platform-independent | |
| - name: check + tests | |
| if: success() | |
| env: | |
| CC: ${{ steps.install_cc.outputs.cc }} | |
| run: | | |
| make distclean | |
| make check $PARALLEL | |
| make tests $PARALLEL | |
| make misalign $PARALLEL | |
| make tool $PARALLEL | |
| - name: core extension disable tests (interpreter + JIT) | |
| if: success() | |
| env: | |
| CC: ${{ steps.install_cc.outputs.cc }} | |
| run: | | |
| set -euo pipefail | |
| # Core extensions + bit manipulation + CSR/fence + performance features | |
| # Tests both interpreter and JIT modes (12 × 2 = 24 builds) | |
| # Consolidated from 25 separate builds (13 interpreter + 12 JIT) | |
| for ext in ENABLE_EXT_M ENABLE_EXT_A ENABLE_EXT_F ENABLE_EXT_C \ | |
| ENABLE_Zba ENABLE_Zbb ENABLE_Zbc ENABLE_Zbs \ | |
| ENABLE_Zicsr ENABLE_Zifencei \ | |
| ENABLE_MOP_FUSION ENABLE_BLOCK_CHAINING; do | |
| echo "Testing ${ext}=0 (interpreter)" | |
| if ! (make distclean && make ${ext}=0 check $PARALLEL); then | |
| echo "ERROR: Interpreter test failed with ${ext}=0" | |
| exit 1 | |
| fi | |
| echo "Testing ${ext}=0 (JIT)" | |
| if ! (make ENABLE_JIT=1 clean && make ${ext}=0 ENABLE_JIT=1 check $PARALLEL); then | |
| echo "ERROR: JIT test failed with ${ext}=0" | |
| exit 1 | |
| fi | |
| done | |
| - name: SDL disable test | |
| if: success() | |
| env: | |
| CC: ${{ steps.install_cc.outputs.cc }} | |
| run: | | |
| # SDL is graphics subsystem, interpreter-only test sufficient | |
| make distclean && make ENABLE_SDL=0 check $PARALLEL | |
| - name: gdbstub test, need RV32 toolchain | |
| if: success() | |
| env: | |
| CC: ${{ steps.install_cc.outputs.cc }} | |
| run: | | |
| make distclean && make ENABLE_GDBSTUB=1 gdbstub-test $PARALLEL | |
| - name: JIT base test | |
| if: success() | |
| env: | |
| CC: ${{ steps.install_cc.outputs.cc }} | |
| run: | | |
| # Base JIT test (extension disable tests handled in consolidated step above) | |
| make ENABLE_JIT=1 clean && make ENABLE_JIT=1 check $PARALLEL | |
| - name: undefined behavior test | |
| if: (success() || failure()) && steps.install_cc.outputs.cc == 'clang' # gcc on macOS/arm64 does not support sanitizers | |
| env: | |
| CC: ${{ steps.install_cc.outputs.cc }} | |
| run: | | |
| # Clear cached config from previous steps to avoid ENABLE_SYSTEM=1 contamination | |
| rm -f build/.config | |
| make distclean && make ENABLE_UBSAN=1 check $PARALLEL | |
| make ENABLE_JIT=1 clean && make ENABLE_JIT=1 ENABLE_UBSAN=1 check $PARALLEL | |
| - name: boot Linux kernel test | |
| if: success() | |
| env: | |
| CC: ${{ steps.install_cc.outputs.cc }} | |
| run: | | |
| make distclean && make INITRD_SIZE=32 ENABLE_SYSTEM=1 $PARALLEL && \ | |
| make ENABLE_SYSTEM=1 artifact $PARALLEL | |
| bash -c "${BOOT_LINUX_TEST}" | |
| make ENABLE_SYSTEM=1 clean | |
| - name: boot Linux kernel test (JIT) | |
| if: success() | |
| env: | |
| CC: ${{ steps.install_cc.outputs.cc }} | |
| run: | | |
| make distclean && make INITRD_SIZE=32 ENABLE_SYSTEM=1 ENABLE_JIT=1 ENABLE_MOP_FUSION=0 $PARALLEL && make ENABLE_SYSTEM=1 artifact $PARALLEL | |
| bash -c "${BOOT_LINUX_TEST}" | |
| make ENABLE_SYSTEM=1 ENABLE_JIT=1 ENABLE_MOP_FUSION=0 clean | |
| - name: Architecture test | |
| if: success() | |
| env: | |
| CC: ${{ steps.install_cc.outputs.cc }} | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| . .ci/common.sh | |
| export LATEST_RELEASE=$(download_with_headers "https://api.github.com/repos/sysprog21/rv32emu-prebuilt/releases" \ | |
| "Authorization: Bearer ${GH_TOKEN}" \ | |
| | grep '"tag_name"' \ | |
| | grep "sail" \ | |
| | head -n 1 \ | |
| | sed -E 's/.*"tag_name": "([^"]+)".*/\1/') | |
| python3 -m venv venv | |
| . venv/bin/activate | |
| .ci/riscv-tests.sh | |
| coding-style: | |
| needs: [detect-code-related-file-changes] | |
| if: needs.detect-code-related-file-changes.outputs.has_code_related_changes == 'true' | |
| timeout-minutes: 15 | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: coding convention | |
| run: | | |
| wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.gpg >/dev/null | |
| echo "deb http://apt.llvm.org/$(lsb_release -cs)/ llvm-toolchain-$(lsb_release -cs)-20 main" | sudo tee /etc/apt/sources.list.d/llvm-20.list | |
| sudo apt-get update -q=2 | |
| sudo apt-get install -q=2 clang-format-20 shfmt python3-pip | |
| pip3 install black==25.1.0 | |
| curl -LSfs https://go.mskelton.dev/dtsfmt/install | sh -s -- -y | |
| .ci/check-newline.sh | |
| .ci/check-format.sh | |
| shell: bash | |
| static-analysis: | |
| needs: [detect-code-related-file-changes] | |
| if: needs.detect-code-related-file-changes.outputs.has_code_related_changes == 'true' | |
| timeout-minutes: 45 | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| submodules: 'true' | |
| # LLVM static analysis | |
| - name: set up scan-build | |
| run: | | |
| set +e # Disable errexit | |
| # Configure apt to tolerate mirror sync failures | |
| sudo tee /etc/apt/apt.conf.d/99-ci-reliability > /dev/null << 'EOF' | |
| APT::Update::Error-Mode "any"; | |
| Acquire::IndexTargets::deb::DEP-11::DefaultEnabled "false"; | |
| Acquire::IndexTargets::deb::DEP-11-icons::DefaultEnabled "false"; | |
| Acquire::IndexTargets::deb::DEP-11-icons-hidpi::DefaultEnabled "false"; | |
| Acquire::Retries "3"; | |
| Acquire::Check-Valid-Until "false"; | |
| EOF | |
| # Update with error tolerance | |
| sudo apt-get update -q=2 2>&1 | |
| UPDATE_EXIT=$? | |
| if [ $UPDATE_EXIT -ne 0 ]; then | |
| echo "WARNING: apt update exited with code $UPDATE_EXIT, continuing..." | |
| fi | |
| sudo apt-get install -q=2 curl libsdl2-dev libsdl2-mixer-dev | |
| LLVM_VERSION=$(cat .ci/llvm-version) | |
| .ci/fetch.sh -q -o llvm.sh https://apt.llvm.org/llvm.sh | |
| chmod +x ./llvm.sh | |
| sudo ./llvm.sh $LLVM_VERSION | |
| sudo apt-get install -q=2 clang-${LLVM_VERSION} clang-tools-${LLVM_VERSION} | |
| set -e # Re-enable errexit | |
| shell: bash | |
| - name: run scan-build without JIT | |
| env: | |
| LATEST_RELEASE: dummy | |
| run: | | |
| LLVM_VERSION=$(cat .ci/llvm-version) | |
| make distclean && scan-build-${LLVM_VERSION} -v -o ~/scan-build --status-bugs --use-cc=clang-${LLVM_VERSION} --force-analyze-debug-code --show-description -analyzer-config stable-report-filename=true -enable-checker valist,nullability make ENABLE_EXT_F=0 ENABLE_SDL=0 ENABLE_JIT=0 | |
| - name: run scan-build with JIT | |
| env: | |
| LATEST_RELEASE: dummy | |
| run: | | |
| LLVM_VERSION=$(cat .ci/llvm-version) | |
| make ENABLE_JIT=1 distclean && scan-build-${LLVM_VERSION} -v -o ~/scan-build --status-bugs --use-cc=clang-${LLVM_VERSION} --force-analyze-debug-code --show-description -analyzer-config stable-report-filename=true -enable-checker valist,nullability make ENABLE_EXT_F=0 ENABLE_SDL=0 ENABLE_JIT=1 | |
| # https://docs.docker.com/build/ci/github-actions/multi-platform/ | |
| docker-hub-build-and-publish: | |
| needs: [detect-code-related-file-changes] | |
| if: needs.detect-code-related-file-changes.outputs.has_code_related_changes == 'true' | |
| timeout-minutes: 60 | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - name: Check out the repo | |
| uses: actions/checkout@v6 | |
| with: | |
| submodules: 'true' | |
| - name: Set up QEMU | |
| uses: docker/setup-qemu-action@v3 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Login to Docker Hub | |
| uses: docker/login-action@v3 | |
| if: github.event_name == 'push' | |
| with: | |
| username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN }} | |
| - name: Get short commit SHA1 | |
| if: github.event_name == 'push' | |
| shell: bash | |
| run: | | |
| echo "short_hash=$(git rev-parse --short "$GITHUB_SHA")" >> "$GITHUB_ENV" | |
| - name: Build and push | |
| if: github.event_name == 'push' | |
| uses: docker/build-push-action@v6 | |
| with: | |
| push: true | |
| context: . | |
| platforms: linux/amd64,linux/arm64/v8 | |
| tags: sysprog21/rv32emu:latest, sysprog21/rv32emu:${{ env.short_hash }} |