Skip to content

Commit 9ffd31f

Browse files
committed
[release/5.0] Support Source Generators in WPF projects (#3846)
[release/5.0] Support source generators in WPF projects (#3846) [5.0.2 servicing]
1 parent 7ab0e58 commit 9ffd31f

File tree

2 files changed

+133
-39
lines changed

2 files changed

+133
-39
lines changed

src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/Microsoft.WinFX.targets

Lines changed: 45 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,8 @@
385385

386386
<_CompileTargetNameForLocalType Condition="'$(_CompileTargetNameForLocalType)' == ''">_CompileTemporaryAssembly</_CompileTargetNameForLocalType>
387387

388+
<!-- The updated .NET 5.0.2 GenerateTemporaryTargetAssembly behavior that supports source generators is off by default. -->
389+
<IncludePackageReferencesDuringMarkupCompilation Condition="'$(IncludePackageReferencesDuringMarkupCompilation)' == ''">false</IncludePackageReferencesDuringMarkupCompilation>
388390
<_ResolveProjectReferencesTargetName Condition="'$(IncludePackageReferencesDuringMarkupCompilation)' != 'false'">ResolveProjectReferences</_ResolveProjectReferencesTargetName>
389391
<_CompileTemporaryAssemblyDependsOn>BuildOnlySettings;ResolveKeySource;$(_ResolveProjectReferencesTargetName);CoreCompile</_CompileTemporaryAssemblyDependsOn>
390392

@@ -418,33 +420,52 @@
418420
<_ParentProjectExtension>$([System.IO.Path]::GetExtension($(MSBuildProjectFullPath)))</_ParentProjectExtension>
419421
<_TemporaryTargetAssemblyProjectNameNoExtension>$([System.String]::Join("_", "$(_ParentProjectName)", "$([System.IO.Path]::GetFileNameWithoutExtension($([System.IO.Path]::GetRandomFileName())))", "wpftmp"))</_TemporaryTargetAssemblyProjectNameNoExtension>
420422
<_TemporaryTargetAssemblyProjectName>$(_TemporaryTargetAssemblyProjectNameNoExtension)$(_ParentProjectExtension)</_TemporaryTargetAssemblyProjectName>
421-
<_TempProjectNuGetExtensionsPath>$([System.Text.RegularExpressions.Regex]::Replace($(MSBuildProjectExtensionsPath), "$(_ParentProjectName)\\$", "$(_TemporaryTargetAssemblyProjectNameNoExtension)\", System.Text.RegularExpressions.RegexOptions.IgnoreCase))</_TempProjectNuGetExtensionsPath>
422423
</PropertyGroup>
423424

424-
<Error Condition="Exists('$(_TempProjectNuGetExtensionsPath)')"
425-
Text="$(_TempProjectNuGetExtensionsPath) should not exist. This is directory name is randomly generated for each temp project compilation." />
426-
427425
<!-- Collect the generated NuGet files from the parent project required to support PackageReferences. -->
428426
<ItemGroup Condition="'$(IncludePackageReferencesDuringMarkupCompilation)' != 'false'">
429-
<SourceGeneratedNuGetPropsAndTargets Include="$(MSBuildProjectExtensionsPath)$(_ParentProjectName)$(_ParentProjectExtension).nuget.g.props"/>
430-
<SourceGeneratedNuGetPropsAndTargets Include="$(MSBuildProjectExtensionsPath)$(_ParentProjectName)$(_ParentProjectExtension).nuget.g.targets"/>
431-
<SourceGeneratedNuGetPropsAndTargets Include="$(MSBuildProjectExtensionsPath)$(_ParentProjectName)$(_ParentProjectExtension).nuget.dgspec.json"/>
432-
<SourceGeneratedNuGetPropsAndTargets Include="$(MSBuildProjectExtensionsPath)project.assets.json"/>
433-
<SourceGeneratedNuGetPropsAndTargets Include="$(MSBuildProjectExtensionsPath)project.nuget.cache"/>
434-
435-
<DestGeneratedNuGetPropsAndTargets Include="$(_TempProjectNuGetExtensionsPath)$(_TemporaryTargetAssemblyProjectName).nuget.g.props"/>
436-
<DestGeneratedNuGetPropsAndTargets Include="$(_TempProjectNuGetExtensionsPath)$(_TemporaryTargetAssemblyProjectName).nuget.g.targets"/>
437-
<DestGeneratedNuGetPropsAndTargets Include="$(_TempProjectNuGetExtensionsPath)$(_TemporaryTargetAssemblyProjectName).nuget.dgspec.json"/>
438-
<DestGeneratedNuGetPropsAndTargets Include="$(_TempProjectNuGetExtensionsPath)project.assets.json"/>
439-
<DestGeneratedNuGetPropsAndTargets Include="$(_TempProjectNuGetExtensionsPath)project.nuget.cache"/>
427+
<_SourceGeneratedNuGetPropsAndTargets Include="$(MSBuildProjectExtensionsPath)$(_ParentProjectName)$(_ParentProjectExtension).nuget.g.props"/>
428+
<_SourceGeneratedNuGetPropsAndTargets Include="$(MSBuildProjectExtensionsPath)$(_ParentProjectName)$(_ParentProjectExtension).nuget.g.targets"/>
429+
<_SourceGeneratedNuGetPropsAndTargets Include="$(MSBuildProjectExtensionsPath)$(_ParentProjectName)$(_ParentProjectExtension).nuget.dgspec.json"/>
430+
431+
<_DestGeneratedNuGetPropsAndTargets Include="$(MSBuildProjectExtensionsPath)$(_TemporaryTargetAssemblyProjectName).nuget.g.props"/>
432+
<_DestGeneratedNuGetPropsAndTargets Include="$(MSBuildProjectExtensionsPath)$(_TemporaryTargetAssemblyProjectName).nuget.g.targets"/>
433+
<_DestGeneratedNuGetPropsAndTargets Include="$(MSBuildProjectExtensionsPath)$(_TemporaryTargetAssemblyProjectName).nuget.dgspec.json"/>
434+
440435
</ItemGroup>
441436

442437
<!-- Copy the renamed outer project NuGet props/targets files to the MSBuildProjectExtensionsPath used by NuGet. -->
443438
<Copy Condition="'$(IncludePackageReferencesDuringMarkupCompilation)' != 'false'"
444-
SourceFiles="@(SourceGeneratedNuGetPropsAndTargets)"
445-
DestinationFiles="@(DestGeneratedNuGetPropsAndTargets)"
439+
SourceFiles="@(_SourceGeneratedNuGetPropsAndTargets)"
440+
DestinationFiles="@(_DestGeneratedNuGetPropsAndTargets)"
446441
/>
447442

443+
<!--
444+
Undo TargetFramework and RID append set in Microsoft.NET.RuntimeIdentifierInference.targets to prevent
445+
a duplicate target framework and runtime identifier in the IntermediateOutputPath when the new BuildEngine
446+
instance runs.
447+
448+
'Append $(RuntimeIdentifier) to directory to output and intermediate paths to prevent bin clashes between
449+
targets. But do not append the implicit default runtime identifier for .NET Framework apps as that would
450+
append a RID the user never mentioned in the path and do so even in the AnyCPU case.'
451+
-->
452+
453+
<PropertyGroup>
454+
<_IntermediateOutputPathNoTargetFrameworkOrRID>$(IntermediateOutputPath)</_IntermediateOutputPathNoTargetFrameworkOrRID>
455+
</PropertyGroup>
456+
457+
<PropertyGroup Condition="'$(IncludePackageReferencesDuringMarkupCompilation)' != 'false' and
458+
'$(AppendRuntimeIdentifierToOutputPath)' == 'true' and '$(RuntimeIdentifier)' != '' and
459+
'$(_UsingDefaultRuntimeIdentifier)' != 'true'">
460+
<_IntermediateOutputPathNoTargetFrameworkOrRID>$([System.Text.RegularExpressions.Regex]::Replace($(_IntermediateOutputPathNoTargetFrameworkOrRID), "$(RuntimeIdentifier)\\$",, System.Text.RegularExpressions.RegexOptions.IgnoreCase))</_IntermediateOutputPathNoTargetFrameworkOrRID>
461+
</PropertyGroup>
462+
463+
<PropertyGroup Condition="'$(IncludePackageReferencesDuringMarkupCompilation)' != 'false' and
464+
'$(AppendTargetFrameworkToOutputPath)' == 'true' and '$(TargetFramework)' != '' and
465+
'$(_UnsupportedTargetFrameworkError)' != 'true'">
466+
<_IntermediateOutputPathNoTargetFrameworkOrRID>$([System.Text.RegularExpressions.Regex]::Replace($(_IntermediateOutputPathNoTargetFrameworkOrRID), "$(TargetFramework)\\$",, System.Text.RegularExpressions.RegexOptions.IgnoreCase))</_IntermediateOutputPathNoTargetFrameworkOrRID>
467+
</PropertyGroup>
468+
448469
<!-- Use the legacy .NET Framework/.NET Core 3.0 GenerateTemporaryTargetAssembly path if 'IncludePackageReferencesDuringMarkupCompilation' is 'false',. -->
449470
<GenerateTemporaryTargetAssembly
450471
CurrentProject="$(MSBuildProjectFullPath)"
@@ -453,7 +474,8 @@
453474
CompileTypeName="Compile"
454475
GeneratedCodeFiles="@(_GeneratedCodeFiles)"
455476
ReferencePath="@(ReferencePath)"
456-
IntermediateOutputPath="$(IntermediateOutputPath)"
477+
BaseIntermediateOutputPath="$(BaseIntermediateOutputPath)"
478+
IntermediateOutputPath="$(_IntermediateOutputPathNoTargetFrameworkOrRID)"
457479
AssemblyName="$(AssemblyName)"
458480
CompileTargetName="$(_CompileTargetNameForLocalType)"
459481
GenerateTemporaryTargetAssemblyDebuggingInformation="$(GenerateTemporaryTargetAssemblyDebuggingInformation)"
@@ -468,18 +490,13 @@
468490
<Output TaskParameter="Include" ItemName="AssemblyForLocalTypeReference" />
469491
</CreateItem>
470492

471-
<!-- Remove generated NuGet props/targets files and temp project directory. -->
472-
<Error
473-
Condition="'$(IncludePackageReferencesDuringMarkupCompilation)' != 'false'
474-
and '!$(_TempProjectNuGetExtensionsPath.Contains(&quot;_wpftmp&quot;))'"
475-
Text="$(_TempProjectNuGetExtensionsPath) is not valid. This directory name is randomly generated for each temp project compilation and must contain a random component and '_wpftmp'."/>
476-
477-
<RemoveDir
493+
<!-- Remove generated NuGet props/targets files. -->
494+
<Delete
478495
Condition="'$(IncludePackageReferencesDuringMarkupCompilation)' != 'false'
479496
and '$(GenerateTemporaryTargetAssemblyDebuggingInformation)' != 'true'"
480-
Directories="$(_TempProjectNuGetExtensionsPath)" />
481-
482-
</Target>
497+
Files="@(_DestGeneratedNuGetPropsAndTargets)" />
498+
499+
</Target>
483500

484501
<!--
485502

src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/Microsoft/Build/Tasks/Windows/GenerateTemporaryTargetAssembly.cs

Lines changed: 88 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -244,32 +244,39 @@ private bool ExecuteGenerateTemporaryTargetAssemblyWithPackageReferenceSupport()
244244
RemoveItemsByName(xmlProjectDoc, MARKUPRESOURCENAME);
245245
RemoveItemsByName(xmlProjectDoc, RESOURCENAME);
246246

247+
// Replace the Reference Item list with ReferencePath
248+
RemoveItemsByName(xmlProjectDoc, REFERENCETYPENAME);
249+
AddNewItems(xmlProjectDoc, ReferencePathTypeName, ReferencePath);
250+
251+
// Add GeneratedCodeFiles to Compile item list.
252+
AddNewItems(xmlProjectDoc, CompileTypeName, GeneratedCodeFiles);
253+
254+
// Replace implicit SDK imports with explicit SDK imports
255+
ReplaceImplicitImports(xmlProjectDoc);
256+
247257
// Add properties required for temporary assembly compilation
248258
var properties = new List<(string PropertyName, string PropertyValue)>
249259
{
250260
( nameof(AssemblyName), AssemblyName ),
251261
( nameof(IntermediateOutputPath), IntermediateOutputPath ),
252-
( "AppendTargetFrameworkToOutputPath", "false"),
262+
( nameof(BaseIntermediateOutputPath), BaseIntermediateOutputPath ),
253263
( "_TargetAssemblyProjectName", Path.GetFileNameWithoutExtension(CurrentProject)),
254-
( nameof(Analyzers), Analyzers)
264+
( nameof(Analyzers), Analyzers )
255265
};
256266

257267
AddNewProperties(xmlProjectDoc, properties);
258268

259-
// Replace the Reference Item list with ReferencePath
260-
RemoveItemsByName(xmlProjectDoc, REFERENCETYPENAME);
261-
AddNewItems(xmlProjectDoc, ReferencePathTypeName, ReferencePath);
262-
263-
// Add GeneratedCodeFiles to Compile item list.
264-
AddNewItems(xmlProjectDoc, CompileTypeName, GeneratedCodeFiles);
265-
266269
// Save the xmlDocument content into the temporary project file.
267270
xmlProjectDoc.Save(TemporaryTargetAssemblyProjectName);
268271

272+
// Disable conflicting Arcade SDK workaround that imports NuGet props/targets
273+
Hashtable globalProperties = new Hashtable(1);
274+
globalProperties["_WpfTempProjectNuGetFilePathNoExt"] = "";
275+
269276
//
270277
// Compile the temporary target assembly project
271278
//
272-
retValue = BuildEngine.BuildProjectFile(TemporaryTargetAssemblyProjectName, new string[] { CompileTargetName }, null, null);
279+
retValue = BuildEngine.BuildProjectFile(TemporaryTargetAssemblyProjectName, new string[] { CompileTargetName }, globalProperties, null);
273280

274281
// Delete the temporary project file and generated files unless diagnostic mode has been requested
275282
if (!GenerateTemporaryTargetAssemblyDebuggingInformation)
@@ -457,6 +464,17 @@ public bool GenerateTemporaryTargetAssemblyDebuggingInformation
457464
public string Analyzers
458465
{ get; set; }
459466

467+
/// <summary>
468+
/// BaseIntermediateOutputPath
469+
///
470+
/// Required for Source Generator support. May be null.
471+
///
472+
/// </summary>
473+
public string BaseIntermediateOutputPath
474+
{
475+
get; set;
476+
}
477+
460478
/// <summary>
461479
/// IncludePackageReferencesDuringMarkupCompilation
462480
///
@@ -665,7 +683,7 @@ private void AddNewProperties(XmlDocument xmlProjectDoc, List<(string PropertyNa
665683

666684
// Create a new PropertyGroup element
667685
XmlNode nodeItemGroup = xmlProjectDoc.CreateElement("PropertyGroup", root.NamespaceURI);
668-
root.InsertAfter(nodeItemGroup, root.FirstChild);
686+
root.PrependChild(nodeItemGroup);
669687

670688
// Append this new ItemGroup item into the list of children of the document root.
671689
foreach(var property in properties)
@@ -683,6 +701,64 @@ private void AddNewProperties(XmlDocument xmlProjectDoc, List<(string PropertyNa
683701
}
684702
}
685703

704+
//
705+
// Replace implicit SDK imports with explicit imports
706+
//
707+
static private void ReplaceImplicitImports(XmlDocument xmlProjectDoc)
708+
{
709+
if (xmlProjectDoc == null)
710+
{
711+
// When the parameters are not valid, simply return it, instead of throwing exceptions.
712+
return;
713+
}
714+
715+
XmlNode root = xmlProjectDoc.DocumentElement;
716+
717+
for (int i = 0; i < root.Attributes.Count; i++)
718+
{
719+
XmlAttribute xmlAttribute = root.Attributes[i] as XmlAttribute;
720+
721+
if (xmlAttribute.Name.Equals("Sdk", StringComparison.OrdinalIgnoreCase))
722+
{
723+
// <Project Sdk="Microsoft.NET.Sdk">
724+
725+
// Remove Sdk attribute
726+
var sdkValue = xmlAttribute.Value;
727+
root.Attributes.Remove(xmlAttribute);
728+
729+
//
730+
// Add explicit top import
731+
//
732+
// <Import Project = "Sdk.props" Sdk="Microsoft.NET.Sdk" />
733+
//
734+
XmlNode nodeImportProps = xmlProjectDoc.CreateElement("Import", root.NamespaceURI);
735+
XmlAttribute projectAttribute = xmlProjectDoc.CreateAttribute("Project", root.NamespaceURI);
736+
projectAttribute.Value = "Sdk.props";
737+
nodeImportProps.Attributes.Append(projectAttribute);
738+
nodeImportProps.Attributes.Append(xmlAttribute);
739+
740+
// Prepend this Import to the root of the XML document
741+
root.PrependChild(nodeImportProps);
742+
743+
//
744+
// Add explicit bottom import
745+
//
746+
// <Import Project = "Sdk.targets" Sdk="Microsoft.NET.Sdk"
747+
//
748+
XmlNode nodeImportTargets = xmlProjectDoc.CreateElement("Import", root.NamespaceURI);
749+
XmlAttribute projectAttribute2 = xmlProjectDoc.CreateAttribute("Project", root.NamespaceURI);
750+
projectAttribute2.Value = "Sdk.targets";
751+
XmlAttribute projectAttribute3 = xmlProjectDoc.CreateAttribute("Sdk", root.NamespaceURI);
752+
projectAttribute3.Value = sdkValue;
753+
nodeImportTargets.Attributes.Append(projectAttribute2);
754+
nodeImportTargets.Attributes.Append(projectAttribute3);
755+
756+
// Append this Import to the end of the XML document
757+
root.AppendChild(nodeImportTargets);
758+
}
759+
}
760+
}
761+
686762
#endregion Private Methods
687763

688764

@@ -733,3 +809,4 @@ private void AddNewProperties(XmlDocument xmlProjectDoc, List<(string PropertyNa
733809

734810
#endregion GenerateProjectForLocalTypeReference Task class
735811
}
812+

0 commit comments

Comments
 (0)