Fix #116: Add /DEBUG /OPT:REF /OPT:ICF to Release linker flags for PDB generation - #162
Fix #116: Add /DEBUG /OPT:REF /OPT:ICF to Release linker flags for PDB generation#162gregorian-09 wants to merge 2 commits into
Conversation
|
Thanks for the PR; I really appreciate you taking the time to implement and write-up the details. But I'm not entirely sure what the right behavior should be here. In my comment on #116 there's two main scenarios:
Implementing (2) seems entirely reasonable, but - at the time of my comment on #116 - there were too many problems with vcpkg's support for using a '3rd party toolchain' - the main one being that vcpkg expects the 'PATH' environment to be updated, but doesn't provide a mechanism for the '3rd party toolchain' to do that. Ideally, vcpkg would not be reliant on the 'PATH' environment variable, it would use I think there's real value in supporting a '3rd party toolchain' for building vcpkg ports - the main use-case I would care about would be to be able to use clang-cl.exe for compiling vcpkg ports to - hopefully - improve the performance of the build. In that case, writing a vcpkg-specific toolchain that wraps |
… flags for PDB generation The CMake platform files for MSVC only add /DEBUG for Debug and RelWithDebInfo configurations. Release and MinSizeRel builds get /INCREMENTAL:NO but no /DEBUG, so no PDB files are produced. Set CMAKE_<TGT>_LINKER_FLAGS_RELEASE_INIT with /DEBUG /OPT:REF /OPT:ICF alongside the required /machine: flag so that Release builds generate PDB files. Windows.EWDK.toolchain.cmake inherits the fix automatically as it includes Windows.MSVC.toolchain.cmake.
Problem: - Generic Release builds should keep CMake's canonical behavior where PDBs are not enabled by default. - vcpkg Release port builds expect /DEBUG /OPT:REF /OPT:ICF, so that behavior needs a vcpkg-specific integration point instead of changing the general MSVC and Clang toolchains. Changes: - Add Windows.Vcpkg.MSVC.toolchain.cmake and Windows.Vcpkg.Clang.toolchain.cmake wrappers that include vcpkg's Windows toolchain before the matching WindowsToolchain compiler wrapper. - Configure the Clang vcpkg wrapper to use clang-cl via the MSVC frontend variant. - Remove the generic Release linker flag defaults from Windows.MSVC.toolchain.cmake and Windows.Clang.toolchain.cmake. - Replace the generic Release linker flag test with a wrapper include-order and vcpkg Release flag test. - Update README guidance to point VCPKG_CHAINLOAD_TOOLCHAIN_FILE at the new vcpkg-specific wrappers. Verification: - cmake -P test/TestVcpkgToolchainWrappers.cmake passed. - git diff --check --cached passed. - Windows cmake.exe configured and built example/CommandLineC with Windows.Vcpkg.MSVC.toolchain.cmake; the Release link line included /DEBUG /INCREMENTAL:NO /OPT:REF /OPT:ICF and produced CommandLineC.pdb. - Windows cmake.exe configured and built example/CommandLineC with Windows.Vcpkg.Clang.toolchain.cmake; clang-cl/lld-link used the same Release linker flags and produced CommandLineC.pdb. Refs: MarkSchofield#116
0604d92 to
27f2b60
Compare
|
Thanks Mark, that distinction makes sense. I reworked the PR around the split you described: the generic The vcpkg-specific behavior now lives behind two dedicated wrappers: I also updated the README, replaced the original generic Release-linker test with a wrapper-focused test, and verified both wrappers by configuring and building the |
|
Thanks, @gregorian-09. I'm still thinking about the best approach here. To help me try things out - and to make sure we're all on the same page for the discussion - I added to the 'example' folder - adding VCPkg support. I've pushed it as the "mschofie/vcpkg" branch. I submoduled vcpkg, added presets based-on my understanding of the current 'best' way to leverage vcpkg. I used a manifest to build 'sqlite3' (because it's small, and should be relatively easy to build) and 'protobuf' (which pulls in 'abseil' and 'utf8-range', and is a bit bigger). And I added custom triplets to specify the new toolchain files. I did have to make a fix to vcpkg - it unconditionally adds And I had to make a fix to your files - I had to And with that, things appear to be looking good. I have to admit, my original feedback might've been wrong - adding more 'toolchain' .cmake files makes things a little confusing, and I'm wondering if we could detect if the toolchain is being used from within a 'port' build (I'm not sure of the best way to detect that right now), should we just use the existing toolchain files ('Windows.Clang.toolchain.cmake'/'Windows.MSVC.toolchain.cmake'), and towards the end, just include 'scripts/toolchains/windows.cmake' as necessary. I'm a little bit worried about VCPkg's support for this - With the example, and the triplet files using clang-cl.exe, building sqlite and protobuf ports was a good deal faster than msvc, which (as I said earlier) is the most compelling use case I can think of. On my machine running cmake configuration of the 'example' folder with preset 'windows-ninja-msvc-x64-vcpkg' took ~270 seconds using 'msvc' to build the ports, but ~217 seconds using clang-cl. That's a ~20% reduction in port-build times. Just sharing my write-up for now; let me know what you think. |
|
Thanks Mark, this is really useful. Having the example branch makes the tradeoff much clearer, because it moves this from "what should the integration look like?" to "what actually works against real vcpkg port builds?" The include-order issue makes sense. If On the shape of the integration, I see the tradeoff the same way. The dedicated I think the main design question is what that signal should be. So my instinct is:
The clang-cl result is encouraging. A ~20% reduction on sqlite/protobuf port builds is a pretty compelling reason to keep exploring this path. |
Fix #116: No PDB files generated in Release mode
The Problem
When building with the MSVC or Clang toolchain in Release configuration, no .pdb files are produced. This means debuggers cannot resolve symbols, crash dumps are unreadable, and post-mortem debugging of Release builds is impossible. This is a significant pain point for anyone shipping Windows software.
The root cause is that CMake's built-in platform files (
Windows-MSVC.cmake) only add the/DEBUGlinker flag for Debug and RelWithDebInfo configurations. For Release (and MinSizeRel), they add/INCREMENTAL:NObut intentionally omit/DEBUG. That makes sense as a conservative default (some projects do not want the extra linker overhead), but a toolchain file's job is to describe the capabilities of the toolchain, and MSVC's linker is perfectly capable of producing PDBs in Release mode.What Changed
Two files were modified; one is new:
Windows.MSVC.toolchain.cmakeandWindows.Clang.toolchain.cmake- Added aforeachblock after the compiler flag setup that setsCMAKE_<TGT>_LINKER_FLAGS_RELEASE_INITfor all three link target types (EXE, SHARED, MODULE):The
/machine:flag is included so that CMake's platform file does not skip setting it (it has anif(NOT DEFINED)guard). CMake then appends/INCREMENTAL:NOon top of our init value, producing the final set:This matches exactly what the vcpkg team reported as expected in the linked issue.
test/TestReleaseLinkerFlags.cmake(new) - A CMake script-mode unit test that validates the linker flags for all four target architectures (x64, ARM64, ARM, X86) and all three target types, plus verifies that Debug, RelWithDebInfo, and MinSizeRel are not affected.Why This Approach
I considered a few alternatives:
Appending to
CMAKE_<TGT>_LINKER_FLAGS_RELEASEdirectly - This would not work in a toolchain file because those cache variables are not created until after the platform files run.Using the
_INITvariables without/machine:- CMake's platform file guards/machine:withif(NOT DEFINED), so if we set the_INITvariable, CMake would skip adding the architecture flag. Every binary would lack the correct machine type.Setting
CMAKE_MSVC_DEBUG_INFORMATION_FORMAT- This is a CMake 3.25+ variable that controls how debug info is stored (embedded vs PDB), not whether it is generated. It does not solve the problem.The chosen approach - Set the full
_INITvalue including/machine:. The platform file detects it is already defined and skips the machine-type initialization, then appends/INCREMENTAL:NO. Clean, predictable, and matches how CMake's own platform files work.Verification
I verified this on the actual MSVC compiler toolchain through the Windows cmake.exe:
All three example targets built successfully in Release mode, each producing a PDB:
Debug mode also confirmed clean. No regressions.
cmake-lint passes on all 14 tracked CMake files with zero issues.
Notes
/DEBUG), but this fix only targets Release to match the scope of the reported issue. MinSizeRel can be addressed separately if needed.CMAKE_STATIC_LINKER_FLAGS_*) are intentionally left alone. PDBs are only relevant for linked images.