Skip to content

Commit cf281c0

Browse files
committed
Dynamically Pack MVVM SourceGen project outputs
Previously, static output path to the MVVM SourceGen assembly was used to pack the MVVM project. This leads to error when OutputPath was updated dynamically when testing or in forks. So, here, we'll update the build so that the SourceGen build outputs will be dynamically packed.
1 parent 895b55f commit cf281c0

8 files changed

+124
-23
lines changed

eng/Toolkit.Common.props

-3
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,4 @@
3939
<!-- Use custom build logic for projects targeting multiple version of the Roslyn Compiler -->
4040
<Import Project="Toolkit.CompilerTargeting.props" Condition="'$(IsCompilerTargeting)' == 'true'" />
4141

42-
<!-- Extends NuGet packaging with specific features required for packing toolkit projects -->
43-
<Import Project="Toolkit.Packaging.props" Condition="'$(IsPackable)' != 'false'" />
44-
4542
</Project>

eng/Toolkit.Common.targets

-3
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,4 @@
4747
</PropertyGroup>
4848
</Target>
4949

50-
<!-- Extends NuGet packaging with specific features required for packing toolkit projects -->
51-
<Import Project="Toolkit.Packaging.targets" Condition="'$(IsPackable)' == 'true'" />
52-
5350
</Project>

eng/Toolkit.CustomAfterCommon.targets

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<Project>
2+
3+
<!--
4+
Supports getting common build outputs out of a project in a coherent and packable way.
5+
This also makes it easy to pack the Roslyn source generator outputs to the NuGet package.
6+
-->
7+
<Import Project="Toolkit.GetBuildOutputs.targets" Condition="'$(IsPackable)' == 'false'" />
8+
9+
</Project>

eng/Toolkit.GetBuildOutputs.targets

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<Project>
2+
3+
<!-- We depend on these Common Targets that gathers various Build Outputs of the included project -->
4+
<PropertyGroup>
5+
<_GetBuildOutputsDependsOn>BuiltProjectOutputGroup;DocumentationProjectOutputGroup</_GetBuildOutputsDependsOn>
6+
<_GetBuildOutputsDependsOn Condition="'$(IncludeSymbols)' != 'false'">$(_GetBuildOutputsDependsOn);DebugSymbolsProjectOutputGroup</_GetBuildOutputsDependsOn>
7+
<_GetBuildOutputsDependsOn Condition="'$(IncludeSatelliteAssemblies)' != 'false'">$(_GetBuildOutputsDependsOn);SatelliteDllsProjectOutputGroup</_GetBuildOutputsDependsOn>
8+
</PropertyGroup>
9+
10+
<!-- Check for 'TargetFramework' input before proceeding. -->
11+
<Target Name="_CheckTargetFrameworkInput"
12+
DependsOnTargets="_CheckForInvalidConfigurationAndPlatform"
13+
Condition="'$(TargetFramework)' == ''">
14+
<Error Code="NCTDEV01" Text="The 'GetBuildOutputsPerTarget' target is meant to output the build items per 'TargetFramework'. Please call the target with a non-empty 'TargetFramework'."/>
15+
</Target>
16+
17+
<!--
18+
Gathers various Build Outputs from included Project per 'TargetFramework'.
19+
This target is called in by the 'GetBuildOutputs' target to combine build outputs from all 'TargetFrameworks'.
20+
-->
21+
<Target Name="GetBuildOutputsPerTarget"
22+
DependsOnTargets="_CheckTargetFrameworkInput;$(_GetBuildOutputsDependsOn)"
23+
Returns="@(BuildOutputPerTarget)">
24+
<ItemGroup>
25+
<BuildOutputPerTarget Include="@(BuiltProjectOutputGroupOutput);@(DocumentationProjectOutputGroupOutput)"/>
26+
<BuildOutputPerTarget Include="@(DebugSymbolsProjectOutputGroupOutput)" Condition="'$(IncludeSymbols)' != 'false'"/>
27+
<BuildOutputPerTarget Include="@(SatelliteDllsProjectOutputGroupOutput)" Condition="'$(IncludeSatelliteAssemblies)' != 'false'"/>
28+
<BuildOutputPerTarget Update="@(BuildOutputPerTarget)" TargetFramework="$(TargetFramework)"/>
29+
</ItemGroup>
30+
31+
<ItemGroup>
32+
<BuildOutputPerTarget Update="@(BuildOutputPerTarget)" TargetCompiler="$(TargetCompiler)" Condition="'$(TargetCompiler)' != ''"/>
33+
<BuildOutputPerTarget Update="@(BuildOutputPerTarget)" TargetLanguage="$(TargetLanguage)" Condition="'$(TargetLanguage)' != ''"/>
34+
<BuildOutputPerTarget Update="@(BuildOutputPerTarget)" IsSourceAnalyzer="true" Condition="'$(IsSourceAnalyzerProject)' == 'true'"/>
35+
<BuildOutputPerTarget Update="@(BuildOutputPerTarget)" IsSourceGenerator="true" Condition="'$(IsSourceGeneratorProject)' == 'true'"/>
36+
</ItemGroup>
37+
</Target>
38+
39+
<!--
40+
Gathers various Build Outputs from this Project across 'TargetFrameworks'.
41+
This target is called in the parent project to pack into a package.
42+
-->
43+
<Target Name="GetBuildOutputs"
44+
DependsOnTargets="_GetTargetFrameworksOutput"
45+
Returns="@(BuildOutput)">
46+
<MSBuild
47+
Projects="$(MSBuildProjectFullPath)"
48+
Targets="GetBuildOutputsPerTarget"
49+
Properties="TargetFramework=%(_TargetFrameworks.Identity)">
50+
<Output TaskParameter="TargetOutputs" ItemName="BuildOutput" />
51+
</MSBuild>
52+
</Target>
53+
54+
</Project>

