[introspection] Migrate .NET code to use the new .NET-style availability attributes. (#13363)

* [tools] Extract the logic to parse OSPlatformAttribute platform names to a separate file/class.

* [introspection] Migrate .NET code to use the new .NET-style availability attributes.

This also means using the 'ApplePlatform' enum instead of the 'PlatformName'
enum, because the latter will be removed in .NET.

* [FileProvider] Exclude some deprecated API from .NET.

* [AVFoundation] Adjust availability attribute for AVCaptureStillImageOutput.HighResolutionStillImageOutputEnabled.

* Update tests.
This commit is contained in:
Rolf Bjarne Kvinge 2021-11-22 20:54:07 +01:00 коммит произвёл GitHub
Родитель b55ee6d521
Коммит 5329b19f62
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
11 изменённых файлов: 481 добавлений и 160 удалений

Просмотреть файл

@ -19,6 +19,8 @@ using Foundation;
using UIKit;
#endif
using Xamarin.Utils;
namespace Xamarin.Tests
{
public sealed class PlatformInfo
@ -50,6 +52,20 @@ namespace Xamarin.Tests
var platformInfo = new PlatformInfo ();
#if NET
if (name.StartsWith ("maccatalyst", StringComparison.Ordinal))
platformInfo.Name = ApplePlatform.MacCatalyst;
else if (name.StartsWith ("mac", StringComparison.Ordinal))
platformInfo.Name = ApplePlatform.MacOSX;
else if (name.StartsWith ("ios", StringComparison.Ordinal) || name.StartsWith ("iphoneos", StringComparison.Ordinal) || name.StartsWith ("ipados", StringComparison.Ordinal))
platformInfo.Name = ApplePlatform.iOS;
else if (name.StartsWith ("tvos", StringComparison.Ordinal))
platformInfo.Name = ApplePlatform.TVOS;
else if (name.StartsWith ("watchos", StringComparison.Ordinal))
platformInfo.Name = ApplePlatform.WatchOS;
else
throw new FormatException ($"Unknown product name: {name}");
#else
if (name.StartsWith ("maccatalyst", StringComparison.Ordinal))
platformInfo.Name = PlatformName.MacCatalyst;
else if (name.StartsWith ("mac", StringComparison.Ordinal))
@ -62,27 +78,39 @@ namespace Xamarin.Tests
platformInfo.Name = PlatformName.WatchOS;
else
throw new FormatException ($"Unknown product name: {name}");
#endif
platformInfo.Version = Version.Parse (version);
#if !NET
if (IntPtr.Size == 4)
platformInfo.Architecture = PlatformArchitecture.Arch32;
else if (IntPtr.Size == 8)
platformInfo.Architecture = PlatformArchitecture.Arch64;
#endif
return platformInfo;
}
public static readonly PlatformInfo Host = GetHostPlatformInfo ();
#if NET
public ApplePlatform Name { get; private set; }
#else
public PlatformName Name { get; private set; }
public PlatformArchitecture Architecture { get; private set; }
#endif
public Version Version { get; private set; }
#if NET
public bool IsMac => Name == ApplePlatform.MacOSX;
public bool IsIos => Name == ApplePlatform.iOS;
#else
public bool IsMac => Name == PlatformName.MacOSX;
public bool IsIos => Name == PlatformName.iOS;
public bool IsArch32 => Architecture.HasFlag (PlatformArchitecture.Arch32);
public bool IsArch64 => Architecture.HasFlag (PlatformArchitecture.Arch64);
#endif
PlatformInfo ()
{
@ -91,57 +119,6 @@ namespace Xamarin.Tests
public static class AvailabilityExtensions
{
#if NET
public static AvailabilityBaseAttribute Convert (this OSPlatformAttribute a)
{
if (a == null)
return null;
PlatformName p;
int n = 0;
switch (a.PlatformName) {
case string dummy when a.PlatformName.StartsWith ("ios"):
p = PlatformName.iOS;
n = "ios".Length;
break;
case string dummy when a.PlatformName.StartsWith ("tvos"):
p = PlatformName.TvOS;
n = "tvos".Length;
break;
case string dummy when a.PlatformName.StartsWith ("watchos"):
p = PlatformName.WatchOS;
n = "watchos".Length;
break;
case string dummy when a.PlatformName.StartsWith ("macos"):
p = PlatformName.MacOSX;
n = "macos".Length;
break;
case string dummy when a.PlatformName.StartsWith ("maccatalyst"):
p = PlatformName.MacCatalyst;
n = "maccatalyst".Length;
break;
default:
return null;
}
bool versioned = Version.TryParse (a.PlatformName [n..], out var v);
if (a is SupportedOSPlatformAttribute) {
if (!versioned)
throw new FormatException (a.PlatformName);
return new IntroducedAttribute (p, v.Major, v.Minor);
}
if (a is UnsupportedOSPlatformAttribute) {
// if a version is provided then it means it was deprecated
// if no version is provided then it means it's unavailable
if (versioned)
return new DeprecatedAttribute (p, v.Major, v.Minor);
else
return new UnavailableAttribute (p);
}
return null;
}
#endif
public static bool IsAvailableOnHostPlatform (this ICustomAttributeProvider attributeProvider)
{
return attributeProvider.IsAvailable (PlatformInfo.Host);
@ -149,34 +126,68 @@ namespace Xamarin.Tests
public static bool IsAvailable (this ICustomAttributeProvider attributeProvider, PlatformInfo targetPlatform)
{
var customAttributes = attributeProvider.GetCustomAttributes (true);
#if NET
var list = new List<AvailabilityBaseAttribute> ();
foreach (var ca in attributeProvider.GetCustomAttributes (true)) {
if (ca is OSPlatformAttribute aa) {
var converted = aa.Convert ();
if (converted is not null)
list.Add (converted);
}
// FIXME - temporary, while older attributes co-exists (in manual bindings)
if (ca is AvailabilityBaseAttribute old)
list.Add (old);
if (ca is ObsoleteAttribute)
return false;
}
return list.IsAvailable (targetPlatform);
#else
return attributeProvider
.GetCustomAttributes (true)
.OfType<AvailabilityBaseAttribute> ()
.IsAvailable (targetPlatform);
customAttributes = customAttributes.ToArray (); // don't iterate twice
if (customAttributes.Any (v => v is ObsoleteAttribute))
return false;
#endif
return customAttributes
#if NET
.OfType<OSPlatformAttribute> ()
#else
.OfType<AvailabilityBaseAttribute> ()
#endif
.IsAvailable (targetPlatform);
}
#if NET
public static bool IsAvailableOnHostPlatform (this IEnumerable<OSPlatformAttribute> attributes)
#else
public static bool IsAvailableOnHostPlatform (this IEnumerable<AvailabilityBaseAttribute> attributes)
#endif
{
return attributes.IsAvailable (PlatformInfo.Host);
}
#if NET
public static bool IsAvailable (this IEnumerable<OSPlatformAttribute> attributes, PlatformInfo targetPlatform)
{
// Sort the attributes so that maccatalyst attributes are processed before ios attributes
// This way we can easily fall back to ios for maccatalyst
attributes = attributes.OrderBy (v => v.PlatformName.ToLowerInvariant ()).Reverse ();
foreach (var attr in attributes) {
if (!attr.TryParse (out ApplePlatform? platform, out var version))
continue;
if (targetPlatform.Name == ApplePlatform.MacCatalyst) {
if (platform != ApplePlatform.iOS && platform != ApplePlatform.MacCatalyst)
continue;
} else if (platform != targetPlatform.Name) {
continue;
}
if (attr is UnsupportedOSPlatformAttribute)
return version is not null && targetPlatform.Version < version;
if (attr is SupportedOSPlatformAttribute)
return version is null || targetPlatform.Version >= version;
}
// Our current attribute logic assumes that no attribute means that an API is available on all versions of that platform.
// This is not correct: the correct logic is that an API is available on a platform if there are no availability attributes
// for any other platforms. However, enforcing this here would make a few of our tests fail, so we must keep the incorrect
// logic until we've got all the right attributes implemented.
return true;
// Correct logic:
// Only available if there aren't any attributes for other platforms
// return attributes.Count () == 0;
}
#else
public static bool IsAvailable (this IEnumerable<AvailabilityBaseAttribute> attributes, PlatformInfo targetPlatform)
{
// always "available" from a binding perspective if
@ -214,5 +225,6 @@ namespace Xamarin.Tests
return available;
}
#endif
}
}

Просмотреть файл

@ -27,6 +27,7 @@ using System.Text;
using NUnit.Framework;
using ObjCRuntime;
using Xamarin.Tests;
using Xamarin.Utils;
namespace Introspection {
@ -34,12 +35,37 @@ namespace Introspection {
protected Version Minimum { get; set; }
protected Version Maximum { get; set; }
#if NET
protected Func<OSPlatformAttribute, bool> Filter { get; set; }
protected ApplePlatform Platform { get; set; }
#else
protected Func<AvailabilityBaseAttribute, bool> Filter { get; set; }
protected PlatformName Platform { get; set; }
#endif
public ApiAvailabilityTest ()
{
Maximum = Version.Parse (Constants.SdkVersion);
#if NET
#if __MACCATALYST__
Platform = ApplePlatform.MacCatalyst;
Minimum = Xamarin.SdkVersions.MinMacCatalystVersion;
#elif __IOS__
Platform = ApplePlatform.iOS;
Minimum = Xamarin.SdkVersions.MiniOSVersion;
#elif __TVOS__
Platform = ApplePlatform.TVOS;
Minimum = Xamarin.SdkVersions.MinTVOSVersion;
#elif __WATCHOS__
Platform = ApplePlatform.WatchOS;
Minimum = Xamarin.SdkVersions.MinWatchOSVersion;
#elif MONOMAC
Platform = ApplePlatform.MacOSX;
Minimum = Xamarin.SdkVersions.MinOSXVersion;
#else
#error No Platform Defined
#endif
#else
#if __MACCATALYST__
Platform = PlatformName.MacCatalyst;
Minimum = Xamarin.SdkVersions.MinMacCatalystVersion;
@ -58,10 +84,21 @@ namespace Introspection {
#else
#error No Platform Defined
#endif
#endif // NET
#if NET
Filter = (OSPlatformAttribute arg) => {
if (!(arg is SupportedOSPlatformAttribute attrib))
return true;
if (!arg.TryParse (out ApplePlatform? platform, out var version))
return true;
return platform != Platform;
};
#else
Filter = (AvailabilityBaseAttribute arg) => {
return (arg.AvailabilityKind != AvailabilityKind.Introduced) || (arg.Platform != Platform);
};
#endif
}
bool FoundInProtocols (MemberInfo m, Type t)
@ -120,7 +157,11 @@ namespace Introspection {
return false;
}
#if NET
void CheckIntroduced (Type t, OSPlatformAttribute ta, MemberInfo m)
#else
void CheckIntroduced (Type t, AvailabilityBaseAttribute ta, MemberInfo m)
#endif
{
var ma = CheckAvailability (m);
if (ta == null || ma == null)
@ -130,8 +171,17 @@ namespace Introspection {
if (FoundInProtocols (m, t))
return;
#if NET
if (!ta.TryParse (out ApplePlatform? platform, out var taVersion))
return;
if (!ma.TryParse (out ApplePlatform? _, out var maVersion))
return;
#else
var taVersion = ta.Version;
var maVersion = ma.Version;
#endif
// Duplicate checks, e.g. same attribute on member and type (extranous metadata)
if (ma.Version == ta.Version) {
if (maVersion == taVersion) {
switch (t.FullName) {
case "AppKit.INSAccessibility":
// special case for [I]NSAccessibility type (10.9) / protocol (10.10) mix up
@ -139,13 +189,13 @@ namespace Introspection {
// better some dupes than being inaccurate when protocol members are inlined
break;
default:
AddErrorLine ($"[FAIL] {ma.Version} ({m}) == {ta.Version} ({t})");
AddErrorLine ($"[FAIL] {maVersion} ({m}) == {taVersion} ({t})");
break;
}
}
// Consistency checks, e.g. member lower than type
// note: that's valid in some cases, like a new base type being introduced
if (ma.Version < ta.Version) {
if (maVersion < taVersion) {
switch (t.FullName) {
case "CoreBluetooth.CBPeer":
switch (m.ToString ()) {
@ -162,7 +212,7 @@ namespace Introspection {
return;
break;
}
AddErrorLine ($"[FAIL] {ma.Version} ({m}) < {ta.Version} ({t})");
AddErrorLine ($"[FAIL] {maVersion} ({m}) < {taVersion} ({t})");
}
}
@ -218,77 +268,121 @@ namespace Introspection {
return s;
}
#if NET
protected OSPlatformAttribute CheckAvailability (ICustomAttributeProvider cap)
#else
protected AvailabilityBaseAttribute CheckAvailability (ICustomAttributeProvider cap)
#endif
{
var attrs = cap.GetCustomAttributes (false);
foreach (var ca in attrs) {
var a = ca;
#if NET
a = (a as OSPlatformAttribute)?.Convert ();
if (!(ca is OSPlatformAttribute aa))
continue;
#else
if (!(ca is AvailabilityBaseAttribute aa))
continue;
#endif
if (a is AvailabilityBaseAttribute aa) {
if (Filter (aa))
continue;
// FIXME should be `<=` but that another large change best done in a different PR
if ((aa.AvailabilityKind == AvailabilityKind.Introduced) && (aa.Version < Minimum)) {
switch (aa.Architecture) {
case PlatformArchitecture.All:
case PlatformArchitecture.None:
AddErrorLine ($"[FAIL] {aa.Version} <= {Minimum} (Min) on '{ToString (cap)}'.");
break;
default:
// An old API still needs the annotations when not available on all architectures
// e.g. NSMenuView is macOS 10.0 but only 32 bits
break;
}
if (Filter (aa))
continue;
#if NET
if (!aa.TryParse (out ApplePlatform? platform, out var aaVersion))
continue;
// FIXME should be `<=` but that another large change best done in a different PR
var isAvailableBeforeMinimum = aa is SupportedOSPlatformAttribute && aaVersion < Minimum;
#else
// FIXME should be `<=` but that another large change best done in a different PR
bool isAvailableBeforeMinimum = false;
var aaVersion = aa.Version;
if ((aa.AvailabilityKind == AvailabilityKind.Introduced) && (aaVersion < Minimum)) {
switch (aa.Architecture) {
case PlatformArchitecture.All:
case PlatformArchitecture.None:
isAvailableBeforeMinimum = true;
break;
default:
// An old API still needs the annotations when not available on all architectures
// e.g. NSMenuView is macOS 10.0 but only 32 bits
break;
}
if (aa.Version > Maximum)
AddErrorLine ($"[FAIL] {aa.Version} > {Maximum} (Max) on '{ToString (cap)}'.");
return aa;
}
#endif
if (isAvailableBeforeMinimum)
AddErrorLine ($"[FAIL] {aaVersion} <= {Minimum} (Min) on '{ToString (cap)}'.");
if (aaVersion > Maximum)
AddErrorLine ($"[FAIL] {aaVersion} > {Maximum} (Max) on '{ToString (cap)}'.");
return aa;
}
return null;
}
bool IsUnavailable (ICustomAttributeProvider cap)
bool IsUnavailable (ICustomAttributeProvider cap, out Version? version)
{
version = null;
foreach (var a in cap.GetCustomAttributes (false)) {
var ca = a;
#if NET
ca = (a as OSPlatformAttribute)?.Convert ();
#endif
if (a is UnsupportedOSPlatformAttribute aa && aa.TryParse (out ApplePlatform? uaPlatform, out version)) {
if (uaPlatform == Platform)
return true;
}
#else
if (ca is UnavailableAttribute ua) {
if (ua.Platform == Platform)
return true;
}
#endif
}
return false;
}
AvailabilityBaseAttribute GetAvailable (ICustomAttributeProvider cap)
#if NET
OSPlatformAttribute GetAvailable (ICustomAttributeProvider cap, out Version? version)
#else
AvailabilityBaseAttribute GetAvailable (ICustomAttributeProvider cap, out Version? version)
#endif
{
version = null;
foreach (var a in cap.GetCustomAttributes (false)) {
var ca = a;
#if NET
ca = (a as OSPlatformAttribute)?.Convert ();
#endif
if (ca is SupportedOSPlatformAttribute aa && aa.TryParse (out ApplePlatform? platform, out version)) {
if (platform == Platform)
return aa;
}
#else
if (ca is AvailabilityBaseAttribute aa) {
if ((aa.AvailabilityKind != AvailabilityKind.Unavailable) && (aa.Platform == Platform))
return aa;
}
#endif
}
return null;
}
void CheckUnavailable (Type t, bool typeUnavailable, MemberInfo m)
void CheckUnavailable (Type t, bool typeUnavailable, Version? typeUnavailableVersion, MemberInfo m)
{
var ma = GetAvailable (m);
if (typeUnavailable && (ma != null))
AddErrorLine ($"[FAIL] {m} is marked with {ma} but the type {t.FullName} is [Unavailable ({Platform})].");
var ma = GetAvailable (m, out var availableVersion);
if (typeUnavailable && (ma != null)) {
if (typeUnavailableVersion is not null && availableVersion is not null) {
if (availableVersion >= typeUnavailableVersion)
AddErrorLine ($"[FAIL] {m} in {m.DeclaringType.FullName} is marked with {ma} in {availableVersion} but the type {t.FullName} is [Unavailable ({Platform})] in {typeUnavailableVersion}");
} else {
AddErrorLine ($"[FAIL] {m} is marked with {ma} but the type {t.FullName} is [Unavailable ({Platform})]");
}
}
var mu = IsUnavailable (m);
if (mu && (ma != null))
AddErrorLine ($"[FAIL] {m} is marked both [Unavailable ({Platform})] and {ma}.");
var mu = IsUnavailable (m, out var unavailableVersion);
if (mu && (ma != null)) {
if (availableVersion is not null && unavailableVersion is not null) {
if (availableVersion >= unavailableVersion)
AddErrorLine ($"[FAIL] {m} is marked both [Unavailable ({Platform})] and {ma}, and it's available in version {availableVersion} which is >= than the unavailable version {unavailableVersion}");
} else {
AddErrorLine ($"[FAIL] {m} in {m.DeclaringType.FullName} is marked both [Unavailable ({Platform})] and {ma}.");
}
}
}
[Test]
@ -297,28 +391,132 @@ namespace Introspection {
//LogProgress = true;
Errors = 0;
foreach (Type t in Assembly.GetTypes ()) {
if (SkipUnavailable (t))
continue;
if (LogProgress)
Console.WriteLine ($"T: {t}");
var tu = IsUnavailable (t);
var ta = GetAvailable (t);
if (tu && (ta != null))
AddErrorLine ($"[FAIL] {t.FullName} is marked both [Unavailable ({Platform})] and {ta}.");
var tu = IsUnavailable (t, out var unavailableVersion);
var ta = GetAvailable (t, out var availableVersion);
if (tu && (ta != null)) {
if (availableVersion is not null && unavailableVersion is not null) {
if (availableVersion >= unavailableVersion)
AddErrorLine ($"[FAIL] {t.FullName} is marked both [Unavailable ({Platform})] and {ta}, and it's available in version {availableVersion} which is >= than the unavailable version {unavailableVersion}");
} else {
AddErrorLine ($"[FAIL] {t.FullName} is marked both [Unavailable ({Platform})] and {ta}. Available: {availableVersion} Unavailable: {unavailableVersion}");
}
}
foreach (var p in t.GetProperties (BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public)) {
if (SkipUnavailable (t, p.Name))
continue;
if (LogProgress)
Console.WriteLine ($"P: {p}");
CheckUnavailable (t, tu, p);
Console.WriteLine ($"P: {p.Name}");
CheckUnavailable (t, tu, unavailableVersion, p);
}
foreach (var m in t.GetMembers (BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public)) {
if (SkipUnavailable (t, m.Name))
continue;
if (LogProgress)
Console.WriteLine ($"M: {m}");
CheckUnavailable (t, tu, m);
Console.WriteLine ($"M: {m.Name}");
CheckUnavailable (t, tu, unavailableVersion, m);
}
}
AssertIfErrors ("{0} API with mixed [Unavailable] and availability attributes", Errors);
}
protected virtual bool SkipUnavailable (Type type)
{
#if __MACCATALYST__
switch (type.Namespace) {
case "AddressBook": {
// The entire framework was introduced and deprecated in the same Mac Catalyst version
return true;
}
}
#endif
switch (type.FullName) {
#if __MACCATALYST__
case "SafariServices.SFContentBlockerErrorCode":
case "SafariServices.SFContentBlockerErrorCodeExtensions":
// introduced and deprecated in the same Mac Catalyst version
return true;
#endif
}
return false;
}
protected virtual bool SkipUnavailable (Type type, string memberName)
{
switch (type.FullName) {
#if __MACCATALYST__
case "AudioUnit.AudioComponent":
switch (memberName) {
case "LastActiveTime":
// introduced and deprecated in the same Mac Catalyst version
return true;
}
break;
#endif
case "CarPlay.CPApplicationDelegate":
switch (memberName) {
case "DidDiscardSceneSessions":
case "GetConfiguration":
case "GetHandlerForIntent":
case "ShouldAutomaticallyLocalizeKeyCommands":
case "ShouldRestoreSecureApplicationState":
case "ShouldSaveSecureApplicationState":
// CPApplicationDelegate is deprecated in macOS 10.15, but these members are pulled in from the UIApplicationDelegate protocol (which is not deprecated)
return true;
}
break;
case "GameKit.GKScore": {
switch (memberName) {
case "ReportLeaderboardScores":
case "ReportLeaderboardScoresAsync":
// Apple introduced and deprecated this method in the same OS version.
return true;
}
break;
}
case "Intents.INNoteContentTypeResolutionResult": {
switch (memberName) {
case "GetConfirmationRequired":
case "GetUnsupported":
// These are static members that have been re-implemented from the base class - the base class isn't deprecated, while INNoteContentTypeResolutionResult is.
return true;
}
break;
}
case "MobileCoreServices.UTType": {
switch (memberName) {
case "UniversalSceneDescriptionMobile":
case "get_UniversalSceneDescriptionMobile":
// Apple added new members to a deprecated enum
return true;
}
break;
}
case "SceneKit.SCNLayer": {
switch (memberName) {
case "CurrentViewport":
case "TemporalAntialiasingEnabled":
case "get_CurrentViewport":
case "get_TemporalAntialiasingEnabled":
case "set_TemporalAntialiasingEnabled":
case "get_UsesReverseZ":
case "set_UsesReverseZ":
case "UsesReverseZ":
// SCNLayer is deprecated in macOS 10.15, but these members are pulled in from the SCNSceneRenderer protocol (which is not deprecated)
return true;
}
break;
}
}
return false;
}
static HashSet<string> member_level = new HashSet<string> ();
void CheckDupes (MemberInfo m, Type t, ISet<string> type_level)
@ -381,12 +579,21 @@ namespace Introspection {
}
#if NET
static bool IsAvailabilityBaseAttributeType (Type type)
{
if (type is null)
return false;
if (type.Name == "AvailabilityBaseAttribute")
return true;
return IsAvailabilityBaseAttributeType (type.BaseType);
}
string CheckLegacyAttributes (ICustomAttributeProvider cap)
{
var sb = new StringBuilder ();
foreach (var a in cap.GetCustomAttributes (false)) {
if (a is AvailabilityBaseAttribute aa) {
sb.AppendLine (aa.ToString ());
if (IsAvailabilityBaseAttributeType (a.GetType ())) {
sb.AppendLine (a.ToString ());
}
}
return sb.ToString ();

Просмотреть файл

@ -24,6 +24,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using NUnit.Framework;
@ -34,6 +35,7 @@ using AppKit;
using UIKit;
#endif
using Foundation;
using Xamarin.Tests;
using Xamarin.Utils;
namespace Introspection
@ -769,8 +771,13 @@ namespace Introspection
return false;
if (mi.GetCustomAttributes<ObsoleteAttribute> (true).Any ())
return true;
#if NET
if (mi.GetCustomAttributes<UnsupportedOSPlatformAttribute> (true).Any ((v) => v.TryParse (out ApplePlatform? platform, out var _) && platform == PlatformInfo.Host.Name))
return true;
#else
if (mi.GetCustomAttributes<ObsoletedAttribute> (true).Any ())
return true;
#endif
return IsObsolete (mi.DeclaringType);
}
@ -888,8 +895,10 @@ namespace Introspection
message = ((AdviceAttribute)attribute).Message;
if (attribute is ObsoleteAttribute)
message = ((ObsoleteAttribute)attribute).Message;
#if !NET
if (attribute is AvailabilityBaseAttribute)
message = ((AvailabilityBaseAttribute)attribute).Message;
#endif
return message;
}

Просмотреть файл

@ -15,6 +15,7 @@
<XamMacArch>x86_64</XamMacArch>
<DefineConstants></DefineConstants>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<DebugSymbols>true</DebugSymbols>

Просмотреть файл

@ -133,6 +133,9 @@
<Compile Include="$(IntrospectionTestDirectory)\..\..\tools\common\ApplePlatform.cs">
<Link>ApplePlatform.cs</Link>
</Compile>
<Compile Include="$(IntrospectionTestDirectory)\..\..\tools\common\OSPlatformAttributeExtensions.cs">
<Link>OSPlatformAttributeExtensions.cs</Link>
</Compile>
<Compile Include="$(IntrospectionTestDirectory)\..\..\tools\common\SdkVersions.cs">
<Link>SdkVersions.cs</Link>
</Compile>

Просмотреть файл

@ -34,6 +34,9 @@
<Compile Include="..\..\..\..\tools\common\Execution.cs">
<Link>Execution.cs</Link>
</Compile>
<Compile Include="..\..\..\..\tools\common\OSPlatformAttributeExtensions.cs">
<Link>OSPlatformAttributeExtensions.cs</Link>
</Compile>
<Compile Include="..\..\..\..\tools\common\TargetFramework.cs">
<Link>TargetFramework.cs</Link>
</Compile>

Просмотреть файл

@ -0,0 +1,119 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.Versioning;
using Xamarin.Utils;
#nullable enable
namespace Xamarin.Utils {
internal static class OSPlatformAttributeExtensions {
#if NET
public static bool TryParse (string? platformName, [NotNullWhen (true)] out string? platform, out Version? version)
#else
public static bool TryParse (string? platformName, out string? platform, out Version? version)
#endif
{
platform = null;
version = null;
if (string.IsNullOrEmpty (platformName))
return false;
var versionIndex = -1;
#if NET
for (var i = 0; i < platformName.Length; i++) {
#else
for (var i = 0; i < platformName!.Length; i++) {
#endif
if (platformName [i] >= '0' && platformName [i] <= '9') {
versionIndex = i;
break;
}
}
string supportedPlatform;
string? supportedVersion = null;
if (versionIndex == -1) {
supportedPlatform = platformName;
} else {
supportedPlatform = platformName.Substring (0, versionIndex);
supportedVersion = platformName.Substring (versionIndex);
}
platform = supportedPlatform.ToLowerInvariant ();
if (string.IsNullOrEmpty (supportedVersion))
return true;
return Version.TryParse (supportedVersion, out version);
}
#if NET
public static bool TryParse (string? platformName, [NotNullWhen (true)] out ApplePlatform? platform, out Version? version)
#else
public static bool TryParse (string? platformName, out ApplePlatform? platform, out Version? version)
#endif
{
platform = null;
if (!TryParse (platformName, out string? supportedPlatform, out version))
return false;
#if NET
return TryGetApplePlatform (supportedPlatform, out platform);
#else
return TryGetApplePlatform (supportedPlatform!, out platform);
#endif
}
#if NET
public static bool TryParse (this OSPlatformAttribute self, [NotNullWhen (true)] out string? platform, out Version? version)
{
if (self is null)
throw new ArgumentNullException (nameof (self));
return TryParse (self.PlatformName, out platform, out version);
}
public static bool TryParse (this OSPlatformAttribute self, [NotNullWhen (true)] out ApplePlatform? platform, out Version? version)
{
platform = null;
if (!TryParse (self, out string? supportedPlatform, out version))
return false;
return TryGetApplePlatform (supportedPlatform, out platform);
}
#endif
#if NET
static bool TryGetApplePlatform (string supportedPlatform, [NotNullWhen (true)] out ApplePlatform? platform)
#else
static bool TryGetApplePlatform (string supportedPlatform, out ApplePlatform? platform)
#endif
{
switch (supportedPlatform) {
case "ios":
platform = ApplePlatform.iOS;
break;
case "tvos":
platform = ApplePlatform.TVOS;
break;
case "macos":
platform = ApplePlatform.MacOSX;
break;
case "maccatalyst":
platform = ApplePlatform.MacCatalyst;
break;
case "watchos":
platform = ApplePlatform.WatchOS;
break;
default:
platform = null;
return false;
}
return true;
}
}
}

Просмотреть файл

@ -1703,7 +1703,6 @@ namespace Registrar {
bool GetDotNetAvailabilityAttribute (ICustomAttribute ca, ApplePlatform currentPlatform, out Version sdkVersion, out string message)
{
var caType = ca.AttributeType;
var platformName = ApplePlatform.None;
sdkVersion = null;
message = null;
@ -1717,53 +1716,12 @@ namespace Registrar {
throw ErrorHelper.CreateError (4163, Errors.MT4163, caType.Name, ca.ConstructorArguments.Count);
}
if (string.IsNullOrEmpty (supportedPlatformAndVersion))
if (!OSPlatformAttributeExtensions.TryParse (supportedPlatformAndVersion, out ApplePlatform? platformName, out sdkVersion))
return false;
var versionIndex = -1;
for (var i = 0; i < supportedPlatformAndVersion.Length; i++) {
if (supportedPlatformAndVersion [i] >= '0' && supportedPlatformAndVersion [i] <= '9') {
versionIndex = i;
break;
}
}
string supportedPlatform;
string supportedVersion = null;
if (versionIndex == -1) {
supportedPlatform = supportedPlatformAndVersion;
} else {
supportedPlatform = supportedPlatformAndVersion.Substring (0, versionIndex);
supportedVersion = supportedPlatformAndVersion.Substring (versionIndex);
}
supportedPlatform = supportedPlatform.ToLowerInvariant ();
switch (supportedPlatform) {
case "ios":
platformName = ApplePlatform.iOS;
break;
case "tvos":
platformName = ApplePlatform.TVOS;
break;
case "macos":
platformName = ApplePlatform.MacOSX;
break;
case "maccatalyst":
platformName = ApplePlatform.MacCatalyst;
break;
case "watchos":
platformName = ApplePlatform.WatchOS;
break;
default:
return false;
}
if (platformName != currentPlatform)
return false;
if (!Version.TryParse (supportedVersion, out sdkVersion))
return false;
return true;
}
#endif // NET

Просмотреть файл

@ -98,6 +98,9 @@
<Compile Include="..\common\PListExtensions.cs">
<Link>external\tools\common\PListExtensions.cs</Link>
</Compile>
<Compile Include="..\common\OSPlatformAttributeExtensions.cs">
<Link>external\tools\common\OSPlatformAttributeExtensions.cs</Link>
</Compile>
<Compile Include="..\common\StaticRegistrar.cs">
<Link>external\tools\common\StaticRegistrar.cs</Link>
</Compile>

Просмотреть файл

@ -354,6 +354,9 @@
<Compile Include="..\common\Execution.cs">
<Link>tools\common\Execution.cs</Link>
</Compile>
<Compile Include="..\common\OSPlatformAttributeExtensions.cs">
<Link>tools\common\OSPlatformAttributeExtensions.cs</Link>
</Compile>
<Compile Include="..\common\StaticRegistrar.cs">
<Link>tools\common\StaticRegistrar.cs</Link>
</Compile>

Просмотреть файл

@ -335,6 +335,9 @@
<Compile Include="..\common\TargetFramework.cs">
<Link>tools\common\TargetFramework.cs</Link>
</Compile>
<Compile Include="..\common\OSPlatformAttributeExtensions.cs">
<Link>tools\common\OSPlatformAttributeExtensions.cs</Link>
</Compile>
<Compile Include="..\common\StaticRegistrar.cs">
<Link>tools\common\StaticRegistrar.cs</Link>
</Compile>