Skip to content

[vcpkg.targets] Allow specifying the root for the installed directory#49738

Draft
stinos wants to merge 1 commit intomicrosoft:masterfrom
stinos:installeddirroot
Draft

[vcpkg.targets] Allow specifying the root for the installed directory#49738
stinos wants to merge 1 commit intomicrosoft:masterfrom
stinos:installeddirroot

Conversation

@stinos
Copy link
Copy Markdown

@stinos stinos commented Jan 31, 2026

In manifest mode overriding VcPkgInstalledDir leads to each triplet being put in that directory so if VcPkgInstalledDir isn't unique per triplet, each ends up in the same directory. That isn't intended vcpkg usage and hence causes various build issues.

The user could circumvent that by crafting a unique VcPkgInstalledDir for each triplet - which is exactly what vcpkg.targets does already when deriving the VcPkgTriplet value - but the user would need to do that manually since they don't have access to VcPkgTriplet before importing vcpkg.targets, and afterwards it is too late.

So introduce a new property VcpkgInstalledDirRoot which allows the user to specify where they want the installed directory, but append the VcPkgTriplet subdirectory to it. This is fully backwards compatible since this root defaults to $(VcpkgManifestRoot)/vcpkg_installed and that is what was used already.

Note: the alternative (which is the behavior I assumed vcpkg would have) would be that when the user specifies VcPkgInstalledDir in manifest mode, vcpkg.targets appends VcpkgTriplet to it then uses that as actual installed directory, but that's not backwards compatible.

In manifest mode overriding VcPkgInstalledDir leads to each triplet being
put in that directory so if VcPkgInstalledDir isn't unique per triplet,
each ends up in the same directory. That isn't intended vcpkg usage
and hence causes various build issues.

The user could circumvent that by crafting a unique VcPkgInstalledDir
for each triplet - which is exactly what vcpkg.targets does already when
deriving the VcPkgTriplet value - but the user would need to do that
manually since they don't have access to VcPkgTriplet before importing
vcpkg.targets, and afterwards it is too late.

So introduce a new property VcpkgInstalledDirRoot which allows the user
to specify where they want the installed directory, but append the
VcPkgTriplet subdirectory to it. This is fully backwards compatible
since this root defaults to $(VcpkgManifestRoot)/vcpkg_installed and
that is what was used already.
@stinos
Copy link
Copy Markdown
Author

stinos commented Jan 31, 2026

@microsoft-github-policy-service agree

@BillyONeal BillyONeal added requires:vcpkg-team-review This PR or issue requires someone on the vcpkg team to take a further look. and removed requires:vcpkg-team-review This PR or issue requires someone on the vcpkg team to take a further look. labels Feb 3, 2026
@BillyONeal
Copy link
Copy Markdown
Member

The paths vcpkg chooses for these are chosen carefully to avoid multiple vcxprojs and/or multiple platform settings stomping on each other. This ends up being kind of a meaningful feature ask as a result.

Can you provide:

and then mark 'Ready for Review'?

@BillyONeal BillyONeal marked this pull request as draft February 3, 2026 02:22
@stinos
Copy link
Copy Markdown
Author

stinos commented Feb 3, 2026

An complete worked example with the problem you are actually trying to fix with this customization, and

