diff --git a/Make.versions b/Make.versions
index 268562e3f22d..94658084879f 100644
--- a/Make.versions
+++ b/Make.versions
@@ -100,5 +100,46 @@ MACCATALYST_NUGET_OS_VERSION=17.2
# So we've made the decision that the default target platform version is
# always the latest target platform version.
-# .NET 7 versions are bumped using maestro.
+#
+# Here we list all the releases we support for each platform.
+#
+# Format: space-separated list of TargetFramework-OSVersion
+#
+# Example:
+#
+# SUPPORTED_API_VERSIONS_IOS=net8.0-17.0 net8.0-17.2
+#
+# This means the iOS workload shipped from the current branch supports projects with:
+# net8.0-17.0
+# and
+# net8.0-17.2
+# and even:
+# net8.0-17.0;net8.0-17.2
+#
+# When shipping support for a preview Xcode, we might add entries here for a preview release into a stable release.
+#
+# Example:
+#
+# SUPPORTED_API_VERSIONS_IOS=net9.0-18.0
+#
+# If the current branch is stable .NET 8 using Xcode 15.0 (aka iOS 17.0), this
+# would add support for trying the preview release by doing:
+#
+# net9.0-18.0
+# true
+#
+# Note that any SUPPORTED_API_VERSIONS entry below for older OS versions need a corresponding entry in
+# the eng/Version.Details.xml and eng/Versions.props files.
+#
+
+# First add the versions for the current branch. DO NOT TOUCH THIS. Add older branches below.
+
+SUPPORTED_API_VERSIONS_IOS=$(DOTNET_TFM)-$(IOS_NUGET_OS_VERSION)
+SUPPORTED_API_VERSIONS_TVOS=$(DOTNET_TFM)-$(TVOS_NUGET_OS_VERSION)
+SUPPORTED_API_VERSIONS_MACOS=$(DOTNET_TFM)-$(MACOS_NUGET_OS_VERSION)
+SUPPORTED_API_VERSIONS_MACCATALYST=$(DOTNET_TFM)-$(MACCATALYST_NUGET_OS_VERSION)
+
+# Add older versions here!
+
+# (work on adding older versions is in progress)
diff --git a/dotnet/Makefile b/dotnet/Makefile
index db439eea13e0..c9129c9319bf 100644
--- a/dotnet/Makefile
+++ b/dotnet/Makefile
@@ -163,10 +163,10 @@ $(foreach platform,$(DOTNET_PLATFORMS),$(eval $(call ImplicitNamespaceImports,$(
define SupportedTargetPlatforms
Microsoft.$(1).Sdk/targets/Microsoft.$(1).Sdk.SupportedTargetPlatforms.props: $(TOP)/builds/Versions-$(1).plist.in Makefile ./generate-target-platforms.csharp Makefile
$(Q) rm -f $$@.tmp
- $(Q) ./generate-target-platforms.csharp $(1) $$@.tmp
+ $(Q) ./generate-target-platforms.csharp $(1) "$(DOTNET_TFM)" "$$(SUPPORTED_API_VERSIONS_$(2))" $$@.tmp
$(Q) mv $$@.tmp $$@
endef
-$(foreach platform,$(DOTNET_PLATFORMS),$(eval $(call SupportedTargetPlatforms,$(platform))))
+$(foreach platform,$(DOTNET_PLATFORMS),$(eval $(call SupportedTargetPlatforms,$(platform),$(shell echo $(platform) | tr a-z A-Z))))
define WorkloadTargets
Workloads/Microsoft.NET.Sdk.$(1)/WorkloadManifest.json: Makefile $(TOP)/Make.config.inc $(GIT_DIRECTORY)/HEAD $(GIT_DIRECTORY)/index Makefile generate-workloadmanifest-json.csharp | Workloads/Microsoft.NET.Sdk.$(1)
diff --git a/dotnet/generate-target-platforms.csharp b/dotnet/generate-target-platforms.csharp
index 469a03e4d081..b7a2ed4ad18e 100755
--- a/dotnet/generate-target-platforms.csharp
+++ b/dotnet/generate-target-platforms.csharp
@@ -6,7 +6,7 @@ using System.IO;
using System.Xml;
var args = Args;
-var expectedArgumentCount = 2;
+var expectedArgumentCount = 4;
if (args.Length != expectedArgumentCount) {
Console.WriteLine ($"Need {expectedArgumentCount} arguments, got {args.Length}");
Environment.Exit (1);
@@ -15,18 +15,22 @@ if (args.Length != expectedArgumentCount) {
var idx = 0;
var platform = args [idx++];
+var dotnetTfm = args [idx++];
+var supportedApiVersions = args [idx++].Split (' ').Select (v => v.Replace (dotnetTfm + "-", "")).ToArray ();
var outputPath = args [idx++];
var plistPath = $"../builds/Versions-{platform}.plist.in"
var doc = new XmlDocument ();
doc.Load (plistPath);
-var knownVersions = doc.SelectNodes ($"/plist/dict/key[text()='KnownVersions']/following-sibling::dict[1]/key[text()='{platform}']/following-sibling::array[1]/string").Cast ().Select (v => v.InnerText).ToArray ();
var supportedTargetPlatformVersions = doc.SelectNodes ($"/plist/dict/key[text()='SupportedTargetPlatformVersions']/following-sibling::dict[1]/key[text()='{platform}']/following-sibling::array[1]/string").Cast ().Select (v => v.InnerText).ToArray ();
+var currentSupportedTPVs = supportedTargetPlatformVersions.Where (v => v.StartsWith (dotnetTfm + "-", StringComparison.Ordinal)).Select (v => v.Substring (dotnetTfm.Length + 1));
var minSdkVersionName = $"DOTNET_MIN_{platform.ToUpper ()}_SDK_VERSION";
var minSdkVersionString = File.ReadAllLines ("../Make.config").Single (v => v.StartsWith (minSdkVersionName + "=", StringComparison.Ordinal)).Substring (minSdkVersionName.Length + 1);
var minSdkVersion = Version.Parse (minSdkVersionString);
+Console.WriteLine (string.Join (";", supportedApiVersions));
+
using (TextWriter writer = new StreamWriter (outputPath)) {
writer.WriteLine ($"");
writer.WriteLine ($"");
@@ -35,7 +39,7 @@ using (TextWriter writer = new StreamWriter (outputPath)) {
foreach (var version in supportedTargetPlatformVersions) {
writer.Write ($"\t\t<{platform}SdkSupportedTargetPlatformVersion Include=\"{version}\" ");
- if (!knownVersions.Contains (version))
+ if (!supportedApiVersions.Contains (version))
writer.Write ($"DefineConstantsOnly=\"true\" ");
writer.WriteLine ("/>");
}
diff --git a/dotnet/targets/Xamarin.Shared.Sdk.targets b/dotnet/targets/Xamarin.Shared.Sdk.targets
index d975f306e11c..490aaa9d0ac3 100644
--- a/dotnet/targets/Xamarin.Shared.Sdk.targets
+++ b/dotnet/targets/Xamarin.Shared.Sdk.targets
@@ -2096,6 +2096,25 @@
+
+
+
+ <_XamarinApplicableTargetPlatformVersion Include="@(SdkSupportedTargetPlatformVersion)" Condition="'@(SdkSupportedTargetPlatformVersion)' != '' and '%(SdkSupportedTargetPlatformVersion.DefineConstantsOnly)' != 'true'" RemoveMetadata="DefineConstantsOnly" />
+ <_XamarinValidTargetPlatformVersion Include="@(_XamarinApplicableTargetPlatformVersion)" Condition="'@(_XamarinApplicableTargetPlatformVersion)' != '' and $([MSBuild]::VersionEquals(%(Identity), $(TargetPlatformVersion)))" />
+
+
+
+ <_XamarinTargetPlatformVersionSupported Condition="'$(_XamarinTargetPlatformVersionSupported)' == '' and '@(_XamarinValidTargetPlatformVersion)' != ''" >true
+ <_XamarinValidTargetPlatformVersions Condition="'@(_XamarinApplicableTargetPlatformVersion)' != ''" >@(_XamarinApplicableTargetPlatformVersion, '%0a')
+ <_XamarinValidTargetPlatformVersions Condition="'@(_XamarinApplicableTargetPlatformVersion)' == ''" >None
+
+
+
+
+
diff --git a/tests/dotnet/UnitTests/ProjectTest.cs b/tests/dotnet/UnitTests/ProjectTest.cs
index d1a66472c19c..9d34ea553b77 100644
--- a/tests/dotnet/UnitTests/ProjectTest.cs
+++ b/tests/dotnet/UnitTests/ProjectTest.cs
@@ -1,5 +1,6 @@
using System.Runtime.InteropServices;
using System.Diagnostics;
+using System.Xml;
using Mono.Cecil;
@@ -1541,15 +1542,13 @@ internal static IList RemovePostCurrentOnMacCatalyst (IList self
[TestCase (ApplePlatform.iOS)]
[TestCase (ApplePlatform.TVOS)]
[TestCase (ApplePlatform.MacOSX)]
- [Ignore ("Multi-targeting support has been temporarily reverted/postponed")]
public void InvalidTargetPlatformVersion (ApplePlatform platform)
{
Configuration.IgnoreIfIgnoredPlatform (platform);
// Pick a target platform version we don't support (such as iOS 6.66).
var targetFrameworks = Configuration.DotNetTfm + "-" + platform.AsString ().ToLowerInvariant () + "6.66";
- var supportedApiVersion = Configuration.GetVariableArray ($"SUPPORTED_API_VERSIONS_{platform.AsString ().ToUpperInvariant ()}");
- var validTargetPlatformVersions = supportedApiVersion.Where (v => v.StartsWith (Configuration.DotNetTfm + "-", StringComparison.Ordinal)).Select (v => v.Substring (Configuration.DotNetTfm.Length + 1));
+ var supportedApiVersions = GetSupportedApiVersions (platform);
var project = "MultiTargetingLibrary";
var project_path = GetProjectPath (project, platform: platform);
@@ -1558,8 +1557,71 @@ public void InvalidTargetPlatformVersion (ApplePlatform platform)
properties ["cmdline:AllTheTargetFrameworks"] = targetFrameworks;
var rv = DotNet.AssertBuildFailure (project_path, properties);
var errors = BinLog.GetBuildLogErrors (rv.BinLogPath).ToArray ();
- Assert.AreEqual (1, errors.Length, "Error count");
- Assert.AreEqual ($"6.66 is not a valid TargetPlatformVersion for {platform.AsString ()}. Valid versions include:\n{string.Join ('\n', validTargetPlatformVersions)}", errors [0].Message, "Error message");
+ AssertErrorMessages (errors, $"6.66 is not a valid TargetPlatformVersion for {platform.AsString ()}. Valid versions include:\n{string.Join ('\n', supportedApiVersions)}");
+ }
+
+ [Test]
+ [TestCase (ApplePlatform.MacCatalyst)]
+ [TestCase (ApplePlatform.iOS)]
+ [TestCase (ApplePlatform.TVOS)]
+ [TestCase (ApplePlatform.MacOSX)]
+ public void UnsupportedTargetPlatformVersion (ApplePlatform platform)
+ {
+ Configuration.IgnoreIfIgnoredPlatform (platform);
+
+ // Pick a target platform version that we don't really support,
+ // but don't show an error in .NET 8 because of backwards compat.
+ // The earliest target OS version should do.
+ var minSupportedOSVersion = GetSupportedTargetPlatformVersions (platform).First ();
+ var targetFrameworks = Configuration.DotNetTfm + "-" + platform.AsString ().ToLowerInvariant () + minSupportedOSVersion;
+ var supportedApiVersions = GetSupportedApiVersions (platform, isCompat: false);
+
+ var project = "MultiTargetingLibrary";
+ var project_path = GetProjectPath (project, platform: platform);
+ Clean (project_path);
+ var properties = GetDefaultProperties ();
+ properties ["cmdline:AllTheTargetFrameworks"] = targetFrameworks;
+
+ if (IsTargetPlatformVersionCompatEnabled) {
+ var rv = DotNet.AssertBuild (project_path, properties);
+ var warnings = BinLog.GetBuildLogWarnings (rv.BinLogPath).ToArray ();
+ AssertWarningMessages (warnings, $"{minSupportedOSVersion} is not a valid TargetPlatformVersion for {platform.AsString ()}. This warning will become an error in future versions of the {platform.AsString ()} workload. Valid versions include:\n{string.Join ('\n', supportedApiVersions)}");
+ } else {
+ var rv = DotNet.AssertBuildFailure (project_path, properties);
+ var errors = BinLog.GetBuildLogErrors (rv.BinLogPath).ToArray ();
+ AssertErrorMessages (errors, $"{minSupportedOSVersion} is not a valid TargetPlatformVersion for {platform.AsString ()}. Valid versions include:\n{string.Join ('\n', supportedApiVersions)}");
+ }
+ }
+
+ bool IsTargetPlatformVersionCompatEnabled {
+ get => Version.Parse (Configuration.DotNetTfm.Replace ("net", "")).Major < 9;
+ }
+
+ string [] GetSupportedApiVersions (ApplePlatform platform, bool? isCompat = null)
+ {
+ if (isCompat is null)
+ isCompat = IsTargetPlatformVersionCompatEnabled;
+ if (isCompat.Value)
+ return GetSupportedTargetPlatformVersions (platform);
+
+ var supportedApiVersions = Configuration.GetVariableArray ($"SUPPORTED_API_VERSIONS_{platform.AsString ().ToUpperInvariant ()}");
+ return supportedApiVersions
+ .Where (v => v.StartsWith (Configuration.DotNetTfm + "-", StringComparison.Ordinal))
+ .Select (v => v.Substring (Configuration.DotNetTfm.Length + 1))
+ .ToArray ();
+ }
+
+ string [] GetSupportedTargetPlatformVersions (ApplePlatform platform)
+ {
+ var plistPath = Path.Combine (Configuration.SourceRoot, "builds", $"Versions-{platform.AsString ()}.plist.in");
+ var doc = new XmlDocument ();
+ doc.Load (plistPath);
+ var query = $"/plist/dict/key[text()='SupportedTargetPlatformVersions']/following-sibling::dict[1]/key[text()='{platform.AsString ()}']/following-sibling::array[1]/string";
+ return doc
+ .SelectNodes (query)!
+ .Cast ()
+ .Select (v => v.InnerText)
+ .ToArray ();
}
[Test]
diff --git a/tests/dotnet/UnitTests/TestBaseClass.cs b/tests/dotnet/UnitTests/TestBaseClass.cs
index 6d1052bdb70e..2d7829fb8bd3 100644
--- a/tests/dotnet/UnitTests/TestBaseClass.cs
+++ b/tests/dotnet/UnitTests/TestBaseClass.cs
@@ -422,23 +422,33 @@ public static void AssertErrorCount (IList errors, int count, str
Assert.Fail ($"Expected {count} errors, got {errors.Count} errors: {message}.\n\t{string.Join ("\n\t", errors.Select (v => v.Message?.TrimEnd ()))}");
}
- public static void AssertErrorMessages (IList errors, params string [] errorMessages)
+ public static void AssertWarningMessages (IList actualWarnings, params string [] expectedWarningMessages)
{
- if (errors.Count != errorMessages.Length) {
- Assert.Fail ($"Expected {errorMessages.Length} errors, got {errors.Count} errors:\n\t{string.Join ("\n\t", errors.Select (v => v.Message?.TrimEnd ()))}");
+ AssertBuildMessages ("warning", actualWarnings, expectedWarningMessages);
+ }
+
+ public static void AssertErrorMessages (IList actualErrors, params string [] expectedErrorMessages)
+ {
+ AssertBuildMessages ("error", actualErrors, expectedErrorMessages);
+ }
+
+ public static void AssertBuildMessages (string type, IList actualMessages, params string [] expectedMessages)
+ {
+ if (actualMessages.Count != expectedMessages.Length) {
+ Assert.Fail ($"Expected {expectedMessages.Length} {type}s, got {actualMessages.Count} {type}s:\n\t{string.Join ("\n\t", actualMessages.Select (v => v.Message?.TrimEnd ()))}");
return;
}
var failures = new List ();
- for (var i = 0; i < errorMessages.Length; i++) {
- if (errors [i].Message != errorMessages [i]) {
- failures.Add ($"\tUnexpected error message #{i}:\n\t\tExpected: {errorMessages [i]}\n\t\tActual: {errors [i].Message?.TrimEnd ()}");
+ for (var i = 0; i < expectedMessages.Length; i++) {
+ if (actualMessages [i].Message != expectedMessages [i]) {
+ failures.Add ($"\tUnexpected {type} message #{i}:\n\t\tExpected: {expectedMessages [i]}\n\t\tActual: {actualMessages [i].Message?.TrimEnd ()}");
}
}
if (!failures.Any ())
return;
- Assert.Fail ($"Failure when comparing error messages:\n{string.Join ("\n", failures)}\n\tAll errors:\n\t\t{string.Join ("\n\t\t", errors.Select (v => v.Message?.TrimEnd ()))}");
+ Assert.Fail ($"Failure when comparing {type} messages:\n{string.Join ("\n", failures)}\n\tAll {type}s:\n\t\t{string.Join ("\n\t\t", actualMessages.Select (v => v.Message?.TrimEnd ()))}");
}
}
}