From a6cf5cc1e3aecebda1eb9330f88540b58ac53d5c Mon Sep 17 00:00:00 2001 From: Hanif Koh Date: Thu, 10 Sep 2026 18:23:30 +0800 Subject: [PATCH 1/4] Add the includes the precompiled header was supplying on macOS A build without SLIC3R_PCH had never been tried on macOS. Three files used what pchheader.hpp happened to include: LocalesUtils.cpp needs and , and the two dialogs need . The GTK port's headers and libstdc++ pull these in transitively, the Cocoa port's headers and libc++ do not. --- src/libslic3r/LocalesUtils.cpp | 2 ++ src/slic3r/GUI/AmsMappingPopup.cpp | 1 + src/slic3r/GUI/PhysicalPrinterDialog.cpp | 1 + 3 files changed, 4 insertions(+) diff --git a/src/libslic3r/LocalesUtils.cpp b/src/libslic3r/LocalesUtils.cpp index 308752cc62..e727b29b09 100644 --- a/src/libslic3r/LocalesUtils.cpp +++ b/src/libslic3r/LocalesUtils.cpp @@ -3,6 +3,8 @@ #ifdef _WIN32 #include #endif +#include +#include #include #include diff --git a/src/slic3r/GUI/AmsMappingPopup.cpp b/src/slic3r/GUI/AmsMappingPopup.cpp index 3e745b0a64..22ffaef034 100644 --- a/src/slic3r/GUI/AmsMappingPopup.cpp +++ b/src/slic3r/GUI/AmsMappingPopup.cpp @@ -11,6 +11,7 @@ #include "MainFrame.hpp" #include "format.hpp" #include "Widgets/ProgressDialog.hpp" +#include #include "Widgets/RoundedRectangle.hpp" #include "Widgets/StaticBox.hpp" diff --git a/src/slic3r/GUI/PhysicalPrinterDialog.cpp b/src/slic3r/GUI/PhysicalPrinterDialog.cpp index 989cf204e1..04317ca46b 100644 --- a/src/slic3r/GUI/PhysicalPrinterDialog.cpp +++ b/src/slic3r/GUI/PhysicalPrinterDialog.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include From 67f77e16c38519f8854b20dffd7caf38ace1f3c8 Mon Sep 17 00:00:00 2001 From: Hanif Koh Date: Thu, 10 Sep 2026 18:23:58 +0800 Subject: [PATCH 2/4] ci: cache compiled objects between runs Every CI leg compiled the whole tree from scratch, 42 to 57 minutes of each build job. Objects are now cached with ccache, one entry per leg kept on the branch that built it: a push saves the cache and drops the previous entry, a pull request restores main's and keeps nothing. The precompiled header is turned off whenever the cache is on: Clang stamps it with the build time, so every file including it missed. With it off, a warm run hits 98.5 to 98.9 % of compiles and the compile steps take 1 to 4 minutes; a cold run costs 25 to 60 % more than before, and a change to a widely included header lands in between. --- .github/workflows/build_orca.yml | 79 ++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/.github/workflows/build_orca.yml b/.github/workflows/build_orca.yml index 8e1e28db13..f1fb29c46b 100644 --- a/.github/workflows/build_orca.yml +++ b/.github/workflows/build_orca.yml @@ -76,6 +76,56 @@ jobs: if (-not (Test-Path "$cmakeBin\cmake.exe")) { throw "cmake.exe not found at $cmakeBin" } Add-Content -Path $env:GITHUB_PATH -Value $cmakeBin + # Compiler cache. Pushes save it, so main keeps it warm; pull requests + # restore it and discard what they compiled. Objects are keyed on the + # preprocessed source, the compiler and the flags, so a leg only ever + # hits its own entries. A failed install costs the caching, not the build. + - name: Name the compiler cache leg + if: ${{ !inputs.macos-combine-only }} + shell: bash + run: | + leg="${{ runner.os }}-${{ inputs.arch || 'amd64' }}${{ runner.os == 'Windows' && format('-{0}', inputs.compiler) || '' }}" + echo "CCACHE_LEG=$leg" >> "$GITHUB_ENV" + echo "CCACHE_ENTRY=ccache-$leg-${{ github.run_id }}-${{ github.run_attempt }}" >> "$GITHUB_ENV" + + # The action only installs and configures ccache. Restore and save go + # through actions/cache with one path string, since the cache service + # only matches entries saved under the identical path and the action + # spells it differently on Windows. + - name: Compiler cache + id: ccache + if: ${{ !inputs.macos-combine-only }} + continue-on-error: true + uses: hendrikmuhs/ccache-action@v1.2 + with: + key: ${{ env.CCACHE_LEG }} + max-size: 3G + restore: false + save: false + + - name: Restore compiler cache + if: ${{ steps.ccache.outcome == 'success' }} + uses: actions/cache/restore@v6 + with: + path: ${{ github.workspace }}/.ccache + key: ${{ env.CCACHE_ENTRY }} + restore-keys: ccache-${{ env.CCACHE_LEG }}- + + - name: Enable compiler cache + if: ${{ steps.ccache.outcome == 'success' }} + shell: bash + run: | + echo "CMAKE_C_COMPILER_LAUNCHER=ccache" >> "$GITHUB_ENV" + echo "CMAKE_CXX_COMPILER_LAUNCHER=ccache" >> "$GITHUB_ENV" + # Headers a fresh checkout has just written, and the few files that + # use __DATE__ or __TIME__. + echo "CCACHE_SLOPPINESS=time_macros,include_file_mtime,include_file_ctime" >> "$GITHUB_ENV" + # Clang rebuilds the precompiled header with a fresh timestamp on + # every run, so everything that includes it would miss. + echo "ORCA_EXTRA_BUILD_ARGS=-DSLIC3R_PCH=OFF" >> "$GITHUB_ENV" + # The restored directory carries the previous run's counters. + ccache -z + - name: Get the version and date on Ubuntu and macOS if: runner.os != 'Windows' run: | @@ -670,3 +720,32 @@ jobs: asset_name: orca_custom_preset_tests.zip asset_content_type: application/octet-stream max_releases: 1 + + - name: Compiler cache statistics + if: ${{ always() && steps.ccache.outcome == 'success' }} + shell: bash + run: ccache -s -v || ccache -s + + # Entries are immutable, so the new one is saved first and the older + # ones for this leg on this ref are dropped afterwards: a failed save + # leaves the previous entry in place. + - name: Save compiler cache + id: ccache_save + if: ${{ steps.ccache.outcome == 'success' && github.event_name != 'pull_request' }} + uses: actions/cache/save@v6 + with: + path: ${{ github.workspace }}/.ccache + key: ${{ env.CCACHE_ENTRY }} + + - name: Drop older compiler cache entries + if: ${{ steps.ccache_save.outcome == 'success' }} + # A read-only token (fork PRs) cannot delete; that only costs storage. + continue-on-error: true + shell: bash + env: + GH_TOKEN: ${{ github.token }} + run: | + gh cache list --ref "$GITHUB_REF" --key "ccache-$CCACHE_LEG-" --limit 100 --json id,key \ + | jq -r --arg keep "$CCACHE_ENTRY" '.[] | select(.key != $keep) | .id' \ + | tr -d '\r' \ + | while read -r id; do gh cache delete "$id"; done From 6f90ff6e93fb8c00d6f343f4322a570c8dcc0c0b Mon Sep 17 00:00:00 2001 From: Hanif Koh Date: Fri, 11 Sep 2026 17:29:28 +0800 Subject: [PATCH 3/4] Allow ccache with PCH Clang records the modification time of every input in the precompiled header, so a fresh checkout produces a different header and every file that includes it misses the compiler cache. -fno-pch-timestamp makes the header reproducible, and pch_defines lets ccache cache the header itself. The precompiled header no longer has to be turned off when the cache is on. --- .github/workflows/build_orca.yml | 10 ++++------ cmake/modules/PrecompiledHeader.cmake | 7 +++++++ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build_orca.yml b/.github/workflows/build_orca.yml index f1fb29c46b..e51cb2e37a 100644 --- a/.github/workflows/build_orca.yml +++ b/.github/workflows/build_orca.yml @@ -117,12 +117,10 @@ jobs: run: | echo "CMAKE_C_COMPILER_LAUNCHER=ccache" >> "$GITHUB_ENV" echo "CMAKE_CXX_COMPILER_LAUNCHER=ccache" >> "$GITHUB_ENV" - # Headers a fresh checkout has just written, and the few files that - # use __DATE__ or __TIME__. - echo "CCACHE_SLOPPINESS=time_macros,include_file_mtime,include_file_ctime" >> "$GITHUB_ENV" - # Clang rebuilds the precompiled header with a fresh timestamp on - # every run, so everything that includes it would miss. - echo "ORCA_EXTRA_BUILD_ARGS=-DSLIC3R_PCH=OFF" >> "$GITHUB_ENV" + # Headers a fresh checkout has just written, the few files that + # use __DATE__ or __TIME__, and the precompiled header, whose + # macros ccache cannot see. + echo "CCACHE_SLOPPINESS=pch_defines,time_macros,include_file_mtime,include_file_ctime" >> "$GITHUB_ENV" # The restored directory carries the previous run's counters. ccache -z diff --git a/cmake/modules/PrecompiledHeader.cmake b/cmake/modules/PrecompiledHeader.cmake index 7ef80aacff..7d8b3a5603 100644 --- a/cmake/modules/PrecompiledHeader.cmake +++ b/cmake/modules/PrecompiledHeader.cmake @@ -256,6 +256,13 @@ function(add_precompiled_header _target _input) message(STATUS "Adding precompiled header ${_input} to target ${_target}.") target_precompile_headers(${_target} PRIVATE ${_input}) + # Clang records the modification time of every input in the precompiled + # header, which makes it differ between two checkouts of the same source + # and defeats a compiler cache. The build system already rebuilds the + # header when an input changes. + target_compile_options(${_target} PRIVATE + "$<$:SHELL:-Xclang -fno-pch-timestamp>") + get_target_property(_sources ${_target} SOURCES) list(FILTER _sources INCLUDE REGEX ".*\\.mm?") From 6a88f0790edaa79f4e09e25023403a32c20edf98 Mon Sep 17 00:00:00 2001 From: Hanif Koh Date: Fri, 11 Sep 2026 23:02:31 +0800 Subject: [PATCH 4/4] Enable ccache Depend Mode A miss used to cost a preprocessor pass for the hash and then the real compile. With the depend mode ccache hashes the include list the compiler reports, so a miss costs only the compile. Ninja already asks every compiler here for that list. --- .github/workflows/build_orca.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build_orca.yml b/.github/workflows/build_orca.yml index e51cb2e37a..1b7fd37a0f 100644 --- a/.github/workflows/build_orca.yml +++ b/.github/workflows/build_orca.yml @@ -121,6 +121,9 @@ jobs: # use __DATE__ or __TIME__, and the precompiled header, whose # macros ccache cannot see. echo "CCACHE_SLOPPINESS=pch_defines,time_macros,include_file_mtime,include_file_ctime" >> "$GITHUB_ENV" + # Hash the includes the compiler reports instead of preprocessing + # every miss before compiling it. + echo "CCACHE_DEPEND=1" >> "$GITHUB_ENV" # The restored directory carries the previous run's counters. ccache -z