Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 42 additions & 1 deletion Make.versions
Original file line number Diff line number Diff line change
Expand Up @@ -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:
# <TargetFramework>net8.0-17.0</TargetFramework>
# and
# <TargetFramework>net8.0-17.2</TargetFramework>
# and even:
# <TargetFrameworks>net8.0-17.0;net8.0-17.2</TargetFrameworks>
#
# 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:
#
# <TargetFramework>net9.0-18.0</TargetFramework>
# <EnablePreviewFeatures>true</EnablePreviewFeatures>
#
# 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)
4 changes: 2 additions & 2 deletions dotnet/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
10 changes: 7 additions & 3 deletions dotnet/generate-target-platforms.csharp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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<XmlNode> ().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<XmlNode> ().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 ($"<!-- This file contains a generated list of the {platform} platform versions that are supported for this SDK -->");
writer.WriteLine ($"<!-- Generation script: https://github.com/xamarin/xamarin-macios/blob/main/dotnet/generate-target-platforms.csharp -->");
Expand All @@ -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 ("/>");
}
Expand Down
19 changes: 19 additions & 0 deletions dotnet/targets/Xamarin.Shared.Sdk.targets
Original file line number Diff line number Diff line change
Expand Up @@ -2096,6 +2096,25 @@
<Error Condition="'$(_IsValidRuntimeIdentifier)' != 'true'" Text="The RuntimeIdentifier '$(RuntimeIdentifier)' is invalid." />
</Target>

<!-- This target can be removed in .NET 9 (when we stop supporting the compat logic to make the invalid TPV just a warning instead of an error) -->
<Target Name="_XamarinValidateTargetPlatformVersion"
BeforeTargets="Build;ResolvedFrameworkReference;ResolveRuntimePackAssets"
Condition="'$(ValidateTargetPlatformVersion)' != 'false'"
>
<ItemGroup>
<_XamarinApplicableTargetPlatformVersion Include="@(SdkSupportedTargetPlatformVersion)" Condition="'@(SdkSupportedTargetPlatformVersion)' != '' and '%(SdkSupportedTargetPlatformVersion.DefineConstantsOnly)' != 'true'" RemoveMetadata="DefineConstantsOnly" />
<_XamarinValidTargetPlatformVersion Include="@(_XamarinApplicableTargetPlatformVersion)" Condition="'@(_XamarinApplicableTargetPlatformVersion)' != '' and $([MSBuild]::VersionEquals(%(Identity), $(TargetPlatformVersion)))" />
</ItemGroup>

<PropertyGroup>
<_XamarinTargetPlatformVersionSupported Condition="'$(_XamarinTargetPlatformVersionSupported)' == '' and '@(_XamarinValidTargetPlatformVersion)' != ''" >true</_XamarinTargetPlatformVersionSupported>
<_XamarinValidTargetPlatformVersions Condition="'@(_XamarinApplicableTargetPlatformVersion)' != ''" >@(_XamarinApplicableTargetPlatformVersion, '%0a')</_XamarinValidTargetPlatformVersions>
<_XamarinValidTargetPlatformVersions Condition="'@(_XamarinApplicableTargetPlatformVersion)' == ''" >None</_XamarinValidTargetPlatformVersions>
</PropertyGroup>

<Warning Condition="'$(_XamarinTargetPlatformVersionSupported)' != 'true'" Text="$(TargetPlatformVersion) is not a valid TargetPlatformVersion for $(_PlatformName). This warning will become an error in future versions of the $(_PlatformName) workload. Valid versions include:%0a$(_XamarinValidTargetPlatformVersions)" />
</Target>

<!-- Install & Run -->

<PropertyGroup>
Expand Down
72 changes: 67 additions & 5 deletions tests/dotnet/UnitTests/ProjectTest.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Xml;

using Mono.Cecil;

Expand Down Expand Up @@ -1541,15 +1542,13 @@ internal static IList<string> RemovePostCurrentOnMacCatalyst (IList<string> 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);
Expand All @@ -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<XmlNode> ()
.Select (v => v.InnerText)
.ToArray ();
}

[Test]
Expand Down
24 changes: 17 additions & 7 deletions tests/dotnet/UnitTests/TestBaseClass.cs
Original file line number Diff line number Diff line change
Expand Up @@ -422,23 +422,33 @@ public static void AssertErrorCount (IList<BuildLogEvent> 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<BuildLogEvent> errors, params string [] errorMessages)
public static void AssertWarningMessages (IList<BuildLogEvent> 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<BuildLogEvent> actualErrors, params string [] expectedErrorMessages)
{
AssertBuildMessages ("error", actualErrors, expectedErrorMessages);
}

public static void AssertBuildMessages (string type, IList<BuildLogEvent> 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<string> ();
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 ()))}");
}
}
}