Skip to content
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

Add GetCompatibilePackageTargetFrameworks Task to the new packageValidation package #7057

Merged
merged 7 commits into from
Mar 9, 2021
Merged
Show file tree
Hide file tree
Changes from 3 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
40 changes: 28 additions & 12 deletions Arcade.sln
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Arcade.Common", "
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Arcade.Test.Common", "src\Common\Microsoft.Arcade.Test.Common\Microsoft.Arcade.Test.Common.csproj", "{6CA09DC9-E654-4906-A977-1279F6EDC109}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.PackageValidation", "src\Microsoft.DotNet.PackageValidation\Microsoft.DotNet.PackageValidation.csproj", "{B691A17B-B577-431C-AF4D-199BBAC8EC97}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.PackageValidation.Tests", "src\Microsoft.DotNet.PackageValidation.Tests\Microsoft.DotNet.PackageValidation.Tests.csproj", "{8BBF14AC-48F0-4282-910E-48E816021660}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -579,18 +583,6 @@ Global
{6E19C6B6-4ADF-4DD6-86CC-6C1624BCDB71}.Release|x64.Build.0 = Release|Any CPU
{6E19C6B6-4ADF-4DD6-86CC-6C1624BCDB71}.Release|x86.ActiveCfg = Release|Any CPU
{6E19C6B6-4ADF-4DD6-86CC-6C1624BCDB71}.Release|x86.Build.0 = Release|Any CPU
{62B929C4-3D15-4D43-AEFC-2D0BD3CFC20D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{62B929C4-3D15-4D43-AEFC-2D0BD3CFC20D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{62B929C4-3D15-4D43-AEFC-2D0BD3CFC20D}.Debug|x64.ActiveCfg = Debug|Any CPU
{62B929C4-3D15-4D43-AEFC-2D0BD3CFC20D}.Debug|x64.Build.0 = Debug|Any CPU
{62B929C4-3D15-4D43-AEFC-2D0BD3CFC20D}.Debug|x86.ActiveCfg = Debug|Any CPU
{62B929C4-3D15-4D43-AEFC-2D0BD3CFC20D}.Debug|x86.Build.0 = Debug|Any CPU
{62B929C4-3D15-4D43-AEFC-2D0BD3CFC20D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{62B929C4-3D15-4D43-AEFC-2D0BD3CFC20D}.Release|Any CPU.Build.0 = Release|Any CPU
{62B929C4-3D15-4D43-AEFC-2D0BD3CFC20D}.Release|x64.ActiveCfg = Release|Any CPU
{62B929C4-3D15-4D43-AEFC-2D0BD3CFC20D}.Release|x64.Build.0 = Release|Any CPU
{62B929C4-3D15-4D43-AEFC-2D0BD3CFC20D}.Release|x86.ActiveCfg = Release|Any CPU
{62B929C4-3D15-4D43-AEFC-2D0BD3CFC20D}.Release|x86.Build.0 = Release|Any CPU
{3376C769-211F-4537-A156-5F841FF7840B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3376C769-211F-4537-A156-5F841FF7840B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3376C769-211F-4537-A156-5F841FF7840B}.Debug|x64.ActiveCfg = Debug|Any CPU
Expand Down Expand Up @@ -819,6 +811,30 @@ Global
{6CA09DC9-E654-4906-A977-1279F6EDC109}.Release|x64.Build.0 = Release|Any CPU
{6CA09DC9-E654-4906-A977-1279F6EDC109}.Release|x86.ActiveCfg = Release|Any CPU
{6CA09DC9-E654-4906-A977-1279F6EDC109}.Release|x86.Build.0 = Release|Any CPU
{B691A17B-B577-431C-AF4D-199BBAC8EC97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B691A17B-B577-431C-AF4D-199BBAC8EC97}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B691A17B-B577-431C-AF4D-199BBAC8EC97}.Debug|x64.ActiveCfg = Debug|Any CPU
{B691A17B-B577-431C-AF4D-199BBAC8EC97}.Debug|x64.Build.0 = Debug|Any CPU
{B691A17B-B577-431C-AF4D-199BBAC8EC97}.Debug|x86.ActiveCfg = Debug|Any CPU
{B691A17B-B577-431C-AF4D-199BBAC8EC97}.Debug|x86.Build.0 = Debug|Any CPU
{B691A17B-B577-431C-AF4D-199BBAC8EC97}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B691A17B-B577-431C-AF4D-199BBAC8EC97}.Release|Any CPU.Build.0 = Release|Any CPU
{B691A17B-B577-431C-AF4D-199BBAC8EC97}.Release|x64.ActiveCfg = Release|Any CPU
{B691A17B-B577-431C-AF4D-199BBAC8EC97}.Release|x64.Build.0 = Release|Any CPU
{B691A17B-B577-431C-AF4D-199BBAC8EC97}.Release|x86.ActiveCfg = Release|Any CPU
{B691A17B-B577-431C-AF4D-199BBAC8EC97}.Release|x86.Build.0 = Release|Any CPU
{8BBF14AC-48F0-4282-910E-48E816021660}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8BBF14AC-48F0-4282-910E-48E816021660}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8BBF14AC-48F0-4282-910E-48E816021660}.Debug|x64.ActiveCfg = Debug|Any CPU
{8BBF14AC-48F0-4282-910E-48E816021660}.Debug|x64.Build.0 = Debug|Any CPU
{8BBF14AC-48F0-4282-910E-48E816021660}.Debug|x86.ActiveCfg = Debug|Any CPU
{8BBF14AC-48F0-4282-910E-48E816021660}.Debug|x86.Build.0 = Debug|Any CPU
{8BBF14AC-48F0-4282-910E-48E816021660}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8BBF14AC-48F0-4282-910E-48E816021660}.Release|Any CPU.Build.0 = Release|Any CPU
{8BBF14AC-48F0-4282-910E-48E816021660}.Release|x64.ActiveCfg = Release|Any CPU
{8BBF14AC-48F0-4282-910E-48E816021660}.Release|x64.Build.0 = Release|Any CPU
{8BBF14AC-48F0-4282-910E-48E816021660}.Release|x86.ActiveCfg = Release|Any CPU
{8BBF14AC-48F0-4282-910E-48E816021660}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Microsoft.DotNet.Build.Tasks.Packaging
{
public class GetCompatibilePackageTargetFrameworks : BuildTask
{
[Required]
public string[] TargetFrameworks { get; set; }

public string PackagePath { get; set; }

[Output]
public string[] FrameworksToTest { get; set; }

public override bool Execute()
{
bool result = true;

try
{
Package package = NupkgParser.CreatePackageObject(PackagePath);
var packageTargetFrameworks = package.PackageAssets.Where(t => t.AssetType != AssetType.RuntimeAsset).Select(t => t.TargetFramework).Distinct();
CompatibilityTable table = new CompatibilityTable(packageTargetFrameworks);
List<string> frameworksToTest = new List<string>();

foreach (var item in TargetFrameworks)
{
IEnumerable<NuGetFramework> compatible = null;
table.TryGetCompatible(NuGetFramework.Parse(item), out compatible);
if (compatible != null)
{
frameworksToTest.Add(item);
}
}

FrameworksToTest = frameworksToTest.ToArray();
}
catch (Exception e)
{
Log.LogErrorFromException(e, showStackTrace: false);
}

return result && !Log.HasLoggedErrors;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using NuGet.Frameworks;
using System.Collections.Generic;
using Xunit;

namespace Microsoft.DotNet.PackageValidation.Tests
{
public class GetCompatibilePackageTargetFrameworksTests
{
public GetCompatibilePackageTargetFrameworksTests()
{
GetCompatibilePackageTargetFrameworks.Initialize();
}

public static IEnumerable<object[]> PackageTFMData => new List<object[]>
{
// single target framework in package
new object[] { new List<NuGetFramework> { FrameworkConstants.CommonFrameworks.NetStandard20}, new List<NuGetFramework> { FrameworkConstants.CommonFrameworks.NetStandard20, FrameworkConstants.CommonFrameworks.NetCoreApp20, FrameworkConstants.CommonFrameworks.Net463, FrameworkConstants.CommonFrameworks.Net461, FrameworkConstants.CommonFrameworks.Net462} },
new object[] { new List<NuGetFramework> { FrameworkConstants.CommonFrameworks.NetCoreApp20}, new List<NuGetFramework> { FrameworkConstants.CommonFrameworks.NetCoreApp20} },
new object[] { new List<NuGetFramework> { FrameworkConstants.CommonFrameworks.NetCoreApp21}, new List<NuGetFramework> { FrameworkConstants.CommonFrameworks.NetCoreApp21} },
new object[] { new List<NuGetFramework> { FrameworkConstants.CommonFrameworks.Net461}, new List<NuGetFramework> { FrameworkConstants.CommonFrameworks.Net461} },
new object[] { new List<NuGetFramework> { FrameworkConstants.CommonFrameworks.Net45}, new List<NuGetFramework> { FrameworkConstants.CommonFrameworks.Net45} },
new object[] { new List<NuGetFramework> { FrameworkConstants.CommonFrameworks.NetCoreApp30}, new List<NuGetFramework> { FrameworkConstants.CommonFrameworks.NetCoreApp30} },
new object[] { new List<NuGetFramework> { FrameworkConstants.CommonFrameworks.NetCoreApp31}, new List<NuGetFramework> { FrameworkConstants.CommonFrameworks.NetCoreApp31} },
new object[] { new List<NuGetFramework> { FrameworkConstants.CommonFrameworks.NetStandard21}, new List<NuGetFramework> { FrameworkConstants.CommonFrameworks.NetStandard21, FrameworkConstants.CommonFrameworks.NetCoreApp30 } },
new object[] { new List<NuGetFramework> { FrameworkConstants.CommonFrameworks.NetStandard12}, new List<NuGetFramework> { FrameworkConstants.CommonFrameworks.NetStandard12, FrameworkConstants.CommonFrameworks.Net451 } },

// two target frameworks in package
new object[] { new List<NuGetFramework> { FrameworkConstants.CommonFrameworks.NetStandard20, FrameworkConstants.CommonFrameworks.Net461}, new List<NuGetFramework> { FrameworkConstants.CommonFrameworks.NetStandard20, FrameworkConstants.CommonFrameworks.NetCoreApp20, FrameworkConstants.CommonFrameworks.Net463, FrameworkConstants.CommonFrameworks.Net461, FrameworkConstants.CommonFrameworks.Net462} },
new object[] { new List<NuGetFramework> { FrameworkConstants.CommonFrameworks.NetStandard20, FrameworkConstants.CommonFrameworks.NetCoreApp30}, new List<NuGetFramework> { FrameworkConstants.CommonFrameworks.NetStandard20, FrameworkConstants.CommonFrameworks.NetCoreApp30, FrameworkConstants.CommonFrameworks.NetCoreApp20, FrameworkConstants.CommonFrameworks.Net463, FrameworkConstants.CommonFrameworks.Net461, FrameworkConstants.CommonFrameworks.Net462} },
new object[] { new List<NuGetFramework> { FrameworkConstants.CommonFrameworks.NetCoreApp30, FrameworkConstants.CommonFrameworks.Net461}, new List<NuGetFramework> { FrameworkConstants.CommonFrameworks.NetCoreApp30, FrameworkConstants.CommonFrameworks.Net461} },
new object[] { new List<NuGetFramework> { FrameworkConstants.CommonFrameworks.NetCoreApp30, FrameworkConstants.CommonFrameworks.Net50}, new List<NuGetFramework> { FrameworkConstants.CommonFrameworks.NetCoreApp30, FrameworkConstants.CommonFrameworks.Net50} },
};

[Theory]
[MemberData(nameof(PackageTFMData))]
public void GetCompatibleFrameworks(List<NuGetFramework> packageFrameworks, List<NuGetFramework> expectedTestFrameworks)
{
List<NuGetFramework> actualTestFrameworks = GetCompatibilePackageTargetFrameworks.GetTestFrameworks(packageFrameworks);
CollectionsEqual(expectedTestFrameworks, actualTestFrameworks);
}

private static void CollectionsEqual<T>(IEnumerable<T> T1, IEnumerable<T> T2)
{
foreach (var item in T1)
{
Assert.Contains(item, T2);
}
foreach (var item in T2)
{
Assert.Contains(item, T1);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="$(MicrosoftBuildUtilitiesCoreVersion)" />
<PackageReference Include="NuGet.Frameworks" Version="$(NuGetVersion)" />

</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Microsoft.DotNet.PackageValidation\Microsoft.DotNet.PackageValidation.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Microsoft.DotNet.Build.Tasks;
using NuGet.Frameworks;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Microsoft.DotNet.PackageValidation
{
public class GetCompatibilePackageTargetFrameworks : BuildTask
{
private static List<NuGetFramework> allTargetFrameworks = allTargetFrameworks = new();
private static Dictionary<NuGetFramework, HashSet<NuGetFramework>> packageTfmMapping = new();

[Required]
public string[] PackagePaths { get; set; }

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

public override bool Execute()
{
bool result = true;
List<ITaskItem> testProjects = new List<ITaskItem>();

try
{
Initialize();
foreach (var packagePath in PackagePaths)
{
Package package = NupkgParser.CreatePackageObject(packagePath);
List<NuGetFramework> packageTargetFrameworks = package.PackageAssets.Where(t => t.AssetType != AssetType.RuntimeAsset).Select(t => t.TargetFramework).Distinct().ToList();

List<NuGetFramework> frameworksToTest = GetTestFrameworks(packageTargetFrameworks);
testProjects.AddRange(CreateItemFromTestFramework(package.Title, package.Version, frameworksToTest, GetRidsFromPackage(package)));
}

// Removing empty items.
TestProjects = testProjects.Where(tfm => tfm.ItemSpec != "").ToArray();
}
catch (Exception e)
{
Log.LogErrorFromException(e, showStackTrace: false);
}

return result && !Log.HasLoggedErrors;
}

public static List<NuGetFramework> GetTestFrameworks(List<NuGetFramework> packageTargetFrameworks)
{
List<NuGetFramework> frameworksToTest = new List<NuGetFramework>();

// Testing the package installation on all tfms linked with package targetframeworks.
foreach (var item in packageTargetFrameworks)
{
if (packageTfmMapping.ContainsKey(item))
frameworksToTest.AddRange(packageTfmMapping[item].ToList());
}

// Pruning the test matrix by removing the frameworks we dont want to test.
frameworksToTest = frameworksToTest.Where(tfm => allTargetFrameworks.Contains(tfm)).ToList();

// Adding the frameworks in the packages to the test matrix;
frameworksToTest.AddRange(packageTargetFrameworks);
frameworksToTest = frameworksToTest.Distinct().ToList();
return frameworksToTest;
}

public static void Initialize()
{
// Defining the set of known frameworks that we care to test
allTargetFrameworks.Add(FrameworkConstants.CommonFrameworks.NetCoreApp20);
allTargetFrameworks.Add(FrameworkConstants.CommonFrameworks.NetCoreApp21);
allTargetFrameworks.Add(FrameworkConstants.CommonFrameworks.NetCoreApp30);
allTargetFrameworks.Add(FrameworkConstants.CommonFrameworks.NetCoreApp31);
allTargetFrameworks.Add(FrameworkConstants.CommonFrameworks.Net50);
allTargetFrameworks.Add(FrameworkConstants.CommonFrameworks.Net45);
allTargetFrameworks.Add(FrameworkConstants.CommonFrameworks.Net451);
allTargetFrameworks.Add(FrameworkConstants.CommonFrameworks.Net452);
allTargetFrameworks.Add(FrameworkConstants.CommonFrameworks.Net46);
allTargetFrameworks.Add(FrameworkConstants.CommonFrameworks.Net461);
allTargetFrameworks.Add(FrameworkConstants.CommonFrameworks.Net462);
allTargetFrameworks.Add(FrameworkConstants.CommonFrameworks.Net463);
allTargetFrameworks.Add(FrameworkConstants.CommonFrameworks.NetStandard10);
allTargetFrameworks.Add(FrameworkConstants.CommonFrameworks.NetStandard11);
allTargetFrameworks.Add(FrameworkConstants.CommonFrameworks.NetStandard12);
allTargetFrameworks.Add(FrameworkConstants.CommonFrameworks.NetStandard13);
allTargetFrameworks.Add(FrameworkConstants.CommonFrameworks.NetStandard14);
allTargetFrameworks.Add(FrameworkConstants.CommonFrameworks.NetStandard15);
allTargetFrameworks.Add(FrameworkConstants.CommonFrameworks.NetStandard16);
allTargetFrameworks.Add(FrameworkConstants.CommonFrameworks.NetStandard17);
allTargetFrameworks.Add(FrameworkConstants.CommonFrameworks.NetStandard20);
allTargetFrameworks.Add(FrameworkConstants.CommonFrameworks.NetStandard21);
allTargetFrameworks.Add(FrameworkConstants.CommonFrameworks.UAP10);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Static known frameworks shouldn't be hardcoded in a compiled asset and instead be passed into the task and supplied via the targets file that invokes the task.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why would it be better to pass the static list, thats not gonna be changed, be provided as an input ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In case we would ever want to mutate these items, we could do so via msbuild without the need to rebuild the assembly and flow it into dotnet/runtime or any other consumer repo.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ie wouldn't we want to remove the UAP entry there when we stop supporting UAP entirely?


// creating a map framework in package => frameworks to test based on default compatibilty mapping.
foreach (var item in DefaultFrameworkMappings.Instance.CompatibilityMappings)
{
NuGetFramework forwardTfm = item.SupportedFrameworkRange.Max;
NuGetFramework reverseTfm = item.TargetFrameworkRange.Min;
if (packageTfmMapping.ContainsKey(forwardTfm))
{
packageTfmMapping[forwardTfm].Add(reverseTfm);
}
else
{
packageTfmMapping.Add(forwardTfm, new HashSet<NuGetFramework> { reverseTfm });
}
}
}

public List<ITaskItem> CreateItemFromTestFramework(string title, string version, List<NuGetFramework> testFrameworks, string rids)
{
List<ITaskItem> testprojects = new List<ITaskItem>();
foreach (var framework in testFrameworks)
{
var supportedPackage = new TaskItem(title);
supportedPackage.SetMetadata("Version", version);
supportedPackage.SetMetadata("TargetFramework", framework.ToString());
supportedPackage.SetMetadata("TargetFrameworkShort", framework.GetShortFolderName());

if (!String.IsNullOrEmpty(rids))
{
supportedPackage.SetMetadata("RuntimeIdentifiers", rids);
}
testprojects.Add(supportedPackage);
}

return testprojects;
}

public string GetRidsFromPackage(Package package)
{
List<string> rids = new List<string>();
foreach (var item in package.PackageAssets)
{
if (item.AssetType == AssetType.RuntimeAsset)
{
if (!rids.Contains(item.Rid + "-x64"))
rids.Add(item.Rid + "-x64");
}
}
return string.Join(";", rids);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<ExcludeFromSourceBuild>false</ExcludeFromSourceBuild>
<PackageType>MSBuildSdk</PackageType>
<IncludeBuildOutput>false</IncludeBuildOutput>
<IsPackable>true</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Build" Version="$(MicrosoftBuildVersion)" Publish="false" />
<PackageReference Include="Microsoft.Build.Tasks.Core" Version="$(MicrosoftBuildTasksCoreVersion)" Publish="false" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="$(MsbuildTaskMicrosoftCodeAnalysisCSharpVersion)" ExcludeAssets="analyzers" />
<PackageReference Include="NuGet.Frameworks" Version="$(NuGetVersion)" />
<PackageReference Include="NuGet.Packaging" Version="$(NuGetVersion)" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Common\Internal\BuildTask.cs" />
</ItemGroup>

<Import Project="$(RepoRoot)eng\BuildTask.targets" />

</Project>
Loading