Here's an illustration of the problem already. In short: fairly standard project, one configuration using Multithreaded Debug Dll, the other using Multithreaded Debug, with corresponding VcpkgUseMD values which are mandatory else linking fails with the canonical "fmtd.lib(format.cc.obj) : error LNK2038: mismatch detected for 'RuntimeLibrary': value 'MTd_StaticDebug' doesn't match value 'MDd_DynamicDebug' in main.obj [c:\temp\Project1\Project1.vcxproj]" . The project file Project1.vcxproj:

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup Label="ProjectConfigurations">
    <ProjectConfiguration Include="DebugStatic|x64">
      <Configuration>DebugStatic</Configuration>
      <Platform>x64</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Debug|x64">
      <Configuration>Debug</Configuration>
      <Platform>x64</Platform>
    </ProjectConfiguration>
  </ItemGroup>
  <PropertyGroup Label="Globals">
    <VCProjectVersion>17.0</VCProjectVersion>
    <Keyword>Win32Proj</Keyword>
    <ProjectGuid>{d1309a6d-9296-4a30-bed0-c89368b75999}</ProjectGuid>
    <RootNamespace>Project1</RootNamespace>
    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
  </PropertyGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration" />
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugStatic|x64'" Label="Configuration" />
  <PropertyGroup>
    <ConfigurationType>Application</ConfigurationType>
    <UseDebugLibraries>true</UseDebugLibraries>
    <PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
    <WholeProgramOptimization>false</WholeProgramOptimization>
    <CharacterSet>Unicode</CharacterSet>
  </PropertyGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
  <ItemDefinitionGroup>
    <ClCompile>
      <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <AdditionalOptions>/utf-8 %(AdditionalOptions)</AdditionalOptions>
    </ClCompile>
    <Link>
      <SubSystem>Console</SubSystem>
      <GenerateDebugInformation>true</GenerateDebugInformation>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
    <ClCompile>
      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
    </ClCompile>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='DebugStatic|x64'">
    <ClCompile>
      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
    </ClCompile>
  </ItemDefinitionGroup>
  <ItemGroup>
    <ClCompile Include="main.cpp" />
  </ItemGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
  <PropertyGroup Label="Vcpkg">
    <VcpkgEnableManifest>true</VcpkgEnableManifest>
    <VcpkgUseStatic>true</VcpkgUseStatic>
    <VcpkgUseMD Condition="'$(Configuration)' == 'Debug'">true</VcpkgUseMD>
    <VcpkgUseMD Condition="'$(Configuration)' == 'DebugStatic'">false</VcpkgUseMD>
  </PropertyGroup>
  <ImportGroup>
    <Import Project="$(VCPKG_ROOT)\scripts\buildsystems\msbuild\vcpkg.targets" />
  </ImportGroup>
</Project>

Building in a VS2022 x64 command prompt, vcpkg config was added using

set VCPKG_ROOT=/path/to/vcpkg
%VCPKG_ROOT%\vcpkg new --application
%VCPKG_ROOT%\vcpkg port add fmt

resulting in vcpkg.json

{
  "dependencies": [
    "fmt"
  ]
}

and vcpkg-configuration.json

{
  "default-registry": {
    "kind": "git",
    "baseline": "cc73782a88db48af17f8bfb8328d4cab3d4c246f",
    "repository": "https://github.com/microsoft/vcpkg"
  },
  "registries": [
    {
      "kind": "artifact",
      "location": "https://github.com/microsoft/vcpkg-ce-catalog/archive/refs/heads/main.zip",
      "name": "microsoft"
    }
  ]
}

Now build each configuration - irrelevant output omitted:

>msbuild Project1.vcxproj /p:Platform=x64;Configuration=Debug
...
VcpkgTripletSelection:
  Using triplet "x64-windows-static-md" from "c:\temp\Project1\vcpkg_installed\x64-windows-static-md\x64-windows-static-md\"
  Using normalized configuration "Debug"
VcpkgInstallManifestDependencies:
  Installing vcpkg dependencies to c:\temp\Project1\vcpkg_installed\x64-windows-static-md\
...
>msbuild Project1.vcxproj /p:Platform=x64;Configuration=DebugStatic
...
VcpkgTripletSelection:
  Using triplet "x64-windows-static" from "c:\temp\Project1\vcpkg_installed\x64-windows-static\x64-windows-static\"
  Using normalized configuration "Debug"
VcpkgInstallManifestDependencies:
  Installing vcpkg dependencies to c:\temp\Project1\vcpkg_installed\x64-windows-static\
...
>tree C:\temp\Project1\vcpkg_installed
├───x64-windows-static
│   ├───vcpkg
│   ├───x64-windows
│   └───x64-windows-static
│       ├───debug
│       │   └───lib
│       ├───include
│       │   └───fmt
│       ├───lib
└───x64-windows-static-md
    ├───vcpkg
    ├───x64-windows
    └───x64-windows-static-md
        ├───debug
        │   └───lib
        ├───include
        │   └───fmt
        ├───lib

