diff --git a/build_win.bat b/build_win.bat index c447d8a71f..840279e4f3 100644 --- a/build_win.bat +++ b/build_win.bat @@ -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 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 no_pch bool "" no-pch "Build without the precompiled header, implied by --cache" call :add_section "Toolchain" 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 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 cache string "" cache "Compiler cache: ccache, sccache or off, requires -l -x (default: off)" 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" @@ -391,6 +393,49 @@ if not "%clang_path%" == "" if not "%using_ninja%" == "ON" ( 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 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 @@ -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" echo Configuration: %build_type%, %arch% 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=" 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% ) + if not "!cache_args!" == "" ( + set "deps_args=!deps_args! !cache_args!" + ) + 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% %error_check% @@ -641,6 +692,16 @@ if "%build_slicer%" == "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 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 @@ -870,6 +931,7 @@ REM get_str_len -> length in %ret% 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% -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 Environment: echo ORCA_DEPS_CMAKE_ARGS Extra arguments for the deps configure @@ -879,8 +941,8 @@ REM get_str_len -> length in %ret% echo NINJA_STATUS Ninja progress format, if you want your own echo debugscript Set to ON to trace this script echo. - echo set ORCA_SLICER_CMAKE_ARGS=-DSLIC3R_PCH=OFF -DSLIC3R_MSVC_PDB=OFF - echo $env:ORCA_SLICER_CMAKE_ARGS = '-DSLIC3R_PCH=OFF' (PowerShell) + echo set ORCA_SLICER_CMAKE_ARGS=-DSLIC3R_BUILD_SANDBOXES=ON -DSLIC3R_WARNINGS=OFF + echo $env:ORCA_SLICER_CMAKE_ARGS = '-DSLIC3R_BUILD_SANDBOXES=ON' (PowerShell) echo. echo --deps-args and --slicer-args cannot carry a value with spaces; use echo these instead. Neither form supports a value containing an ampersand. diff --git a/scripts/test_build_win.ps1 b/scripts/test_build_win.ps1 index 563430559f..34f64117af 100644 --- a/scripts/test_build_win.ps1 +++ b/scripts/test_build_win.ps1 @@ -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 $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 # stands in for a machine whose Visual Studio has no clang toolset. $noVs = Join-Path $fixtures 'no-vs' @@ -121,7 +127,7 @@ $cases = @( 'Examples:', 'Environment:') } @{ 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', - '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 Match = @('^ -d, --deps +Download') } # Windows Terminal opens at 120 columns and wraps at 120, so 119 is the @@ -294,6 +300,14 @@ $cases = @( Contains = @('-DBUILD_TESTS=ON') } @{ Name = '-a enables ASAN for the slicer'; Args = @('-s', '-a') 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') Contains = @('run_gettext.bat') } # 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') 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' @{ Name = '--slicer-target builds one target'; Args = @('-s', '--slicer-target', 'libslic3r') Contains = @('--config Release --target libslic3r') } diff --git a/src/libslic3r/BlacklistedLibraryCheck.cpp b/src/libslic3r/BlacklistedLibraryCheck.cpp index 938f542497..cf7e6da2aa 100644 --- a/src/libslic3r/BlacklistedLibraryCheck.cpp +++ b/src/libslic3r/BlacklistedLibraryCheck.cpp @@ -1,6 +1,7 @@ #include "BlacklistedLibraryCheck.hpp" #include +#include #include #ifdef WIN32 diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index c33b7be429..d07c42d8f6 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -665,6 +665,13 @@ if(SLIC3R_PROFILE) target_link_libraries(libslic3r PRIVATE Shiny) 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) add_precompiled_header(libslic3r pchheader.hpp FORCEINCLUDE) endif () diff --git a/src/libslic3r/MultiPoint.hpp b/src/libslic3r/MultiPoint.hpp index de386b501c..096230d22c 100644 --- a/src/libslic3r/MultiPoint.hpp +++ b/src/libslic3r/MultiPoint.hpp @@ -24,6 +24,7 @@ public: explicit MultiPoint(const Points &_points) : points(_points) {} MultiPoint& operator=(const MultiPoint &other) { points = 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_x, double factor_y); void translate(double x, double y) { this->translate(Point(coord_t(x), coord_t(y))); } diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index e96304e874..aee50db534 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -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 +#endif #include "Config.hpp" #include "Exception.hpp" #include "Print.hpp" diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp index df62127770..f50cdbced2 100644 --- a/src/libslic3r/SLA/Hollowing.cpp +++ b/src/libslic3r/SLA/Hollowing.cpp @@ -1,4 +1,5 @@ #include +#include #include #include diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 1e39e30b0d..83ad94e563 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -916,6 +916,9 @@ endif () if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY) 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 () if (APPLE) diff --git a/src/slic3r/GUI/DragCanvas.hpp b/src/slic3r/GUI/DragCanvas.hpp index aa8d8ddfa8..f39e836673 100644 --- a/src/slic3r/GUI/DragCanvas.hpp +++ b/src/slic3r/GUI/DragCanvas.hpp @@ -3,6 +3,7 @@ #include "wx/bitmap.h" #include "wx/dragimag.h" +#include "wx/panel.h" namespace Slic3r { namespace GUI { diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 5b80a866ca..3500493329 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -12,6 +12,7 @@ #include "BuildCommit.hpp" #include "Downloader.hpp" #include +#include #include #include #include diff --git a/src/slic3r/GUI/OptionsGroup.hpp b/src/slic3r/GUI/OptionsGroup.hpp index c808545145..b20d16aca9 100644 --- a/src/slic3r/GUI/OptionsGroup.hpp +++ b/src/slic3r/GUI/OptionsGroup.hpp @@ -199,7 +199,7 @@ public: OptionsGroup(wxWindow *_parent, const wxString &title, const wxString &icon, bool is_tab_opt = false, column_t extra_clmn = nullptr); - ~OptionsGroup() { clear(true); } + virtual ~OptionsGroup() { clear(true); } wxGridSizer* get_grid_sizer() { return m_grid_sizer; } const std::vector& get_lines() { return m_lines; } diff --git a/src/slic3r/GUI/wxMediaCtrl3.h b/src/slic3r/GUI/wxMediaCtrl3.h index 1d64955ffd..ff4595ad32 100644 --- a/src/slic3r/GUI/wxMediaCtrl3.h +++ b/src/slic3r/GUI/wxMediaCtrl3.h @@ -8,6 +8,9 @@ #ifndef wxMediaCtrl3_h #define wxMediaCtrl3_h +#include +#include "wx/window.h" +#include "wx/bitmap.h" #include "wx/uri.h" #include "wx/mediactrl.h" diff --git a/src/slic3r/Utils/Bonjour.hpp b/src/slic3r/Utils/Bonjour.hpp index 50b71791fa..3ef8f0e1a2 100644 --- a/src/slic3r/Utils/Bonjour.hpp +++ b/src/slic3r/Utils/Bonjour.hpp @@ -115,6 +115,7 @@ class UdpSession { public: UdpSession(Bonjour::ReplyFn rfn); + virtual ~UdpSession() = default; virtual void handle_receive(const boost::system::error_code& error, size_t bytes) = 0; std::vector buffer; boost::asio::ip::udp::endpoint remote_endpoint; diff --git a/src/slic3r/win_platform.hpp b/src/slic3r/win_platform.hpp new file mode 100644 index 0000000000..0c5a8e81b2 --- /dev/null +++ b/src/slic3r/win_platform.hpp @@ -0,0 +1,19 @@ +#pragma once + +// Force-included into libslic3r_gui when SLIC3R_PCH is OFF, standing in for +// pchheader.hpp, which includes 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 + #include + #include +#endif // _WIN32 +