diff --git a/Make.versions b/Make.versions index 7182fdd9abc8..701e10189530 100644 --- a/Make.versions +++ b/Make.versions @@ -71,6 +71,12 @@ WATCHOS_NUGET_OS_VERSION=11.0 MACOS_NUGET_OS_VERSION=15.0 MACCATALYST_NUGET_OS_VERSION=18.0 +# The following are the OS versions we first supported with the current .NET version. +# These versions must *not* change with minor .NET updates, only major .NET releases. +IOS_TARGET_PLATFORM_VERSION_LIBRARY=18.0 +TVOS_TARGET_PLATFORM_VERSION_LIBRARY=18.0 +MACOS_TARGET_PLATFORM_VERSION_LIBRARY=15.0 +MACCATALYST_TARGET_PLATFORM_VERSION_LIBRARY=18.0 # In theory we should define the default platform version if it's not specified in the TFM. The default should not change for a given .NET version: # * We release support for iOS 14.5 with .NET 6 @@ -100,6 +106,13 @@ MACCATALYST_NUGET_OS_VERSION=18.0 # So we've made the decision that the default target platform version is # always the latest target platform version. +# However, this turns out to be somewhat of a complication for library developers, +# because they typically don't need Xcode to build their projects, and if we auto- +# update their TargetPlatformVersion to the latest, then all their customers +# have to also update their workloads, which for some people end up being a rather +# nasty surprise (because with the above algorithm it happens without developer +# action). Thus we follow .NET's default platform version scheme for library projects: +# it won't change in minor .NET releases. # # Here we list all the releases we support for each platform. diff --git a/dotnet/Makefile b/dotnet/Makefile index a67f5289fe37..25fb75ee8e1a 100644 --- a/dotnet/Makefile +++ b/dotnet/Makefile @@ -119,7 +119,8 @@ targets/Microsoft.$(1).Sdk.Versions.props: targets/Microsoft.Sdk.Versions.templa -e "s/@PLATFORM@/$(1)/g" \ -e "s/@NUGET_VERSION_NO_METADATA@/$$($(2)_NUGET_VERSION_NO_METADATA)/g" \ -e "s/@NUGET_VERSION_FULL@/$$($(2)_NUGET_VERSION_FULL)/g" \ - -e "s/@TARGET_PLATFORM_VERSION@/$$($(2)_NUGET_OS_VERSION)/g" \ + -e "s/@TARGET_PLATFORM_VERSION_EXE@/$$($(2)_NUGET_OS_VERSION)/g" \ + -e "s/@TARGET_PLATFORM_VERSION_LIBRARY@/$$($(2)_TARGET_PLATFORM_VERSION_LIBRARY)/g" \ -e "s/@CURRENT_BRANCH@/$$(CURRENT_BRANCH_SED_ESCAPED)/g" \ -e "s/@CURRENT_HASH_LONG@/$$(CURRENT_HASH_LONG)/g" \ -e 's*@VALID_RUNTIME_IDENTIFIERS@*$(foreach rid,$(3),\n\t\t<_XamarinValidRuntimeIdentifier Include="$(rid)" Platform="$(1)" />)*' \ diff --git a/dotnet/targets/Microsoft.Sdk.Versions.template.props b/dotnet/targets/Microsoft.Sdk.Versions.template.props index d77b1f8d68c0..500353a521ae 100644 --- a/dotnet/targets/Microsoft.Sdk.Versions.template.props +++ b/dotnet/targets/Microsoft.Sdk.Versions.template.props @@ -6,7 +6,8 @@ <_ShortPackageVersion>@NUGET_VERSION_NO_METADATA@ <_PackageVersion>@NUGET_VERSION_FULL@ - <_XamarinTargetPlatformVersion>@TARGET_PLATFORM_VERSION@ + <_XamarinTargetPlatformVersionExecutable>@TARGET_PLATFORM_VERSION_EXE@ + <_XamarinTargetPlatformVersionLibrary>@TARGET_PLATFORM_VERSION_LIBRARY@ <_XamarinIsPreviewRelease>@XCODE_IS_PREVIEW@ <_XamarinDotNetVersion>@DOTNET_TFM@ <_XamarinPackSuffix>@DOTNET_TFM@_@NUGET_OS_VERSION@ diff --git a/dotnet/targets/Xamarin.Shared.Sdk.TargetFrameworkInference.props b/dotnet/targets/Xamarin.Shared.Sdk.TargetFrameworkInference.props index 0f585a4db207..3e082daf79fc 100644 --- a/dotnet/targets/Xamarin.Shared.Sdk.TargetFrameworkInference.props +++ b/dotnet/targets/Xamarin.Shared.Sdk.TargetFrameworkInference.props @@ -12,6 +12,7 @@ - $(_XamarinTargetPlatformVersion) + $(_XamarinTargetPlatformVersionExecutable) + $(_XamarinTargetPlatformVersionLibrary) diff --git a/tests/dotnet/UnitTests/ApplePlatformExtensions.cs b/tests/dotnet/UnitTests/ApplePlatformExtensions.cs index b24e66995275..48b6a6ad9707 100644 --- a/tests/dotnet/UnitTests/ApplePlatformExtensions.cs +++ b/tests/dotnet/UnitTests/ApplePlatformExtensions.cs @@ -2,10 +2,10 @@ namespace Xamarin.Utils { public static class ApplePlatformExtensionsWithVersions { - public static string ToFrameworkWithPlatformVersion (this ApplePlatform @this) + public static string ToFrameworkWithPlatformVersion (this ApplePlatform @this, bool isExecutable /* and not library */) { var netVersion = Configuration.DotNetTfm; - var targetPlatformVersion = GetTargetPlatformVersion (@this); + var targetPlatformVersion = isExecutable ? GetDefaultTargetPlatformVersionExecutable (@this) : GetDefaultTargetPlatformVersionLibrary (@this); switch (@this) { case ApplePlatform.iOS: return netVersion + "-ios" + targetPlatformVersion; @@ -20,16 +20,53 @@ public static string ToFrameworkWithPlatformVersion (this ApplePlatform @this) } } - public static string GetTargetPlatformVersion (this ApplePlatform @this) + public static string GetDefaultTargetPlatformVersionExecutable (this ApplePlatform @this) { switch (@this) { - case ApplePlatform.iOS: return SdkVersions.TargetPlatformVersioniOS; - case ApplePlatform.TVOS: return SdkVersions.TargetPlatformVersiontvOS; - case ApplePlatform.MacOSX: return SdkVersions.TargetPlatformVersionmacOS; - case ApplePlatform.MacCatalyst: return SdkVersions.TargetPlatformVersionMacCatalyst; + case ApplePlatform.iOS: return SdkVersions.TargetPlatformVersionExecutableiOS; + case ApplePlatform.TVOS: return SdkVersions.TargetPlatformVersionExecutabletvOS; + case ApplePlatform.MacOSX: return SdkVersions.TargetPlatformVersionExecutablemacOS; + case ApplePlatform.MacCatalyst: return SdkVersions.TargetPlatformVersionExecutableMacCatalyst; default: return "Unknown"; } } + + public static string GetDefaultTargetPlatformVersionLibrary (this ApplePlatform @this) + { + switch (@this) { + case ApplePlatform.iOS: return SdkVersions.TargetPlatformVersionLibraryiOS; + case ApplePlatform.TVOS: return SdkVersions.TargetPlatformVersionLibrarytvOS; + case ApplePlatform.MacOSX: return SdkVersions.TargetPlatformVersionLibrarymacOS; + case ApplePlatform.MacCatalyst: return SdkVersions.TargetPlatformVersionLibraryMacCatalyst; + default: + return "Unknown"; + } + } + } + + [TestFixture] + public class TargetFrameworkTest { + [TestCase (ApplePlatform.iOS)] + [TestCase (ApplePlatform.MacCatalyst)] + [TestCase (ApplePlatform.TVOS)] + [TestCase (ApplePlatform.MacOSX)] + public void DefaultLibraryTargetPlatformVersion (ApplePlatform platform) + { + // We might have to change the assert if the first minor OS version we release for a given .NET version is >0 (this happened for both .NET 7 and .NET 8). + Assert.That (platform.GetDefaultTargetPlatformVersionLibrary (), Does.EndWith (".0"), "Default TPV for a library must end with .0"); + } + + [TestCase (ApplePlatform.iOS)] + [TestCase (ApplePlatform.MacCatalyst)] + [TestCase (ApplePlatform.TVOS)] + [TestCase (ApplePlatform.MacOSX)] + public void MajorTargetPlatformVersion (ApplePlatform platform) + { + var vLibrary = Version.Parse (platform.GetDefaultTargetPlatformVersionLibrary ()); + var vExecutable = Version.Parse (platform.GetDefaultTargetPlatformVersionExecutable ()); + // We might have to change the assert if we release support for a new major OS version within a .NET releases (this happened for .NET 8) + Assert.AreEqual (vExecutable.Major, vLibrary.Major, "The major version must be the same between the default TPV for library and executable projects."); + } } } diff --git a/tests/dotnet/UnitTests/MlaunchTest.cs b/tests/dotnet/UnitTests/MlaunchTest.cs index 76823f08da19..e4a369159473 100644 --- a/tests/dotnet/UnitTests/MlaunchTest.cs +++ b/tests/dotnet/UnitTests/MlaunchTest.cs @@ -90,7 +90,7 @@ public void GetMlaunchRunArguments (ApplePlatform platform, string runtimeIdenti expectedArguments.Append (appPath.Substring (Path.GetDirectoryName (project_path)!.Length + 1)).Append ('/'); if (isSim) { expectedArguments.Append (" --device \""); - expectedArguments.Append (device.Replace ("%TPV%", platform.GetTargetPlatformVersion ().Replace ('.', '-'))); + expectedArguments.Append (device); expectedArguments.Append ('"'); } expectedArguments.Append ($" --wait-for-exit:true"); diff --git a/tests/dotnet/UnitTests/PackTest.cs b/tests/dotnet/UnitTests/PackTest.cs index d6d02b4e69a7..59da47dc18f4 100644 --- a/tests/dotnet/UnitTests/PackTest.cs +++ b/tests/dotnet/UnitTests/PackTest.cs @@ -62,6 +62,7 @@ public void BindingFrameworksProject (ApplePlatform platform, bool noBindingEmbe var archive = ZipFile.OpenRead (nupkg); var files = archive.Entries.Select (v => v.FullName).ToHashSet (); + var tfm = platform.ToFrameworkWithPlatformVersion (isExecutable: false); var hasSymlinks = noBindingEmbedding && (platform == ApplePlatform.MacCatalyst || platform == ApplePlatform.MacOSX); if (noBindingEmbedding) { Assert.That (archive.Entries.Count, Is.EqualTo (hasSymlinks ? 6 : 10), $"nupkg file count - {nupkg}"); @@ -71,17 +72,17 @@ public void BindingFrameworksProject (ApplePlatform platform, bool noBindingEmbe Assert.That (files, Does.Contain (project + ".nuspec"), "nuspec"); Assert.That (files, Does.Contain ("_rels/.rels"), ".rels"); Assert.That (files, Does.Contain ("[Content_Types].xml"), "[Content_Types].xml"); - Assert.That (files, Does.Contain ($"lib/{platform.ToFrameworkWithPlatformVersion ()}/{project}.dll"), $"{project}.dll"); + Assert.That (files, Does.Contain ($"lib/{tfm}/{project}.dll"), $"{project}.dll"); Assert.That (files, Has.Some.Matches (v => v.StartsWith ("package/services/metadata/core-properties/", StringComparison.Ordinal) && v.EndsWith (".psmdcp", StringComparison.Ordinal)), "psmdcp"); if (noBindingEmbedding) { if (hasSymlinks) { - Assert.That (files, Does.Contain ($"lib/{platform.ToFrameworkWithPlatformVersion ()}/{project}.resources.zip"), $"{project}.resources.zip"); + Assert.That (files, Does.Contain ($"lib/{tfm}/{project}.resources.zip"), $"{project}.resources.zip"); } else { - Assert.That (files, Does.Contain ($"lib/{platform.ToFrameworkWithPlatformVersion ()}/{project}.resources/XStaticArTest.framework/XStaticArTest"), $"XStaticArTest.framework/XStaticArTest"); - Assert.That (files, Does.Contain ($"lib/{platform.ToFrameworkWithPlatformVersion ()}/{project}.resources/XStaticObjectTest.framework/XStaticObjectTest"), $"XStaticObjectTest.framework/XStaticObjectTest"); - Assert.That (files, Does.Contain ($"lib/{platform.ToFrameworkWithPlatformVersion ()}/{project}.resources/XTest.framework/XTest"), $"XTest.framework/XTest"); - Assert.That (files, Does.Contain ($"lib/{platform.ToFrameworkWithPlatformVersion ()}/{project}.resources/XTest.framework/Info.plist"), $"XTest.framework/Info.plist"); - Assert.That (files, Does.Contain ($"lib/{platform.ToFrameworkWithPlatformVersion ()}/{project}.resources/manifest"), $"manifest"); + Assert.That (files, Does.Contain ($"lib/{tfm}/{project}.resources/XStaticArTest.framework/XStaticArTest"), $"XStaticArTest.framework/XStaticArTest"); + Assert.That (files, Does.Contain ($"lib/{tfm}/{project}.resources/XStaticObjectTest.framework/XStaticObjectTest"), $"XStaticObjectTest.framework/XStaticObjectTest"); + Assert.That (files, Does.Contain ($"lib/{tfm}/{project}.resources/XTest.framework/XTest"), $"XTest.framework/XTest"); + Assert.That (files, Does.Contain ($"lib/{tfm}/{project}.resources/XTest.framework/Info.plist"), $"XTest.framework/Info.plist"); + Assert.That (files, Does.Contain ($"lib/{tfm}/{project}.resources/manifest"), $"manifest"); } } } @@ -127,14 +128,15 @@ public void BindingXcFrameworksProject (ApplePlatform platform, bool noBindingEm var archive = ZipFile.OpenRead (nupkg); var files = archive.Entries.Select (v => v.FullName).ToHashSet (); + var tfm = platform.ToFrameworkWithPlatformVersion (isExecutable: false); Assert.That (archive.Entries.Count, Is.EqualTo (noBindingEmbedding ? 6 : 5), $"nupkg file count - {nupkg}"); Assert.That (files, Does.Contain (assemblyName + ".nuspec"), "nuspec"); Assert.That (files, Does.Contain ("_rels/.rels"), ".rels"); Assert.That (files, Does.Contain ("[Content_Types].xml"), "[Content_Types].xml"); - Assert.That (files, Does.Contain ($"lib/{platform.ToFrameworkWithPlatformVersion ()}/{assemblyName}.dll"), $"{assemblyName}.dll"); + Assert.That (files, Does.Contain ($"lib/{tfm}/{assemblyName}.dll"), $"{assemblyName}.dll"); Assert.That (files, Has.Some.Matches (v => v.StartsWith ("package/services/metadata/core-properties/", StringComparison.Ordinal) && v.EndsWith (".psmdcp", StringComparison.Ordinal)), "psmdcp"); if (noBindingEmbedding) { - Assert.That (files, Does.Contain ($"lib/{platform.ToFrameworkWithPlatformVersion ()}/{assemblyName}.resources.zip"), $"{assemblyName}.resources.zip"); + Assert.That (files, Does.Contain ($"lib/{tfm}/{assemblyName}.resources.zip"), $"{assemblyName}.resources.zip"); } } @@ -164,7 +166,7 @@ public void LibraryProject (ApplePlatform platform) Assert.That (files, Does.Contain (project + ".nuspec"), "nuspec"); Assert.That (files, Does.Contain ("_rels/.rels"), ".rels"); Assert.That (files, Does.Contain ("[Content_Types].xml"), "[Content_Types].xml"); - Assert.That (files, Does.Contain ($"lib/{platform.ToFrameworkWithPlatformVersion ()}/{project}.dll"), $"{project}.dll"); + Assert.That (files, Does.Contain ($"lib/{platform.ToFrameworkWithPlatformVersion (isExecutable: false)}/{project}.dll"), $"{project}.dll"); Assert.That (files, Has.Some.Matches (v => v.StartsWith ("package/services/metadata/core-properties/", StringComparison.Ordinal) && v.EndsWith (".psmdcp", StringComparison.Ordinal)), "psmdcp"); } diff --git a/tests/dotnet/UnitTests/XcodeProjectTests.cs b/tests/dotnet/UnitTests/XcodeProjectTests.cs index 30a7485df8d8..a3a7362f34e5 100644 --- a/tests/dotnet/UnitTests/XcodeProjectTests.cs +++ b/tests/dotnet/UnitTests/XcodeProjectTests.cs @@ -179,9 +179,10 @@ public void PackBinding (ApplePlatform platform) Assert.That (expectedNupkgOutput, Does.Exist, $"Expected pack output '{expectedNupkgOutput}' did not exist."); List zipContent = ZipHelpers.List (expectedNupkgOutput); - var expectedFxPath = $"lib/{platform.ToFrameworkWithPlatformVersion ()}/{TestName}.resources/{xcodeProjName}{platform.AsString ()}.xcframework/Info.plist"; + var tfm = platform.ToFrameworkWithPlatformVersion (isExecutable: false); + var expectedFxPath = $"lib/{tfm}/{TestName}.resources/{xcodeProjName}{platform.AsString ()}.xcframework/Info.plist"; if (platform == ApplePlatform.MacOSX || platform == ApplePlatform.MacCatalyst) { - zipContent = ZipHelpers.ListInnerZip (expectedNupkgOutput, $"lib/{platform.ToFrameworkWithPlatformVersion ()}/{TestName}.resources.zip"); + zipContent = ZipHelpers.ListInnerZip (expectedNupkgOutput, $"lib/{tfm}/{TestName}.resources.zip"); expectedFxPath = $"{xcodeProjName}{platform.AsString ()}.xcframework/Info.plist"; } Assert.Contains (expectedFxPath, zipContent, $"Expected xcframework output was not found in '{expectedNupkgOutput}'."); diff --git a/tools/common/Make.common b/tools/common/Make.common index 530d21d7e4cd..0a68d8fc8221 100644 --- a/tools/common/Make.common +++ b/tools/common/Make.common @@ -50,10 +50,15 @@ $(abspath ../common/SdkVersions.cs): ../common/SdkVersions.in.cs Makefile $(TOP) -e "s/@MACOS_NUGET_VERSION@/$(MACOS_NUGET_VERSION)/g" \ -e "s/@MACOS_NUGET_REVISION@/$(NUGET_PRERELEASE_IDENTIFIER)$(MACOS_NUGET_COMMIT_DISTANCE)$(NUGET_BUILD_METADATA)/g" \ \ - -e "s/@TARGET_PLATFORM_VERSION_IOS@/$(IOS_NUGET_OS_VERSION)/g" \ - -e "s/@TARGET_PLATFORM_VERSION_TVOS@/$(TVOS_NUGET_OS_VERSION)/g" \ - -e "s/@TARGET_PLATFORM_VERSION_MACOS@/$(MACOS_NUGET_OS_VERSION)/g" \ - -e "s/@TARGET_PLATFORM_VERSION_MACCATALYST@/$(MACCATALYST_NUGET_OS_VERSION)/g" \ + -e "s/@TARGET_PLATFORM_VERSION_EXECUTABLE_IOS@/$(IOS_NUGET_OS_VERSION)/g" \ + -e "s/@TARGET_PLATFORM_VERSION_EXECUTABLE_TVOS@/$(TVOS_NUGET_OS_VERSION)/g" \ + -e "s/@TARGET_PLATFORM_VERSION_EXECUTABLE_MACOS@/$(MACOS_NUGET_OS_VERSION)/g" \ + -e "s/@TARGET_PLATFORM_VERSION_EXECUTABLE_MACCATALYST@/$(MACCATALYST_NUGET_OS_VERSION)/g" \ + \ + -e "s/@TARGET_PLATFORM_VERSION_LIBRARY_IOS@/$(IOS_TARGET_PLATFORM_VERSION_LIBRARY)/g" \ + -e "s/@TARGET_PLATFORM_VERSION_LIBRARY_TVOS@/$(TVOS_TARGET_PLATFORM_VERSION_LIBRARY)/g" \ + -e "s/@TARGET_PLATFORM_VERSION_LIBRARY_MACOS@/$(MACOS_TARGET_PLATFORM_VERSION_LIBRARY)/g" \ + -e "s/@TARGET_PLATFORM_VERSION_LIBRARY_MACCATALYST@/$(MACCATALYST_TARGET_PLATFORM_VERSION_LIBRARY)/g" \ \ -e "s/@PRODUCT_HASH@/$(CURRENT_HASH_LONG)/g" \ \ diff --git a/tools/common/SdkVersions.cs b/tools/common/SdkVersions.cs index 1413ffbded8b..fdddeec68821 100644 --- a/tools/common/SdkVersions.cs +++ b/tools/common/SdkVersions.cs @@ -57,10 +57,15 @@ static class SdkVersions { public const string MaxWatchDeploymentTarget = "11.0"; public const string MaxTVOSDeploymentTarget = "18.0"; - public const string TargetPlatformVersioniOS = "18.0"; - public const string TargetPlatformVersiontvOS = "18.0"; - public const string TargetPlatformVersionmacOS = "15.0"; - public const string TargetPlatformVersionMacCatalyst = "18.0"; + public const string TargetPlatformVersionExecutableiOS = "18.0"; + public const string TargetPlatformVersionExecutabletvOS = "18.0"; + public const string TargetPlatformVersionExecutablemacOS = "15.0"; + public const string TargetPlatformVersionExecutableMacCatalyst = "18.0"; + + public const string TargetPlatformVersionLibraryiOS = "18.0"; + public const string TargetPlatformVersionLibrarytvOS = "18.0"; + public const string TargetPlatformVersionLibrarymacOS = "15.0"; + public const string TargetPlatformVersionLibraryMacCatalyst = "18.0"; public static Version OSXVersion { get { return new Version (OSX); } } public static Version iOSVersion { get { return new Version (iOS); } } diff --git a/tools/common/SdkVersions.in.cs b/tools/common/SdkVersions.in.cs index 78d07d826860..77761f44bc53 100644 --- a/tools/common/SdkVersions.in.cs +++ b/tools/common/SdkVersions.in.cs @@ -57,10 +57,15 @@ static class SdkVersions { public const string MaxWatchDeploymentTarget = "@MAX_WATCH_DEPLOYMENT_TARGET@"; public const string MaxTVOSDeploymentTarget = "@MAX_TVOS_DEPLOYMENT_TARGET@"; - public const string TargetPlatformVersioniOS = "@TARGET_PLATFORM_VERSION_IOS@"; - public const string TargetPlatformVersiontvOS = "@TARGET_PLATFORM_VERSION_TVOS@"; - public const string TargetPlatformVersionmacOS = "@TARGET_PLATFORM_VERSION_MACOS@"; - public const string TargetPlatformVersionMacCatalyst = "@TARGET_PLATFORM_VERSION_MACCATALYST@"; + public const string TargetPlatformVersionExecutableiOS = "@TARGET_PLATFORM_VERSION_EXECUTABLE_IOS@"; + public const string TargetPlatformVersionExecutabletvOS = "@TARGET_PLATFORM_VERSION_EXECUTABLE_TVOS@"; + public const string TargetPlatformVersionExecutablemacOS = "@TARGET_PLATFORM_VERSION_EXECUTABLE_MACOS@"; + public const string TargetPlatformVersionExecutableMacCatalyst = "@TARGET_PLATFORM_VERSION_EXECUTABLE_MACCATALYST@"; + + public const string TargetPlatformVersionLibraryiOS = "@TARGET_PLATFORM_VERSION_LIBRARY_IOS@"; + public const string TargetPlatformVersionLibrarytvOS = "@TARGET_PLATFORM_VERSION_LIBRARY_TVOS@"; + public const string TargetPlatformVersionLibrarymacOS = "@TARGET_PLATFORM_VERSION_LIBRARY_MACOS@"; + public const string TargetPlatformVersionLibraryMacCatalyst = "@TARGET_PLATFORM_VERSION_LIBRARY_MACCATALYST@"; public static Version OSXVersion { get { return new Version (OSX); } } public static Version iOSVersion { get { return new Version (iOS); } }