From 8effa27f4abd8bb943bc97a925489c6bd590784b Mon Sep 17 00:00:00 2001 From: Kris Austin Date: Wed, 16 Sep 2026 12:28:50 -0500 Subject: [PATCH] build: build the dependencies with clang-cl under the Visual Studio generator (#15673) The deps superbuild passes the Visual Studio generator and platform to every sub-build but not the toolset, so build_win.bat -d -l without -x compiled every dependency with cl even though the superbuild had been configured with -T ClangCL; CMake replaces the forwarded CMAKE__COMPILER with whatever the toolset ran. The recipes that adapt to clang-cl then disagreed with what had been built, and wxInspector told FindwxWidgets to look in lib/clang_x64_lib while the cl-built wxWidgets had installed into lib/vc_x64_lib: Could NOT find wxWidgets (missing: wxWidgets_LIBRARIES wxWidgets_INCLUDE_DIRS core base aui propgrid) Forward CMAKE_GENERATOR_TOOLSET as well, so the dependencies compile with clang-cl under MSBuild the way they already do under Ninja. Four of them need more than that: - OpenSSL always builds with cl, and MSBuild runs its nmake steps in the project's toolset environment, where ClangCL puts clang's include directory first and cl trips over clang's stdint.h. The project gets the default toolset. - Boost.Container's dlmalloc needs -Wno-incompatible-pointer-types under clang. boost_container links as C++, and the Visual Studio generator writes only the link language's flags into the project, so its C file never saw CMAKE_C_FLAGS. Under that generator the option goes through the C++ flags as well, with the defaults kept. - Draco's tools and NLopt's testopt compile sources their own static library also contains. MSBuild lists libraries before objects and lld-link resolves archive members as each input arrives, so the library's copy is pulled in before the executable's own object and the link fails on duplicate symbols; link.exe defers the search and Ninja lists the objects first. Nothing uses those executables, so they get /FORCE:MULTIPLE there. The Ninja path is unchanged: the generated configure commands of all 29 dependencies are identical before and after. OCCT's arm64 override to cl still applies under Ninja but not under the Visual Studio generator, where the toolset wins; that combination never built and is left for a follow-up. --- deps/Boost/Boost.cmake | 8 ++++++++ deps/CMakeLists.txt | 5 +++++ deps/Draco/Draco.cmake | 3 +++ deps/NLopt/NLopt.cmake | 2 ++ deps/OpenSSL/OpenSSL.cmake | 6 ++++++ deps/deps-windows.cmake | 9 +++++++++ 6 files changed, 33 insertions(+) diff --git a/deps/Boost/Boost.cmake b/deps/Boost/Boost.cmake index 08b62b9fb8..1526928094 100644 --- a/deps/Boost/Boost.cmake +++ b/deps/Boost/Boost.cmake @@ -27,8 +27,15 @@ endif () # Boost.Container's bundled dlmalloc passes int* where the Win32 Interlocked API # takes volatile long*; cl compiles that with a warning, clang errors out. set(_boost_c_flags_line "") +set(_boost_cxx_flags_line "") if (MSVC AND CMAKE_C_COMPILER_ID STREQUAL "Clang") set(_boost_c_flags_line "-DCMAKE_C_FLAGS:STRING=-Wno-incompatible-pointer-types") + # The Visual Studio generator applies only the link language's flags to a + # project, and boost_container links as C++, so its C file never sees + # CMAKE_C_FLAGS. The C++ flags reach every file; keep CMake's defaults. + if (CMAKE_GENERATOR MATCHES "Visual Studio") + set(_boost_cxx_flags_line "-DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS} -Wno-incompatible-pointer-types") + endif () endif () orcaslicer_add_cmake_project(Boost @@ -46,6 +53,7 @@ orcaslicer_add_cmake_project(Boost "${_context_arch_line}" "${_context_impl_line}" "${_boost_c_flags_line}" + "${_boost_cxx_flags_line}" ) set(DEP_Boost_DEPENDS ZLIB) diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index ed3af70d03..4b1daf5aa8 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -184,6 +184,11 @@ function(orcaslicer_add_cmake_project projectname) if (_dep_msvc_gen) set(_gen CMAKE_GENERATOR "${DEP_MSVC_GEN}" CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}") + # The toolset picks the compiler here, not the CMAKE__COMPILER + # forwarded below, so without it a clang-cl superbuild builds with cl. + if (CMAKE_GENERATOR_TOOLSET) + list(APPEND _gen CMAKE_GENERATOR_TOOLSET "${CMAKE_GENERATOR_TOOLSET}") + endif () else() set(_gen "") endif() diff --git a/deps/Draco/Draco.cmake b/deps/Draco/Draco.cmake index 02ac7efe13..77f73684f6 100644 --- a/deps/Draco/Draco.cmake +++ b/deps/Draco/Draco.cmake @@ -7,4 +7,7 @@ orcaslicer_add_cmake_project(Draco ${_options} URL https://github.com/google/draco/archive/refs/tags/1.5.7.zip URL_HASH SHA256=27b72ba2d5ff3d0a9814ad40d4cb88f8dc89a35491c0866d952473f8f9416b77 + CMAKE_ARGS + # The encoder and decoder tools duplicate draco.lib; see deps-windows.cmake. + "${DEP_LLD_FORCE_MULTIPLE}" ) \ No newline at end of file diff --git a/deps/NLopt/NLopt.cmake b/deps/NLopt/NLopt.cmake index fdd6341f2b..07afc95a48 100644 --- a/deps/NLopt/NLopt.cmake +++ b/deps/NLopt/NLopt.cmake @@ -8,6 +8,8 @@ orcaslicer_add_cmake_project(NLopt -DNLOPT_GUILE:BOOL=OFF -DNLOPT_SWIG:BOOL=OFF -DNLOPT_TESTS:BOOL=OFF + # testopt is built regardless of NLOPT_TESTS; see deps-windows.cmake. + "${DEP_LLD_FORCE_MULTIPLE}" ) if (MSVC) diff --git a/deps/OpenSSL/OpenSSL.cmake b/deps/OpenSSL/OpenSSL.cmake index 2fb4b51757..4a2d49572b 100644 --- a/deps/OpenSSL/OpenSSL.cmake +++ b/deps/OpenSSL/OpenSSL.cmake @@ -80,6 +80,12 @@ ExternalProject_Add(dep_OpenSSL INSTALL_COMMAND ${_install_cmd} ) +if (CMAKE_GENERATOR MATCHES "Visual Studio") + # OpenSSL builds with cl, but MSBuild runs nmake in this project's toolset + # environment, and ClangCL's puts clang's headers first. Use the default. + set_target_properties(dep_OpenSSL PROPERTIES VS_PLATFORM_TOOLSET "$(DefaultPlatformToolset)") +endif () + ExternalProject_Add_Step(dep_OpenSSL install_cmake_files DEPENDEES install diff --git a/deps/deps-windows.cmake b/deps/deps-windows.cmake index 6e73f7d4b5..4305489758 100644 --- a/deps/deps-windows.cmake +++ b/deps/deps-windows.cmake @@ -42,6 +42,15 @@ else () message(FATAL_ERROR "Unsupported OS architecture: ${DEPS_ARCH}") endif () +# Draco's tools and NLopt's testopt compile sources that are also in their +# static library. MSBuild passes the library before the objects and lld-link +# resolves as it goes, so the library's copy wins and the object then reads as +# a duplicate. Nothing uses those executables, so let lld keep the first one. +set(DEP_LLD_FORCE_MULTIPLE "") +if (CMAKE_GENERATOR MATCHES "Visual Studio" AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set(DEP_LLD_FORCE_MULTIPLE "-DCMAKE_EXE_LINKER_FLAGS:STRING=${CMAKE_EXE_LINKER_FLAGS} /FORCE:MULTIPLE") +endif () + if (${DEP_DEBUG}) set(DEP_BOOST_DEBUG "debug") else ()