Skip to content

Ensure that MSBuild integration bootstraps Vcpkg #41605

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

mschofie
Copy link
Member

Fixes #23366

The current MSBuild integration doesn't bootstrap VCPkg when running in 'manifest' mode - meaning that consumers have to manually run, say, bootstrap-vcpkg.bat before building, or the build fails with errors. #23366 tracks this and calls out the problems in running bootstrap-vcpkg.bat - just adding a target that runs bootstrap-vcpkg.bat can cause concurrency problems on parallel, multi-project builds. When building in parallel, multiple MSBuild 'engines' are invoked for the multiple projects, and multiple could attempt to run bootstrap-vcpkg.bat concurrently, and bootstrap-vcpkg.bat doesn't protect against concurrent execution, resulting in errors writing files. Under CMake this isn't a problem since the CMake integration runs during the CMake 'configuration' phase, which is executed sequentially. But MSBuild doesn't have a similar, single threaded equivalent.

MSBuild does provide a threading guarantee that we can use - it guarantees that an MSBuild task for a given project, and a given set of global properties will run once (called out in the documentation for the MSBuild task). So if we invoke an MSBuild task with a global property of the Vcpkg root to bootstrap then MSBuild will ensure that it is only run once, blocking all other MSBuild engines until the bootstrap is complete. And that's what this PR does.

There's a few pieces to this change:

  1. I introduce the VcpkgAutoBootstrap property and default it to true. This allows consumers to opt-out of the functionality. I'm fine with defaulting to false, and making it opt-in if a more cautious roll-out is needed.
  2. In scripts/buildsystems/msbuild/vcpkg.targets I conditionally import scripts/buildsystems/msbuild/support/Bootstrap.targets that contains the bulk of the implementation. By putting the logic in a separate 'targets' file, then it can be 'firewalled' a little. The conditionality requires:
    1. VcpkgEnableManifest to be true, to only run on manifest builds (a requirement called out in the tracking issue)
    2. VcpkgAutoBootstrap to be true, to allow folks to opt-out (a requirement called out in the tracking issue)
    3. MSBuild is 16.5 and higher - The implementation depends on IBuildEngine6.GetGlobalProperties, which was introduced in MSBuild 16.5. The MSBuild 16.5 release notes don't call this out, I binary searched the "Microsoft.Build.Framework" NuGet packages, and 16.5 is the first version to contain IBuildEngine6.
    4. Windows - I only need this on Windows, so I'm just scoping it for now. If non-Windows support is needed, it can be added separately.
  3. scripts/buildsystems/msbuild/support/Bootstrap.targets provides a VcpkgBootstrap target that marks itself as BeforeTargets="VcpkgInstallManifestDependencies" to ensure that Vcpkg is bootstrapped before it is used. The target also configures Inputs and Outputs appropriately, so that when bootstrapped the target won't be run, and the added cost is minimal. The implementation uses a custom task to get the current global property names, and then invokes MSBuild to run the VcpkgBootstrapImplementation removing all global properties and setting the _ZVcpkgRoot property. MSBuild will only run a single VcpkgBootstrapImplementation instance, and so the work that VcpkgBootstrapImplementation does will only run once.
  4. scripts\buildsystems\msbuild\support\GetGlobalProperties.task provides the implementation of the custom task to get the global properties. The task has a single output parameter - GlobalPropertyNames - which is a semi-colon delimited list of the global property names, that the VcpkgBootstrap target specifies as the RemoveProperties parameter to MSBuild.

I've been using an implementation of this fix in my consuming repo for a week or so, and I've buddy tested this fix by forcing a long delay in bootstrap-vcpkg.bat. On a build where vcpkg.exe isn't present, the VcpkgBootstrap target is 'built' by multiple projects (showing that there's contention by default), but the VcpkgBootstrapImplementation task is only run once (so MSBuild is protecting it). On an incremental build, the VcpkgBootstrap task is skipped entirely, thanks to the MSBuild up-to-date checks.

@JonLiu1993 JonLiu1993 added the category:infrastructure Pertaining to the CI/Testing infrastrucutre label Oct 17, 2024
@Cheney-W Cheney-W added category:tool-update The issue is with build tool or build script, which requires update or should be executed correctly and removed category:infrastructure Pertaining to the CI/Testing infrastrucutre labels Oct 17, 2024
@Cheney-W Cheney-W requested a review from data-queue October 17, 2024 06:13
@mschofie mschofie marked this pull request as ready for review October 17, 2024 17:04
@JonLiu1993 JonLiu1993 added the info:reviewed Pull Request changes follow basic guidelines label Oct 21, 2024
@BillyONeal BillyONeal added requires:vcpkg-team-review This PR or issue requires someone on the vcpkg team to take a further look. and removed info:reviewed Pull Request changes follow basic guidelines labels Oct 23, 2024
@BillyONeal
Copy link
Member