eng/Toolkit.Packaging.targets

+44-1
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,16 @@
55
Use 'TargetsForTfmSpecificContentInPackage' extensibility point to include custom TFM-specific assets in the package.
66
-->
77
<PropertyGroup>
8-
<GenerateNuSpecDependsOn>_AddGlobalPackageFilesToNuGetPack;$(GenerateNuSpecDependsOn)</GenerateNuSpecDependsOn>
8+
<GenerateNuSpecDependsOn>_GetNonReferencingProjectBuildOutputs;_AddGlobalPackageFilesToNuGetPack;$(GenerateNuSpecDependsOn)</GenerateNuSpecDependsOn>
99
<TargetsForTfmSpecificContentInPackage>_AddPackageFilesPerTargetFrameworkToNuGetPack</TargetsForTfmSpecificContentInPackage>
1010
</PropertyGroup>
1111

12+
<!--
13+
=====================================================================================================================================================
14+
Generic PackageFile support
15+
=====================================================================================================================================================
16+
-->
17+
1218
<!--
1319
When the '$(IncludeContentInPack)' property is false, files specified via '@(None)', '@(Content)' items
1420
are excluded from the NuGet package. Adding '@(PackageFile)' items directly to '%(_PackageFiles)' item
@@ -45,4 +51,41 @@
4551
Text="The package file ('%(Identity)') is 'TargetFramework' specific and should include the value ('$(TargetFramework)') somewhere in the target path ('%(PackagePath)')." />
4652
</Target>
4753

54+
<!--
55+
=====================================================================================================================================================
56+
Pack Non-Referencing Projects
57+
=====================================================================================================================================================
58+
-->
59+
60+
<!--
61+
Pack non-referencing project's build outputs to the correct package folders depending on their intended usage.
62+
The following target uses 'GenerateNuSpecDependsOn' extensibility point to pack the build assets
63+
by using the custom 'GetBuildOutputs' target imported within the target project.
64+
-->
65+
<Target Name="_GetNonReferencingProjectBuildOutputs">
66+
<!-- Get the build outputs of projects which don't reference the output assembly -->
67+
<MSBuild
68+
Projects="@(ProjectReference->WithMetadataValue('ReferenceOutputAssembly', 'false'))"
69+
Targets="GetBuildOutputs">
70+
<Output TaskParameter="TargetOutputs" ItemName="NonReferencingProjectBuildOutput" />
71+
</MSBuild>
72+
</Target>
73+
74+
<Target Name="_PackNonReferencingProjectBuildOutputs"
75+
DependsOnTargets="_GetNonReferencingProjectBuildOutputs"
76+
BeforeTargets="_AddGlobalPackageFilesToNuGetPack"
77+
Condition="'$(PackNonReferencingProjects)' == 'true'">
78+
79+
<!-- Include the Build Outputs (also for each Custom target, like Roslyn analyzers and generators) in the package -->
80+
<ItemGroup>
81+
<_NonReferencingProjectPackageFile Include="@(NonReferencingProjectBuildOutput)">
82+
<TargetPath Condition="'%(IsSourceAnalyzer)' == 'true'">$([System.IO.Path]::Combine('analyzers', 'dotnet', '%(TargetCompiler)', '%(TargetLanguage)'))</TargetPath>
83+
<TargetPath Condition="'%(IsSourceGenerator)' == 'true'">$([System.IO.Path]::Combine('analyzers', 'dotnet', '%(TargetCompiler)', '%(TargetLanguage)'))</TargetPath>
84+
<TargetPath Condition="'%(TargetPath)' == ''">$([System.IO.Path]::Combine('tools', '%(TargetFramework)'))</TargetPath>
85+
</_NonReferencingProjectPackageFile>
86+
<PackageFile Include="@(_NonReferencingProjectPackageFile)" />
87+
<_NonReferencingProjectPackageFile Remove="@(_NonReferencingProjectPackageFile)" />
88+
</ItemGroup>
89+
</Target>
90+
4891
</Project>

