-
Notifications
You must be signed in to change notification settings - Fork 391
Avoid duplicate 'WindowsAppRuntimeAutoInitializer.cpp' compilation #5895
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
base: main
Are you sure you want to change the base?
Conversation
LGTM |
@mschofie thanks for taking a stab at this. I don't think it's a complete fix, since we had to scrap a couple earlier attempts (#5685, #5782). My concern is that any batching at all (whether PreprocessorDefinitions, AdditionalOptions, etc) just sets a trap for subsequent dynamic source additions. Can we avoid batching entirely by applying macros to all sources after they've been dynamically added, and that supports Xaml generated sources, etc? |
Thanks for linking to the earlier attempts. TBH, both of those attempts appear to have the same problem as the original bug, that my approach doesn't. For example, #5782 says:
And that's not right - it's not that they're modifying the same
I don't understand how that mitigates the batching. If there's two <Project>
<ItemGroup>
<ClCompile Include="foo" PreprocessorDefinitions="FOO" />
<ClCompile Include="bar" PreprocessorDefinitions="BAR" />
</ItemGroup>
<Target Name="Build">
<Message Text="Batch: %(ClCompile.PreprocessorDefinitions) Items: %(ClCompile.Identity)" />
</Target>
</Project> If you're OK applying the preprocessor defines globally, then there's a much easier fix - simply decouple the ClCompile Item creation from setting the PreprocessorDefinitions metadata; i.e.
Property evaluation and ItemDefinitionGroup updates would have to happen outside of the 'Target', but I'm not sure that's a problem...? This is more 'order dependent' than this current PR, but we're in a .targets file, so I think it's OK. Thoughts? So, something like (I'm coding in an edit box; apologies for typos) <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<WindowsAppRuntimeAutoInitializerPath>$(MSBuildThisFileDirectory)..\..\include\WindowsAppRuntimeAutoInitializer.cpp</WindowsAppRuntimeAutoInitializerPath>
<WindowsAppRuntimeAutoInitializerEnabled>false</WindowsAppRuntimeAutoInitializerEnabled>
<WindowsAppRuntimeAutoInitializerEnabled Condition="'$(WindowsAppSdkBootstrapInitialize)'=='true'">true</WindowsAppRuntimeAutoInitializerEnabled>
<WindowsAppRuntimeAutoInitializerEnabled Condition="'$(WindowsAppSdkDeploymentManagerInitialize)'=='true'">true</WindowsAppRuntimeAutoInitializerEnabled>
<WindowsAppRuntimeAutoInitializerEnabled Condition="'$(WindowsAppSdkUndockedRegFreeWinRTInitialize)'=='true'">true</WindowsAppRuntimeAutoInitializerEnabled>
<WindowsAppRuntimeAutoInitializerEnabled Condition="'$(WindowsAppSdkCompatibilityInitialize)'=='true'">true</WindowsAppRuntimeAutoInitializerEnabled>
</PropertyGroup>
<Target Name="WindowsAppRuntimeAutoInitializer" Condition=" '$(WindowsAppRuntimeAutoInitializerEnabled)'=='true' ">
<ItemGroup>
<ClCompile Include="$(WindowsAppRuntimeAutoInitializerPath)" />
</ItemGroup>
</Target>
<ItemDefinitionGroup>
<ClCompile>
<PreprocessorDefinitions Condition="'$(WindowsAppSdkBootstrapInitialize)'=='true'">MICROSOFT_WINDOWSAPPSDK_AUTOINITIALIZE_BOOTSTRAP;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="'$(WindowsAppSdkDeploymentManagerInitialize)'=='true'">MICROSOFT_WINDOWSAPPSDK_AUTOINITIALIZE_DEPLOYMENTMANAGER;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="'$(WindowsAppSdkUndockedRegFreeWinRTInitialize)'=='true'">MICROSOFT_WINDOWSAPPSDK_AUTOINITIALIZE_UNDOCKEDREGFREEWINRT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="'$(WindowsAppSdkCompatibilityInitialize)'=='true'">MICROSOFT_WINDOWSAPPSDK_AUTOINITIALIZE_COMPATIBILITY;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
</Project> |
Agreed - that's not a correct RCA
Right - I'm suggesting we eliminate batching via speciation of PreprocessorDefinitions (or any other %BatchingMetadata) and just apply metadata globally to all sources - pch, module, whatever. I had suggested doing that during execution after all sources were sure to have been ClCompile included. But I'm fine with doing it during evaluation phase, via ItemDefinitionGroup - that's very appealing. |
Sorry, I don't understand your suggestion. How would you add the PreprocessorDefines? You'd still need to utter At the minute, the only two paths I see are:
I'll update this PR with the |
Yes, you would - but that's not the issue per se. Having different definitions of PreprocessorDefines is what causes the batching. If we apply the same value to all sources, we get no batching and consequently no duplicate source compilation.
|
Indeed, and I don't think we can prevent different definitions of PreprocessorDefines, because customers can easily write valid vcxproj files with different definitions of PreprocessorDefines. A customer can write the following in their .vcxproj: <Project>
<ItemGroup>
<ClCompile Include="CustomerSource.cpp" />
<ClCompile Include="MoreCustomerSource.cpp" PreprocessorDefines="%(PreprocessorDefines);ENABLE_FANCY_MODE"/>
</ItemGroup>
</Project> And then batching will occur in an ItemGroup (in a Target) that uses |
That's true - we can't avoid that. But we can at least avoid self-inflicted issues like this one, by applying PreprocessorDefinitions globally. The interesting thing about this issue is that it's the result of our two targets both including ClCompile items with specific PreprocessorDefinitions: AddUndockedRegFreeWinRTCppDefines and WindowsAppRuntimeAutoInitializer. It's actually the second target that loses, by including its source file into separate batches that the first target has already created. So I'm fine with any solution that avoids our own (re)batching of existing batches. |
…temDefinitionGroup approach)
I've updated this PR with an implementation that takes the ItemDefinitionGroup-approach. This is - IMHO - a tidier approach. The only downside is that if customers are setting any of
within a 'Target', then it won't be honored, they need to be set during evaluation. This seems like a reasonable expectation to me. Thoughts? |
</PropertyGroup> | ||
|
||
<!-- If WindowsAppRuntimeAutoInitializerEnabled is true add a Target to add the WindowsAppRuntimeAutoInitializer.cpp file --> | ||
<Target Name="WindowsAppRuntimeAutoInitializer" Condition=" '$(WindowsAppRuntimeAutoInitializerEnabled)'=='true' "> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we're going to rely on consuming projects setting these properties before importing the .targets file, this inclusion can be an evaluation-time conditional inclusion; no target needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIUC, the point of adding the 'ClCompile' in the Target is to 'hide it' from Solution Explorer. If the ClCompile is added outside of a Target (in a 'top level' ItemGroup) it shows in Solution Explorer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like <Visible>false</Visible>
metadata doesn't work anymore for cases like this. A target seems fine then.
</BeforeClCompileTargets> | ||
</PropertyGroup> | ||
|
||
<ItemDefinitionGroup> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor: consider putting the IDG after the PropertyGroup and before the targets so that a top-to-bottom read more closely matches the evaluation order.
<Target Name="WindowsAppRuntimeAutoInitializer"> | ||
<PropertyGroup> | ||
<WindowsAppRuntimeAutoInitializerPath>$(MSBuildThisFileDirectory)..\..\include\WindowsAppRuntimeAutoInitializer.cpp</WindowsAppRuntimeAutoInitializerPath> | ||
<WindowsAppRuntimeAutoInitializerEnabled>false</WindowsAppRuntimeAutoInitializerEnabled> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<WindowsAppRuntimeAutoInitializerEnabled>false</WindowsAppRuntimeAutoInitializerEnabled> | |
<WindowsAppRuntimeAutoInitializerEnabled Condition=" '$(WindowsAppRuntimeAutoInitializerEnabled)' == '' ">false</WindowsAppRuntimeAutoInitializerEnabled> |
In case a consuming project has already set this to try, a conditional assignment will avoid clobbering that value.
Consider moving the false assignment after all the other conditions so that it gets set to false as a last resort if not set to anything else first.
Up to you if you want to test against '' or != 'true'
Fixes #5395.
build/NuSpecs/WindowsAppSDK-Nuget-Native.AutoInitializer.targets
attempts to add aClCompile
Item during theWindowsAppRuntimeAutoInitializer
Target. In doing so it attempts to use the existing metadata by uttering%(PreprocessorDefinitions)
. This syntax is fine forItemGroup
s outside of a Target, but within a Target, it causes the (implicit) task that creates the Item to be batched (documentation) - causing an Item with the Identity ofWindowsAppRuntimeAutoInitializer.cpp
to be added for every unique set ofClCompile.PreprocessorDefinitions
. The duplicateClCompile
Items cause a cascade of subsequent errors.The fix is to add the
ClCompile
in two steps:ClCompile
without specifying any metadata.'%(Identity)' == '$(WindowsAppRuntimeAutoInitializerPath)'
to force a single task batch (for the Item we're looking for).Thanks to @chwarr for help with this.