This was the normal and expected output.

Now add the following into the .vcxproj, in the PropertyGroup with label Vcpkg, to illustrate the issue: vcpkg puts everything in the same directory and by doing so deletes the previous triplets' files plus confuses the up-to-date tracking.

    <VcpkgInstalledDir>$(MsBuildThisFileDirectory)\custom_vcpkg_installed</VcpkgInstalledDir>

Build output:

>msbuild Project1.vcxproj /p:Platform=x64;Configuration=Debug
...
VcpkgTripletSelection:
  Using triplet "x64-windows-static-md" from "c:\temp\Project1\\custom_vcpkg_installed\x64-windows-static-md\"
  Using normalized configuration "Debug"
VcpkgInstallManifestDependencies:
  Installing vcpkg dependencies to c:\temp\Project1\\custom_vcpkg_installed\
...
>tree C:\temp\Project1\custom_vcpkg_installed
├───vcpkg
├───x64-windows
└───x64-windows-static-md
    ├───debug
    │   └───lib
    ├───include
    │   └───fmt
    ├───lib
>msbuild Project1.vcxproj /p:Platform=x64;Configuration=Debug
...
VcpkgTripletSelection:
  Using triplet "x64-windows-static" from "c:\temp\Project1\\custom_vcpkg_installed\x64-windows-static\"
  Using normalized configuration "Debug"
VcpkgInstallManifestDependencies:
  Installing vcpkg dependencies to c:\temp\Project1\\custom_vcpkg_installed\
...
> tree C:\temp\Project1\custom_vcpkg_installed
├───vcpkg
├───x64-windows
└───x64-windows-static
    ├───debug
    │   └───lib
    ├───include
    │   └───fmt
    ├───lib

So far so good, but now build the Debug configuration again:

>msbuild Project1.vcxproj /p:Platform=x64;Configuration=Debug
...
VcpkgTripletSelection:
  Using triplet "x64-windows-static-md" from "c:\temp\Project1\\custom_vcpkg_installed\x64-windows-static-md\"
  Using normalized configuration "Debug"
VcpkgInstallManifestDependencies:
Skipping target "VcpkgInstallManifestDependencies" because all output files are up-to-date with respect to the input files.
ClCompile:
  C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\bin\HostX64\x64\CL.exe /c /I"c:\temp\Project1\\custom_vc
  pkg_installed\x64-windows-static-md\include" /ZI /JMC /nologo /W1 /WX- /diagnostics:column /Od /D _DEBUG /D _CONSOLE /D _UNICODE /D UNICOD
  E /Gm- /EHsc /RTC1 /MDd /GS /fp:precise /Zc:wchar_t /Zc:forScope /Zc:inline /permissive- /Fo"Project1\x64\Debug\\" /Fd"Project1\x64\Debug\
  vc143.pdb" /external:W1 /Gd /TP /FC /errorReport:queue /utf-8 main.cpp
  main.cpp
c:\temp\Project1\main.cpp(1,10): error C1083: Cannot open include file: 'fmt/core.h': No such file or directory [c:\temp\Project1\Project1.v
cxproj]
Done Building Project "c:\temp\Project1\Project1.vcxproj" (default targets) -- FAILED.

Note this can be resolved by removing the .stamp files to force vcpkg has to go through the install process again, but that's not really a solution because of the time that takes.

>del C:\temp\Project1\custom_vcpkg_installed\*.stamp
>msbuild Project1.vcxproj /p:Platform=x64;Configuration=Debug
...
VcpkgTripletSelection:
  Using triplet "x64-windows-static-md" from "c:\temp\Project1\\custom_vcpkg_installed\x64-windows-static-md\"
  Using normalized configuration "Debug"
VcpkgInstallManifestDependencies:
  Installing vcpkg dependencies to c:\temp\Project1\\custom_vcpkg_installed\
...

