diff --git a/src/NuGet.Core/NuGet.Commands/RestoreCommand/LockFileBuilder.cs b/src/NuGet.Core/NuGet.Commands/RestoreCommand/LockFileBuilder.cs index d46e8702cb0..80572729569 100644 --- a/src/NuGet.Core/NuGet.Commands/RestoreCommand/LockFileBuilder.cs +++ b/src/NuGet.Core/NuGet.Commands/RestoreCommand/LockFileBuilder.cs @@ -162,6 +162,8 @@ public LockFile CreateLockFile(LockFile previousLockFile, .OrderBy(graph => graph.Framework.ToString(), StringComparer.Ordinal) .ThenBy(graph => graph.RuntimeIdentifier, StringComparer.Ordinal)) { + var librariesWithMonoAndroidWarnings = new HashSet(); + var target = lockFile.Version >= LockFileFormat.AliasedVersion ? new LockFileTarget { @@ -186,6 +188,8 @@ public LockFile CreateLockFile(LockFile previousLockFile, && (target.TargetFramework is FallbackFramework || target.TargetFramework is AssetTargetFallbackFramework); + bool checkMonoAndroidDeprecation = MonoAndroidDeprecation.ShouldCheck(project, targetGraph.Framework); + foreach (var graphItem in targetGraph.Flattened.OrderBy(x => x.Key)) { var library = graphItem.Key; @@ -226,7 +230,7 @@ public LockFile CreateLockFile(LockFile previousLockFile, var package = packageInfo.Package; var libraryDependency = tfi.Dependencies.FirstOrDefault(e => e.Name.Equals(library.Name, StringComparison.OrdinalIgnoreCase)); - (LockFileTargetLibrary targetLibrary, bool usedFallbackFramework) = LockFileUtils.CreateLockFileTargetLibrary( + (LockFileTargetLibrary targetLibrary, bool usedFallbackFramework, NuGetFramework compileAssetFramework, NuGetFramework runtimeAssetFramework) = LockFileUtils.CreateLockFileTargetLibrary( libraryDependency?.Aliases, libraries[ValueTuple.Create(library.Name, library.Version)], package, @@ -280,6 +284,29 @@ public LockFile CreateLockFile(LockFile previousLockFile, librariesWithWarnings.Add(library); } } + + // Log NU1703 warning if the package uses the deprecated MonoAndroid framework + if (checkMonoAndroidDeprecation + && !librariesWithMonoAndroidWarnings.Contains(library) + && (MonoAndroidDeprecation.IsMonoAndroidFramework(compileAssetFramework) + || MonoAndroidDeprecation.IsMonoAndroidFramework(runtimeAssetFramework))) + { + var message = string.Format(CultureInfo.CurrentCulture, + Strings.Warning_MonoAndroidFrameworkDeprecated, + library.Name, + library.Version); + + var logMessage = RestoreLogMessage.CreateWarning( + NuGetLogCode.NU1703, + message, + library.Name, + targetGraph.TargetGraphName); + + _logger.Log(logMessage); + + // only log the warning once per library + librariesWithMonoAndroidWarnings.Add(library); + } } } diff --git a/src/NuGet.Core/NuGet.Commands/RestoreCommand/LockFileBuilderCache.cs b/src/NuGet.Core/NuGet.Commands/RestoreCommand/LockFileBuilderCache.cs index eab49cbf8a1..48a00a380af 100644 --- a/src/NuGet.Core/NuGet.Commands/RestoreCommand/LockFileBuilderCache.cs +++ b/src/NuGet.Core/NuGet.Commands/RestoreCommand/LockFileBuilderCache.cs @@ -31,7 +31,7 @@ public class LockFileBuilderCache private readonly ConcurrentDictionary, bool)>> _criteriaSets = new(); - private readonly ConcurrentDictionary<(CriteriaKey, string path, string aliases, LibraryIncludeFlags, int dependencyCount), Lazy<(LockFileTargetLibrary, bool)>> _lockFileTargetLibraryCache = + private readonly ConcurrentDictionary<(CriteriaKey, string path, string aliases, LibraryIncludeFlags, int dependencyCount), Lazy<(LockFileTargetLibrary, bool, NuGetFramework, NuGetFramework)>> _lockFileTargetLibraryCache = new(); /// @@ -106,7 +106,7 @@ public ContentItemCollection GetContentItems(LockFileLibrary library, LocalPacka /// /// Try to get a LockFileTargetLibrary from the cache. /// - internal (LockFileTargetLibrary, bool) GetLockFileTargetLibrary(RestoreTargetGraph graph, NuGetFramework framework, LocalPackageInfo localPackageInfo, string aliases, LibraryIncludeFlags libraryIncludeFlags, List dependencies, Func<(LockFileTargetLibrary, bool)> valueFactory) + internal (LockFileTargetLibrary, bool, NuGetFramework, NuGetFramework) GetLockFileTargetLibrary(RestoreTargetGraph graph, NuGetFramework framework, LocalPackageInfo localPackageInfo, string aliases, LibraryIncludeFlags libraryIncludeFlags, List dependencies, Func<(LockFileTargetLibrary, bool, NuGetFramework, NuGetFramework)> valueFactory) { // Comparing RuntimeGraph for equality is very expensive, // so in case of a request where the RuntimeGraph is not empty we avoid using the cache. @@ -117,7 +117,7 @@ public ContentItemCollection GetContentItems(LockFileLibrary library, LocalPacka var criteriaKey = new CriteriaKey(graph.TargetGraphName, framework); var packagePath = localPackageInfo.ExpandedPath; return _lockFileTargetLibraryCache.GetOrAdd((criteriaKey, packagePath, aliases, libraryIncludeFlags, dependencies.Count), - key => new Lazy<(LockFileTargetLibrary, bool)>(valueFactory)).Value; + key => new Lazy<(LockFileTargetLibrary, bool, NuGetFramework, NuGetFramework)>(valueFactory)).Value; } private class CriteriaKey : IEquatable diff --git a/src/NuGet.Core/NuGet.Commands/RestoreCommand/MonoAndroidDeprecation.cs b/src/NuGet.Core/NuGet.Commands/RestoreCommand/MonoAndroidDeprecation.cs new file mode 100644 index 00000000000..4bbd0b0a61c --- /dev/null +++ b/src/NuGet.Core/NuGet.Commands/RestoreCommand/MonoAndroidDeprecation.cs @@ -0,0 +1,58 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using NuGet.Frameworks; +using NuGet.ProjectModel; + +namespace NuGet.Commands +{ + /// + /// Detects when a package uses the deprecated MonoAndroid framework instead of net6.0-android or later. + /// This warning is gated on .NET 11 SDK (SdkAnalysisLevel >= 11.0.100) and targeting net11.0-android or later. + /// + internal static class MonoAndroidDeprecation + { + /// + /// Determines whether the MonoAndroid deprecation check should be performed for the given project and target framework. + /// + /// The package spec containing restore metadata. + /// The target framework of the current graph. + /// True if the deprecation check should be performed. + internal static bool ShouldCheck(PackageSpec project, NuGetFramework framework) + { + if (project.RestoreMetadata == null) + { + return false; + } + + // Gate on SDK analysis level >= 11.0.100 + if (!SdkAnalysisLevelMinimums.IsEnabled( + project.RestoreMetadata.SdkAnalysisLevel, + project.RestoreMetadata.UsingMicrosoftNETSdk, + SdkAnalysisLevelMinimums.V11_0_100)) + { + return false; + } + + // Only check for .NETCoreApp frameworks targeting android with version >= 11.0 + return StringComparer.OrdinalIgnoreCase.Equals(framework.Framework, FrameworkConstants.FrameworkIdentifiers.NetCoreApp) + && framework.Version.Major >= 11 + && framework.HasPlatform + && framework.Platform.Equals("android", StringComparison.OrdinalIgnoreCase); + } + + /// + /// Checks whether the given framework is a MonoAndroid framework. + /// + /// The framework to check, or null. + /// True if the framework uses the MonoAndroid framework identifier. + internal static bool IsMonoAndroidFramework(NuGetFramework framework) + { + return framework != null + && StringComparer.OrdinalIgnoreCase.Equals( + framework.Framework, + FrameworkConstants.FrameworkIdentifiers.MonoAndroid); + } + } +} diff --git a/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/LockFileUtils.cs b/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/LockFileUtils.cs index 5ad5dcf0ee7..7f604bb7d9c 100644 --- a/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/LockFileUtils.cs +++ b/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/LockFileUtils.cs @@ -34,7 +34,7 @@ public static LockFileTargetLibrary CreateLockFileTargetLibrary( RestoreTargetGraph targetGraph, LibraryIncludeFlags dependencyType) { - var (lockFileTargetLibrary, _) = CreateLockFileTargetLibrary( + var (lockFileTargetLibrary, _, _, _) = CreateLockFileTargetLibrary( aliases: null, library, package, @@ -57,8 +57,8 @@ public static LockFileTargetLibrary CreateLockFileTargetLibrary( /// The original framework if the asset selection is happening for a fallback framework. /// The dependencies of this package. /// The lock file build cache. - /// The LockFileTargetLibrary, and whether a fallback framework criteria was used to select it. - internal static (LockFileTargetLibrary, bool) CreateLockFileTargetLibrary( + /// The LockFileTargetLibrary, whether a fallback framework criteria was used to select it, the framework selected for compile assets, and the framework selected for runtime assets. + internal static (LockFileTargetLibrary, bool, NuGetFramework, NuGetFramework) CreateLockFileTargetLibrary( string aliases, LockFileLibrary library, LocalPackageInfo package, @@ -75,6 +75,8 @@ internal static (LockFileTargetLibrary, bool) CreateLockFileTargetLibrary( () => { LockFileTargetLibrary lockFileLib = null; + NuGetFramework compileAssetFramework = null; + NuGetFramework runtimeAssetFramework = null; // This will throw an appropriate error if the nuspec is missing var nuspec = package.Nuspec; @@ -86,7 +88,7 @@ internal static (LockFileTargetLibrary, bool) CreateLockFileTargetLibrary( for (var i = 0; i < orderedCriteriaSets.Count; i++) { - lockFileLib = CreateLockFileTargetLibrary(aliases, library, package, targetGraph.Conventions, dependencyType, + (lockFileLib, compileAssetFramework, runtimeAssetFramework) = CreateLockFileTargetLibrary(aliases, library, package, targetGraph.Conventions, dependencyType, framework, runtimeIdentifier, contentItems, nuspec, packageTypes, orderedCriteriaSets[i].orderedCriteria); // Check if compatible assets were found. // If no compatible assets were found and this is the last check @@ -108,7 +110,7 @@ internal static (LockFileTargetLibrary, bool) CreateLockFileTargetLibrary( lockFileLib.Freeze(); - return (lockFileLib, fallbackUsed); + return (lockFileLib, fallbackUsed, compileAssetFramework, runtimeAssetFramework); }); } @@ -165,7 +167,7 @@ private static void ApplyAliases(string aliases, LockFileItem item) /// /// Populate assets for a . /// - internal static LockFileTargetLibrary CreateLockFileTargetLibrary( + internal static (LockFileTargetLibrary lockFileLib, NuGetFramework compileAssetFramework, NuGetFramework runtimeAssetFramework) CreateLockFileTargetLibrary( string aliases, LockFileLibrary library, LocalPackageInfo package, @@ -198,6 +200,7 @@ internal static LockFileTargetLibrary CreateLockFileTargetLibrary( orderedCriteria, contentItems, applyAliases, + out NuGetFramework compileFramework, managedCodeConventions.Patterns.CompileRefAssemblies, managedCodeConventions.Patterns.CompileLibAssemblies); @@ -205,6 +208,8 @@ internal static LockFileTargetLibrary CreateLockFileTargetLibrary( lockFileLib.RuntimeAssemblies = GetLockFileItems( orderedCriteria, contentItems, + additionalAction: null, + out NuGetFramework runtimeFramework, managedCodeConventions.Patterns.RuntimeAssemblies); // Embed @@ -242,7 +247,7 @@ internal static LockFileTargetLibrary CreateLockFileTargetLibrary( // Apply filters from the node in the nuspec ApplyReferenceFilter(lockFileLib, framework, nuspec); - return lockFileLib; + return (lockFileLib, compileFramework, runtimeFramework); } private static void AddMSBuildAssets( @@ -659,15 +664,17 @@ private static List ConvertToProjectPaths( } /// - /// Create lock file items for the best matching group. + /// Create lock file items for the best matching group, and optionally output the selected framework. /// /// Enumerate this once after calling. private static IList GetLockFileItems( List criteria, ContentItemCollection items, Action additionalAction, + out NuGetFramework selectedFramework, params PatternSet[] patterns) { + selectedFramework = null; List result = null; // Loop through each criteria taking the first one that matches one or more items. foreach (var managedCriteria in criteria) @@ -678,6 +685,12 @@ private static IList GetLockFileItems( if (group != null) { + if (group.Properties.TryGetValue( + ManagedCodeConventions.PropertyNames.TargetFrameworkMoniker, out object tfmObj)) + { + selectedFramework = tfmObj as NuGetFramework; + } + result = new(group.Items.Count); foreach (var item in group.Items.NoAllocEnumerate()) { @@ -711,7 +724,7 @@ private static IList GetLockFileItems( ContentItemCollection items, params PatternSet[] patterns) { - return GetLockFileItems(criteria, items, additionalAction: null, patterns); + return GetLockFileItems(criteria, items, additionalAction: null, out _, patterns); } /// diff --git a/src/NuGet.Core/NuGet.Commands/Strings.Designer.cs b/src/NuGet.Core/NuGet.Commands/Strings.Designer.cs index 06bd26772ce..6fdf08a5a7b 100644 --- a/src/NuGet.Core/NuGet.Commands/Strings.Designer.cs +++ b/src/NuGet.Core/NuGet.Commands/Strings.Designer.cs @@ -2629,6 +2629,15 @@ internal static string Warning_MinVersionNonInclusive { } } + /// + /// Looks up a localized string similar to Package '{0}' {1} uses the deprecated MonoAndroid framework instead of 'net6.0-android' or later. Consider upgrading to a newer version of this package or contacting the package author.. + /// + internal static string Warning_MonoAndroidFrameworkDeprecated { + get { + return ResourceManager.GetString("Warning_MonoAndroidFrameworkDeprecated", resourceCulture); + } + } + /// /// Looks up a localized string similar to Package '{0}' {1} has a known {2} severity vulnerability, {3}. /// diff --git a/src/NuGet.Core/NuGet.Commands/Strings.resx b/src/NuGet.Core/NuGet.Commands/Strings.resx index d52747a4410..9d319418126 100644 --- a/src/NuGet.Core/NuGet.Commands/Strings.resx +++ b/src/NuGet.Core/NuGet.Commands/Strings.resx @@ -167,6 +167,11 @@ Package '{0}' was restored using '{1}' instead of the project target framework '{2}'. This package may not be fully compatible with your project. + + Package '{0}' {1} uses the deprecated MonoAndroid framework instead of 'net6.0-android' or later. Consider upgrading to a newer version of this package or contacting the package author. + {0} - package id +{1} - package version + Cycle detected. diff --git a/src/NuGet.Core/NuGet.Commands/Utility/SdkAnalysisLevelMinimums.cs b/src/NuGet.Core/NuGet.Commands/Utility/SdkAnalysisLevelMinimums.cs index 53bf2bdb4cc..1830f5f9c24 100644 --- a/src/NuGet.Core/NuGet.Commands/Utility/SdkAnalysisLevelMinimums.cs +++ b/src/NuGet.Core/NuGet.Commands/Utility/SdkAnalysisLevelMinimums.cs @@ -37,6 +37,7 @@ internal static class SdkAnalysisLevelMinimums /// /// Minimum SDK Analysis Level required for: /// + /// warning when packages use the deprecated MonoAndroid framework /// error when TargetFramework alias contains non-ASCII characters /// /// diff --git a/src/NuGet.Core/NuGet.Commands/xlf/Strings.cs.xlf b/src/NuGet.Core/NuGet.Commands/xlf/Strings.cs.xlf index 254ff56015f..676359b776e 100644 --- a/src/NuGet.Core/NuGet.Commands/xlf/Strings.cs.xlf +++ b/src/NuGet.Core/NuGet.Commands/xlf/Strings.cs.xlf @@ -1496,6 +1496,12 @@ NuGet vyžaduje zdroje HTTPS. Další informace najdete na https://aka.ms/nuget- {0} neposkytuje inkluzivní dolní mez pro závislost {1}. Místo toho bylo přeloženo: {2}. + + Package '{0}' {1} uses the deprecated MonoAndroid framework instead of 'net6.0-android' or later. Consider upgrading to a newer version of this package or contacting the package author. + Package '{0}' {1} uses the deprecated MonoAndroid framework instead of 'net6.0-android' or later. Consider upgrading to a newer version of this package or contacting the package author. + {0} - package id +{1} - package version + Package '{0}' {1} has a known {2} severity vulnerability, {3} Balíček „{0}“ {1} má známé {2} ohrožení zabezpečení závažnosti, {3} diff --git a/src/NuGet.Core/NuGet.Commands/xlf/Strings.de.xlf b/src/NuGet.Core/NuGet.Commands/xlf/Strings.de.xlf index f43cf607ae4..5248319bff2 100644 --- a/src/NuGet.Core/NuGet.Commands/xlf/Strings.de.xlf +++ b/src/NuGet.Core/NuGet.Commands/xlf/Strings.de.xlf @@ -1496,6 +1496,12 @@ NuGet erfordert HTTPS-Quellen. Weitere Informationen finden Sie unter https://ak {0} stellt keine inklusive untere Grenze für Abhängigkeiten {1} bereit. {2} wurde stattdessen aufgelöst. + + Package '{0}' {1} uses the deprecated MonoAndroid framework instead of 'net6.0-android' or later. Consider upgrading to a newer version of this package or contacting the package author. + Package '{0}' {1} uses the deprecated MonoAndroid framework instead of 'net6.0-android' or later. Consider upgrading to a newer version of this package or contacting the package author. + {0} - package id +{1} - package version + Package '{0}' {1} has a known {2} severity vulnerability, {3} Das Paket "{0}" {1} weist eine bekannte {2} Schweregrad-Sicherheitsanfälligkeit auf, {3}. diff --git a/src/NuGet.Core/NuGet.Commands/xlf/Strings.es.xlf b/src/NuGet.Core/NuGet.Commands/xlf/Strings.es.xlf index eabf5729871..058a93d3591 100644 --- a/src/NuGet.Core/NuGet.Commands/xlf/Strings.es.xlf +++ b/src/NuGet.Core/NuGet.Commands/xlf/Strings.es.xlf @@ -1496,6 +1496,12 @@ NuGet requiere orígenes HTTPS. Consulte https://aka.ms/nuget-https-everywhere p {0} no proporciona un límite inferior inclusivo para la dependencia {1}. {2} se resolvió en su lugar. + + Package '{0}' {1} uses the deprecated MonoAndroid framework instead of 'net6.0-android' or later. Consider upgrading to a newer version of this package or contacting the package author. + Package '{0}' {1} uses the deprecated MonoAndroid framework instead of 'net6.0-android' or later. Consider upgrading to a newer version of this package or contacting the package author. + {0} - package id +{1} - package version + Package '{0}' {1} has a known {2} severity vulnerability, {3} El paquete "{0}" {1} tiene una vulnerabilidad de gravedad {2} conocida, {3} diff --git a/src/NuGet.Core/NuGet.Commands/xlf/Strings.fr.xlf b/src/NuGet.Core/NuGet.Commands/xlf/Strings.fr.xlf index c9dc314576b..9c8abb245b7 100644 --- a/src/NuGet.Core/NuGet.Commands/xlf/Strings.fr.xlf +++ b/src/NuGet.Core/NuGet.Commands/xlf/Strings.fr.xlf @@ -1496,6 +1496,12 @@ NuGet nécessite des sources HTTPS. Reportez-vous à https://aka.ms/nuget-https- {0} ne fournit pas de limite inférieure inclusive pour la dépendance {1}. {2} a été résolu à la place. + + Package '{0}' {1} uses the deprecated MonoAndroid framework instead of 'net6.0-android' or later. Consider upgrading to a newer version of this package or contacting the package author. + Package '{0}' {1} uses the deprecated MonoAndroid framework instead of 'net6.0-android' or later. Consider upgrading to a newer version of this package or contacting the package author. + {0} - package id +{1} - package version + Package '{0}' {1} has a known {2} severity vulnerability, {3} Le package '{0}' {1} présente une vulnérabilité de gravité {2} connue, {3}. diff --git a/src/NuGet.Core/NuGet.Commands/xlf/Strings.it.xlf b/src/NuGet.Core/NuGet.Commands/xlf/Strings.it.xlf index e0baacc9562..bd844b26ce3 100644 --- a/src/NuGet.Core/NuGet.Commands/xlf/Strings.it.xlf +++ b/src/NuGet.Core/NuGet.Commands/xlf/Strings.it.xlf @@ -1496,6 +1496,12 @@ NuGet richiede origini HTTPS. Vedi https://aka.ms/nuget-https-everywhere per alt In {0} non è specificato un limite inferiore inclusivo per la dipendenza {1}. {2} è stato risolto. + + Package '{0}' {1} uses the deprecated MonoAndroid framework instead of 'net6.0-android' or later. Consider upgrading to a newer version of this package or contacting the package author. + Package '{0}' {1} uses the deprecated MonoAndroid framework instead of 'net6.0-android' or later. Consider upgrading to a newer version of this package or contacting the package author. + {0} - package id +{1} - package version + Package '{0}' {1} has a known {2} severity vulnerability, {3} Il pacchetto '{0}' {1} presenta una vulnerabilità nota di gravità {2}, {3} diff --git a/src/NuGet.Core/NuGet.Commands/xlf/Strings.ja.xlf b/src/NuGet.Core/NuGet.Commands/xlf/Strings.ja.xlf index 1ddf7d15e3d..603f59ab134 100644 --- a/src/NuGet.Core/NuGet.Commands/xlf/Strings.ja.xlf +++ b/src/NuGet.Core/NuGet.Commands/xlf/Strings.ja.xlf @@ -1496,6 +1496,12 @@ NuGet には HTTPS ソースが必要です。詳しくは、https://aka.ms/nuge {0} では、依存関係 {1} の下限値 (その値を含む) を指定しませんでした。代わりに、{2} が解決されました。 + + Package '{0}' {1} uses the deprecated MonoAndroid framework instead of 'net6.0-android' or later. Consider upgrading to a newer version of this package or contacting the package author. + Package '{0}' {1} uses the deprecated MonoAndroid framework instead of 'net6.0-android' or later. Consider upgrading to a newer version of this package or contacting the package author. + {0} - package id +{1} - package version + Package '{0}' {1} has a known {2} severity vulnerability, {3} パッケージ '{0}' {1} に既知の {2} 重大度の脆弱性があります、{3} diff --git a/src/NuGet.Core/NuGet.Commands/xlf/Strings.ko.xlf b/src/NuGet.Core/NuGet.Commands/xlf/Strings.ko.xlf index 3c24e70b7cb..bfae604008b 100644 --- a/src/NuGet.Core/NuGet.Commands/xlf/Strings.ko.xlf +++ b/src/NuGet.Core/NuGet.Commands/xlf/Strings.ko.xlf @@ -1496,6 +1496,12 @@ NuGet에는 HTTPS 원본이 필요합니다. https://aka.ms/nuget-https-everywhe {0}은(는) 종속성 {1}에 대한 포괄적인 하한을 제공하지 않습니다. 대신 {2}이(가) 확인되었습니다. + + Package '{0}' {1} uses the deprecated MonoAndroid framework instead of 'net6.0-android' or later. Consider upgrading to a newer version of this package or contacting the package author. + Package '{0}' {1} uses the deprecated MonoAndroid framework instead of 'net6.0-android' or later. Consider upgrading to a newer version of this package or contacting the package author. + {0} - package id +{1} - package version + Package '{0}' {1} has a known {2} severity vulnerability, {3} '{0}' {1} 패키지에 알려진 {2} 심각도 취약성인 {3}이(가) 있습니다. diff --git a/src/NuGet.Core/NuGet.Commands/xlf/Strings.pl.xlf b/src/NuGet.Core/NuGet.Commands/xlf/Strings.pl.xlf index 095178d51b5..48ac07d4f6a 100644 --- a/src/NuGet.Core/NuGet.Commands/xlf/Strings.pl.xlf +++ b/src/NuGet.Core/NuGet.Commands/xlf/Strings.pl.xlf @@ -1496,6 +1496,12 @@ Menedżer NuGet wymaga źródeł HTTPS. Aby uzyskać więcej informacji, sprawd {0} nie zapewnia łącznego dolnego ograniczenia dla zależności {1}. {2} został rozwiązany zamiast tego. + + Package '{0}' {1} uses the deprecated MonoAndroid framework instead of 'net6.0-android' or later. Consider upgrading to a newer version of this package or contacting the package author. + Package '{0}' {1} uses the deprecated MonoAndroid framework instead of 'net6.0-android' or later. Consider upgrading to a newer version of this package or contacting the package author. + {0} - package id +{1} - package version + Package '{0}' {1} has a known {2} severity vulnerability, {3} Pakiet „{0}” {1} ma znane {2} luki w zabezpieczeniach o ważności, {3} diff --git a/src/NuGet.Core/NuGet.Commands/xlf/Strings.pt-BR.xlf b/src/NuGet.Core/NuGet.Commands/xlf/Strings.pt-BR.xlf index 48f967fa30b..7eadcda373c 100644 --- a/src/NuGet.Core/NuGet.Commands/xlf/Strings.pt-BR.xlf +++ b/src/NuGet.Core/NuGet.Commands/xlf/Strings.pt-BR.xlf @@ -1496,6 +1496,12 @@ O NuGet exige fontes HTTPS. Consulte https://aka.ms/nuget-https-everywhere para {0} não fornece um limite inferior inclusivo para a dependência {1}. {2} foi resolvido. + + Package '{0}' {1} uses the deprecated MonoAndroid framework instead of 'net6.0-android' or later. Consider upgrading to a newer version of this package or contacting the package author. + Package '{0}' {1} uses the deprecated MonoAndroid framework instead of 'net6.0-android' or later. Consider upgrading to a newer version of this package or contacting the package author. + {0} - package id +{1} - package version + Package '{0}' {1} has a known {2} severity vulnerability, {3} O pacote '{0}' {1} tem uma {2} vulnerabilidade de gravidade conhecida, {3} diff --git a/src/NuGet.Core/NuGet.Commands/xlf/Strings.ru.xlf b/src/NuGet.Core/NuGet.Commands/xlf/Strings.ru.xlf index 4aa9c65095a..899aed1c750 100644 --- a/src/NuGet.Core/NuGet.Commands/xlf/Strings.ru.xlf +++ b/src/NuGet.Core/NuGet.Commands/xlf/Strings.ru.xlf @@ -1496,6 +1496,12 @@ NuGet requires HTTPS sources. Refer to https://aka.ms/nuget-https-everywhere for {0} не задает включенную нижнюю границу для зависимости {1}. Использовалась версия {2}. + + Package '{0}' {1} uses the deprecated MonoAndroid framework instead of 'net6.0-android' or later. Consider upgrading to a newer version of this package or contacting the package author. + Package '{0}' {1} uses the deprecated MonoAndroid framework instead of 'net6.0-android' or later. Consider upgrading to a newer version of this package or contacting the package author. + {0} - package id +{1} - package version + Package '{0}' {1} has a known {2} severity vulnerability, {3} У пакета "{0}" {1} есть известная уязвимость {3} (уровень серьезности: {2}) diff --git a/src/NuGet.Core/NuGet.Commands/xlf/Strings.tr.xlf b/src/NuGet.Core/NuGet.Commands/xlf/Strings.tr.xlf index 7e1727b8cc9..ea31c6e1f96 100644 --- a/src/NuGet.Core/NuGet.Commands/xlf/Strings.tr.xlf +++ b/src/NuGet.Core/NuGet.Commands/xlf/Strings.tr.xlf @@ -1496,6 +1496,12 @@ NuGet için HTTPS kaynakları gereklidir. Daha fazla bilgi için şuraya başvur {0}, {1} bağımlılığı için kapsayıcı bir alt sınır sağlamıyor. Bunun yerine {2} çözümlendi. + + Package '{0}' {1} uses the deprecated MonoAndroid framework instead of 'net6.0-android' or later. Consider upgrading to a newer version of this package or contacting the package author. + Package '{0}' {1} uses the deprecated MonoAndroid framework instead of 'net6.0-android' or later. Consider upgrading to a newer version of this package or contacting the package author. + {0} - package id +{1} - package version + Package '{0}' {1} has a known {2} severity vulnerability, {3} '{0}' {1} paketinde önem derecesi {2} olan bilinen bir {3} güvenlik açığı var diff --git a/src/NuGet.Core/NuGet.Commands/xlf/Strings.zh-Hans.xlf b/src/NuGet.Core/NuGet.Commands/xlf/Strings.zh-Hans.xlf index 5e0c5257fe4..5c6fe0c9664 100644 --- a/src/NuGet.Core/NuGet.Commands/xlf/Strings.zh-Hans.xlf +++ b/src/NuGet.Core/NuGet.Commands/xlf/Strings.zh-Hans.xlf @@ -1496,6 +1496,12 @@ NuGet 需要 HTTPS 源。有关详细信息,请参阅 https://aka.ms/nuget-htt {0} 不提供依赖项 {1} 的下限(含)。已改为解析 {2}。 + + Package '{0}' {1} uses the deprecated MonoAndroid framework instead of 'net6.0-android' or later. Consider upgrading to a newer version of this package or contacting the package author. + Package '{0}' {1} uses the deprecated MonoAndroid framework instead of 'net6.0-android' or later. Consider upgrading to a newer version of this package or contacting the package author. + {0} - package id +{1} - package version + Package '{0}' {1} has a known {2} severity vulnerability, {3} 包 "{0}" {1} 具有已知的 {2} 严重性漏洞,{3} diff --git a/src/NuGet.Core/NuGet.Commands/xlf/Strings.zh-Hant.xlf b/src/NuGet.Core/NuGet.Commands/xlf/Strings.zh-Hant.xlf index 806cd5bf554..beaead806a1 100644 --- a/src/NuGet.Core/NuGet.Commands/xlf/Strings.zh-Hant.xlf +++ b/src/NuGet.Core/NuGet.Commands/xlf/Strings.zh-Hant.xlf @@ -1496,6 +1496,12 @@ NuGet 需要 HTTPS 來源。參閱 https://aka.ms/nuget-https-everywhere 以取 {0}未提供相依性 {1} 的內含下限。已改為解析 {2}。 + + Package '{0}' {1} uses the deprecated MonoAndroid framework instead of 'net6.0-android' or later. Consider upgrading to a newer version of this package or contacting the package author. + Package '{0}' {1} uses the deprecated MonoAndroid framework instead of 'net6.0-android' or later. Consider upgrading to a newer version of this package or contacting the package author. + {0} - package id +{1} - package version + Package '{0}' {1} has a known {2} severity vulnerability, {3} 套件 '{0}' {1} 具有已知的 {2} 嚴重性弱點,{3}。 diff --git a/src/NuGet.Core/NuGet.Common/Errors/NuGetLogCode.cs b/src/NuGet.Core/NuGet.Common/Errors/NuGetLogCode.cs index 29607d6a0fd..bf31ac6f961 100644 --- a/src/NuGet.Core/NuGet.Common/Errors/NuGetLogCode.cs +++ b/src/NuGet.Core/NuGet.Common/Errors/NuGetLogCode.cs @@ -381,7 +381,8 @@ public enum NuGetLogCode NU1702 = 1702, /// - /// MacCatalyst platform fell back to xamarin.ios - Added in 6.0, removed in 6.1. + /// Package uses a deprecated legacy Xamarin framework (e.g. MonoAndroid) instead of a modern .NET TFM. + /// Originally added in 6.0 for MacCatalyst/Xamarin.iOS (removed in 6.1), reused for MonoAndroid in 11.0. /// NU1703 = 1703, diff --git a/test/NuGet.Core.Tests/NuGet.Commands.Test/RestoreCommandTests/MonoAndroidDeprecationTests.cs b/test/NuGet.Core.Tests/NuGet.Commands.Test/RestoreCommandTests/MonoAndroidDeprecationTests.cs new file mode 100644 index 00000000000..aea50ceabbf --- /dev/null +++ b/test/NuGet.Core.Tests/NuGet.Commands.Test/RestoreCommandTests/MonoAndroidDeprecationTests.cs @@ -0,0 +1,266 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#nullable disable + +using System.Linq; +using System.Threading.Tasks; +using FluentAssertions; +using NuGet.Common; +using NuGet.Frameworks; +using NuGet.Packaging; +using NuGet.ProjectModel; +using NuGet.Test.Utility; +using NuGet.Versioning; +using Xunit; + +namespace NuGet.Commands.Test.RestoreCommandTests +{ + public class MonoAndroidDeprecationTests + { + #region ShouldCheck tests + + [Theory] + [InlineData("net11.0-android35.0", "11.0.100", true, true, "net11.0-android with SdkLevel 11")] + [InlineData("net12.0-android35.0", "11.0.100", true, true, "net12.0-android with SdkLevel 11")] + [InlineData("net10.0-android35.0", "11.0.100", true, false, "net10.0-android below version threshold")] + [InlineData("net6.0-android31.0", "11.0.100", true, false, "net6.0-android below version threshold")] + [InlineData("net11.0-android35.0", "10.0.100", true, false, "SdkAnalysisLevel too old")] + [InlineData("net11.0-ios18.0", "11.0.100", true, false, "iOS platform, not android")] + [InlineData("net11.0", "11.0.100", true, false, "no platform")] + public void ShouldCheck_WithSdkAnalysisLevel_ReturnsExpected( + string frameworkString, + string sdkAnalysisLevel, + bool usingMicrosoftNETSdk, + bool expected, + string because) + { + var spec = CreatePackageSpec(frameworkString); + spec.RestoreMetadata.SdkAnalysisLevel = NuGetVersion.Parse(sdkAnalysisLevel); + spec.RestoreMetadata.UsingMicrosoftNETSdk = usingMicrosoftNETSdk; + var framework = NuGetFramework.Parse(frameworkString); + + MonoAndroidDeprecation.ShouldCheck(spec, framework).Should().Be(expected, because); + } + + [Fact] + public void ShouldCheck_NullRestoreMetadata_ReturnsFalse() + { + var spec = new PackageSpec(); + spec.RestoreMetadata = null; + var framework = NuGetFramework.Parse("net11.0-android35.0"); + + MonoAndroidDeprecation.ShouldCheck(spec, framework).Should().BeFalse(); + } + + [Fact] + public void ShouldCheck_NullSdkAnalysisLevel_UsingMicrosoftNETSdk_ReturnsFalse() + { + var spec = CreatePackageSpec("net11.0-android35.0"); + spec.RestoreMetadata.SdkAnalysisLevel = null; + spec.RestoreMetadata.UsingMicrosoftNETSdk = true; + var framework = NuGetFramework.Parse("net11.0-android35.0"); + + MonoAndroidDeprecation.ShouldCheck(spec, framework).Should().BeFalse(); + } + + [Fact] + public void ShouldCheck_NullSdkAnalysisLevel_NotUsingMicrosoftNETSdk_ReturnsTrue() + { + var spec = CreatePackageSpec("net11.0-android35.0"); + spec.RestoreMetadata.SdkAnalysisLevel = null; + spec.RestoreMetadata.UsingMicrosoftNETSdk = false; + var framework = NuGetFramework.Parse("net11.0-android35.0"); + + MonoAndroidDeprecation.ShouldCheck(spec, framework).Should().BeTrue(); + } + + #endregion + + #region IsMonoAndroidFramework tests + + [Fact] + public void IsMonoAndroidFramework_MonoAndroid_ReturnsTrue() + { + var framework = NuGetFramework.Parse("monoandroid10.0"); + + MonoAndroidDeprecation.IsMonoAndroidFramework(framework).Should().BeTrue(); + } + + [Fact] + public void IsMonoAndroidFramework_MonoAndroidNoVersion_ReturnsTrue() + { + var framework = new NuGetFramework(FrameworkConstants.FrameworkIdentifiers.MonoAndroid); + + MonoAndroidDeprecation.IsMonoAndroidFramework(framework).Should().BeTrue(); + } + + [Fact] + public void IsMonoAndroidFramework_NetCoreApp_ReturnsFalse() + { + var framework = NuGetFramework.Parse("net6.0-android31.0"); + + MonoAndroidDeprecation.IsMonoAndroidFramework(framework).Should().BeFalse(); + } + + [Fact] + public void IsMonoAndroidFramework_NetStandard_ReturnsFalse() + { + var framework = NuGetFramework.Parse("netstandard2.0"); + + MonoAndroidDeprecation.IsMonoAndroidFramework(framework).Should().BeFalse(); + } + + [Fact] + public void IsMonoAndroidFramework_Null_ReturnsFalse() + { + MonoAndroidDeprecation.IsMonoAndroidFramework(null).Should().BeFalse(); + } + + #endregion + + #region Integration tests + + [Fact] + public async Task Restore_Net11Android_MonoAndroidPackage_SdkLevel11_EmitsNU1703() + { + // Arrange + using var pathContext = new SimpleTestPathContext(); + + var packageA = new SimpleTestPackageContext("a", "1.0.0"); + packageA.AddFile("lib/monoandroid10.0/a.dll"); + + await SimpleTestPackageUtility.CreateFolderFeedV3Async( + pathContext.PackageSource, + PackageSaveMode.Defaultv3, + packageA); + + var spec = ProjectTestHelpers.GetPackageSpec("Project1", + pathContext.SolutionRoot, + framework: "net11.0-android35.0", + dependencyName: "a"); + spec.RestoreMetadata.SdkAnalysisLevel = NuGetVersion.Parse("11.0.100"); + spec.RestoreMetadata.UsingMicrosoftNETSdk = true; + + var logger = new TestLogger(); + var command = new RestoreCommand(ProjectTestHelpers.CreateRestoreRequest(pathContext, logger, spec)); + + // Act + var result = await command.ExecuteAsync(); + + // Assert + result.Success.Should().BeTrue(because: logger.ShowMessages()); + result.LockFile.LogMessages.Should().HaveCount(1); + result.LockFile.LogMessages[0].Code.Should().Be(NuGetLogCode.NU1703); + result.LockFile.LogMessages[0].Level.Should().Be(LogLevel.Warning); + result.LockFile.LogMessages[0].LibraryId.Should().Be("a"); + result.LockFile.LogMessages[0].Message.Should().Contain("MonoAndroid"); + result.LockFile.LogMessages[0].TargetGraphs.Should().HaveCount(1); + result.LockFile.LogMessages[0].TargetGraphs[0].Should().Be("net11.0-android35.0"); + logger.Errors.Should().Be(0); + logger.Warnings.Should().Be(1); + } + + [Fact] + public async Task Restore_Net11Android_MultiplePackages_OnlyMonoAndroidPackageGetsNU1703() + { + // One package with monoandroid, one with netstandard - only monoandroid package should warn + using var pathContext = new SimpleTestPathContext(); + + var packageA = new SimpleTestPackageContext("a", "1.0.0"); + packageA.AddFile("lib/monoandroid10.0/a.dll"); + + var packageB = new SimpleTestPackageContext("b", "2.0.0"); + packageB.AddFile("lib/netstandard2.0/b.dll"); + + await SimpleTestPackageUtility.CreateFolderFeedV3Async( + pathContext.PackageSource, + PackageSaveMode.Defaultv3, + packageA, + packageB); + + var spec = ProjectTestHelpers.GetPackageSpec("Project1", + pathContext.SolutionRoot, + framework: "net11.0-android35.0", + dependencyName: "a"); + // Add second dependency + var tfi = spec.TargetFrameworks[0]; + tfi.Dependencies.Add(new LibraryModel.LibraryDependency + { + LibraryRange = new LibraryModel.LibraryRange("b", VersionRange.Parse("2.0.0"), LibraryModel.LibraryDependencyTarget.Package) + }); + + spec.RestoreMetadata.SdkAnalysisLevel = NuGetVersion.Parse("11.0.100"); + spec.RestoreMetadata.UsingMicrosoftNETSdk = true; + + var logger = new TestLogger(); + var command = new RestoreCommand(ProjectTestHelpers.CreateRestoreRequest(pathContext, logger, spec)); + + // Act + var result = await command.ExecuteAsync(); + + // Assert + result.Success.Should().BeTrue(because: logger.ShowMessages()); + result.LockFile.LogMessages.Where(m => m.Code == NuGetLogCode.NU1703).Should().HaveCount(1); + result.LockFile.LogMessages.Single(m => m.Code == NuGetLogCode.NU1703).LibraryId.Should().Be("a"); + logger.Warnings.Should().Be(1); + } + + [Fact] + public async Task Restore_Net11Android_MonoAndroidPackage_SdkLevel11_NU1703_PackageLevelSuppression() + { + // NoWarn on the package dependency itself should suppress the warning + using var pathContext = new SimpleTestPathContext(); + + var packageA = new SimpleTestPackageContext("a", "1.0.0"); + packageA.AddFile("lib/monoandroid10.0/a.dll"); + + await SimpleTestPackageUtility.CreateFolderFeedV3Async( + pathContext.PackageSource, + PackageSaveMode.Defaultv3, + packageA); + + var spec = ProjectTestHelpers.GetPackageSpec("Project1", + pathContext.SolutionRoot, + framework: "net11.0-android35.0", + dependencyName: "a"); + spec.RestoreMetadata.SdkAnalysisLevel = NuGetVersion.Parse("11.0.100"); + spec.RestoreMetadata.UsingMicrosoftNETSdk = true; + + // Reconstruct the dependency with package-level NoWarn for NU1703 + var oldTfi = spec.TargetFrameworks[0]; + var oldDep = oldTfi.Dependencies[0]; + var newDep = new LibraryModel.LibraryDependency(oldDep) + { + NoWarn = System.Collections.Immutable.ImmutableArray.Create(NuGetLogCode.NU1703) + }; + spec.TargetFrameworks[0] = new TargetFrameworkInformation(oldTfi) + { + Dependencies = System.Collections.Immutable.ImmutableArray.Create(newDep) + }; + + var logger = new TestLogger(); + var command = new RestoreCommand(ProjectTestHelpers.CreateRestoreRequest(pathContext, logger, spec)); + + // Act + var result = await command.ExecuteAsync(); + + // Assert + result.Success.Should().BeTrue(because: logger.ShowMessages()); + result.LockFile.LogMessages.Should().NotContain(m => m.Code == NuGetLogCode.NU1703); + logger.Warnings.Should().Be(0); + } + + #endregion + + #region Helpers + + private static PackageSpec CreatePackageSpec(string framework) + { + var spec = ProjectTestHelpers.GetPackageSpec("TestProject", @"C:\", framework); + return spec; + } + + #endregion + } +}