From 22609f8e53f1952e138367fd10029202fac992fb Mon Sep 17 00:00:00 2001 From: Taylor Southwick Date: Wed, 25 Aug 2021 13:42:25 -0700 Subject: [PATCH] Move NuGet API usage to its own extension NuGet API may not be stable and loading an arbitrary version with MSBuild may cause problems. This change isolates those calls so we can pin it to our own version. This change does a couple of things: - Moves all NuGet usages to extension. This will now load in its own AssemblyLoadContext so we won't have any conflicts - Updates to the latest NuGet client API libraries - Adds a 'required' extension concept so that they will load in all cases even when loading is disabled. Fixes #817 --- Microsoft.DotNet.UpgradeAssistant.sln | 36 +++ docs/images/dependency-validation.png | Bin 42462 -> 42462 bytes .../DependencyValidation.modelproj | 4 + .../UpgradeAssistant.layerdiagram | 13 ++ .../UpgradeAssistant.layerdiagram.layout | 11 +- ...crosoft.DotNet.UpgradeAssistant.Cli.csproj | 3 + .../UpgradeAssistantHostExtensions.cs | 8 - .../appsettings.json | 4 + .../IProjectFile.cs | 2 + .../ITargetFrameworkCollection.cs | 12 + .../ExtensionAssemblyLoadContext.cs | 4 +- .../ExtensionOptions.cs | 2 + .../ExtensionProvider.cs | 10 +- .../ExtensionProviderExtensions.cs | 18 +- .../Factories.cs | 25 ++ .../MSBuildProject.File.cs | 6 +- .../MSBuildProject.NuGetPackages.cs | 191 +--------------- .../MSBuildProject.cs | 7 +- .../MSBuildWorkspaceUpgradeContext.cs | 5 +- .../UpgraderMsBuildExtensions.cs | 21 +- .../ExtensionManifest.json | 7 + .../INuGetPackageSourceFactory.cs | 2 +- ...t.UpgradeAssistant.Extensions.NuGet.csproj | 48 ++++ .../NuGetCredentialsStartup.cs | 2 +- .../NuGetDownloaderOptions.cs | 2 +- .../NuGetExtensionBuilder.cs | 34 +++ .../NuGetExtensions.cs | 2 +- .../NuGetLogger.cs | 2 +- .../NuGetPackageSourceFactory.cs | 2 +- .../NuGetTargetFrameworkMonikerComparer.cs | 2 +- .../NuGetVersionComparer.cs | 2 +- .../PackageConfig.cs | 2 +- .../PackageLoader.cs | 5 +- .../ProjectNuGetReferences.cs | 213 ++++++++++++++++++ .../TargetFrameworkMonikerCollection.cs | 4 +- ...adeAssistant.Extensions.NuGet.Tests.csproj | 9 + .../NuGetExtensionsTests.cs | 3 +- ...uGetTargetFrameworkMonikerComparerTests.cs | 3 +- .../NuGetVersionComparerTests.cs | 3 +- .../PackageLoaderTests.cs | 3 +- .../TargetFrameworkMonikerCollectionTests.cs | 3 +- 41 files changed, 478 insertions(+), 257 deletions(-) create mode 100644 src/common/Microsoft.DotNet.UpgradeAssistant.Abstractions/ITargetFrameworkCollection.cs create mode 100644 src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/Factories.cs create mode 100644 src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/ExtensionManifest.json rename src/{components/Microsoft.DotNet.UpgradeAssistant.MSBuild => extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet}/INuGetPackageSourceFactory.cs (84%) create mode 100644 src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.csproj rename src/{components/Microsoft.DotNet.UpgradeAssistant.MSBuild => extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet}/NuGetCredentialsStartup.cs (94%) rename src/{components/Microsoft.DotNet.UpgradeAssistant.MSBuild => extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet}/NuGetDownloaderOptions.cs (83%) create mode 100644 src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/NuGetExtensionBuilder.cs rename src/{components/Microsoft.DotNet.UpgradeAssistant.MSBuild => extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet}/NuGetExtensions.cs (96%) rename src/{components/Microsoft.DotNet.UpgradeAssistant.MSBuild => extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet}/NuGetLogger.cs (97%) rename src/{components/Microsoft.DotNet.UpgradeAssistant.MSBuild => extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet}/NuGetPackageSourceFactory.cs (95%) rename src/{components/Microsoft.DotNet.UpgradeAssistant.MSBuild => extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet}/NuGetTargetFrameworkMonikerComparer.cs (98%) rename src/{components/Microsoft.DotNet.UpgradeAssistant.MSBuild => extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet}/NuGetVersionComparer.cs (95%) rename src/{components/Microsoft.DotNet.UpgradeAssistant.MSBuild => extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet}/PackageConfig.cs (94%) rename src/{components/Microsoft.DotNet.UpgradeAssistant.MSBuild => extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet}/PackageLoader.cs (99%) create mode 100644 src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/ProjectNuGetReferences.cs rename src/{components/Microsoft.DotNet.UpgradeAssistant.MSBuild => extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet}/TargetFrameworkMonikerCollection.cs (96%) create mode 100644 tests/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.Tests/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.Tests.csproj rename tests/{components/Microsoft.DotNet.UpgradeAssistant.MSBuild.Tests => extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.Tests}/NuGetExtensionsTests.cs (91%) rename tests/{components/Microsoft.DotNet.UpgradeAssistant.MSBuild.Tests => extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.Tests}/NuGetTargetFrameworkMonikerComparerTests.cs (98%) rename tests/{components/Microsoft.DotNet.UpgradeAssistant.MSBuild.Tests => extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.Tests}/NuGetVersionComparerTests.cs (92%) rename tests/{components/Microsoft.DotNet.UpgradeAssistant.MSBuild.Tests => extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.Tests}/PackageLoaderTests.cs (99%) rename tests/{components/Microsoft.DotNet.UpgradeAssistant.MSBuild.Tests => extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.Tests}/TargetFrameworkMonikerCollectionTests.cs (98%) diff --git a/Microsoft.DotNet.UpgradeAssistant.sln b/Microsoft.DotNet.UpgradeAssistant.sln index 53dd67794..5cd467835 100644 --- a/Microsoft.DotNet.UpgradeAssistant.sln +++ b/Microsoft.DotNet.UpgradeAssistant.sln @@ -172,6 +172,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.UpgradeAss EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.UpgradeAssistant.Steps.Source.Tests", "tests\extensions\default\Microsoft.DotNet.UpgradeAssistant.Steps.Source.Tests\Microsoft.DotNet.UpgradeAssistant.Steps.Source.Tests.csproj", "{134CE4A1-E9A4-49C0-A998-7C531E6207CE}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "nuget", "nuget", "{021F1610-F2D0-4F00-81C7-AD7397DA296C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet", "src\extensions\nuget\Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet\Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.csproj", "{E18A3CB0-BFDB-4672-AD53-7EAAF26C52AB}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "nuget", "nuget", "{824AFFC6-40A2-4AD6-8010-B939841CA16E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.Tests", "tests\extensions\nuget\Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.Tests\Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.Tests.csproj", "{58633641-85F3-4F8E-AC18-0EFACC36D142}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -758,6 +766,30 @@ Global {134CE4A1-E9A4-49C0-A998-7C531E6207CE}.Release|x64.Build.0 = Release|Any CPU {134CE4A1-E9A4-49C0-A998-7C531E6207CE}.Release|x86.ActiveCfg = Release|Any CPU {134CE4A1-E9A4-49C0-A998-7C531E6207CE}.Release|x86.Build.0 = Release|Any CPU + {E18A3CB0-BFDB-4672-AD53-7EAAF26C52AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E18A3CB0-BFDB-4672-AD53-7EAAF26C52AB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E18A3CB0-BFDB-4672-AD53-7EAAF26C52AB}.Debug|x64.ActiveCfg = Debug|Any CPU + {E18A3CB0-BFDB-4672-AD53-7EAAF26C52AB}.Debug|x64.Build.0 = Debug|Any CPU + {E18A3CB0-BFDB-4672-AD53-7EAAF26C52AB}.Debug|x86.ActiveCfg = Debug|Any CPU + {E18A3CB0-BFDB-4672-AD53-7EAAF26C52AB}.Debug|x86.Build.0 = Debug|Any CPU + {E18A3CB0-BFDB-4672-AD53-7EAAF26C52AB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E18A3CB0-BFDB-4672-AD53-7EAAF26C52AB}.Release|Any CPU.Build.0 = Release|Any CPU + {E18A3CB0-BFDB-4672-AD53-7EAAF26C52AB}.Release|x64.ActiveCfg = Release|Any CPU + {E18A3CB0-BFDB-4672-AD53-7EAAF26C52AB}.Release|x64.Build.0 = Release|Any CPU + {E18A3CB0-BFDB-4672-AD53-7EAAF26C52AB}.Release|x86.ActiveCfg = Release|Any CPU + {E18A3CB0-BFDB-4672-AD53-7EAAF26C52AB}.Release|x86.Build.0 = Release|Any CPU + {58633641-85F3-4F8E-AC18-0EFACC36D142}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {58633641-85F3-4F8E-AC18-0EFACC36D142}.Debug|Any CPU.Build.0 = Debug|Any CPU + {58633641-85F3-4F8E-AC18-0EFACC36D142}.Debug|x64.ActiveCfg = Debug|Any CPU + {58633641-85F3-4F8E-AC18-0EFACC36D142}.Debug|x64.Build.0 = Debug|Any CPU + {58633641-85F3-4F8E-AC18-0EFACC36D142}.Debug|x86.ActiveCfg = Debug|Any CPU + {58633641-85F3-4F8E-AC18-0EFACC36D142}.Debug|x86.Build.0 = Debug|Any CPU + {58633641-85F3-4F8E-AC18-0EFACC36D142}.Release|Any CPU.ActiveCfg = Release|Any CPU + {58633641-85F3-4F8E-AC18-0EFACC36D142}.Release|Any CPU.Build.0 = Release|Any CPU + {58633641-85F3-4F8E-AC18-0EFACC36D142}.Release|x64.ActiveCfg = Release|Any CPU + {58633641-85F3-4F8E-AC18-0EFACC36D142}.Release|x64.Build.0 = Release|Any CPU + {58633641-85F3-4F8E-AC18-0EFACC36D142}.Release|x86.ActiveCfg = Release|Any CPU + {58633641-85F3-4F8E-AC18-0EFACC36D142}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -831,6 +863,10 @@ Global {7304F25F-0F4D-450F-A529-3C7AFE9D98CA} = {82BC0AAB-94CA-4D5B-BF95-F7329D1150CB} {CEDAAB8D-613C-4A3F-8114-7D6B480F1EE3} = {7304F25F-0F4D-450F-A529-3C7AFE9D98CA} {134CE4A1-E9A4-49C0-A998-7C531E6207CE} = {4E12F429-CEC0-4080-B0FE-39F9B8B9AE4C} + {021F1610-F2D0-4F00-81C7-AD7397DA296C} = {AA22EE67-3BBE-49A2-8868-531FE68FE162} + {E18A3CB0-BFDB-4672-AD53-7EAAF26C52AB} = {021F1610-F2D0-4F00-81C7-AD7397DA296C} + {824AFFC6-40A2-4AD6-8010-B939841CA16E} = {82BC0AAB-94CA-4D5B-BF95-F7329D1150CB} + {58633641-85F3-4F8E-AC18-0EFACC36D142} = {824AFFC6-40A2-4AD6-8010-B939841CA16E} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D02F665B-C14D-43C2-955C-9338A23836E0} diff --git a/docs/images/dependency-validation.png b/docs/images/dependency-validation.png index c5b39e3cb5568adbe6a4850de856ab2458c9b807..7880b709c7ea1bd7457cad01f82307aa2cd6dbc6 100644 GIT binary patch literal 42462 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%0A(W=0b5=S!+-*}j)N+b1h` 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.DotNet.UpgradeAssistant.Extensions.Maui {a122dd5b-4b80-4558-8db2-8e9ebff12a1a} + + Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet + {e18a3cb0-bfdb-4672-ad53-7eaaf26c52ab} + Microsoft.DotNet.UpgradeAssistant.Extensions.VisualBasic {608e7ce5-5674-49eb-8728-719a468fc62e} diff --git a/eng/DependencyValidation/UpgradeAssistant.layerdiagram b/eng/DependencyValidation/UpgradeAssistant.layerdiagram index 996839f84..428d4845a 100644 --- a/eng/DependencyValidation/UpgradeAssistant.layerdiagram +++ b/eng/DependencyValidation/UpgradeAssistant.layerdiagram @@ -205,6 +205,19 @@ + + + + + + + + + + + + + diff --git a/eng/DependencyValidation/UpgradeAssistant.layerdiagram.layout b/eng/DependencyValidation/UpgradeAssistant.layerdiagram.layout index 1e93f95a5..68340c930 100644 --- a/eng/DependencyValidation/UpgradeAssistant.layerdiagram.layout +++ b/eng/DependencyValidation/UpgradeAssistant.layerdiagram.layout @@ -50,9 +50,12 @@ + + + - + @@ -79,7 +82,7 @@ - + @@ -103,7 +106,7 @@ - + @@ -124,7 +127,7 @@ - + diff --git a/src/cli/Microsoft.DotNet.UpgradeAssistant.Cli/Microsoft.DotNet.UpgradeAssistant.Cli.csproj b/src/cli/Microsoft.DotNet.UpgradeAssistant.Cli/Microsoft.DotNet.UpgradeAssistant.Cli.csproj index 3655bbf31..d75dffe64 100644 --- a/src/cli/Microsoft.DotNet.UpgradeAssistant.Cli/Microsoft.DotNet.UpgradeAssistant.Cli.csproj +++ b/src/cli/Microsoft.DotNet.UpgradeAssistant.Cli/Microsoft.DotNet.UpgradeAssistant.Cli.csproj @@ -108,6 +108,9 @@ windows + + nuget + diff --git a/src/cli/Microsoft.DotNet.UpgradeAssistant.Cli/UpgradeAssistantHostExtensions.cs b/src/cli/Microsoft.DotNet.UpgradeAssistant.Cli/UpgradeAssistantHostExtensions.cs index f38104832..f73f6832d 100644 --- a/src/cli/Microsoft.DotNet.UpgradeAssistant.Cli/UpgradeAssistantHostExtensions.cs +++ b/src/cli/Microsoft.DotNet.UpgradeAssistant.Cli/UpgradeAssistantHostExtensions.cs @@ -101,14 +101,6 @@ public static IHostBuilder UseUpgradeAssistant(this IHostBuilder host, IUp } }); - services.AddNuGet(optionss => - { - if (upgradeOptions.Project?.FullName is string fullname) - { - optionss.PackageSourcePath = Path.GetDirectoryName(fullname); - } - }); - services.AddUserInput(); services.AddAnalysis(); diff --git a/src/cli/Microsoft.DotNet.UpgradeAssistant.Cli/appsettings.json b/src/cli/Microsoft.DotNet.UpgradeAssistant.Cli/appsettings.json index 7679c31ae..5578f6ae0 100644 --- a/src/cli/Microsoft.DotNet.UpgradeAssistant.Cli/appsettings.json +++ b/src/cli/Microsoft.DotNet.UpgradeAssistant.Cli/appsettings.json @@ -15,6 +15,10 @@ "Extensions": { "Source": "https://upgradeassistant.blob.core.windows.net/extensions/index.json", + "required": [ + "nuget" + ], + "Default": [ "default", "vb", diff --git a/src/common/Microsoft.DotNet.UpgradeAssistant.Abstractions/IProjectFile.cs b/src/common/Microsoft.DotNet.UpgradeAssistant.Abstractions/IProjectFile.cs index 7d0f93371..9747527b2 100644 --- a/src/common/Microsoft.DotNet.UpgradeAssistant.Abstractions/IProjectFile.cs +++ b/src/common/Microsoft.DotNet.UpgradeAssistant.Abstractions/IProjectFile.cs @@ -15,6 +15,8 @@ public interface IProjectFile string FilePath { get; } + IEnumerable PackageReferences { get; } + void AddFrameworkReferences(IEnumerable frameworkReferences); void RemoveFrameworkReferences(IEnumerable frameworkReferences); diff --git a/src/common/Microsoft.DotNet.UpgradeAssistant.Abstractions/ITargetFrameworkCollection.cs b/src/common/Microsoft.DotNet.UpgradeAssistant.Abstractions/ITargetFrameworkCollection.cs new file mode 100644 index 000000000..f7da49a0a --- /dev/null +++ b/src/common/Microsoft.DotNet.UpgradeAssistant.Abstractions/ITargetFrameworkCollection.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +namespace Microsoft.DotNet.UpgradeAssistant +{ + public interface ITargetFrameworkCollection : IReadOnlyCollection + { + void SetTargetFramework(TargetFrameworkMoniker tfm); + } +} diff --git a/src/components/Microsoft.DotNet.UpgradeAssistant.Extensions/ExtensionAssemblyLoadContext.cs b/src/components/Microsoft.DotNet.UpgradeAssistant.Extensions/ExtensionAssemblyLoadContext.cs index afe39ef6c..f4d92e27d 100644 --- a/src/components/Microsoft.DotNet.UpgradeAssistant.Extensions/ExtensionAssemblyLoadContext.cs +++ b/src/components/Microsoft.DotNet.UpgradeAssistant.Extensions/ExtensionAssemblyLoadContext.cs @@ -64,7 +64,9 @@ private void Load(ExtensionInstance extension, string[] assemblies) protected override Assembly? Load(AssemblyName assemblyName) { // If available in the default, we want to ensure that is used. - var inDefault = Default.Assemblies.FirstOrDefault(a => string.Equals(a.GetName().Name, assemblyName.Name, StringComparison.Ordinal)); + var inDefault = Default.Assemblies + .Where(a => !a.GetName().Name!.Contains("NuGet", StringComparison.OrdinalIgnoreCase)) + .FirstOrDefault(a => string.Equals(a.GetName().Name, assemblyName.Name, StringComparison.Ordinal)); if (inDefault is Assembly existing) { diff --git a/src/components/Microsoft.DotNet.UpgradeAssistant.Extensions/ExtensionOptions.cs b/src/components/Microsoft.DotNet.UpgradeAssistant.Extensions/ExtensionOptions.cs index d57362085..84a8e5458 100644 --- a/src/components/Microsoft.DotNet.UpgradeAssistant.Extensions/ExtensionOptions.cs +++ b/src/components/Microsoft.DotNet.UpgradeAssistant.Extensions/ExtensionOptions.cs @@ -14,6 +14,8 @@ public class ExtensionOptions public ICollection DefaultExtensions { get; } = new List(); + public ICollection RequiredExtensions { get; } = new List(); + public ICollection ExtensionPaths { get; } = new List(); public IEnumerable AdditionalOptions { get; set; } = Enumerable.Empty(); diff --git a/src/components/Microsoft.DotNet.UpgradeAssistant.Extensions/ExtensionProvider.cs b/src/components/Microsoft.DotNet.UpgradeAssistant.Extensions/ExtensionProvider.cs index c2555dcfb..c16f7391f 100644 --- a/src/components/Microsoft.DotNet.UpgradeAssistant.Extensions/ExtensionProvider.cs +++ b/src/components/Microsoft.DotNet.UpgradeAssistant.Extensions/ExtensionProvider.cs @@ -46,11 +46,19 @@ public ExtensionProvider( _extensions = new Lazy>(() => { + var list = new List(); + var opts = options.Value; + foreach (var path in opts.RequiredExtensions) + { + LoadPath(path, isDefault: true); + } + + // Required extensions must load, otherwise they may be turned off if (!opts.LoadExtensions) { - return Enumerable.Empty(); + return list; } var list = new List(); diff --git a/src/components/Microsoft.DotNet.UpgradeAssistant.Extensions/ExtensionProviderExtensions.cs b/src/components/Microsoft.DotNet.UpgradeAssistant.Extensions/ExtensionProviderExtensions.cs index f8db82160..a7924c4a2 100644 --- a/src/components/Microsoft.DotNet.UpgradeAssistant.Extensions/ExtensionProviderExtensions.cs +++ b/src/components/Microsoft.DotNet.UpgradeAssistant.Extensions/ExtensionProviderExtensions.cs @@ -87,16 +87,22 @@ public static OptionsBuilder AddDefaultExtensions(this Options return builder.Configure(options => { const string ExtensionDirectory = "extensions"; - var settings = configuration.GetSection("Extensions").Get(); - var defaultExtensions = settings.Default - .Select(n => Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, ExtensionDirectory, n))); options.DefaultSource = settings.Source; - foreach (var path in defaultExtensions) + AddExtensions(options.DefaultExtensions, settings.Default); + AddExtensions(options.RequiredExtensions, settings.Required); + + static void AddExtensions(ICollection collection, string[] names) { - options.DefaultExtensions.Add(path); + var extensionFullPaths = names + .Select(n => Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, ExtensionDirectory, n))); + + foreach (var path in extensionFullPaths) + { + collection.Add(path); + } } }); } @@ -106,6 +112,8 @@ private class ExtensionSettings public string Source { get; set; } = string.Empty; public string[] Default { get; set; } = Array.Empty(); + + public string[] Required { get; set; } = Array.Empty(); } public static IServiceCollection AddExtensionOption(this IServiceCollection services, TOption option) diff --git a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/Factories.cs b/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/Factories.cs new file mode 100644 index 000000000..e599770b6 --- /dev/null +++ b/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/Factories.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Microsoft.DotNet.UpgradeAssistant.MSBuild +{ + internal class Factories + { + private readonly Func _nugetReferenceFactory; + private readonly Func _tfmCollectionFactory; + + public Factories( + Func nugetReferenceFactory, + Func tfmCollectionFactory) + { + _nugetReferenceFactory = nugetReferenceFactory; + _tfmCollectionFactory = tfmCollectionFactory; + } + + public INuGetReferences CreateNuGetReferences(IUpgradeContext context, IProject project) => _nugetReferenceFactory(context, project); + + public ITargetFrameworkCollection CreateTfmCollection(IProjectFile project) => _tfmCollectionFactory(project); + } +} diff --git a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/MSBuildProject.File.cs b/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/MSBuildProject.File.cs index 12217a737..de10a64a5 100644 --- a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/MSBuildProject.File.cs +++ b/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/MSBuildProject.File.cs @@ -44,7 +44,7 @@ public ProjectRootElement ProjectRoot public ICollection Sdk => new SdkCollection(ProjectRoot); - public void SetTFM(TargetFrameworkMoniker tfm) => new TargetFrameworkMonikerCollection(this, _comparer).SetTargetFramework(tfm); + public void SetTFM(TargetFrameworkMoniker tfm) => _factories.CreateTfmCollection(this).SetTargetFramework(tfm); public void AddPackages(IEnumerable references) { @@ -61,7 +61,7 @@ public void AddPackages(IEnumerable references) public void RemovePackages(IEnumerable references) { - foreach (var reference in PackageReferences) + foreach (var reference in NuGetReferences.PackageReferences) { if (references.Contains(reference)) { @@ -184,7 +184,7 @@ public void RemoveProperty(string propertyName) { Project.RemoveProperty(property); } - } + } private static string GetPathRelativeToProject(string path, string projectDir) => Path.IsPathFullyQualified(path) diff --git a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/MSBuildProject.NuGetPackages.cs b/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/MSBuildProject.NuGetPackages.cs index d457a4b33..848f942c0 100644 --- a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/MSBuildProject.NuGetPackages.cs +++ b/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/MSBuildProject.NuGetPackages.cs @@ -1,200 +1,15 @@ // 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.IO; using System.Linq; -using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis; -using Microsoft.Extensions.Logging; -using NuGet.Frameworks; -using NuGet.Packaging.Core; -using NuGet.ProjectModel; namespace Microsoft.DotNet.UpgradeAssistant.MSBuild { - internal partial class MSBuildProject : INuGetReferences + internal partial class MSBuildProject { - public INuGetReferences NuGetReferences => this; + public INuGetReferences NuGetReferences => _factories.CreateNuGetReferences(Context, this); - public NugetPackageFormat PackageReferenceFormat - { - get - { - if (GetPackagesConfigPath() is not null) - { - return NugetPackageFormat.PackageConfig; - } - else if (ProjectRoot.GetAllPackageReferences().ToList() is IEnumerable list && list.Any()) - { - return NugetPackageFormat.PackageReference; - } - else - { - return NugetPackageFormat.None; - } - } - } - - private string? GetPackagesConfigPath() => FindFiles("packages.config", ProjectItemType.Content).FirstOrDefault(); - - public IEnumerable PackageReferences - { - get - { - var packagesConfig = GetPackagesConfigPath(); - - if (packagesConfig is null) - { - var packages = ProjectRoot.GetAllPackageReferences(); - - return packages.Select(p => p.AsNuGetReference()); - } - else - { - return PackageConfig.GetPackages(packagesConfig); - } - } - } - - public IAsyncEnumerable GetTransitivePackageReferencesAsync(TargetFrameworkMoniker tfm, CancellationToken token) - => PackageReferenceFormat switch - { - NugetPackageFormat.PackageConfig => PackageReferences.ToAsyncEnumerable(), - NugetPackageFormat.PackageReference => GetAllPackageReferenceDependenciesAsync(tfm, token).Select(l => new NuGetReference(l.Name, l.Version.ToNormalizedString())), - _ => AsyncEnumerable.Empty() - }; - - public async ValueTask IsTransitivelyAvailableAsync(string packageName, CancellationToken token) - => PackageReferences.Any(p => p.Name.Equals(packageName, StringComparison.OrdinalIgnoreCase)) - || (PackageReferenceFormat == NugetPackageFormat.PackageReference && await TargetFrameworks.ToAsyncEnumerable().AnyAwaitAsync(tfm => ContainsPackageDependencyAsync(tfm, d => string.Equals(packageName, d.Id, StringComparison.OrdinalIgnoreCase), token), cancellationToken: token).ConfigureAwait(false)); - - public async ValueTask IsTransitiveDependencyAsync(NuGetReference nugetReference, CancellationToken token) - => PackageReferenceFormat == NugetPackageFormat.PackageReference && await TargetFrameworks.ToAsyncEnumerable().AnyAwaitAsync(tfm => ContainsPackageDependencyAsync(tfm, d => ReferenceSatisfiesDependency(d, nugetReference, true), token), token).ConfigureAwait(false); - - private static bool ReferenceSatisfiesDependency(PackageDependency dependency, NuGetReference packageReference, bool minVersionMatchOnly) - { - // If the dependency's name doesn't match the reference's name, return false - if (!dependency.Id.Equals(packageReference.Name, StringComparison.OrdinalIgnoreCase)) - { - return false; - } - - if (!packageReference.TryGetNuGetVersion(out var packageVersion)) - { - throw new InvalidOperationException("Package references from a lock file should always have a specific version"); - } - - // Return false if the reference's version falls outside of the dependency range - var versionRange = dependency.VersionRange; - if (versionRange.HasLowerBound && packageVersion < versionRange.MinVersion) - { - return false; - } - - if (versionRange.HasUpperBound && packageVersion > versionRange.MaxVersion) - { - return false; - } - - // In some cases (looking for transitive dependencies), it's interesting to only match packages that are the minimum version - if (minVersionMatchOnly && versionRange.HasLowerBound && packageVersion != versionRange.MinVersion) - { - return false; - } - - // Otherwise, return true - return true; - } - - private ValueTask ContainsPackageDependencyAsync(TargetFrameworkMoniker tfm, Func filter, CancellationToken token) - => GetAllPackageReferenceDependenciesAsync(tfm, token).AnyAsync(l => l.Dependencies.Any(d => filter(d)), token); - - private async IAsyncEnumerable GetAllPackageReferenceDependenciesAsync(TargetFrameworkMoniker tfm, [EnumeratorCancellation] CancellationToken token) - { - if (!IsRestored) - { - throw new InvalidOperationException("Project should have already been restored. Please file an issue at https://github.com/dotnet/upgrade-assistant"); - } - - if (PackageReferenceFormat != NugetPackageFormat.PackageReference) - { - throw new InvalidOperationException("PackageReference restore for transitive dependencies should only happen for PackageReference package reference format"); - } - - var parsedTfm = NuGetFramework.Parse(tfm.Name); - var target = GetLockFileTarget(parsedTfm); - - if (target is null) - { - // Break if there are no packages in the project. Otherwise, we end up performing restores too often. - if (!PackageReferences.Any()) - { - _logger.LogDebug("Skipping restore as no package references exist in project file {Path}", FileInfo.FullName); - yield break; - } - - _logger.LogDebug("Attempting a restore to retrieve missing lock file data {Path}", FileInfo.FullName); - - await _restorer.RestorePackagesAsync(Context, this, token).ConfigureAwait(false); - - // If the LockFilePath is defined but does not exist, there are no libraries - if (!File.Exists(LockFilePath)) - { - yield break; - } - - target = GetLockFileTarget(parsedTfm); - } - - if (target is null) - { - _logger.LogError("NuGet target in project.assets.json is still unavailable after restore. Please verify that the project has been restored."); - throw new UpgradeException("Restore has not restored the expected TFMs. Please review any warnings from dotnet-restore."); - } - - foreach (var library in target.Libraries) - { - yield return library; - } - - LockFileTarget? GetLockFileTarget(NuGetFramework parsedTfm) - { - var lockFile = LockFileUtilities.GetLockFile(LockFilePath, NuGet.Common.NullLogger.Instance); - - if (lockFile?.Targets is null) - { - return null; - } - - return lockFile.Targets - .FirstOrDefault(t => t.TargetFramework.DotNetFrameworkName.Equals(parsedTfm.DotNetFrameworkName, StringComparison.Ordinal)); - } - } - - private bool IsRestored => LockFilePath is not null; - - private string? LockFilePath - { - get - { - var lockFilePath = Path.Combine(GetPropertyValue("MSBuildProjectExtensionsPath"), "project.assets.json"); - - if (string.IsNullOrEmpty(lockFilePath)) - { - return null; - } - - if (!Path.IsPathFullyQualified(lockFilePath)) - { - lockFilePath = Path.Combine(FileInfo.DirectoryName ?? string.Empty, lockFilePath); - } - - return lockFilePath; - } - } + public IEnumerable PackageReferences => ProjectRoot.GetAllPackageReferences().Select(p => p.AsNuGetReference()); } } diff --git a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/MSBuildProject.cs b/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/MSBuildProject.cs index 9639a5bff..1e5b2220b 100644 --- a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/MSBuildProject.cs +++ b/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/MSBuildProject.cs @@ -20,7 +20,7 @@ internal partial class MSBuildProject : IProject private readonly ILogger _logger; private readonly IEnumerable _componentIdentifiers; private readonly IPackageRestorer _restorer; - private readonly ITargetFrameworkMonikerComparer _comparer; + private readonly Factories _factories; public MSBuildWorkspaceUpgradeContext Context { get; } @@ -29,6 +29,7 @@ internal partial class MSBuildProject : IProject public MSBuildProject( MSBuildWorkspaceUpgradeContext context, IEnumerable componentIdentifiers, + Factories factories, IPackageRestorer restorer, ITargetFrameworkMonikerComparer comparer, FileInfo file, @@ -37,9 +38,9 @@ public MSBuildProject( FileInfo = file ?? throw new ArgumentNullException(nameof(file)); Context = context ?? throw new ArgumentNullException(nameof(context)); + _factories = factories ?? throw new ArgumentNullException(nameof(factories)); _componentIdentifiers = componentIdentifiers ?? throw new ArgumentNullException(nameof(componentIdentifiers)); _restorer = restorer ?? throw new ArgumentNullException(nameof(restorer)); - _comparer = comparer ?? throw new ArgumentNullException(nameof(comparer)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } @@ -141,7 +142,7 @@ public IEnumerable FindFiles(ProjectItemMatcher matcher, ProjectItemType public IEnumerable References => ProjectRoot.GetAllReferences().Select(r => r.AsReference()).ToList(); - public IReadOnlyCollection TargetFrameworks => new TargetFrameworkMonikerCollection(this, _comparer); + public IReadOnlyCollection TargetFrameworks => _factories.CreateTfmCollection(this); public IEnumerable ProjectTypes => GetPropertyValue("ProjectTypeGuids").Split(';'); diff --git a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/MSBuildWorkspaceUpgradeContext.cs b/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/MSBuildWorkspaceUpgradeContext.cs index 56af463b3..1534cf076 100644 --- a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/MSBuildWorkspaceUpgradeContext.cs +++ b/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/MSBuildWorkspaceUpgradeContext.cs @@ -23,6 +23,7 @@ internal sealed class MSBuildWorkspaceUpgradeContext : IUpgradeContext, IDisposa private readonly ILogger _logger; private readonly Dictionary _projectCache; private readonly IOptions _options; + private readonly Factories _factories; private List? _entryPointPaths; private FileInfo? _projectPath; @@ -58,6 +59,7 @@ public MSBuildWorkspaceUpgradeContext( IOptions options, Func infoGenerator, IPackageRestorer restorer, + Factories factories, ITargetFrameworkMonikerComparer comparer, IEnumerable componentIdentifiers, ILogger logger) @@ -67,6 +69,7 @@ public MSBuildWorkspaceUpgradeContext( throw new ArgumentNullException(nameof(infoGenerator)); } + _factories = factories ?? throw new ArgumentNullException(nameof(factories)); _projectCache = new Dictionary(StringComparer.OrdinalIgnoreCase); _options = options ?? throw new ArgumentNullException(nameof(options)); _restorer = restorer ?? throw new ArgumentNullException(nameof(restorer)); @@ -96,7 +99,7 @@ public IProject GetOrAddProject(FileInfo path) return cached; } - var project = new MSBuildProject(this, _componentIdentifiers, _restorer, _comparer, path, _logger); + var project = new MSBuildProject(this, _componentIdentifiers, _factories, _restorer, _comparer, path, _logger); _projectCache.Add(path.FullName, project); diff --git a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/UpgraderMsBuildExtensions.cs b/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/UpgraderMsBuildExtensions.cs index 3dedd9737..56415f534 100644 --- a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/UpgraderMsBuildExtensions.cs +++ b/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/UpgraderMsBuildExtensions.cs @@ -8,7 +8,6 @@ using Microsoft.DotNet.UpgradeAssistant.MSBuild; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -using NuGet.Configuration; namespace Microsoft.DotNet.UpgradeAssistant { @@ -22,6 +21,8 @@ public static void AddMsBuild(this IServiceCollection services, Action(); + services.AddTransient, VisualStudioFinder>(); services.AddTransient, MSBuildWorkspaceOptionsConfigure>(); services.AddTransient(); @@ -35,24 +36,6 @@ public static void AddMsBuild(this IServiceCollection services, Action>(sp => () => sp.GetRequiredService()); } - public static void AddNuGet(this IServiceCollection services, Action configure) - { - services.AddSingleton(); - services.AddTransient(ctx => ctx.GetRequiredService()); - services.AddTransient(ctx => ctx.GetRequiredService()); - services.AddSingleton(); - services.AddTransient(); - services.AddSingleton(); - services.AddSingleton(); - services.AddOptions() - .Configure(configure) - .Configure(options => - { - var settings = Settings.LoadDefaultSettings(null); - options.CachePath = SettingsUtility.GetGlobalPackagesFolder(settings); - }); - } - // TEMPORARY WORKAROUND // https://github.com/dotnet/roslyn/issues/36781 // Adding documents to a project can result in extra "" items diff --git a/src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/ExtensionManifest.json b/src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/ExtensionManifest.json new file mode 100644 index 000000000..5deb1af86 --- /dev/null +++ b/src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/ExtensionManifest.json @@ -0,0 +1,7 @@ +{ + "ExtensionName": "NuGet", + + "ExtensionServiceProviders": [ + "Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.dll" + ] +} \ No newline at end of file diff --git a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/INuGetPackageSourceFactory.cs b/src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/INuGetPackageSourceFactory.cs similarity index 84% rename from src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/INuGetPackageSourceFactory.cs rename to src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/INuGetPackageSourceFactory.cs index 9df369754..5937f1a72 100644 --- a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/INuGetPackageSourceFactory.cs +++ b/src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/INuGetPackageSourceFactory.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using NuGet.Configuration; -namespace Microsoft.DotNet.UpgradeAssistant.MSBuild +namespace Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet { public interface INuGetPackageSourceFactory { diff --git a/src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.csproj b/src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.csproj new file mode 100644 index 000000000..a44e14bb5 --- /dev/null +++ b/src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.csproj @@ -0,0 +1,48 @@ + + + + net5.0 + + + + + + + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + + + True + True + Resources.resx + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + diff --git a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/NuGetCredentialsStartup.cs b/src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/NuGetCredentialsStartup.cs similarity index 94% rename from src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/NuGetCredentialsStartup.cs rename to src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/NuGetCredentialsStartup.cs index 37fad51b3..e846c5a64 100644 --- a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/NuGetCredentialsStartup.cs +++ b/src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/NuGetCredentialsStartup.cs @@ -7,7 +7,7 @@ using Microsoft.Extensions.Logging; using NuGet.Credentials; -namespace Microsoft.DotNet.UpgradeAssistant.MSBuild +namespace Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet { public class NuGetCredentialsStartup : IUpgradeStartup { diff --git a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/NuGetDownloaderOptions.cs b/src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/NuGetDownloaderOptions.cs similarity index 83% rename from src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/NuGetDownloaderOptions.cs rename to src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/NuGetDownloaderOptions.cs index 759189ef3..420d87615 100644 --- a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/NuGetDownloaderOptions.cs +++ b/src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/NuGetDownloaderOptions.cs @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.DotNet.UpgradeAssistant.MSBuild +namespace Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet { public class NuGetDownloaderOptions { diff --git a/src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/NuGetExtensionBuilder.cs b/src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/NuGetExtensionBuilder.cs new file mode 100644 index 000000000..60324f71d --- /dev/null +++ b/src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/NuGetExtensionBuilder.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.DotNet.UpgradeAssistant.MSBuild; +using Microsoft.Extensions.DependencyInjection; +using NuGet.Configuration; + +namespace Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet +{ + public class NuGetExtensionBuilder : IExtensionServiceProvider + { + public void AddServices(IExtensionServiceCollection services) + => AddNuGet(services.Services); + + private static void AddNuGet(IServiceCollection services) + { + services.AddTransient(); + services.AddTransient(); + services.AddSingleton(); + services.AddTransient(ctx => ctx.GetRequiredService()); + services.AddTransient(ctx => ctx.GetRequiredService()); + services.AddSingleton(); + services.AddTransient(); + services.AddSingleton(); + services.AddSingleton(); + services.AddOptions() + .Configure(options => + { + var settings = Settings.LoadDefaultSettings(null); + options.CachePath = SettingsUtility.GetGlobalPackagesFolder(settings); + }); + } + } +} diff --git a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/NuGetExtensions.cs b/src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/NuGetExtensions.cs similarity index 96% rename from src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/NuGetExtensions.cs rename to src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/NuGetExtensions.cs index 46f0594b2..0094dcfb6 100644 --- a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/NuGetExtensions.cs +++ b/src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/NuGetExtensions.cs @@ -4,7 +4,7 @@ using System.Diagnostics.CodeAnalysis; using NuGet.Versioning; -namespace Microsoft.DotNet.UpgradeAssistant.MSBuild +namespace Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet { public static class NuGetExtensions { diff --git a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/NuGetLogger.cs b/src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/NuGetLogger.cs similarity index 97% rename from src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/NuGetLogger.cs rename to src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/NuGetLogger.cs index e8928ac7b..19149a176 100644 --- a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/NuGetLogger.cs +++ b/src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/NuGetLogger.cs @@ -8,7 +8,7 @@ using ILogger = NuGet.Common.ILogger; using LogLevel = NuGet.Common.LogLevel; -namespace Microsoft.DotNet.UpgradeAssistant.MSBuild +namespace Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet { public class NuGetLogger : ILogger { diff --git a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/NuGetPackageSourceFactory.cs b/src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/NuGetPackageSourceFactory.cs similarity index 95% rename from src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/NuGetPackageSourceFactory.cs rename to src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/NuGetPackageSourceFactory.cs index 8d4c15995..cf1699e1a 100644 --- a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/NuGetPackageSourceFactory.cs +++ b/src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/NuGetPackageSourceFactory.cs @@ -6,7 +6,7 @@ using Microsoft.Extensions.Logging; using NuGet.Configuration; -namespace Microsoft.DotNet.UpgradeAssistant.MSBuild +namespace Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet { public class NuGetPackageSourceFactory : INuGetPackageSourceFactory { diff --git a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/NuGetTargetFrameworkMonikerComparer.cs b/src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/NuGetTargetFrameworkMonikerComparer.cs similarity index 98% rename from src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/NuGetTargetFrameworkMonikerComparer.cs rename to src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/NuGetTargetFrameworkMonikerComparer.cs index b95603413..79ac6754f 100644 --- a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/NuGetTargetFrameworkMonikerComparer.cs +++ b/src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/NuGetTargetFrameworkMonikerComparer.cs @@ -6,7 +6,7 @@ using Microsoft.Extensions.Logging; using NuGet.Frameworks; -namespace Microsoft.DotNet.UpgradeAssistant.MSBuild +namespace Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet { public class NuGetTargetFrameworkMonikerComparer : ITargetFrameworkMonikerComparer { diff --git a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/NuGetVersionComparer.cs b/src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/NuGetVersionComparer.cs similarity index 95% rename from src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/NuGetVersionComparer.cs rename to src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/NuGetVersionComparer.cs index 2203be4a9..edb34de33 100644 --- a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/NuGetVersionComparer.cs +++ b/src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/NuGetVersionComparer.cs @@ -3,7 +3,7 @@ using NuGet.Versioning; -namespace Microsoft.DotNet.UpgradeAssistant.MSBuild +namespace Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet { public class NuGetVersionComparer : IVersionComparer { diff --git a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/PackageConfig.cs b/src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/PackageConfig.cs similarity index 94% rename from src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/PackageConfig.cs rename to src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/PackageConfig.cs index 04710ac5e..28ac0ee84 100644 --- a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/PackageConfig.cs +++ b/src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/PackageConfig.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using System.Xml.Linq; -namespace Microsoft.DotNet.UpgradeAssistant.MSBuild +namespace Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet { internal static class PackageConfig { diff --git a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/PackageLoader.cs b/src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/PackageLoader.cs similarity index 99% rename from src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/PackageLoader.cs rename to src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/PackageLoader.cs index 8ab41cf5b..42e319b25 100644 --- a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/PackageLoader.cs +++ b/src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/PackageLoader.cs @@ -11,7 +11,6 @@ using System.Runtime.Versioning; using System.Threading; using System.Threading.Tasks; -using Microsoft.DotNet.UpgradeAssistant.Extensions; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using NuGet.Configuration; @@ -21,7 +20,7 @@ using NuGet.Protocol.Core.Types; using NuGet.Versioning; -namespace Microsoft.DotNet.UpgradeAssistant.MSBuild +namespace Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet { public sealed class PackageLoader : IPackageLoader, IPackageDownloader, IDisposable { @@ -30,7 +29,7 @@ public sealed class PackageLoader : IPackageLoader, IPackageDownloader, IDisposa private readonly SourceCacheContext _cache; private readonly Lazy> _packageSources; private readonly ILogger _logger; - private readonly NuGet.Common.ILogger _nugetLogger; + private readonly global::NuGet.Common.ILogger _nugetLogger; private readonly Dictionary _sourceRepositoryCache; private readonly NuGetDownloaderOptions _options; diff --git a/src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/ProjectNuGetReferences.cs b/src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/ProjectNuGetReferences.cs new file mode 100644 index 000000000..ea5c63173 --- /dev/null +++ b/src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/ProjectNuGetReferences.cs @@ -0,0 +1,213 @@ +// 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.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.Extensions.Logging; +using NuGet.Frameworks; +using NuGet.Packaging.Core; +using NuGet.ProjectModel; + +namespace Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet +{ + public class ProjectNuGetReferences : INuGetReferences + { + private readonly IUpgradeContext _context; + private readonly IProject _project; + private readonly IPackageRestorer _restorer; + private readonly ILogger _logger; + + public ProjectNuGetReferences( + IUpgradeContext context, + IProject project, + IPackageRestorer restorer, + ILogger logger) + { + _context = context ?? throw new ArgumentNullException(nameof(context)); + _project = project ?? throw new ArgumentNullException(nameof(project)); + _restorer = restorer ?? throw new ArgumentNullException(nameof(restorer)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + public NugetPackageFormat PackageReferenceFormat + { + get + { + if (GetPackagesConfigPath() is not null) + { + return NugetPackageFormat.PackageConfig; + } + else if (_project.GetFile().PackageReferences.Any()) + { + return NugetPackageFormat.PackageReference; + } + else + { + return NugetPackageFormat.None; + } + } + } + + private string? GetPackagesConfigPath() => _project.FindFiles("packages.config", ProjectItemType.Content).FirstOrDefault(); + + public IEnumerable PackageReferences + { + get + { + var packagesConfig = GetPackagesConfigPath(); + + if (packagesConfig is null) + { + return _project.GetFile().PackageReferences; + } + else + { + return PackageConfig.GetPackages(packagesConfig); + } + } + } + + public IAsyncEnumerable GetTransitivePackageReferencesAsync(TargetFrameworkMoniker tfm, CancellationToken token) + => PackageReferenceFormat switch + { + NugetPackageFormat.PackageConfig => PackageReferences.ToAsyncEnumerable(), + NugetPackageFormat.PackageReference => GetAllPackageReferenceDependenciesAsync(tfm, token).Select(l => new NuGetReference(l.Name, l.Version.ToNormalizedString())), + _ => AsyncEnumerable.Empty() + }; + + public async ValueTask IsTransitivelyAvailableAsync(string packageName, CancellationToken token) + => PackageReferences.Any(p => p.Name.Equals(packageName, StringComparison.OrdinalIgnoreCase)) + || (PackageReferenceFormat == NugetPackageFormat.PackageReference && await _project.TargetFrameworks.ToAsyncEnumerable().AnyAwaitAsync(tfm => ContainsPackageDependencyAsync(tfm, d => string.Equals(packageName, d.Id, StringComparison.OrdinalIgnoreCase), token), cancellationToken: token).ConfigureAwait(false)); + + public async ValueTask IsTransitiveDependencyAsync(NuGetReference nugetReference, CancellationToken token) + => PackageReferenceFormat == NugetPackageFormat.PackageReference && await _project.TargetFrameworks.ToAsyncEnumerable().AnyAwaitAsync(tfm => ContainsPackageDependencyAsync(tfm, d => ReferenceSatisfiesDependency(d, nugetReference, true), token), token).ConfigureAwait(false); + + private static bool ReferenceSatisfiesDependency(PackageDependency dependency, NuGetReference packageReference, bool minVersionMatchOnly) + { + // If the dependency's name doesn't match the reference's name, return false + if (!dependency.Id.Equals(packageReference.Name, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + if (!packageReference.TryGetNuGetVersion(out var packageVersion)) + { + throw new InvalidOperationException("Package references from a lock file should always have a specific version"); + } + + // Return false if the reference's version falls outside of the dependency range + var versionRange = dependency.VersionRange; + if (versionRange.HasLowerBound && packageVersion < versionRange.MinVersion) + { + return false; + } + + if (versionRange.HasUpperBound && packageVersion > versionRange.MaxVersion) + { + return false; + } + + // In some cases (looking for transitive dependencies), it's interesting to only match packages that are the minimum version + if (minVersionMatchOnly && versionRange.HasLowerBound && packageVersion != versionRange.MinVersion) + { + return false; + } + + // Otherwise, return true + return true; + } + + private ValueTask ContainsPackageDependencyAsync(TargetFrameworkMoniker tfm, Func filter, CancellationToken token) + => GetAllPackageReferenceDependenciesAsync(tfm, token).AnyAsync(l => l.Dependencies.Any(d => filter(d)), token); + + private async IAsyncEnumerable GetAllPackageReferenceDependenciesAsync(TargetFrameworkMoniker tfm, [EnumeratorCancellation] CancellationToken token) + { + if (!IsRestored) + { + throw new InvalidOperationException("Project should have already been restored. Please file an issue at https://github.com/dotnet/upgrade-assistant"); + } + + if (PackageReferenceFormat != NugetPackageFormat.PackageReference) + { + throw new InvalidOperationException("PackageReference restore for transitive dependencies should only happen for PackageReference package reference format"); + } + + var parsedTfm = NuGetFramework.Parse(tfm.Name); + var target = GetLockFileTarget(parsedTfm); + + if (target is null) + { + // Break if there are no packages in the project. Otherwise, we end up performing restores too often. + if (!PackageReferences.Any()) + { + _logger.LogDebug("Skipping restore as no package references exist in project file {Path}", _project.FileInfo.FullName); + yield break; + } + + _logger.LogDebug("Attempting a restore to retrieve missing lock file data {Path}", _project.FileInfo.FullName); + + await _restorer.RestorePackagesAsync(_context, _project, token).ConfigureAwait(false); + + // If the LockFilePath is defined but does not exist, there are no libraries + if (!File.Exists(LockFilePath)) + { + yield break; + } + + target = GetLockFileTarget(parsedTfm); + } + + if (target is null) + { + _logger.LogError("NuGet target in project.assets.json is still unavailable after restore. Please verify that the project has been restored."); + throw new UpgradeException("Restore has not restored the expected TFMs. Please review any warnings from dotnet-restore."); + } + + foreach (var library in target.Libraries) + { + yield return library; + } + + LockFileTarget? GetLockFileTarget(NuGetFramework parsedTfm) + { + var lockFile = LockFileUtilities.GetLockFile(LockFilePath, global::NuGet.Common.NullLogger.Instance); + + if (lockFile?.Targets is null) + { + return null; + } + + return lockFile.Targets + .FirstOrDefault(t => t.TargetFramework.DotNetFrameworkName.Equals(parsedTfm.DotNetFrameworkName, StringComparison.Ordinal)); + } + } + + private bool IsRestored => LockFilePath is not null; + + private string? LockFilePath + { + get + { + var lockFilePath = Path.Combine(_project.GetFile().GetPropertyValue("MSBuildProjectExtensionsPath"), "project.assets.json"); + + if (string.IsNullOrEmpty(lockFilePath)) + { + return null; + } + + if (!Path.IsPathFullyQualified(lockFilePath)) + { + lockFilePath = Path.Combine(_project.FileInfo.DirectoryName ?? string.Empty, lockFilePath); + } + + return lockFilePath; + } + } + } +} diff --git a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/TargetFrameworkMonikerCollection.cs b/src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/TargetFrameworkMonikerCollection.cs similarity index 96% rename from src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/TargetFrameworkMonikerCollection.cs rename to src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/TargetFrameworkMonikerCollection.cs index 32317564c..3c35a0b88 100644 --- a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/TargetFrameworkMonikerCollection.cs +++ b/src/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet/TargetFrameworkMonikerCollection.cs @@ -5,9 +5,9 @@ using System.Collections.Generic; using NuGet.Frameworks; -namespace Microsoft.DotNet.UpgradeAssistant.MSBuild +namespace Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet { - public class TargetFrameworkMonikerCollection : IReadOnlyCollection + public class TargetFrameworkMonikerCollection : IReadOnlyCollection, ITargetFrameworkCollection { private const string SdkSingleTargetFramework = "TargetFramework"; private const string SdkMultipleTargetFrameworks = "TargetFrameworks"; diff --git a/tests/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.Tests/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.Tests.csproj b/tests/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.Tests/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.Tests.csproj new file mode 100644 index 000000000..e5717e1f6 --- /dev/null +++ b/tests/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.Tests/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.Tests.csproj @@ -0,0 +1,9 @@ + + + net5.0 + + + + + + \ No newline at end of file diff --git a/tests/components/Microsoft.DotNet.UpgradeAssistant.MSBuild.Tests/NuGetExtensionsTests.cs b/tests/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.Tests/NuGetExtensionsTests.cs similarity index 91% rename from tests/components/Microsoft.DotNet.UpgradeAssistant.MSBuild.Tests/NuGetExtensionsTests.cs rename to tests/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.Tests/NuGetExtensionsTests.cs index 09fe47e92..c350deeae 100644 --- a/tests/components/Microsoft.DotNet.UpgradeAssistant.MSBuild.Tests/NuGetExtensionsTests.cs +++ b/tests/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.Tests/NuGetExtensionsTests.cs @@ -4,9 +4,8 @@ using System; using Xunit; -namespace Microsoft.DotNet.UpgradeAssistant.MSBuild.Tests +namespace Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.Tests { - [Collection(MSBuildStepTestCollection.Name)] public class NuGetExtensionsTests { [Fact] diff --git a/tests/components/Microsoft.DotNet.UpgradeAssistant.MSBuild.Tests/NuGetTargetFrameworkMonikerComparerTests.cs b/tests/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.Tests/NuGetTargetFrameworkMonikerComparerTests.cs similarity index 98% rename from tests/components/Microsoft.DotNet.UpgradeAssistant.MSBuild.Tests/NuGetTargetFrameworkMonikerComparerTests.cs rename to tests/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.Tests/NuGetTargetFrameworkMonikerComparerTests.cs index e89e2c5c8..3c773c22e 100644 --- a/tests/components/Microsoft.DotNet.UpgradeAssistant.MSBuild.Tests/NuGetTargetFrameworkMonikerComparerTests.cs +++ b/tests/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.Tests/NuGetTargetFrameworkMonikerComparerTests.cs @@ -7,9 +7,8 @@ using static Microsoft.DotNet.UpgradeAssistant.TargetFrameworkMonikerParser; -namespace Microsoft.DotNet.UpgradeAssistant.MSBuild.Tests +namespace Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.Tests { - [Collection(MSBuildStepTestCollection.Name)] public class NuGetTargetFrameworkMonikerComparerTests { [InlineData(Net50, NetCoreApp31, true)] diff --git a/tests/components/Microsoft.DotNet.UpgradeAssistant.MSBuild.Tests/NuGetVersionComparerTests.cs b/tests/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.Tests/NuGetVersionComparerTests.cs similarity index 92% rename from tests/components/Microsoft.DotNet.UpgradeAssistant.MSBuild.Tests/NuGetVersionComparerTests.cs rename to tests/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.Tests/NuGetVersionComparerTests.cs index 6ddab9d84..226b91978 100644 --- a/tests/components/Microsoft.DotNet.UpgradeAssistant.MSBuild.Tests/NuGetVersionComparerTests.cs +++ b/tests/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.Tests/NuGetVersionComparerTests.cs @@ -3,9 +3,8 @@ using Xunit; -namespace Microsoft.DotNet.UpgradeAssistant.MSBuild.Tests +namespace Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.Tests { - [Collection(MSBuildStepTestCollection.Name)] public class NuGetVersionComparerTests { [InlineData("1.0", "1.0", 0)] diff --git a/tests/components/Microsoft.DotNet.UpgradeAssistant.MSBuild.Tests/PackageLoaderTests.cs b/tests/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.Tests/PackageLoaderTests.cs similarity index 99% rename from tests/components/Microsoft.DotNet.UpgradeAssistant.MSBuild.Tests/PackageLoaderTests.cs rename to tests/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.Tests/PackageLoaderTests.cs index c586348a8..16619f125 100644 --- a/tests/components/Microsoft.DotNet.UpgradeAssistant.MSBuild.Tests/PackageLoaderTests.cs +++ b/tests/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.Tests/PackageLoaderTests.cs @@ -12,9 +12,8 @@ using NuGet.Protocol.Core.Types; using Xunit; -namespace Microsoft.DotNet.UpgradeAssistant.MSBuild.Tests +namespace Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.Tests { - [Collection(MSBuildStepTestCollection.Name)] public class PackageLoaderTests { private readonly Fixture _fixture; diff --git a/tests/components/Microsoft.DotNet.UpgradeAssistant.MSBuild.Tests/TargetFrameworkMonikerCollectionTests.cs b/tests/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.Tests/TargetFrameworkMonikerCollectionTests.cs similarity index 98% rename from tests/components/Microsoft.DotNet.UpgradeAssistant.MSBuild.Tests/TargetFrameworkMonikerCollectionTests.cs rename to tests/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.Tests/TargetFrameworkMonikerCollectionTests.cs index d508b0b49..c135a20a1 100644 --- a/tests/components/Microsoft.DotNet.UpgradeAssistant.MSBuild.Tests/TargetFrameworkMonikerCollectionTests.cs +++ b/tests/extensions/nuget/Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.Tests/TargetFrameworkMonikerCollectionTests.cs @@ -8,9 +8,8 @@ using static Microsoft.DotNet.UpgradeAssistant.TargetFrameworkMonikerParser; -namespace Microsoft.DotNet.UpgradeAssistant.MSBuild.Tests +namespace Microsoft.DotNet.UpgradeAssistant.Extensions.NuGet.Tests { - [Collection(MSBuildStepTestCollection.Name)] [System.Diagnostics.CodeAnalysis.SuppressMessage("Assertions", "xUnit2013:Do not use equality check to check for collection size.", Justification = "Need to verify .Count property")] public class TargetFrameworkMonikerCollectionTests {