src/CommunityToolkit.Mvvm/CommunityToolkit.Mvvm.csproj

+6-16
Original file line numberDiff line numberDiff line change
@@ -76,22 +76,12 @@
7676
</ItemGroup>
7777

7878
<!--
79-
Pack the source generator build outputs to the correct package folders (for each target Roslyn version)
80-
for them to be used as analyzers. Roslyn compilers will automatically load the highest version compatible
81-
with the Roslyn's version in the SDK.
82-
83-
HACK: The recommended way to pack is to get the build outputs using the project's built-in targets,
84-
and include them using NuGet's Pack targets. However, due to a bug in v5 of those targets,
85-
the outputs were not included in the package. So, including them directly via final
86-
output path and building it first is the only way to ensure they are included.
79+
Pack the source generator build outputs to the correct package folders for it to be used as an analyzer.
80+
Roslyn compilers will automatically load the highest version compatible with it's version in the SDK.
81+
This uses the Custom Packaging targets that the toolkit build infrastructure imports.
8782
-->
88-
<ItemGroup>
89-
<!-- Roslyn v4.0 target -->
90-
<PackageFile Include="..\CommunityToolkit.Mvvm.SourceGenerators\bin\$(Configuration)\netstandard2.0-roslyn4.0\CommunityToolkit.Mvvm.SourceGenerators.dll"
91-
TargetPath="analyzers\dotnet\roslyn4.0\cs" />
92-
<!-- Roslyn v4.3 target -->
93-
<PackageFile Include="..\CommunityToolkit.Mvvm.SourceGenerators\bin\$(Configuration)\netstandard2.0-roslyn4.3\CommunityToolkit.Mvvm.SourceGenerators.dll"
94-
TargetPath="analyzers\dotnet\roslyn4.3\cs" />
95-
</ItemGroup>
83+
<PropertyGroup>
84+
<PackNonReferencingProjects>true</PackNonReferencingProjects>
85+
</PropertyGroup>
9686

9787
</Project>

src/Directory.Build.props

+8
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,12 @@
1616
<PackageReference Include="PolySharp" Version="1.8.1" PrivateAssets="All" IncludeAssets="Analyzers;Build" />
1717
</ItemGroup>
1818

19+
<!-- Hook various extension targets at the right place in MSBuild target graph using built-in extension points -->
20+
<PropertyGroup>
21+
<CustomAfterMicrosoftCommonTargets>$(BuildToolsDirectory)Toolkit.CustomAfterCommon.targets</CustomAfterMicrosoftCommonTargets>
22+
</PropertyGroup>
23+
24+
<!-- Extends NuGet packaging with specific features required for packing toolkit projects -->
25+
<Import Project="$(BuildToolsDirectory)Toolkit.Packaging.props" Condition="'$(IsPackable)' != 'false'" />
26+
1927
</Project>

src/Directory.Build.targets

+3
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,7 @@
2020
<InternalsVisibleTo Update="@(InternalsVisibleTo)" PublicKey="$(AssemblySignPublicKey)" Condition="@(InternalsVisibleTo->Count()) != 0" />
2121
</ItemGroup>
2222

23+
<!-- Extends NuGet packaging with specific features required for packing toolkit projects -->
24+
<Import Project="$(BuildToolsDirectory)Toolkit.Packaging.targets" Condition="'$(IsPackable)' == 'true'" />
25+
2326
</Project>

0 commit comments

Comments
 (0)