Compare commits

...

2 Commits

Author SHA1 Message Date
Clint Rutkas
4939de08f4 Fix MSB3030 parallel-build race in ImageResizer.UnitTests
ImageResizerUI is a self-contained WinUI 3 app whose runtimeconfig.json and deps.json are written late into the shared WinUI3Apps output folder. ImageResizer.UnitTests references it only for types (InternalsVisibleTo), never launches the app, yet MSBuild pulled those two app runtime json files into the test's copy-to-output list. Under a highly parallel build the copy step could run before the files existed, producing MSB3030. Remove just those two never-needed files from the copy lists so the race is impossible; the referenced PowerToys.ImageResizer.dll is still copied so tests run normally.
2026-07-01 22:17:14 -07:00
Clint Rutkas
0ef68e4cd4 first part of cold full reboot fix 2026-07-01 21:53:57 -07:00
3 changed files with 74 additions and 0 deletions

View File

@@ -2,6 +2,34 @@
<Project ToolsVersion="4.0"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!--
Cold "Rebuild All" ordering fix for native PackageReference projects.
A handful of native projects use <RestoreProjectStyle>PackageReference</RestoreProjectStyle>
(Microsoft.CommandPalette.Extensions, Microsoft.Terminal.UI, PowerToys.MeasureToolCore,
FindMyMouse, PowerRenameUILib, runner). For those, the CppWinRT configuration - most importantly
the MIDL default <EnableWindowsRuntime>true</EnableWindowsRuntime> that makes IInspectable /
IUnknown resolvable - only arrives via the restore-generated obj\*.nuget.g.props.
On a cold "Rebuild All", MSBuild evaluates the project (resolving imports) before the in-session
NuGet restore regenerates those obj props, so MIDL runs in non-WinRT mode and fails with
"MIDL2009: undefined symbol IInspectable". Because Microsoft.CommandPalette.Extensions and
Microsoft.Terminal.UI are winmd producers consumed (by file path) across the entire Command
Palette graph, that single race cascades into dozens of downstream CS0006/CS0234 failures. A
second build "works" only because restore has since written the obj props. This is why the
RestoreThenBuild PowerShell helper (two separate msbuild invocations) already avoids the issue.
Statically importing the committed CppWinRT props here makes the WinRT MIDL configuration present
at evaluation time regardless of restore ordering, so it is durable across Visual Studio, the
build scripts, and CI. It is scoped to PackageReference-style vcxproj only (the ~100 other native
projects already import this same committed package directly), and the Exists() guard plus
CppWinRT's own import guards keep it a safe no-op once restore has run.
-->
<Import Project="$(MSBuildThisFileDirectory)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.props"
Condition="'$(MSBuildProjectExtension)' == '.vcxproj'
and '$(RestoreProjectStyle)' == 'PackageReference'
and Exists('$(MSBuildThisFileDirectory)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.props')" />
<!-- Skip building C++ test projects when BuildTests=false -->
<PropertyGroup Condition="'$(_IsSkippedTestProject)' == 'true'">
<UsePrecompiledHeaders>false</UsePrecompiledHeaders>

View File

@@ -13,4 +13,25 @@
-->
<Import Project="$(VcpkgRoot)\scripts\buildsystems\msbuild\vcpkg.targets"
Condition="'$(MSBuildProjectExtension)' == '.vcxproj' and '$(VcpkgEnabled)' == 'true' and Exists('$(VcpkgRoot)\scripts\buildsystems\msbuild\vcpkg.targets')" />
<!--
Cold "Rebuild All" ordering fix (matching partner to the CppWinRT props import in Cpp.Build.props).
Native PackageReference projects (Microsoft.CommandPalette.Extensions, Microsoft.Terminal.UI,
PowerToys.MeasureToolCore, FindMyMouse, PowerRenameUILib, runner) receive the CppWinRT MIDL
metadata wiring (CppWinRTMidlResponseFile, AdditionalMetadataDirectories, the MdMerge step that
produces the .winmd) only from the restore-generated obj\*.nuget.g.targets. On a cold "Rebuild
All" that file has not been regenerated yet when the project is evaluated, so MIDL cannot resolve
IInspectable / IUnknown and the Command Palette graph fails with MIDL2009 - until a second build.
Importing the committed CppWinRT targets here - after Microsoft.Cpp.targets, exactly where the
statically-wired native projects put their own CppWinRT ExtensionTargets import - makes that wiring
present regardless of restore ordering. Scoped to PackageReference-style vcxproj only; the Exists()
guard and CppWinRT's own import guards make it a safe no-op once restore has run or for the ~100
native projects that already import this committed package directly.
-->
<Import Project="$(MSBuildThisFileDirectory)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.targets"
Condition="'$(MSBuildProjectExtension)' == '.vcxproj'
and '$(RestoreProjectStyle)' == 'PackageReference'
and Exists('$(MSBuildThisFileDirectory)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.targets')" />
</Project>

View File

@@ -26,6 +26,31 @@
<ProjectReference Include="..\ui\ImageResizerUI.csproj" />
</ItemGroup>
<!--
MSB3030 parallel-build race hardening.
ImageResizerUI is a self-contained WinUI 3 app whose PowerToys.ImageResizer.runtimeconfig.json
and PowerToys.ImageResizer.deps.json are written late into the shared WinUI3Apps output folder.
This unit test project references ImageResizerUI only for its types (see InternalsVisibleTo
"ImageResizer.Test" in ImageResizerUI.csproj); it never launches the app, so it does not need
those two app runtime json files. During a highly parallel "Build All" the test's
copy-to-output step can run before those files exist, producing:
MSB3030: Could not copy the file "...\PowerToys.ImageResizer.runtimeconfig.json" because it was not found.
Removing just those two never-needed files from this project's copy-to-output lists makes the
race impossible. The referenced PowerToys.ImageResizer.dll is still copied, so tests run normally.
-->
<Target Name="ExcludeReferencedAppRuntimeJsonFromCopy"
AfterTargets="GetCopyToOutputDirectoryItems">
<ItemGroup>
<_SourceItemsToCopyToOutputDirectory
Remove="@(_SourceItemsToCopyToOutputDirectory)"
Condition="'%(Filename)%(Extension)' == 'PowerToys.ImageResizer.runtimeconfig.json' or '%(Filename)%(Extension)' == 'PowerToys.ImageResizer.deps.json'" />
<_SourceItemsToCopyToOutputDirectoryAlways
Remove="@(_SourceItemsToCopyToOutputDirectoryAlways)"
Condition="'%(Filename)%(Extension)' == 'PowerToys.ImageResizer.runtimeconfig.json' or '%(Filename)%(Extension)' == 'PowerToys.ImageResizer.deps.json'" />
</ItemGroup>
</Target>
<ItemGroup>
<Content Include="Test.gif">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>