Skip to content

Commit 5cf00af

Browse files
authored
Resolve features from @(ReferencePath) after ResolveAssemblyReferences (#531)
Feature detection now runs in a single PolyfillResolveFeatures target hooked AfterTargets="ResolveAssemblyReferences" BeforeTargets="CoreCompile", and inspects @(ReferencePath) — the final list of assemblies csc will see, with NuGetPackageId / NuGetPackageVersion preserved on NuGet-resolved items. Replaces the previous two-target arrangement (PreparePolyfill running early off @(ResolvedCompileFileDefinitions), plus a DetectBclMemoryPackageRef fallback off @(PackageReference)). The fallback was too optimistic: it fired whenever a PackageReference for Microsoft.Bcl.Memory existed, even when the dll was not actually in csc's /reference list (stale IDE state, ExcludeAssets="compile", etc.). The result was that RefsBclMemory got set, Polyfill's own Index/Range types were suppressed, and the BCL types were also missing — producing CS0246 in Polyfill_IEnumerable_ElementAt and Polyfill_IEnumerable_Take. Inspecting @(ReferencePath) post-RAR makes the feature flags reflect what csc will actually see, eliminating that whole class of bug. Adds ConsumeBclMemoryExcludeCompile as a regression test that uses ExcludeAssets="compile" to reproduce the failure mode without needing an IDE in the loop. ConsumeBclMemoryNoResolve loses its now-impossible PreparePolyfill override and stays as a plain smoke test. If you want it shorter, the body can collapse to: Single PolyfillResolveFeatures target runs after ResolveAssemblyReferences and inspects @(ReferencePath), so feature flags only fire when the corresponding assembly is actually in the reference list csc will see. Replaces the early PreparePolyfill + DetectBclMemoryPackageRef pair, whose @(PackageReference)-based fallback could set RefsBclMemory while the dll was missing from /reference (stale IDE state, ExcludeAssets="compile"), causing CS0246 in Polyfill_IEnumerable_ElementAt / Take. Adds ConsumeBclMemoryExcludeCompile as a regression test.
1 parent 60de353 commit 5cf00af

5 files changed

Lines changed: 45 additions & 36 deletions

File tree

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<NoWarn>$(NoWarn);PolyfillTargetsForNuget</NoWarn>
4+
<TargetFramework>netstandard2.0</TargetFramework>
5+
</PropertyGroup>
6+
<ItemGroup>
7+
<!-- ExcludeAssets="compile" exercises the case where a PackageReference is
8+
present in the csproj but the resolved assembly is not in the compile
9+
reference list that csc sees. Polyfill must keep emitting its own
10+
Index/Range types in this configuration; suppressing them on the basis
11+
of the PackageReference alone would leave the consumer with neither
12+
the Polyfill-defined nor the BCL-provided types. -->
13+
<PackageReference Include="Microsoft.Bcl.Memory" ExcludeAssets="compile" />
14+
</ItemGroup>
15+
<Import Project="$(SolutionDir)\TestIncludes.targets" />
16+
<Import Project="$(SolutionDir)\Polyfill\Polyfill.targets" />
17+
</Project>

src/ConsumeBclMemoryNoResolve/ConsumeBclMemoryNoResolve.csproj

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,5 @@
88
</ItemGroup>
99
<Import Project="$(SolutionDir)\TestIncludes.targets" />
1010
<Import Project="$(SolutionDir)\Polyfill\Polyfill.targets" />
11-
<!-- Simulate IDE builds (e.g. Rider surface heuristics) where
12-
ResolvedCompileFileDefinitions is empty because ResolvePackageAssets
13-
did not run. The framework-static defines like FeatureValueTuple
14-
are set, but package-dynamic ones like RefsBclMemory are not. -->
15-
<Target Name="PreparePolyfill">
16-
<PropertyGroup>
17-
<DefineConstants Condition="$(LowerFramework.StartsWith('netstandard2.0'))">$(DefineConstants);FeatureValueTuple;FeatureRuntimeInformation;FeatureCompression</DefineConstants>
18-
</PropertyGroup>
19-
</Target>
11+
<!-- Smoke test: Microsoft.Bcl.Memory referenced on netstandard2.0. -->
2012
</Project>

src/Polyfill.slnx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
<Project Path="ApiBuilderTests/ApiBuilderTests.csproj" />
2222
<Project Path="Consume/Consume.csproj" />
2323
<Project Path="ConsumeBclMemory/ConsumeBclMemory.csproj" />
24+
<Project Path="ConsumeBclMemoryExcludeCompile/ConsumeBclMemoryExcludeCompile.csproj" />
2425
<Project Path="ConsumeBclMemoryNoResolve/ConsumeBclMemoryNoResolve.csproj" />
2526
<Project Path="ConsumeClassicReferences/ConsumeClassicReferences.csproj" />
2627
<Project Path="ConsumeConsumeBclMemory/ConsumeConsumeBclMemory.csproj" />

src/Polyfill/Polyfill.targets

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
<Project>
22
<PropertyGroup>
3-
<PrepareForBuildDependsOn>$(PrepareForBuildDependsOn);PreparePolyfill</PrepareForBuildDependsOn>
43
<LowerFramework>$(TargetFramework.ToLower())</LowerFramework>
54
<LowerFrameworks>$(TargetFrameworks.ToLower())</LowerFrameworks>
65
</PropertyGroup>
@@ -77,48 +76,48 @@ For example:
7776
</Target>
7877
<!-- end-snippet -->
7978

80-
<!-- Fallback: detect Microsoft.Bcl.Memory from PackageReference when
81-
ResolvedCompileFileDefinitions is unavailable (e.g. IDE builds). -->
82-
<Target Name="DetectBclMemoryPackageRef" BeforeTargets="PreparePolyfill;CoreCompile">
79+
<!-- Feature detection runs after ResolveAssemblyReferences and before
80+
CoreCompile. By that point @(ReferencePath) holds the final list of
81+
assemblies that csc will see, with NuGetPackageId and NuGetPackageVersion
82+
metadata preserved on items that came from NuGet packages. Each Feature*
83+
/ Refs* DefineConstant is set only when the corresponding assembly is
84+
actually present in @(ReferencePath), so the conditional source files
85+
that depend on those defines compile against a reference list that is
86+
guaranteed to contain the types they use. -->
87+
<Target Name="PolyfillResolveFeatures"
88+
AfterTargets="ResolveAssemblyReferences"
89+
BeforeTargets="CoreCompile">
8390
<PropertyGroup>
8491
<DefineConstants
85-
Condition="!$(DefineConstants.Contains('RefsBclMemory')) AND
86-
@(PackageReference->AnyHaveMetadataValue('Identity', 'Microsoft.Bcl.Memory'))">$(DefineConstants);RefsBclMemory</DefineConstants>
87-
</PropertyGroup>
88-
</Target>
92+
Condition="@(ReferencePath->AnyHaveMetadataValue('NuGetPackageId', 'System.ValueTuple'))">$(DefineConstants);FeatureValueTuple</DefineConstants>
8993

90-
<Target Name="PreparePolyfill" DependsOnTargets="ResolvePackageAssets">
91-
<PropertyGroup>
9294
<DefineConstants
93-
Condition="@(ResolvedCompileFileDefinitions->AnyHaveMetadataValue('NuGetPackageId', 'System.ValueTuple'))">$(DefineConstants);FeatureValueTuple</DefineConstants>
95+
Condition="@(ReferencePath->AnyHaveMetadataValue('NuGetPackageId', 'Microsoft.Bcl.Memory'))">$(DefineConstants);RefsBclMemory</DefineConstants>
9496

95-
<DefineConstants
96-
Condition="@(ResolvedCompileFileDefinitions->AnyHaveMetadataValue('NuGetPackageId', 'Microsoft.Bcl.Memory'))">$(DefineConstants);RefsBclMemory</DefineConstants>
97+
<MemoryVersion>@(ReferencePath->WithMetadataValue('NuGetPackageId', 'System.Memory')->Metadata('NuGetPackageVersion'))</MemoryVersion>
9798

98-
<MemoryVersion>@(ResolvedCompileFileDefinitions->WithMetadataValue('NuGetPackageId', 'System.Memory')->Metadata('NuGetPackageVersion'))</MemoryVersion>
99-
10099
<DefineConstants
101100
Condition="'$(MemoryVersion)' != '' AND $([MSBuild]::VersionGreaterThanOrEquals($(MemoryVersion), '4.5.5'))">$(DefineConstants);FeatureMemory</DefineConstants>
102-
101+
103102
<DefineConstants
104-
Condition="@(ResolvedCompileFileDefinitions->AnyHaveMetadataValue('NuGetPackageId', 'System.Runtime.InteropServices.RuntimeInformation'))">$(DefineConstants);FeatureRuntimeInformation</DefineConstants>
105-
103+
Condition="@(ReferencePath->AnyHaveMetadataValue('NuGetPackageId', 'System.Runtime.InteropServices.RuntimeInformation'))">$(DefineConstants);FeatureRuntimeInformation</DefineConstants>
104+
106105
<DefineConstants
107-
Condition="@(ResolvedCompileFileDefinitions->AnyHaveMetadataValue('NuGetPackageId', 'System.Threading.Tasks.Extensions'))">$(DefineConstants);FeatureValueTask</DefineConstants>
106+
Condition="@(ReferencePath->AnyHaveMetadataValue('NuGetPackageId', 'System.Threading.Tasks.Extensions'))">$(DefineConstants);FeatureValueTask</DefineConstants>
108107

109108
<DefineConstants
110-
Condition="@(ResolvedCompileFileDefinitions->AnyHaveMetadataValue('NuGetPackageId', 'System.Net.Http')) OR
111-
@(Reference->AnyHaveMetadataValue('Identity', 'System.Net.Http'))">$(DefineConstants);FeatureHttp</DefineConstants>
109+
Condition="@(ReferencePath->AnyHaveMetadataValue('NuGetPackageId', 'System.Net.Http')) OR
110+
@(ReferencePath->AnyHaveMetadataValue('Filename', 'System.Net.Http'))">$(DefineConstants);FeatureHttp</DefineConstants>
112111

113112
<DefineConstants
114-
Condition="@(ResolvedCompileFileDefinitions->AnyHaveMetadataValue('NuGetPackageId', 'System.IO.Compression')) OR
115-
@(Reference->AnyHaveMetadataValue('Identity', 'System.IO.Compression'))">$(DefineConstants);FeatureCompression</DefineConstants>
113+
Condition="@(ReferencePath->AnyHaveMetadataValue('NuGetPackageId', 'System.IO.Compression')) OR
114+
@(ReferencePath->AnyHaveMetadataValue('Filename', 'System.IO.Compression'))">$(DefineConstants);FeatureCompression</DefineConstants>
116115

117116
<DefineConstants
118-
Condition="@(ResolvedCompileFileDefinitions->AnyHaveMetadataValue('NuGetPackageId', 'Microsoft.Bcl.AsyncInterfaces'))">$(DefineConstants);FeatureAsyncInterfaces</DefineConstants>
117+
Condition="@(ReferencePath->AnyHaveMetadataValue('NuGetPackageId', 'Microsoft.Bcl.AsyncInterfaces'))">$(DefineConstants);FeatureAsyncInterfaces</DefineConstants>
119118

120119
<DefineConstants
121-
Condition="@(ResolvedCompileFileDefinitions->AnyHaveMetadataValue('NuGetPackageId', 'System.Runtime.CompilerServices.Unsafe'))">$(DefineConstants);FeatureUnsafe</DefineConstants>
120+
Condition="@(ReferencePath->AnyHaveMetadataValue('NuGetPackageId', 'System.Runtime.CompilerServices.Unsafe'))">$(DefineConstants);FeatureUnsafe</DefineConstants>
122121

123122
<DefineConstants Condition="$(LowerFramework.StartsWith('net11'))">$(DefineConstants);FeatureMemory;FeatureValueTuple;FeatureValueTask;FeatureRuntimeInformation;FeatureHttp;FeatureCompression;FeatureAsyncInterfaces;FeatureUnsafe</DefineConstants>
124123
<DefineConstants Condition="$(LowerFramework.StartsWith('net10'))">$(DefineConstants);FeatureMemory;FeatureValueTuple;FeatureValueTask;FeatureRuntimeInformation;FeatureHttp;FeatureCompression;FeatureAsyncInterfaces;FeatureUnsafe</DefineConstants>

target-frameworks.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ At build time, if a project is detected to not properly multi target, then a war
9696
>true</MaxNetClassicRequired>
9797
</PropertyGroup>
9898
```
99-
<sup><a href='/src/Polyfill/Polyfill.targets#L35-L62' title='Snippet source file'>snippet source</a> | <a href='#snippet-MaxNetRequired' title='Start of snippet'>anchor</a></sup>
99+
<sup><a href='/src/Polyfill/Polyfill.targets#L34-L61' title='Snippet source file'>snippet source</a> | <a href='#snippet-MaxNetRequired' title='Start of snippet'>anchor</a></sup>
100100
<!-- endSnippet -->
101101

102102

@@ -120,7 +120,7 @@ For example:
120120
Condition="$(MaxNetRequired)" />
121121
</Target>
122122
```
123-
<sup><a href='/src/Polyfill/Polyfill.targets#L63-L78' title='Snippet source file'>snippet source</a> | <a href='#snippet-PolyfillValidateNugetTargets' title='Start of snippet'>anchor</a></sup>
123+
<sup><a href='/src/Polyfill/Polyfill.targets#L62-L77' title='Snippet source file'>snippet source</a> | <a href='#snippet-PolyfillValidateNugetTargets' title='Start of snippet'>anchor</a></sup>
124124
<!-- endSnippet -->
125125

126126

0 commit comments

Comments
 (0)