@stinos
Copy link
Copy Markdown
Author

stinos commented Feb 3, 2026

Docs update added in MicrosoftDocs/vcpkg-docs#566.

To continue from previous comment: instead of changing VcpkgInstalledDir, use the new property from this PR instead:

    <VcpkgInstalledDirRoot>$(MsBuildThisFileDirectory)\custom_vcpkg_installed</VcpkgInstalledDirRoot>

Result:

>msbuild Project1.vcxproj /p:Platform=x64;Configuration=Debug
...
VcpkgTripletSelection:
  Using triplet "x64-windows-static-md" from "c:\temp\Project1\\custom_vcpkg_installed\x64-windows-static-md\x64-windows-static-md\"
  Using normalized configuration "Debug"
VcpkgInstallManifestDependencies:
  Installing vcpkg dependencies to c:\temp\Project1\\custom_vcpkg_installed\x64-windows-static-md\
...
>msbuild Project1.vcxproj /p:Platform=x64;Configuration=DebugStaticVcpkgTripletSelection:
...
VcpkgTripletSelection:
  Using triplet "x64-windows-static" from "c:\temp\Project1\\custom_vcpkg_installed\x64-windows-static\x64-windows-static\"
  Using normalized configuration "Debug"
VcpkgInstallManifestDependencies:
  Installing vcpkg dependencies to c:\temp\Project1\\custom_vcpkg_installed\x64-windows-static\
...
>msbuild Project1.vcxproj /p:Platform=x64;Configuration=Debug
...
VcpkgTripletSelection:
  Using triplet "x64-windows-static-md" from "c:\temp\Project1\\custom_vcpkg_installed\x64-windows-static-md\x64-windows-static-md\"
  Using normalized configuration "Debug"
VcpkgInstallManifestDependencies:
Skipping target "VcpkgInstallManifestDependencies" because all output files are up-to-date with respect to the input files.
...
Build succeeded.
>tree C:\temp\Project1\custom_vcpkg_installed
├───x64-windows-static
│   ├───vcpkg
│   ├───x64-windows
│   └───x64-windows-static
│       ├───debug
│       ├───include
│       │   └───fmt
│       ├───lib
└───x64-windows-static-md
    ├───vcpkg
    ├───x64-windows
    └───x64-windows-static-md
        ├───debug
        │   └───lib
        ├───include
        │   └───fmt
        ├───lib

@stinos stinos marked this pull request as ready for review February 3, 2026 13:35
@BillyONeal
Copy link
Copy Markdown
Member

Your writeup above is a great example of how the option works and the docs PR is great, thank you!

What I'm missing is really the problem being solved: why is VcpkgInstalledDir being changed in the first place? This part:

Now add the following into the .vcxproj, in the PropertyGroup with label Vcpkg, to illustrate the issue: vcpkg puts everything in the same directory and by doing so deletes the previous triplets' files plus confuses the up-to-date tracking.

    <VcpkgInstalledDir>$(MsBuildThisFileDirectory)\custom_vcpkg_installed</VcpkgInstalledDir>

kinda takes it as a given that that value is set to something dangerous.

@JavierMatosD JavierMatosD marked this pull request as draft February 19, 2026 20:53
@stinos
Copy link
Copy Markdown
Author

stinos commented Feb 25, 2026

What I'm missing is really the problem being solved: why is VcpkgInstalledDir being changed in the first place?

Right maybe that should be in the docs as well.

I prefer out of tree artefacts for everything. It's a habit of which I don't actually remember the origin but putting everything next to the project file means for example:

  • have to add it to .gitignore
  • need to delete it again for really clean builds
  • might prefer to make text editors ignore it as well because text editors with only basic linting get confused when all the headers appear multiple times (once for each build configuration, multiplied by number of projects using the same package) so tend to show multiple sources of declarations

Also, though I still have to experiment with it, if pointing VcpkgInstalledDir to the same directory for multiple projects works then a package has to be built only once even when multiple projects depend on it.

stinos added a commit to stinos/vcpkg-docs that referenced this pull request May 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants