Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/Tasks/Common/ConflictResolution/ConflictItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ internal enum ConflictItemType
Reference,
CopyLocal,
Runtime,
Platform
Platform,
Analyzer
}

internal interface IConflictItem
Expand Down
7 changes: 7 additions & 0 deletions src/Tasks/Common/ConflictResolution/FrameworkListReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,13 @@ private static IEnumerable<ConflictItem> LoadConflictItems(string frameworkListP
var ret = new List<ConflictItem>();
foreach (var file in frameworkList.Root.Elements("File"))
{
var type = file.Attribute("Type")?.Value;

if (type?.Equals("Analyzer", StringComparison.OrdinalIgnoreCase) ?? false)
{
continue;
}

var assemblyName = file.Attribute("AssemblyName")?.Value;
var assemblyVersionString = file.Attribute("Version")?.Value;

Expand Down
22 changes: 22 additions & 0 deletions src/Tasks/Common/ConflictResolution/ResolvePackageFileConflicts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@ namespace Microsoft.NET.Build.Tasks.ConflictResolution
public class ResolvePackageFileConflicts : TaskWithAssemblyResolveHooks
{
private HashSet<ITaskItem> referenceConflicts = new HashSet<ITaskItem>();
private HashSet<ITaskItem> analyzerConflicts = new HashSet<ITaskItem>();
private HashSet<ITaskItem> copyLocalConflicts = new HashSet<ITaskItem>();
private HashSet<ConflictItem> compilePlatformWinners = new HashSet<ConflictItem>();
private HashSet<ConflictItem> allConflicts = new HashSet<ConflictItem>();

public ITaskItem[] References { get; set; }

public ITaskItem[] Analyzers { get; set; }

public ITaskItem[] ReferenceCopyLocalPaths { get; set; }

public ITaskItem[] OtherRuntimeItems { get; set; }
Expand Down Expand Up @@ -46,6 +49,10 @@ public class ResolvePackageFileConflicts : TaskWithAssemblyResolveHooks
[Output]
public ITaskItem[] ReferencesWithoutConflicts { get; set; }

[Output]
public ITaskItem[] AnalyzersWithoutConflicts { get; set; }


[Output]
public ITaskItem[] ReferenceCopyLocalPathsWithoutConflicts { get; set; }

Expand Down Expand Up @@ -90,6 +97,14 @@ protected override void ExecuteCore()
// Remove platform items which won a conflict with a reference but subsequently lost to something else
compilePlatformWinners.ExceptWith(allConflicts);

// resolve analyzer conflicts
var analyzerItems = GetConflictTaskItems(Analyzers, ConflictItemType.Analyzer).ToArray();

using (var analyzerConflictScope = new ConflictResolver<ConflictItem>(packageRanks, packageOverrides, log))
{
analyzerConflictScope.ResolveConflicts(analyzerItems, ci => ci.FileName, HandleAnalyzerConflict);
}

// resolve conflicts that clash in output
IEnumerable<ConflictItem> copyLocalItems;
IEnumerable<ConflictItem> otherRuntimeItems;
Expand Down Expand Up @@ -140,6 +155,7 @@ protected override void ExecuteCore()
}

ReferencesWithoutConflicts = RemoveConflicts(References, referenceConflicts);
AnalyzersWithoutConflicts = RemoveConflicts(Analyzers, analyzerConflicts);
ReferenceCopyLocalPathsWithoutConflicts = RemoveConflicts(ReferenceCopyLocalPaths, copyLocalConflicts);
Conflicts = CreateConflictTaskItems(allConflicts);

Expand Down Expand Up @@ -233,6 +249,12 @@ private void HandleCompileConflict(ConflictItem winner, ConflictItem loser)
allConflicts.Add(loser);
}

private void HandleAnalyzerConflict(ConflictItem winner, ConflictItem loser)
{
analyzerConflicts.Add(loser.OriginalItem);
allConflicts.Add(loser);
}

private void HandleRuntimeConflict(ConflictItem winner, ConflictItem loser)
{
if (loser.ItemType == ConflictItemType.Reference)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,23 @@ public void Given_ResolvedTargetingPacks_with_valid_PATH_in_PlatformManifest_It_
})
};

task.ProjectLanguage = "C#";

task.Execute().Should().BeTrue();

task.ReferencesToAdd[0].ItemSpec.Should().Be(Path.Combine(mockPackageDirectory, "lib/Microsoft.Windows.SDK.NET.dll"));
task.PlatformManifests[0].ItemSpec.Should().Be(Path.Combine(mockPackageDirectory, $"data{Path.DirectorySeparatorChar}PlatformManifest.txt"));
task.AnalyzersToAdd.Length.Should().Be(2);
task.AnalyzersToAdd[0].ItemSpec.Should().Be(Path.Combine(mockPackageDirectory, "analyzers/dotnet/anyAnalyzer.dll"));
task.AnalyzersToAdd[1].ItemSpec.Should().Be(Path.Combine(mockPackageDirectory, "analyzers/dotnet/cs/csAnalyzer.dll"));
}

private readonly string _frameworkList =
"<FileList Name=\"cswinrt .NET Core 5.0\"> <File Type=\"Managed\" Path=\"lib/Microsoft.Windows.SDK.NET.dll\" PublicKeyToken=\"null\" AssemblyName=\"Microsoft.Windows.SDK.NET\" AssemblyVersion=\"10.0.18362.3\" FileVersion=\"10.0.18362.3\" /></FileList>";
@"<FileList Name=""cswinrt .NET Core 5.0"">
<File Type=""Managed"" Path=""lib/Microsoft.Windows.SDK.NET.dll"" PublicKeyToken=""null"" AssemblyName=""Microsoft.Windows.SDK.NET"" AssemblyVersion=""10.0.18362.3"" FileVersion=""10.0.18362.3"" />
<File Type=""Analyzer"" Path=""analyzers/dotnet/anyAnalyzer.dll"" PublicKeyToken=""null"" AssemblyName=""anyAnalyzer"" AssemblyVersion=""10.0.18362.3"" FileVersion=""10.0.18362.3"" />
<File Type=""Analyzer"" Language=""cs"" Path=""analyzers/dotnet/cs/csAnalyzer.dll"" PublicKeyToken=""null"" AssemblyName=""csAnalyzer"" AssemblyVersion=""10.0.18362.3"" FileVersion=""10.0.18362.3"" />
<File Type=""Analyzer"" Language=""vb"" Path=""analyzers/dotnet/vb/vbAnalyzer.dll"" PublicKeyToken=""null"" AssemblyName=""vbAnalyzer"" AssemblyVersion=""10.0.18362.3"" FileVersion=""10.0.18362.3"" />
</FileList>";
}
}
58 changes: 45 additions & 13 deletions src/Tasks/Microsoft.NET.Build.Tasks/ResolveTargetingPackAssets.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,14 @@ public class ResolveTargetingPackAssets : TaskBase

public string NetCoreTargetingPackRoot { get; set; }

public string ProjectLanguage { get; set; }

[Output]
public ITaskItem[] ReferencesToAdd { get; set; }

[Output]
public ITaskItem[] AnalyzersToAdd { get; set; }

[Output]
public ITaskItem[] PlatformManifests { get; set; }

