fix: SLIC3R_PCH=OFF now builds on Windows, allowing compiler caching (#15552)

This commit is contained in:
Kris Austin
2026-09-06 18:06:18 -03:00
committed by GitHub
parent 0365304ae0
commit 43ce8c5e46
14 changed files with 159 additions and 4 deletions
+64 -2
View File
@@ -52,6 +52,7 @@ call :add_section "Build configuration"
call :add_arg config string "" config "release, debug, relwithdebinfo or minsizerel (default: release)" call :add_arg config string "" config "release, debug, relwithdebinfo or minsizerel (default: release)"
call :add_arg target_arch string "" arch "x64 or arm64 (default: the host architecture)" call :add_arg target_arch string "" arch "x64 or arm64 (default: the host architecture)"
call :add_arg slicer_asan bool a asan "Build the slicer with ASAN enabled" call :add_arg slicer_asan bool a asan "Build the slicer with ASAN enabled"
call :add_arg no_pch bool "" no-pch "Build without the precompiled header, implied by --cache"
call :add_section "Toolchain" call :add_section "Toolchain"
call :add_arg use_clang_cl bool l clang-cl "Use clang-cl as the compiler" call :add_arg use_clang_cl bool l clang-cl "Use clang-cl as the compiler"
@@ -60,6 +61,7 @@ call :add_arg use_ninja bool x ninja "Use the Ninja Multi-Config generator"
call :add_arg use_msbuild bool "" msbuild "Use the Visual Studio generator (default)" call :add_arg use_msbuild bool "" msbuild "Use the Visual Studio generator (default)"
call :add_arg vs_version string "" vs "Visual Studio release: 2019, 2022 or 2026 (default: autodetect)" call :add_arg vs_version string "" vs "Visual Studio release: 2019, 2022 or 2026 (default: autodetect)"
call :add_arg clang_path string "" clang-path "Path to clang-cl.exe, requires -x (default: the one from Visual Studio)" call :add_arg clang_path string "" clang-path "Path to clang-cl.exe, requires -x (default: the one from Visual Studio)"
call :add_arg cache string "" cache "Compiler cache: ccache, sccache or off, requires -l -x (default: off)"
call :add_section "How much gets rebuilt" call :add_section "How much gets rebuilt"
call :add_arg slicer_target string "" slicer-target "Build one slicer target instead of all, e.g. libslic3r" call :add_arg slicer_target string "" slicer-target "Build one slicer target instead of all, e.g. libslic3r"
@@ -391,6 +393,49 @@ if not "%clang_path%" == "" if not "%using_ninja%" == "ON" (
exit /b 1 exit /b 1
) )
set "cache_args="
if "%cache%" == "" goto :cache_ready
if /I "%cache%" == "off" goto :cache_ready
if /I "%cache%" == "ccache" goto :cache_named
if /I "%cache%" == "sccache" goto :cache_named
echo Unknown --cache value "%cache%". Expected ccache, sccache or off.
exit /b 1
REM CMake accepts a compiler launcher under any generator but only runs it
REM under Makefile and Ninja. ccache also refuses cl.exe because the build
REM passes /Zi.
:cache_named
REM Only a configure records the launcher, so --no-configure needs none.
if "%no_configure%" == "ON" goto :cache_ready
if "%build_deps%%build_slicer%" == "" goto :cache_ready
if not "%using_ninja%" == "ON" goto :cache_needs_clang
if not "%use_clang_cl%" == "ON" goto :cache_needs_clang
goto :cache_tool
:cache_needs_clang
echo --cache needs clang-cl and Ninja; add -l -x.
exit /b 1
REM Name a full path, so the recorded launcher does not depend on PATH.
:cache_tool
set "cache_exe="
for /f "tokens=*" %%i in ('where %cache% 2^>nul') do (
if not defined cache_exe set "cache_exe=%%i"
)
if not defined cache_exe (
echo %cache% is not on PATH. Install it, or leave --cache off.
exit /b 1
)
REM Forward slashes, so CMake does not read a backslash as an escape.
set "cache_exe=%cache_exe:\=/%"
set "cache_args=-DCMAKE_C_COMPILER_LAUNCHER="%cache_exe%" -DCMAKE_CXX_COMPILER_LAUNCHER="%cache_exe%""
REM Neither cache stores a compile that uses a precompiled header. sccache
REM refuses /Fp outright. ccache does too unless its sloppiness is loosened,
REM and even then most hits fall back to the slower preprocessed mode.
set "no_pch=ON"
:cache_ready
REM Ninja prints [123/456] by default, which says nothing about how far REM Ninja prints [123/456] by default, which says nothing about how far
REM along it is or how long is left. %p is a percentage, %w and %W are REM along it is or how long is left. %p is a percentage, %w and %W are
REM elapsed and remaining, but those two arrived in ninja 1.12 and an REM elapsed and remaining, but those two arrived in ninja 1.12 and an
@@ -474,6 +519,8 @@ REM real path rather than one glued onto the repository root.
for %%p in ("!build_dir!") do set "build_full=%%~fp" for %%p in ("!build_dir!") do set "build_full=%%~fp"
echo Configuration: %build_type%, %arch% echo Configuration: %build_type%, %arch%
if not "%clang_exe%" == "" echo Compiler: %clang_exe% if not "%clang_exe%" == "" echo Compiler: %clang_exe%
if "%no_pch%" == "ON" echo Precompiled header: off
if not "%cache_args%" == "" echo Compiler cache: %cache_exe%
set "SIG_FLAG=" set "SIG_FLAG="
if defined ORCA_UPDATER_SIG_KEY set "SIG_FLAG=-DORCA_UPDATER_SIG_KEY=%ORCA_UPDATER_SIG_KEY%" if defined ORCA_UPDATER_SIG_KEY set "SIG_FLAG=-DORCA_UPDATER_SIG_KEY=%ORCA_UPDATER_SIG_KEY%"
@@ -570,6 +617,10 @@ if "%build_deps%" == "ON" (
%error_check% %error_check%
) )
if not "!cache_args!" == "" (
set "deps_args=!deps_args! !cache_args!"
)
if not "%no_configure%" == "ON" ( if not "%no_configure%" == "ON" (
call :print_and_run cmake -S deps -B "!DEP_TREE!" -G "%generator%" %gen_args% -DCMAKE_BUILD_TYPE=%build_type% !deps_args! %ORCA_DEPS_CMAKE_ARGS% call :print_and_run cmake -S deps -B "!DEP_TREE!" -G "%generator%" %gen_args% -DCMAKE_BUILD_TYPE=%build_type% !deps_args! %ORCA_DEPS_CMAKE_ARGS%
%error_check% %error_check%
@@ -641,6 +692,16 @@ if "%build_slicer%" == "ON" (
set "slicer_args=!slicer_args! -DSLIC3R_ASAN=ON" set "slicer_args=!slicer_args! -DSLIC3R_ASAN=ON"
) )
REM A later -DSLIC3R_PCH=ON in ORCA_SLICER_CMAKE_ARGS still wins, because
REM CMake takes the last definition on the command line.
if "%no_pch%" == "ON" (
set "slicer_args=!slicer_args! -DSLIC3R_PCH=OFF"
)
if not "!cache_args!" == "" (
set "slicer_args=!slicer_args! !cache_args!"
)
REM Configuring against a tree that was never built fails deep inside REM Configuring against a tree that was never built fails deep inside
REM package resolution. Name it here instead. Skipped when -d is about to REM package resolution. Name it here instead. Skipped when -d is about to
REM build it in this same run, and under a dry run, which configures REM build it in this same run, and under a dry run, which configures
@@ -870,6 +931,7 @@ REM get_str_len <string> -> length in %ret%
echo %script_name% -s --no-configure -j 8 Rebuild quickly while iterating echo %script_name% -s --no-configure -j 8 Rebuild quickly while iterating
echo %script_name% -s --slicer-target glad Compile one target to check the toolchain echo %script_name% -s --slicer-target glad Compile one target to check the toolchain
echo %script_name% -l -x --run-tests Test that toolchain's build, not the default one echo %script_name% -l -x --run-tests Test that toolchain's build, not the default one
echo %script_name% -s -l -x --cache ccache Rebuild through a compiler cache
echo. echo.
echo Environment: echo Environment:
echo ORCA_DEPS_CMAKE_ARGS Extra arguments for the deps configure echo ORCA_DEPS_CMAKE_ARGS Extra arguments for the deps configure
@@ -879,8 +941,8 @@ REM get_str_len <string> -> length in %ret%
echo NINJA_STATUS Ninja progress format, if you want your own echo NINJA_STATUS Ninja progress format, if you want your own
echo debugscript Set to ON to trace this script echo debugscript Set to ON to trace this script
echo. echo.
echo set ORCA_SLICER_CMAKE_ARGS=-DSLIC3R_PCH=OFF -DSLIC3R_MSVC_PDB=OFF echo set ORCA_SLICER_CMAKE_ARGS=-DSLIC3R_BUILD_SANDBOXES=ON -DSLIC3R_WARNINGS=OFF
echo $env:ORCA_SLICER_CMAKE_ARGS = '-DSLIC3R_PCH=OFF' (PowerShell) echo $env:ORCA_SLICER_CMAKE_ARGS = '-DSLIC3R_BUILD_SANDBOXES=ON' (PowerShell)
echo. echo.
echo --deps-args and --slicer-args cannot carry a value with spaces; use echo --deps-args and --slicer-args cannot carry a value with spaces; use
echo these instead. Neither form supports a value containing an ampersand. echo these instead. Neither form supports a value containing an ampersand.
+51 -1
View File
@@ -92,6 +92,12 @@ New-Item -ItemType Directory -Force -Path $clangDir | Out-Null
Copy-Item "$env:SystemRoot\System32\where.exe" (Join-Path $clangDir 'clang-cl.exe') -Force Copy-Item "$env:SystemRoot\System32\where.exe" (Join-Path $clangDir 'clang-cl.exe') -Force
$clangOnPath = "$clangDir;$env:PATH" $clangOnPath = "$clangDir;$env:PATH"
# A ccache that only has to exist. Nothing runs it; the script only locates it.
$cacheDir = Join-Path $fixtures 'cache'
New-Item -ItemType Directory -Force -Path $cacheDir | Out-Null
Copy-Item "$env:SystemRoot\System32\where.exe" (Join-Path $cacheDir 'ccache.exe') -Force
$ccacheOnPath = "$cacheDir;$env:PATH"
# ProgramFiles(x86) is where the script looks for vswhere, so an empty one # ProgramFiles(x86) is where the script looks for vswhere, so an empty one
# stands in for a machine whose Visual Studio has no clang toolset. # stands in for a machine whose Visual Studio has no clang toolset.
$noVs = Join-Path $fixtures 'no-vs' $noVs = Join-Path $fixtures 'no-vs'
@@ -121,7 +127,7 @@ $cases = @(
'Examples:', 'Environment:') } 'Examples:', 'Environment:') }
@{ Name = 'the environment section shows what to set'; Args = @('--help'); DryRun = $false @{ Name = 'the environment section shows what to set'; Args = @('--help'); DryRun = $false
Contains = @('ORCA_DEPS_CMAKE_ARGS', 'ORCA_SLICER_CMAKE_ARGS', 'ORCA_UPDATER_SIG_KEY', 'NINJA_STATUS', Contains = @('ORCA_DEPS_CMAKE_ARGS', 'ORCA_SLICER_CMAKE_ARGS', 'ORCA_UPDATER_SIG_KEY', 'NINJA_STATUS',
'set ORCA_SLICER_CMAKE_ARGS=-DSLIC3R_PCH=OFF', '(PowerShell)', 'debugscript') } 'set ORCA_SLICER_CMAKE_ARGS=-DSLIC3R_BUILD_SANDBOXES=ON', '(PowerShell)', 'debugscript') }
@{ Name = 'section headers do not widen the flag column'; Args = @('--help'); DryRun = $false @{ Name = 'section headers do not widen the flag column'; Args = @('--help'); DryRun = $false
Match = @('^ -d, --deps +Download') } Match = @('^ -d, --deps +Download') }
# Windows Terminal opens at 120 columns and wraps at 120, so 119 is the # Windows Terminal opens at 120 columns and wraps at 120, so 119 is the
@@ -294,6 +300,14 @@ $cases = @(
Contains = @('-DBUILD_TESTS=ON') } Contains = @('-DBUILD_TESTS=ON') }
@{ Name = '-a enables ASAN for the slicer'; Args = @('-s', '-a') @{ Name = '-a enables ASAN for the slicer'; Args = @('-s', '-a')
Contains = @('-DSLIC3R_ASAN=ON') } Contains = @('-DSLIC3R_ASAN=ON') }
@{ Name = '--no-pch turns the precompiled header off'; Args = @('-s', '--no-pch')
Contains = @('-DSLIC3R_PCH=OFF') }
@{ Name = '--no-pch says so in the banner'; Args = @('-s', '--no-pch')
Contains = @('Precompiled header: off') }
@{ Name = 'the precompiled header is on unless asked'; Args = @('-s')
NotContains = @('SLIC3R_PCH') }
@{ Name = '--no-pch works without a cache'; Args = @('-s', '--no-pch')
NotContains = @('COMPILER_LAUNCHER') }
@{ Name = 'the slicer build runs gettext'; Args = @('-s') @{ Name = 'the slicer build runs gettext'; Args = @('-s')
Contains = @('run_gettext.bat') } Contains = @('run_gettext.bat') }
# tools\7z.exe needs a 7z.dll beside it, which the repo does not carry, # tools\7z.exe needs a 7z.dll beside it, which the repo does not carry,
@@ -324,6 +338,42 @@ $cases = @(
@{ Name = 'deps and slicer build in one invocation'; Args = @('-d', '-s', '-x', '-l') @{ Name = 'deps and slicer build in one invocation'; Args = @('-d', '-s', '-x', '-l')
Contains = @('cmake -S deps', 'cmake -B "build-clang" ') } Contains = @('cmake -S deps', 'cmake -B "build-clang" ') }
'the compiler cache'
@{ Name = '--cache needs clang-cl and Ninja'; Args = @('-s', '--cache', 'ccache'); ExpectExit = 1
Contains = @('needs clang-cl and Ninja') }
# cl.exe is out of scope, since ccache refuses every compile under /Zi.
@{ Name = '--cache under Ninja still needs clang-cl'; Args = @('-s', '-x', '--cache', 'ccache'); ExpectExit = 1
Contains = @('needs clang-cl and Ninja') }
@{ Name = 'an unknown --cache value is rejected'; Args = @('-s', '-x', '--cache', 'nope'); ExpectExit = 1
Contains = @('Expected ccache, sccache or off') }
# A bare PATH, since the machine running the tests may have sccache installed.
@{ Name = 'a --cache tool that is not there is caught early'; Args = @('-s', '-l', '-x', '--cache', 'sccache'); ExpectExit = 1
Env = @{ PATH = 'C:\Windows\system32;C:\Windows' }
Contains = @('is not on PATH') }
@{ Name = '--cache takes any casing'; Args = @('-s', '-l', '-x', '--cache', 'CCACHE')
Env = @{ PATH = $ccacheOnPath }
Contains = @('ccache.exe') }
@{ Name = '--cache off asks for no launcher'; Args = @('-s', '-x', '--cache', 'off')
NotContains = @('COMPILER_LAUNCHER') }
@{ Name = 'no --cache asks for no launcher'; Args = @('-s', '-x')
NotContains = @('COMPILER_LAUNCHER') }
@{ Name = '--cache turns the precompiled header off'; Args = @('-s', '-l', '-x', '--cache', 'ccache')
Env = @{ PATH = $ccacheOnPath }
Contains = @('-DSLIC3R_PCH=OFF', 'COMPILER_LAUNCHER') }
# The resolved path, not the bare name, so PATH cannot change it later.
@{ Name = '--cache names the resolved path in the banner'; Args = @('-s', '-l', '-x', '--cache', 'ccache')
Env = @{ PATH = $ccacheOnPath }
Match = @('^Compiler cache: .*/ccache\.exe$') }
@{ Name = '--cache reaches the dependency configure too'; Args = @('-d', '-l', '-x', '--cache', 'ccache')
Env = @{ PATH = $ccacheOnPath }
Contains = @('-DCMAKE_C_COMPILER_LAUNCHER=') }
# Nothing records a launcher without a configure, so the tool is not needed.
# Reaching the cmake check on a bare PATH is what proves it was skipped.
@{ Name = '--no-configure asks for no cache tool'; Args = @('-s', '-l', '-x', '--no-configure', '--cache', 'ccache'); ExpectExit = 1
Env = @{ PATH = 'C:\Windows\system32;C:\Windows' }
Contains = @('CMake was not found')
NotContains = @('is not on PATH') }
'the developer loop' 'the developer loop'
@{ Name = '--slicer-target builds one target'; Args = @('-s', '--slicer-target', 'libslic3r') @{ Name = '--slicer-target builds one target'; Args = @('-s', '--slicer-target', 'libslic3r')
Contains = @('--config Release --target libslic3r') } Contains = @('--config Release --target libslic3r') }
@@ -1,6 +1,7 @@
#include "BlacklistedLibraryCheck.hpp" #include "BlacklistedLibraryCheck.hpp"
#include <cstdio> #include <cstdio>
#include <boost/filesystem/path.hpp>
#include <boost/nowide/convert.hpp> #include <boost/nowide/convert.hpp>
#ifdef WIN32 #ifdef WIN32
+7
View File
@@ -665,6 +665,13 @@ if(SLIC3R_PROFILE)
target_link_libraries(libslic3r PRIVATE Shiny) target_link_libraries(libslic3r PRIVATE Shiny)
endif() endif()
if (WIN32)
# Public, since BlacklistedLibraryCheck.hpp includes windows.h. Empty
# WIN32_LEAN_AND_MEAN matches the sources that define it themselves; bare
# NOMINMAX matches the one libigl already passes.
target_compile_definitions(libslic3r PUBLIC "WIN32_LEAN_AND_MEAN=" "NOMINMAX")
endif ()
if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY) if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
add_precompiled_header(libslic3r pchheader.hpp FORCEINCLUDE) add_precompiled_header(libslic3r pchheader.hpp FORCEINCLUDE)
endif () endif ()
+1
View File
@@ -24,6 +24,7 @@ public:
explicit MultiPoint(const Points &_points) : points(_points) {} explicit MultiPoint(const Points &_points) : points(_points) {}
MultiPoint& operator=(const MultiPoint &other) { points = other.points; return *this; } MultiPoint& operator=(const MultiPoint &other) { points = other.points; return *this; }
MultiPoint& operator=(MultiPoint &&other) { points = std::move(other.points); return *this; } MultiPoint& operator=(MultiPoint &&other) { points = std::move(other.points); return *this; }
virtual ~MultiPoint() = default;
void scale(double factor); void scale(double factor);
void scale(double factor_x, double factor_y); void scale(double factor_x, double factor_y);
void translate(double x, double y) { this->translate(Point(coord_t(x), coord_t(y))); } void translate(double x, double y) { this->translate(Point(coord_t(x), coord_t(y))); }
+5
View File
@@ -1,3 +1,8 @@
#ifdef _WIN32
// Keep this first. A header below reaches boost/regex, whose w32_regex_traits
// needs the Win32 types declared already.
#include <Windows.h>
#endif
#include "Config.hpp" #include "Config.hpp"
#include "Exception.hpp" #include "Exception.hpp"
#include "Print.hpp" #include "Print.hpp"
+1
View File
@@ -1,4 +1,5 @@
#include <functional> #include <functional>
#include <numeric>
#include <optional> #include <optional>
#include <libslic3r/OpenVDBUtils.hpp> #include <libslic3r/OpenVDBUtils.hpp>
+3
View File
@@ -916,6 +916,9 @@ endif ()
if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY) if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE) add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE)
elseif (MSVC)
# Puts the Windows headers first when the PCH is off.
target_compile_options(libslic3r_gui PRIVATE "/FIslic3r/win_platform.hpp")
endif () endif ()
if (APPLE) if (APPLE)
+1
View File
@@ -3,6 +3,7 @@
#include "wx/bitmap.h" #include "wx/bitmap.h"
#include "wx/dragimag.h" #include "wx/dragimag.h"
#include "wx/panel.h"
namespace Slic3r { namespace GUI { namespace Slic3r { namespace GUI {
+1
View File
@@ -12,6 +12,7 @@
#include "BuildCommit.hpp" #include "BuildCommit.hpp"
#include "Downloader.hpp" #include "Downloader.hpp"
#include <boost/chrono/duration.hpp> #include <boost/chrono/duration.hpp>
#include <boost/locale/encoding_utf.hpp>
#include <boost/log/detail/native_typeof.hpp> #include <boost/log/detail/native_typeof.hpp>
#include <libslic3r/Config.hpp> #include <libslic3r/Config.hpp>
#include <mutex> #include <mutex>
+1 -1
View File
@@ -199,7 +199,7 @@ public:
OptionsGroup(wxWindow *_parent, const wxString &title, const wxString &icon, bool is_tab_opt = false, OptionsGroup(wxWindow *_parent, const wxString &title, const wxString &icon, bool is_tab_opt = false,
column_t extra_clmn = nullptr); column_t extra_clmn = nullptr);
~OptionsGroup() { clear(true); } virtual ~OptionsGroup() { clear(true); }
wxGridSizer* get_grid_sizer() { return m_grid_sizer; } wxGridSizer* get_grid_sizer() { return m_grid_sizer; }
const std::vector<Line>& get_lines() { return m_lines; } const std::vector<Line>& get_lines() { return m_lines; }
+3
View File
@@ -8,6 +8,9 @@
#ifndef wxMediaCtrl3_h #ifndef wxMediaCtrl3_h
#define wxMediaCtrl3_h #define wxMediaCtrl3_h
#include <chrono>
#include "wx/window.h"
#include "wx/bitmap.h"
#include "wx/uri.h" #include "wx/uri.h"
#include "wx/mediactrl.h" #include "wx/mediactrl.h"
+1
View File
@@ -115,6 +115,7 @@ class UdpSession
{ {
public: public:
UdpSession(Bonjour::ReplyFn rfn); UdpSession(Bonjour::ReplyFn rfn);
virtual ~UdpSession() = default;
virtual void handle_receive(const boost::system::error_code& error, size_t bytes) = 0; virtual void handle_receive(const boost::system::error_code& error, size_t bytes) = 0;
std::vector<char> buffer; std::vector<char> buffer;
boost::asio::ip::udp::endpoint remote_endpoint; boost::asio::ip::udp::endpoint remote_endpoint;
+19
View File
@@ -0,0 +1,19 @@
#pragma once
// Force-included into libslic3r_gui when SLIC3R_PCH is OFF, standing in for
// pchheader.hpp, which includes <Windows.h> before anything else. Arriving
// late and transitively, rpcndr.h defines a global `byte` that collides with
// std::byte under `using namespace std`, and the control and URL moniker
// types the GUI uses are never declared.
#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <Windows.h>
#include <CommCtrl.h>
#include <urlmon.h>
#endif // _WIN32