Search code examples
c++cmakevcpkg

cmake + vcpkg : how to verify platform toolset for VC/Windows builds?


I have a cmake project that targets both windows and linux. For the windows build, I'm specifying the v142 platform toolset using VCPKG_TARGET_TRIPLET, with a custom triplet that specifies the appropriate VCPKG_PLATFORM_TOOLSET, because the users of my library are still building with VS2019 and v142 toolset.

This is my custom triplet:

set(VCPKG_TARGET_ARCHITECTURE x86)
set(VCPKG_CRT_LINKAGE dynamic)
set(VCPKG_LIBRARY_LINKAGE static)
set(VCPKG_PLATFORM_TOOLSET "v142")

It's getting passed to vcpkg via cmake by setting the VCPKG_TARGET_TRIPLET cache variable equal to the name of my custom triplet. (yes, I deleted the cmake cache and ran configure before building again).

Everything in my project builds fine, and vcpkg output shows that it's using my triplet. When attempting to link my static library into the application using it, it works for Debug builds; but release builds are failing with the following error:

1>LINK : fatal error C1007: unrecognized flag '-Zc:nrvo' in 'p2'
1>LINK : fatal error LNK1257: code generation failed

I'm not explicitly setting this flag, but according to this documentation it's auto-enabled in the latest toolset versions when using permissive- (which I am using). This particular linker flag is only supported in VS2022, version 17.4 or later (v143 toolset). So I can only conclude that somehow my library is getting built with the wrong toolset.

Any thoughts on how to troubleshoot/fix this? I've tried passing --debug to vcpkg to troubleshoot, but when I look in vcpkg-manifest-install.log I don't see anything that would indicate what's going on. The v143 toolset only shows up in the logfile in relation to the host triplet vcpkg uses for compiler detection stuff. When building targets it appears to be using my custom triplet. I even tried specifying my custom triplet for the VCPKG_HOST_TRIPLET var, but that didn't seem to make any difference in the error I'm getting (although it did remove the reference to v143 toolset that was showing up in the log).


Solution

  • So I've mostly got this working now, figured I'd document my findings since there's very little on the web about this topic.

    The first issue is that when using CMakePresets.json, when IDE's such as VSCode or Visual Studio open your project, they're going to configure the build environment based on the properties in the preset. So if there's nothing in the pre-set telling the IDE what toolset to use, you're going to get the latest one installed.

    The second, and larger, issue is that even though VCPKG knows how to configure the platform toolset and other variables correctly, it doesn't actually do so in the case of a cmake/ninja/vcpkg build. VCPKG will use the existing environment and silently build with wrong platform toolset. As far as I can tell, setting VCPKG_PLATFORM_TOOLSET/VCPKG_PLATFORM_TOOLSET_VERSION in your triplet has no effect on the build when using cmake with ninja.

    I don't know if there's some long and complicated explanation why it has to be that way, or if it's just a bug. But at the very least, I really think VCPKG should create a warning or error if it detects that the current build environment doesn't match what's in the specified triplet.

    For the IDE scenario, I found a few different recommendations that didn't actually work, and one that did. If you want to specify the platform toolset, you need to add the following section to your CMakePreset (use the version number appropriate for your needs - in my case, this is the latest v142 platform toolset from VS2019):

    "toolset": { "value": "version=14.29", "strategy": "external" }
    

    This is the only way the IDE will know what build environment you need.

    Building from the command prompt is a little more difficult. For instance, if you have VS2022 installed with v142 and v143 toolsets, getting to a command prompt for building with v142 isn't possible with any of the Developer Command Prompt shortcuts created by the Visual Studio Installer. The only way I've found to get the correct environment loaded is by calling:

    %ProgramFiles%\Microsoft Visual Studio\2022\Professional\Common7\Tools\VsDevCmd.bat -vcvars_ver="14.29"
    

    At this point, you're on a command prompt that can build with the correct toolset.

    Unfortunately, there's no easy way to load these variables into Powershell. Launch-VsDevShell.ps1 doesn't support the "-vcvars_ver" parameter; and running VsDevCmd.bat will put you in a cmd.exe command prompt, which means when you exit back to Powershell the environment variables are gone.

    Since I use Powershell scripts for building on the command line (and in CI), this was a real hassle to get around. The only way I found was by using the Invoke-Batchfile cmdlet from the Powershell User Community extensions. This mostly works, although it seems like more trouble than should be necessary.

    VCPKG also has a "cmd" command that will load a build environment for you, but it starts a new cmd.exe session to create the environment, which means this functionality can't be used from scripts (unless you break your scripts up and pass an "inner" script to the vcpkg command, but that's just an ugly hack for something that should work better IMHO).

    TLDR: So to summarize, if you want to build with cmake/ninja/vcpkg using a platform toolset other than the latest installed, you need to do the following:

    • Add the "toolset" property to your CMakePreset so IDE's know which toolset to load
    • Call VsDevCmd.bat -vcvars_var="14.xx" to set up the environment for command-line building.
    • Don't bother with setting VCPKG_PLATFORM_TOOLSET/VCPKG_PLATFORM_TOOLSET_VERSION in your triplet, they don't work.