We discussed this and confirm that we would be happy to make this work in MSBuild if a solution to the concurrency problem can be found and the perf impacts aren't too significant.

3. MSBuild is 16.5 and higher - The implementation depends on IBuildEngine6.GetGlobalProperties, which was introduced in MSBuild 16.5. The MSBuild 16.5 release notes don't call this out, I binary searched the "Microsoft.Build.Framework" NuGet packages, and 16.5 is the first version to contain IBuildEngine6.

We still support back to VS2015 so this ends up being kind of a problem. On older MSBuild does this 'explode' or just fail to bootstrap?

I am somewhat concerned with the dependency on RoslynCodeTaskFactory, as not all MSBuild distributions are going to have that.

@mschofie
Copy link
Member Author

Thanks for taking a look.

The reason I structured the change the way I did was to 'firewall' the new stuff. Everything in the edited files (vcpkg.props and vcpkg.targets) is as 'portable' MSBuild as I can make it. I couldn't find an explicit declaration of how far back VCPkg's support of MSBuild goes, but in vcpkg.target there's already usage of MSBuild property functions, so I used those in the version check. All 'modern' constructs are in Bootstrap.targets, and that won't be imported if the checks don't pass - that means that older MSBuild's won't even look for Bootstrap.targets and they'll get the existing behavior.

Bootstrap.targets uses a couple of 'modern' MSBuild features:

  1. IBuildEngine6.GetGlobalProperties - This is what motivated the MSBuild 16.5 check blocking the import of Bootstrap.targets. It appears to have first shipped in MSBuild 16.5.
  2. RoslynCodeTaskFactory - The documentation for RoslynCodeTaskFactory calls out that it first shipped in MSBuild 15.8. They give an example to show to use support down-level MSBuild (using CodeTaskFactory instead of RoslynCodeTaskFactory), but since I need MSBuild 16.5 and higher for the IBuildEngine6.GetGlobalProperties call, then I just left RoslynCodeTaskFactory in.

@PhoebeHui PhoebeHui assigned LilyWangLL and unassigned JonLiu1993 Apr 2, 2025
@LilyWangLL
Copy link
Contributor

/azp run

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@BillyONeal BillyONeal added the info:reviewed Pull Request changes follow basic guidelines label Jun 20, 2025
@BillyONeal
Copy link
Member

All 'modern' constructs are in Bootstrap.targets, and that won't be imported if the checks don't pass - that means that older MSBuild's won't even look for Bootstrap.targets and they'll get the existing behavior.

OK, this means no blocker to merging it.

I still have reservations about using CodeTaskFactory or RoslynCodeTaskFactory; this pattern has exploded on me on a number of occasions. But I think if that ends up being a serious issue we could just revert this.

I asked for one more confirmation from the other maintainers before landing this, probably Tuesday of next week

4. The OS is Windows.
-->
<Import Condition="'$(VcpkgEnableManifest)' == 'true' and '$(VcpkgAutoBootstrap)' == 'true' and $([System.Version]::new($(MSBuildVersion))) >= $([System.Version]::new(16, 5)) and $([MSBuild]::IsOSPlatform('Windows'))"
Project="$(MSBuildThisFileDirectory)\support\Bootstrap.targets" />
Copy link
Member

@BillyONeal BillyONeal Jun 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to make sure this file is in neither the VS version or the "one-liner" version. (No change requested in this PR)

Or export

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

@BillyONeal BillyONeal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to check: We really only want this to run in the "git clone" vcpkg deployment paradigm

@mschofie
Copy link
Member Author

mschofie commented Jul 4, 2025

I need to check: We really only want this to run in the "git clone" vcpkg deployment paradigm

Ah, interesting. I just looked at the VS redistribution of 'vcpkg', and it doesn't have bootstrap-vcpkg.bat at all. Which means that this PR needs to accommodate that - at the minute it assumes that bootstrap-vcpkg.bat is present. Would checking for the existence of bootstrap-vcpkg.bat be sufficient to limit this functionality to the "git clone" vcpkg deployment paradigms?

@mschofie
Copy link
Member Author

mschofie commented Jul 4, 2025

I'll convert to a draft to prevent this from being completed (is there a better way? I'm more familiar with ADO than GitHub), until I've handled the bootstrap-vcpkg.bat-isn't-present problem.

@mschofie mschofie marked this pull request as draft July 4, 2025 17:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
category:tool-update The issue is with build tool or build script, which requires update or should be executed correctly info:reviewed Pull Request changes follow basic guidelines requires:vcpkg-team-review This PR or issue requires someone on the vcpkg team to take a further look.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

VS/MSBuild per-project integration that "just works"
5 participants