diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0bf36a0ac3a..e4ad4bfc38f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,6 +40,12 @@ jobs: cppcheck: runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + # The platformio.ini default_envs. A bare `pio check` walks them one after + # another (~15 min); one job each runs them in parallel. + environment: [default, sticky, x4-pro, x4-classic] steps: - uses: actions/checkout@v6 with: @@ -74,12 +80,12 @@ jobs: run: sudo apt-get update -q && sudo apt-get install -y -q libpcre3 - name: Run cppcheck - run: pio check --fail-on-defect low --fail-on-defect medium --fail-on-defect high + run: pio check -e ${{ matrix.environment }} --fail-on-defect low --fail-on-defect medium --fail-on-defect high build: # Firmware artifacts are validated on pull requests; release builds stay - # in the dedicated release workflow. - if: github.event_name == 'pull_request' + # in the dedicated release workflow. Pushes build too, only to save the + # ccache: PR runs can restore caches from the base branch, not from other PRs. runs-on: ubuntu-latest strategy: fail-fast: false @@ -129,10 +135,34 @@ jobs: uv pip install --python ~/.platformio/penv/bin/python "pioarduino==6.1.19" ~/.platformio/penv/bin/pio --version + - name: Install ccache + run: | + sudo apt-get update -q && sudo apt-get install -y -q ccache + echo "CCACHE_DIR=$HOME/.cache/ccache" >> "$GITHUB_ENV" + + - name: Cache ccache + uses: actions/cache@v4 + with: + path: ~/.cache/ccache + key: ${{ runner.os }}-ccache-${{ matrix.environment }}-${{ github.sha }} + restore-keys: ${{ runner.os }}-ccache-${{ matrix.environment }}- + - name: Build ${{ matrix.environment }} firmware + env: + # scripts/pio_ccache.py prefixes the SCons compile commands with ccache; + # IDF_CCACHE_ENABLE covers the ESP-IDF CMake build of custom_sdkconfig envs. + # Absolute path: the platform's nested framework build runs from another dir. + XINK_CCACHE: /usr/bin/ccache + PLATFORMIO_EXTRA_SCRIPTS: pre:${{ github.workspace }}/scripts/pio_ccache.py + IDF_CCACHE_ENABLE: "1" + CCACHE_BASEDIR: ${{ github.workspace }} + CCACHE_NOHASHDIR: "1" + CCACHE_MAXSIZE: 400M run: | set -euo pipefail + ccache -z pio run -j4 -e ${{ matrix.environment }} | tee pio-${{ matrix.environment }}.log + ccache -s - name: Extract firmware stats run: | @@ -143,6 +173,7 @@ jobs: } >> "$GITHUB_STEP_SUMMARY" - name: Upload ${{ matrix.environment }} firmware + if: github.event_name == 'pull_request' # Flashable app image for testing PR builds; the release workflows publish # the same renamed firmware-.bin. uses: actions/upload-artifact@v7 diff --git a/scripts/pio_ccache.py b/scripts/pio_ccache.py new file mode 100644 index 00000000000..95327f898ba --- /dev/null +++ b/scripts/pio_ccache.py @@ -0,0 +1,17 @@ +# PlatformIO pre-script: put ccache in front of the compile commands (CI build job, cloud +# sessions). Enabled only when $XINK_CCACHE names the ccache binary; set it with +# PLATFORMIO_EXTRA_SCRIPTS=pre:/scripts/pio_ccache.py. +# (Pre, so the change is in place before library/source environments are cloned from env; +# $CC itself is expanded later, when commands run.) +# CC/CXX stay untouched because ESP-IDF steps build paths from them ($TOOLCHAIN/bin/$CC); +# only the C/C++ compile command lines get the ccache prefix. +import os + +Import("env") # noqa: F821 (SCons builtin) + +cc = os.environ.get("XINK_CCACHE") +if cc: + for var in ("CCCOM", "CXXCOM", "SHCCCOM", "SHCXXCOM"): + cur = env.get(var) # noqa: F821 + if cur and "ccache" not in str(cur): + env.Replace(**{var: f'"{cc}" {cur}'}) # noqa: F821