Expand All @@ -48,6 +53,7 @@ public ResolveTargetingPackAssets()
protected override void ExecuteCore()
{
List<TaskItem> referencesToAdd = new List<TaskItem>();
List<TaskItem> analyzersToAdd = new List<TaskItem>();
List<TaskItem> platformManifests = new List<TaskItem>();
PackageConflictPreferredPackages = string.Empty;
List<TaskItem> packageConflictOverrides = new List<TaskItem>();
Expand Down Expand Up @@ -123,8 +129,8 @@ protected override void ExecuteCore()

string frameworkListPath = Path.Combine(targetingPackDataPath, "FrameworkList.xml");

AddReferencesFromFrameworkList(frameworkListPath, targetingPackRoot, targetingPackDllFolder,
targetingPack, referencesToAdd);
AddItemsFromFrameworkList(frameworkListPath, targetingPackRoot, targetingPackDllFolder,
targetingPack, referencesToAdd, analyzersToAdd);

if (File.Exists(platformManifestPath))
{
Expand All @@ -148,7 +154,10 @@ protected override void ExecuteCore()

// Filter out duplicate references (which can happen when referencing two different profiles that overlap)
List<TaskItem> deduplicatedReferences = DeduplicateItems(referencesToAdd);
ReferencesToAdd = deduplicatedReferences.Distinct() .ToArray();
ReferencesToAdd = deduplicatedReferences.Distinct().ToArray();

List<TaskItem> deduplicatedAnalyzers = DeduplicateItems(analyzersToAdd);
AnalyzersToAdd = deduplicatedAnalyzers.Distinct().ToArray();

PlatformManifests = platformManifests.ToArray();
PackageConflictOverrides = packageConflictOverrides.ToArray();
Expand Down Expand Up @@ -184,7 +193,7 @@ private void AddNetStandardTargetingPackAssets(ITaskItem targetingPack, string t

foreach (var dll in Directory.GetFiles(targetingPackAssetPath, "*.dll"))
{
var reference = CreateReferenceItem(dll, targetingPack);
var reference = CreateItem(dll, targetingPack);

if (!Path.GetFileName(dll).Equals("netstandard.dll", StringComparison.OrdinalIgnoreCase))
{
Expand All @@ -195,9 +204,9 @@ private void AddNetStandardTargetingPackAssets(ITaskItem targetingPack, string t
}
}

private void AddReferencesFromFrameworkList(string frameworkListPath, string targetingPackRoot,
private void AddItemsFromFrameworkList(string frameworkListPath, string targetingPackRoot,
string targetingPackDllFolder,
ITaskItem targetingPack, List<TaskItem> referenceItems)
ITaskItem targetingPack, List<TaskItem> referenceItems, List<TaskItem> analyzerItems)
{
XDocument frameworkListDoc = XDocument.Load(frameworkListPath);

Expand Down Expand Up @@ -234,17 +243,40 @@ private void AddReferencesFromFrameworkList(string frameworkListPath, string tar
continue;
}

string dllPath = usePathElementsInFrameworkListAsFallBack ?
string itemType = fileElement.Attribute("Type")?.Value;
bool isAnalyzer = itemType?.Equals("Analyzer", StringComparison.OrdinalIgnoreCase) ?? false;

string dllPath = usePathElementsInFrameworkListAsFallBack || isAnalyzer ?
Path.Combine(targetingPackRoot, fileElement.Attribute("Path").Value) :
GetDllPathViaAssemblyName(targetingPackDllFolder, fileElement);

var referenceItem = CreateReferenceItem(dllPath, targetingPack);
var item = CreateItem(dllPath, targetingPack);

referenceItem.SetMetadata("AssemblyVersion", fileElement.Attribute("AssemblyVersion").Value);
referenceItem.SetMetadata("FileVersion", fileElement.Attribute("FileVersion").Value);
referenceItem.SetMetadata("PublicKeyToken", fileElement.Attribute("PublicKeyToken").Value);
item.SetMetadata("AssemblyVersion", fileElement.Attribute("AssemblyVersion").Value);
item.SetMetadata("FileVersion", fileElement.Attribute("FileVersion").Value);
item.SetMetadata("PublicKeyToken", fileElement.Attribute("PublicKeyToken").Value);

if (isAnalyzer)
{
string itemLanguage = fileElement.Attribute("Language")?.Value;

if (itemLanguage != null)
{
// expect cs instead of C#, fs rather than F# per NuGet conventions
string projectLanguage = ProjectLanguage?.Replace('#', 's');

referenceItems.Add(referenceItem);
if (projectLanguage == null || !projectLanguage.Equals(itemLanguage, StringComparison.OrdinalIgnoreCase))
{
continue;
}
}

analyzerItems.Add(item);
}
else
{
referenceItems.Add(item);
}
}
}

Expand Down Expand Up @@ -279,7 +311,7 @@ private static string GetDllPathViaAssemblyName(string targetingPackDllFolder, X
return dllPath;
}

private TaskItem CreateReferenceItem(string dll, ITaskItem targetingPack)
private TaskItem CreateItem(string dll, ITaskItem targetingPack)
{
var reference = new TaskItem(dll);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,15 @@ Copyright (c) .NET Foundation. All rights reserved.
</ItemGroup>

<ResolvePackageFileConflicts References="@(Reference)"
Analyzers="@(Analyzer)"
ReferenceCopyLocalPaths="@(ReferenceCopyLocalPaths)"
OtherRuntimeItems="@(_RuntimeAssetsForConflictResolution)"
PlatformManifests="@(PackageConflictPlatformManifests)"
TargetFrameworkDirectories="$(TargetFrameworkDirectory)"
PackageOverrides="@(PackageConflictOverrides)"
PreferredPackages="$(PackageConflictPreferredPackages)">
<Output TaskParameter="ReferencesWithoutConflicts" ItemName="_ReferencesWithoutConflicts" />
<Output TaskParameter="AnalyzersWithoutConflicts" ItemName="_AnalyzersWithoutConflicts" />
<Output TaskParameter="ReferenceCopyLocalPathsWithoutConflicts" ItemName="_ReferenceCopyLocalPathsWithoutConflicts" />
<Output TaskParameter="Conflicts" ItemName="_ConflictPackageFiles" />
</ResolvePackageFileConflicts>
Expand All @@ -56,6 +58,8 @@ Copyright (c) .NET Foundation. All rights reserved.
<ItemGroup>
<Reference Remove="@(Reference)" />
<Reference Include="@(_ReferencesWithoutConflicts)" />
<Analyzer Remove="@(Analyzer)" />
<Analyzer Include="@(_AnalyzersWithoutConflicts)" />
<ReferenceCopyLocalPaths Remove="@(ReferenceCopyLocalPaths)" />
<ReferenceCopyLocalPaths Include="@(_ReferenceCopyLocalPathsWithoutConflicts)" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -326,12 +326,14 @@ Copyright (c) .NET Foundation. All rights reserved.
</PropertyGroup>

<ResolveTargetingPackAssets FrameworkReferences="@(FrameworkReference)"
ProjectLanguage="$(Language)"
ResolvedTargetingPacks="@(ResolvedTargetingPack)"
RuntimeFrameworks="@(RuntimeFramework)"
GenerateErrorForMissingTargetingPacks="$(GenerateErrorForMissingTargetingPacks)"
NuGetRestoreSupported="$(_NuGetRestoreSupported)"
NetCoreTargetingPackRoot="$(NetCoreTargetingPackRoot)">
<Output TaskParameter="ReferencesToAdd" ItemName="Reference" />
<Output TaskParameter="AnalyzersToAdd" Condition="'$(DisableFrameworkReferenceAnalyzers)' != 'true'" ItemName="Analyzer" />
<Output TaskParameter="PlatformManifests" ItemName="PlatformManifestsFromTargetingPacks" />
<Output TaskParameter="PackageConflictPreferredPackages" PropertyName="PackageConflictPreferredPackages" />
<Output TaskParameter="PackageConflictOverrides" ItemName="PackageConflictOverrides" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,5 +226,36 @@ public void FilesFromAspNetCoreSharedFrameworkAreNotIncluded()

outputDirectory.Should().NotHaveFile("Microsoft.Extensions.DependencyInjection.Abstractions.dll");
}

[Fact]
public void AnalyzersAreConflictResolved()
{
var testProject = new TestProject()
{
Name = nameof(AnalyzersAreConflictResolved),
TargetFrameworks = "net5.0"
};

// add the package referenced analyzers
testProject.PackageReferences.Add(new TestPackageReference("Microsoft.CodeAnalysis.NetAnalyzers", "5.0.3"));

// enable inbox analyzers too
var testAsset = _testAssetsManager.CreateTestProject(testProject)
.WithProjectChanges(project =>
{
var ns = project.Root.Name.Namespace;
var itemGroup = new XElement(ns + "PropertyGroup");
project.Root.Add(itemGroup);
itemGroup.Add(new XElement(ns + "EnableNETAnalyzers", "true"));
itemGroup.Add(new XElement(ns + "TreatWarningsAsErrors", "true"));
});

var buildCommand = new BuildCommand(testAsset);

buildCommand
.Execute()
.Should()
.Pass();
}
}
}