For posterity: another of these CMake "hacks" — No CMAKE_C_COMPILER could be found.

So, CMake seems incapable of passing through /pdbaltpath:%_PDB% when given in a CMakeLists.txt (first learning). It somehow ends up as %%%%_PDB%%%% which then will evaluate — assuming a foobar.exe — to %%%foobar.pdb%%% in the PE file. Using generator expressions ($<1:...>) didn’t help either.

So obviously one would start using a workaround like this dumped into a Directory.Build.targets (because the corresponding .props ends up too early in the MSBuild sequencing, so we need to delay things a bit):

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
	<Target Name="OverrideLinkerSwitches" AfterTargets="ComputeLinkSwitches" BeforeTargets="Link">
		<ItemGroup>
			<Link>
				<AdditionalOptions>%(Link.AdditionalOptions) /pdbaltpath:%_PDB%</AdditionalOptions>
			</Link>
		</ItemGroup>
	</Target>
</Project>

For those not “in the know” this manipulates the <ItemGroup /> named Link after it has been defined in the project file, if we assume a vanilla .vcxproj in order to append the linker switch to the end.

Well done, nothing to see here, move on.

Nope, not so! This is what trips up CompilerIdCXX.vcxproj (Modules/CMakeDetermineCXXCompiler.cmakeModules/CMakeFindBinUtils.cmake) with:

error MSB4096: The item “Debug\CMakeCXXCompilerId.obj” in item list “Link” does not define a value for metadata “AdditionalOptions”. In order to use this metadata, either qualify it by specifying %(Link.AdditionalOptions), or ensure that all items in this list define a value for this metadata.

Hmm, very interesting. Why would Link items not have this metadata which gets set by every vanilla C/C++ .vcxproj file?! And why did I even ask?

Of course wasted a massive amount of time on this, because the CMake generator step for VS does considerably more than is conventional in this ecosystem and generates throwaway projects with paths hardcoded to the system and the CMake version and other goodness. It’s the Unix ./configure bolted onto default VS builds for no particular reason.

At a minimum I would have expected that the underhanded detection mechanisms inhibit Directory.Build.* and friends (like with ImportDirectoryBuildProps=false and friends), so the developer can see the failure in the generated projects which then do include them, but the detection with “out-of-tree” inside subdirectory (build dir underneath source dir) doesn’t accidentally catch ambient MSBuild stuff during its “ssssshhhhh”-phase.

Instead I have to work around CMake’s hacks now with this:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
	<ItemDefinitionGroup>
		<Link><!-- Bolt the metadata onto the Link items now, using whatever may have been set before -->
			<AdditionalOptions>%(AdditionalOptions)</AdditionalOptions>
		</Link>
	</ItemDefinitionGroup>
	<Target Name="OverrideLinkerSwitches" AfterTargets="ComputeLinkSwitches" BeforeTargets="Link">
		<ItemGroup>
			<Link>
				<AdditionalOptions>%(Link.AdditionalOptions) /pdbaltpath:%_PDB%</AdditionalOptions>
			</Link>
		</ItemGroup>
	</Target>
</Project>

If you can tell I am no big fan of CMake, that’s no coincidence. But at least this way others will know how to work around it.

At least I got to drive -Wdev --debug-find --debug-output --log-level=DEBUG again.

[imagine a hide the pain Harold GIF here]

PS: it doesn’t find either, so this thread is equally valid for CMAKE_CXX_COMPILER.

FYI: place this into the first <PropertyGroup /> in the .vcxproj and you’ll inhibit these:

    <ImportDirectoryBuildProps>false</ImportDirectoryBuildProps>
    <ImportDirectoryBuildTargets>false</ImportDirectoryBuildTargets>

However, there are more alike (e.g. ImportUserLocationsByWildcardBeforeMicrosoftCommonProps … use grep). MSBuild has many extension mechanisms. Perhaps a subst-drive to an otherwise empty directory would be better than this, because even %TEMP% isn’t safe from ambient MSBuild extension files.

Did you try using bracket syntax, like [[/pdbaltpath:%_PDB%]]

It’s not super clear to me what exactly the goal is here, just that this isn’t the best way to accomplish it

Hi. The goal is to remove all but the file name from the PDB path that gets imbued into the binary. Symbol servers and matching the symbols does the rest. No full paths needed (the normal way is to store the full path to the PDB on the build system). For this to work the %_PDB% must not expand prior to passing it to the linker.

%_PDB% gets internally set during linking with link.exe.

I haven’t tried [[/pdbaltpath:%_PDB%]] and will give it a shot. Thanks. You’re referring to these here, right?

Tested now. The outcome is /pdbaltpath:%%_PDB%% as opposed to four % on either side.

Then this indeed sounds like a CMake bug, or at least undocumented behavior. Bracket arguments are supposed to not perform any escaping. Maybe @brad.king can offer some help here?