diff --git a/src/NuGet.Core/NuGet.Commands/RestoreCommand/LockFileBuilder.cs b/src/NuGet.Core/NuGet.Commands/RestoreCommand/LockFileBuilder.cs
index d46e8702cb0..cffbfb0394d 100644
--- a/src/NuGet.Core/NuGet.Commands/RestoreCommand/LockFileBuilder.cs
+++ b/src/NuGet.Core/NuGet.Commands/RestoreCommand/LockFileBuilder.cs
@@ -186,6 +186,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;
@@ -280,6 +282,28 @@ public LockFile CreateLockFile(LockFile previousLockFile,
librariesWithWarnings.Add(library);
}
}
+
+ // Log NU1703 warning if the package uses the deprecated MonoAndroid framework
+ if (checkMonoAndroidDeprecation
+ && !librariesWithWarnings.Contains(library)
+ && MonoAndroidDeprecation.UsesMonoAndroidFramework(targetLibrary))
+ {
+ 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
+ librariesWithWarnings.Add(library);
+ }
}
}
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..d4c5a449a3e
--- /dev/null
+++ b/src/NuGet.Core/NuGet.Commands/RestoreCommand/MonoAndroidDeprecation.cs
@@ -0,0 +1,84 @@
+// 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 System.Collections.Generic;
+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 lock file target library uses the deprecated MonoAndroid framework
+ /// by inspecting the paths of compile-time and runtime assemblies.
+ ///
+ /// The lock file target library to check.
+ /// True if the library uses MonoAndroid framework assets.
+ internal static bool UsesMonoAndroidFramework(LockFileTargetLibrary library)
+ {
+ return ContainsMonoAndroidItem(library.CompileTimeAssemblies)
+ || ContainsMonoAndroidItem(library.RuntimeAssemblies);
+ }
+
+ private static bool ContainsMonoAndroidItem(IList items)
+ {
+ for (int i = 0; i < items.Count; i++)
+ {
+ string path = items[i].Path;
+
+ // Paths are like "lib/monoandroid10.0/Assembly.dll" or "ref/monoandroid10.0/Assembly.dll"
+ // Extract the framework folder segment (between first and second '/').
+ int firstSlash = path.IndexOf('/');
+ if (firstSlash >= 0)
+ {
+ int secondSlash = path.IndexOf('/', firstSlash + 1);
+ if (secondSlash > firstSlash + 1)
+ {
+ var folderName = path.AsSpan(firstSlash + 1, secondSlash - firstSlash - 1);
+ if (folderName.StartsWith("monoandroid".AsSpan(), StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/src/NuGet.Core/NuGet.Commands/Strings.Designer.cs b/src/NuGet.Core/NuGet.Commands/Strings.Designer.cs
index 43b322ddfd6..60358e7d295 100644
--- a/src/NuGet.Core/NuGet.Commands/Strings.Designer.cs
+++ b/src/NuGet.Core/NuGet.Commands/Strings.Designer.cs
@@ -2611,6 +2611,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 169ac0ec224..d9058496a79 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 10a708756fe..8797f00ad14 100644
--- a/src/NuGet.Core/NuGet.Commands/Utility/SdkAnalysisLevelMinimums.cs
+++ b/src/NuGet.Core/NuGet.Commands/Utility/SdkAnalysisLevelMinimums.cs
@@ -33,6 +33,14 @@ internal static class SdkAnalysisLevelMinimums
///
internal static readonly NuGetVersion V10_0_300 = new("10.0.300");
+ ///
+ /// Minimum SDK Analysis Level required for:
+ ///
+ /// warning when packages use the deprecated MonoAndroid framework
+ ///
+ ///
+ internal static readonly NuGetVersion V11_0_100 = new("11.0.100");
+
///
/// Determines whether the feature is enabled based on the SDK analysis level.
///
diff --git a/src/NuGet.Core/NuGet.Commands/xlf/Strings.cs.xlf b/src/NuGet.Core/NuGet.Commands/xlf/Strings.cs.xlf
index 8d045dbbf49..b28f485e277 100644
--- a/src/NuGet.Core/NuGet.Commands/xlf/Strings.cs.xlf
+++ b/src/NuGet.Core/NuGet.Commands/xlf/Strings.cs.xlf
@@ -1486,6 +1486,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 fb51958d874..71f29e03593 100644
--- a/src/NuGet.Core/NuGet.Commands/xlf/Strings.de.xlf
+++ b/src/NuGet.Core/NuGet.Commands/xlf/Strings.de.xlf
@@ -1486,6 +1486,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 08e8a0537a0..321d8a27f73 100644
--- a/src/NuGet.Core/NuGet.Commands/xlf/Strings.es.xlf
+++ b/src/NuGet.Core/NuGet.Commands/xlf/Strings.es.xlf
@@ -1486,6 +1486,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 689c429363f..4b7710be0c5 100644
--- a/src/NuGet.Core/NuGet.Commands/xlf/Strings.fr.xlf
+++ b/src/NuGet.Core/NuGet.Commands/xlf/Strings.fr.xlf
@@ -1486,6 +1486,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 af232eb51ff..25c0ee6cb3d 100644
--- a/src/NuGet.Core/NuGet.Commands/xlf/Strings.it.xlf
+++ b/src/NuGet.Core/NuGet.Commands/xlf/Strings.it.xlf
@@ -1486,6 +1486,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 a9849704ef0..d110154ef19 100644
--- a/src/NuGet.Core/NuGet.Commands/xlf/Strings.ja.xlf
+++ b/src/NuGet.Core/NuGet.Commands/xlf/Strings.ja.xlf
@@ -1486,6 +1486,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 a8367bf4303..ebb28c38cd8 100644
--- a/src/NuGet.Core/NuGet.Commands/xlf/Strings.ko.xlf
+++ b/src/NuGet.Core/NuGet.Commands/xlf/Strings.ko.xlf
@@ -1486,6 +1486,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 19335382ff2..51fa6be9d6e 100644
--- a/src/NuGet.Core/NuGet.Commands/xlf/Strings.pl.xlf
+++ b/src/NuGet.Core/NuGet.Commands/xlf/Strings.pl.xlf
@@ -1486,6 +1486,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 a6477b4eaed..743a65d5633 100644
--- a/src/NuGet.Core/NuGet.Commands/xlf/Strings.pt-BR.xlf
+++ b/src/NuGet.Core/NuGet.Commands/xlf/Strings.pt-BR.xlf
@@ -1486,6 +1486,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 ec691099784..81aaea4e8db 100644
--- a/src/NuGet.Core/NuGet.Commands/xlf/Strings.ru.xlf
+++ b/src/NuGet.Core/NuGet.Commands/xlf/Strings.ru.xlf
@@ -1486,6 +1486,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 e7d3d260263..b4f715cbc44 100644
--- a/src/NuGet.Core/NuGet.Commands/xlf/Strings.tr.xlf
+++ b/src/NuGet.Core/NuGet.Commands/xlf/Strings.tr.xlf
@@ -1486,6 +1486,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 fa45230aee2..cd908393e9e 100644
--- a/src/NuGet.Core/NuGet.Commands/xlf/Strings.zh-Hans.xlf
+++ b/src/NuGet.Core/NuGet.Commands/xlf/Strings.zh-Hans.xlf
@@ -1486,6 +1486,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 519e11a6bce..278fab0c375 100644
--- a/src/NuGet.Core/NuGet.Commands/xlf/Strings.zh-Hant.xlf
+++ b/src/NuGet.Core/NuGet.Commands/xlf/Strings.zh-Hant.xlf
@@ -1486,6 +1486,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 861fd1d7ac5..60f9b5b5017 100644
--- a/src/NuGet.Core/NuGet.Common/Errors/NuGetLogCode.cs
+++ b/src/NuGet.Core/NuGet.Common/Errors/NuGetLogCode.cs
@@ -376,7 +376,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..5586575ab68
--- /dev/null
+++ b/test/NuGet.Core.Tests/NuGet.Commands.Test/RestoreCommandTests/MonoAndroidDeprecationTests.cs
@@ -0,0 +1,535 @@
+// 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.Globalization;
+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
+
+ [Fact]
+ public void ShouldCheck_Net11Android_SdkLevel11_ReturnsTrue()
+ {
+ // Arrange
+ var spec = CreatePackageSpec("net11.0-android35.0");
+ spec.RestoreMetadata.SdkAnalysisLevel = NuGetVersion.Parse("11.0.100");
+ spec.RestoreMetadata.UsingMicrosoftNETSdk = true;
+ var framework = NuGetFramework.Parse("net11.0-android35.0");
+
+ // Act & Assert
+ MonoAndroidDeprecation.ShouldCheck(spec, framework).Should().BeTrue();
+ }
+
+ [Fact]
+ public void ShouldCheck_Net12Android_SdkLevel11_ReturnsTrue()
+ {
+ // Arrange
+ var spec = CreatePackageSpec("net12.0-android35.0");
+ spec.RestoreMetadata.SdkAnalysisLevel = NuGetVersion.Parse("11.0.100");
+ spec.RestoreMetadata.UsingMicrosoftNETSdk = true;
+ var framework = NuGetFramework.Parse("net12.0-android35.0");
+
+ // Act & Assert
+ MonoAndroidDeprecation.ShouldCheck(spec, framework).Should().BeTrue();
+ }
+
+ [Fact]
+ public void ShouldCheck_Net10Android_SdkLevel11_ReturnsFalse()
+ {
+ // net10.0-android has version major 10, which is < 11
+ var spec = CreatePackageSpec("net10.0-android35.0");
+ spec.RestoreMetadata.SdkAnalysisLevel = NuGetVersion.Parse("11.0.100");
+ spec.RestoreMetadata.UsingMicrosoftNETSdk = true;
+ var framework = NuGetFramework.Parse("net10.0-android35.0");
+
+ MonoAndroidDeprecation.ShouldCheck(spec, framework).Should().BeFalse();
+ }
+
+ [Fact]
+ public void ShouldCheck_Net11Android_SdkLevel10_ReturnsFalse()
+ {
+ // SDK analysis level 10.0.100 is too old
+ var spec = CreatePackageSpec("net11.0-android35.0");
+ spec.RestoreMetadata.SdkAnalysisLevel = NuGetVersion.Parse("10.0.100");
+ spec.RestoreMetadata.UsingMicrosoftNETSdk = true;
+ var framework = NuGetFramework.Parse("net11.0-android35.0");
+
+ MonoAndroidDeprecation.ShouldCheck(spec, framework).Should().BeFalse();
+ }
+
+ [Fact]
+ public void ShouldCheck_Net11iOS_SdkLevel11_ReturnsFalse()
+ {
+ // iOS platform, not android
+ var spec = CreatePackageSpec("net11.0-ios18.0");
+ spec.RestoreMetadata.SdkAnalysisLevel = NuGetVersion.Parse("11.0.100");
+ spec.RestoreMetadata.UsingMicrosoftNETSdk = true;
+ var framework = NuGetFramework.Parse("net11.0-ios18.0");
+
+ MonoAndroidDeprecation.ShouldCheck(spec, framework).Should().BeFalse();
+ }
+
+ [Fact]
+ public void ShouldCheck_Net11_NoPlatform_SdkLevel11_ReturnsFalse()
+ {
+ // net11.0 without platform
+ var spec = CreatePackageSpec("net11.0");
+ spec.RestoreMetadata.SdkAnalysisLevel = NuGetVersion.Parse("11.0.100");
+ spec.RestoreMetadata.UsingMicrosoftNETSdk = true;
+ var framework = NuGetFramework.Parse("net11.0");
+
+ MonoAndroidDeprecation.ShouldCheck(spec, framework).Should().BeFalse();
+ }
+
+ [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()
+ {
+ // When SdkAnalysisLevel is null and UsingMicrosoftNETSdk is true, IsEnabled returns false
+ 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()
+ {
+ // When SdkAnalysisLevel is null and UsingMicrosoftNETSdk is false, IsEnabled returns true
+ // but the framework check should still apply
+ 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();
+ }
+
+ [Fact]
+ public void ShouldCheck_Net6Android_SdkLevel11_ReturnsFalse()
+ {
+ // net6.0-android has version major 6, which is < 11
+ var spec = CreatePackageSpec("net6.0-android31.0");
+ spec.RestoreMetadata.SdkAnalysisLevel = NuGetVersion.Parse("11.0.100");
+ spec.RestoreMetadata.UsingMicrosoftNETSdk = true;
+ var framework = NuGetFramework.Parse("net6.0-android31.0");
+
+ MonoAndroidDeprecation.ShouldCheck(spec, framework).Should().BeFalse();
+ }
+
+ #endregion
+
+ #region UsesMonoAndroidFramework tests
+
+ [Fact]
+ public void UsesMonoAndroidFramework_CompileTimeAssemblyWithMonoAndroid_ReturnsTrue()
+ {
+ var library = new LockFileTargetLibrary();
+ library.CompileTimeAssemblies.Add(new LockFileItem("lib/monoandroid10.0/a.dll"));
+
+ MonoAndroidDeprecation.UsesMonoAndroidFramework(library).Should().BeTrue();
+ }
+
+ [Fact]
+ public void UsesMonoAndroidFramework_RuntimeAssemblyWithMonoAndroid_ReturnsTrue()
+ {
+ var library = new LockFileTargetLibrary();
+ library.RuntimeAssemblies.Add(new LockFileItem("lib/monoandroid10.0/a.dll"));
+
+ MonoAndroidDeprecation.UsesMonoAndroidFramework(library).Should().BeTrue();
+ }
+
+ [Fact]
+ public void UsesMonoAndroidFramework_RefFolderWithMonoAndroid_ReturnsTrue()
+ {
+ var library = new LockFileTargetLibrary();
+ library.CompileTimeAssemblies.Add(new LockFileItem("ref/monoandroid10.0/a.dll"));
+
+ MonoAndroidDeprecation.UsesMonoAndroidFramework(library).Should().BeTrue();
+ }
+
+ [Fact]
+ public void UsesMonoAndroidFramework_MonoAndroidCaseInsensitive_ReturnsTrue()
+ {
+ var library = new LockFileTargetLibrary();
+ library.CompileTimeAssemblies.Add(new LockFileItem("lib/MonoAndroid10.0/a.dll"));
+
+ MonoAndroidDeprecation.UsesMonoAndroidFramework(library).Should().BeTrue();
+ }
+
+ [Fact]
+ public void UsesMonoAndroidFramework_Net6Android_ReturnsFalse()
+ {
+ var library = new LockFileTargetLibrary();
+ library.CompileTimeAssemblies.Add(new LockFileItem("lib/net6.0-android31.0/a.dll"));
+
+ MonoAndroidDeprecation.UsesMonoAndroidFramework(library).Should().BeFalse();
+ }
+
+ [Fact]
+ public void UsesMonoAndroidFramework_NetStandard_ReturnsFalse()
+ {
+ var library = new LockFileTargetLibrary();
+ library.CompileTimeAssemblies.Add(new LockFileItem("lib/netstandard2.0/a.dll"));
+
+ MonoAndroidDeprecation.UsesMonoAndroidFramework(library).Should().BeFalse();
+ }
+
+ [Fact]
+ public void UsesMonoAndroidFramework_EmptyAssemblies_ReturnsFalse()
+ {
+ var library = new LockFileTargetLibrary();
+
+ MonoAndroidDeprecation.UsesMonoAndroidFramework(library).Should().BeFalse();
+ }
+
+ [Fact]
+ public void UsesMonoAndroidFramework_MonoAndroidWithoutVersion_ReturnsTrue()
+ {
+ var library = new LockFileTargetLibrary();
+ library.CompileTimeAssemblies.Add(new LockFileItem("lib/monoandroid/a.dll"));
+
+ MonoAndroidDeprecation.UsesMonoAndroidFramework(library).Should().BeTrue();
+ }
+
+ [Fact]
+ public void UsesMonoAndroidFramework_MultipleAssemblies_OneMonoAndroid_ReturnsTrue()
+ {
+ var library = new LockFileTargetLibrary();
+ library.CompileTimeAssemblies.Add(new LockFileItem("lib/monoandroid10.0/a.dll"));
+ library.CompileTimeAssemblies.Add(new LockFileItem("lib/monoandroid10.0/b.dll"));
+
+ MonoAndroidDeprecation.UsesMonoAndroidFramework(library).Should().BeTrue();
+ }
+
+ #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");
+ logger.Errors.Should().Be(0);
+ logger.Warnings.Should().Be(1);
+ }
+
+ [Fact]
+ public async Task Restore_Net10Android_MonoAndroidPackage_SdkLevel11_DoesNotEmitNU1703()
+ {
+ // net10.0-android has version major 10, below the 11 threshold
+ 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: "net10.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().NotContain(m => m.Code == NuGetLogCode.NU1703);
+ }
+
+ [Fact]
+ public async Task Restore_Net11Android_MonoAndroidPackage_SdkLevel10_DoesNotEmitNU1703()
+ {
+ // SDK analysis level too old
+ 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("10.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().NotContain(m => m.Code == NuGetLogCode.NU1703);
+ }
+
+ [Fact]
+ public async Task Restore_Net11Android_NetAndroidPackage_SdkLevel11_DoesNotEmitNU1703()
+ {
+ // Package uses net6.0-android, not monoandroid - should not warn
+ using var pathContext = new SimpleTestPathContext();
+
+ var packageA = new SimpleTestPackageContext("a", "1.0.0");
+ packageA.AddFile("lib/net6.0-android31.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().NotContain(m => m.Code == NuGetLogCode.NU1703);
+ }
+
+ [Fact]
+ public async Task Restore_Net11iOS_MonoAndroidPackage_SdkLevel11_DoesNotEmitNU1703()
+ {
+ // iOS project, not android - should not warn even with monoandroid package
+ using var pathContext = new SimpleTestPathContext();
+
+ var packageA = new SimpleTestPackageContext("a", "1.0.0");
+ packageA.AddFile("lib/monoandroid10.0/a.dll");
+ // Also add netstandard so the package resolves for iOS
+ packageA.AddFile("lib/netstandard2.0/a.dll");
+
+ await SimpleTestPackageUtility.CreateFolderFeedV3Async(
+ pathContext.PackageSource,
+ PackageSaveMode.Defaultv3,
+ packageA);
+
+ var spec = ProjectTestHelpers.GetPackageSpec("Project1",
+ pathContext.SolutionRoot,
+ framework: "net11.0-ios18.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().NotContain(m => m.Code == NuGetLogCode.NU1703);
+ }
+
+ [Fact]
+ public async Task Restore_Net11Android_MonoAndroidPackage_SdkLevel11_NU1703_CanBeSuppressed()
+ {
+ // NoWarn for NU1703 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;
+ spec.RestoreMetadata.ProjectWideWarningProperties = new WarningProperties(
+ warningsAsErrors: new System.Collections.Generic.HashSet(),
+ noWarn: new System.Collections.Generic.HashSet { NuGetLogCode.NU1703 },
+ allWarningsAsErrors: false,
+ warningsNotAsErrors: new System.Collections.Generic.HashSet());
+
+ 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);
+ }
+
+ [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_WarningMessageFormat()
+ {
+ // Verify the exact warning message format
+ using var pathContext = new SimpleTestPathContext();
+
+ var packageA = new SimpleTestPackageContext("MyPackage", "3.2.1");
+ packageA.AddFile("lib/monoandroid10.0/MyPackage.dll");
+
+ await SimpleTestPackageUtility.CreateFolderFeedV3Async(
+ pathContext.PackageSource,
+ PackageSaveMode.Defaultv3,
+ packageA);
+
+ var spec = ProjectTestHelpers.GetPackageSpec("Project1",
+ pathContext.SolutionRoot,
+ framework: "net11.0-android35.0",
+ dependencyName: "MyPackage",
+ dependencyVersion: "3.2.1");
+ 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());
+ var logMessage = result.LockFile.LogMessages.Single(m => m.Code == NuGetLogCode.NU1703);
+ var expectedMessage = string.Format(CultureInfo.CurrentCulture,
+ Strings.Warning_MonoAndroidFrameworkDeprecated,
+ "MyPackage",
+ "3.2.1");
+ logMessage.Message.Should().Be(expectedMessage);
+ }
+
+ #endregion
+
+ #region Helpers
+
+ private static PackageSpec CreatePackageSpec(string framework)
+ {
+ var spec = ProjectTestHelpers.GetPackageSpec("TestProject", @"C:\", framework);
+ return spec;
+ }
+
+ #endregion
+ }
+}