Skip to content
This repository was archived by the owner on Jul 6, 2026. It is now read-only.

Commit d3ea180

Browse files
authored
[generator] Add support for [ObsoletedOSPlatform] (#1026)
Context: dotnet/android#7234 Add support for a new option `generator --lang-features=obsoleted-platform-attributes`. When used, for an API that was obsoleted in API levels greater than our .NET 7 minimum (API-21), we will generate .NET 7's new `[ObsoletedOSPlatform]` attribute *instead of* `[Obsolete]`: // New [global::System.Runtime.Versioning.ObsoletedOSPlatform ("android22.0", @"This class is obsoleted in this android platform")] public partial class CookieSpecParamBean : Org.Apache.Http.Params.HttpAbstractParamBean { } // Previous [global::System.Obsolete (@"This class is obsoleted in this android platform")] public partial class CookieSpecParamBean : Org.Apache.Http.Params.HttpAbstractParamBean { } This is useful in a .NET 7+ context because we always *compile* against the "latest" `Mono.Android`, even if you are *targeting* an earlier version. For example, the use of `[Obsolete]` means that using `CookieSpecParamBean` would always result in a CS0618 obsolete usage warning, even when building with `$(SupportedOSPlatformVersion)`=21. (`CookieSpecParamBean` was obsoleted in API-22.)
1 parent 6d1ae4e commit d3ea180

26 files changed

Lines changed: 180 additions & 27 deletions

‎src/utils/XmlExtensions.cs‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,18 @@ public static string XGetAttribute (this XPathNavigator nav, string name, string
2020
varattr=nav.GetAttribute(name,ns);
2121
returnattr!=null?attr.Trim():null;
2222
}
23+
24+
publicstaticint?XGetAttributeAsIntOrNull(thisXElementelement,stringname)
25+
{
26+
varattr=element.Attribute(name);
27+
28+
if(attr?.Valueisnull)
29+
returnnull;
30+
31+
if(int.TryParse(attr.Value,outvarval))
32+
returnval;
33+
34+
returnnull;
35+
}
2336
}
2437
}

‎tests/generator-Tests/Unit-Tests/CodeGeneratorTests.cs‎

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,86 @@ public void ObsoleteBoundMethodAbstractDeclaration ()
531531
Assert.True(writer.ToString().Contains("[global::System.Obsolete (@\"This is so old!\")]"),writer.ToString());
532532
}
533533

534+
[Test]
535+
publicvoidObsoletedOSPlatformAttributeSupport()
536+
{
537+
varxml=@"<api>
538+
<package name='java.lang' jni-name='java/lang'>
539+
<class abstract='false' deprecated='not deprecated' final='false' name='Object' static='false' visibility='public' jni-signature='Ljava/lang/Object;' />
540+
</package>
541+
<package name='com.xamarin.android' jni-name='com/xamarin/android'>
542+
<class abstract='false' deprecated='This is a class deprecated since 25!' extends='java.lang.Object' extends-generic-aware='java.lang.Object' jni-extends='Ljava/lang/Object;' final='false' name='MyClass' static='false' visibility='public' jni-signature='Lcom/xamarin/android/MyClass;' deprecated-since='25'>
543+
<field deprecated='This is a field deprecated since 25!' final='true' name='ACCEPT_HANDOVER' jni-signature='Ljava/lang/String;' static='true' transient='false' type='java.lang.String' type-generic-aware='java.lang.String' value='&quot;android.permission.ACCEPT_HANDOVER&quot;' visibility='public' volatile='false' deprecated-since='25'></field>
544+
<constructor deprecated='This is a constructor deprecated since 25!' final='false' name='MyClass' jni-signature='()V' bridge='false' static='false' type='com.xamarin.android.MyClass' synthetic='false' visibility='public' deprecated-since='25'></constructor>
545+
<method abstract='true' deprecated='This is a method deprecated since 25!' final='false' name='countAffectedRows' jni-signature='()I' bridge='false' native='false' return='int' jni-return='I' static='false' synchronized='false' synthetic='false' visibility='public' deprecated-since='25'></method>
546+
<method abstract='false' deprecated='This is a property getter deprecated since 25!' final='false' name='getCount' jni-signature='()I' bridge='false' native='false' return='int' jni-return='I' static='false' synchronized='false' synthetic='false' visibility='public' deprecated-since='25'></method>
547+
<method abstract='false' deprecated='This is a property setter deprecated since 25!' final='false' name='setCount' jni-signature='(I)V' bridge='false' native='false' return='void' jni-return='V' static='false' synchronized='false' synthetic='false' visibility='public' deprecated-since='25'>
548+
<parameter name='count' type='int' jni-type='I'></parameter>
549+
</method>
550+
</class>
551+
</package>
552+
</api>";
553+
554+
options.UseObsoletedOSPlatformAttributes=true;
555+
556+
vargens=ParseApiDefinition(xml);
557+
variface=gens.Single(g =>g.Name=="MyClass");
558+
559+
generator.Context.ContextTypes.Push(iface);
560+
generator.WriteType(iface,string.Empty,newGenerationInfo("","","MyAssembly"));
561+
generator.Context.ContextTypes.Pop();
562+
563+
// Ensure [ObsoletedOSPlatform] was written
564+
Assert.True(writer.ToString().Contains("[global::System.Runtime.Versioning.ObsoletedOSPlatform (\"android25.0\", @\"This is a class deprecated since 25!\")]"),writer.ToString());
565+
Assert.True(writer.ToString().Contains("[global::System.Runtime.Versioning.ObsoletedOSPlatform (\"android25.0\", @\"This is a field deprecated since 25!\")]"),writer.ToString());
566+
Assert.True(writer.ToString().Contains("[global::System.Runtime.Versioning.ObsoletedOSPlatform (\"android25.0\", @\"This is a constructor deprecated since 25!\")]"),writer.ToString());
567+
Assert.True(writer.ToString().Contains("[global::System.Runtime.Versioning.ObsoletedOSPlatform (\"android25.0\", @\"This is a method deprecated since 25!\")]"),writer.ToString());
568+
Assert.True(writer.ToString().Contains("[global::System.Runtime.Versioning.ObsoletedOSPlatform (\"android25.0\", @\"This is a property getter deprecated since 25! This is a property setter deprecated since 25!\")]"),writer.ToString());
569+
}
570+
571+
[Test]
572+
publicvoidObsoletedOSPlatformAttributeUnneededSupport()
573+
{
574+
varxml=@"<api>
575+
<package name='java.lang' jni-name='java/lang'>
576+
<class abstract='false' deprecated='not deprecated' final='false' name='Object' static='false' visibility='public' jni-signature='Ljava/lang/Object;' />
577+
</package>
578+
<package name='com.xamarin.android' jni-name='com/xamarin/android'>
579+
<class abstract='false' deprecated='This is a class deprecated since 19!' extends='java.lang.Object' extends-generic-aware='java.lang.Object' jni-extends='Ljava/lang/Object;' final='false' name='MyClass' static='false' visibility='public' jni-signature='Lcom/xamarin/android/MyClass;' deprecated-since='19'>
580+
<field deprecated='This is a field deprecated since 0!' final='true' name='ACCEPT_HANDOVER' jni-signature='Ljava/lang/String;' static='true' transient='false' type='java.lang.String' type-generic-aware='java.lang.String' value='&quot;android.permission.ACCEPT_HANDOVER&quot;' visibility='public' volatile='false' deprecated-since='0'></field>
581+
<constructor deprecated='This is a constructor deprecated since empty string!' final='false' name='MyClass' jni-signature='()V' bridge='false' static='false' type='com.xamarin.android.MyClass' synthetic='false' visibility='public' deprecated-since=''></constructor>
582+
<method abstract='true' deprecated='deprecated' final='false' name='countAffectedRows' jni-signature='()I' bridge='false' native='false' return='int' jni-return='I' static='false' synchronized='false' synthetic='false' visibility='public' deprecated-since='25'></method>
583+
<method abstract='true' deprecated='This method has an invalid deprecated-since!' final='false' name='countAffectedRows2' jni-signature='()I' bridge='false' native='false' return='int' jni-return='I' static='false' synchronized='false' synthetic='false' visibility='public' deprecated-since='foo'></method>
584+
<method abstract='false' deprecated='deprecated' final='false' name='getCount' jni-signature='()I' bridge='false' native='false' return='int' jni-return='I' static='false' synchronized='false' synthetic='false' visibility='public' deprecated-since='22'></method>
585+
<method abstract='false' deprecated='deprecated' final='false' name='setCount' jni-signature='(I)V' bridge='false' native='false' return='void' jni-return='V' static='false' synchronized='false' synthetic='false' visibility='public' deprecated-since='22'>
586+
<parameter name='count' type='int' jni-type='I'></parameter>
587+
</method>
588+
</class>
589+
</package>
590+
</api>";
591+
592+
options.UseObsoletedOSPlatformAttributes=true;
593+
594+
vargens=ParseApiDefinition(xml);
595+
variface=gens.Single(g =>g.Name=="MyClass");
596+
597+
generator.Context.ContextTypes.Push(iface);
598+
generator.WriteType(iface,string.Empty,newGenerationInfo("","","MyAssembly"));
599+
generator.Context.ContextTypes.Pop();
600+
601+
// These should use [Obsolete] because they have always been obsolete in all currently supported versions (21+)
602+
Assert.True(writer.ToString().Contains("[global::System.Obsolete (@\"This is a class deprecated since 19!\")]"),writer.ToString());
603+
Assert.True(writer.ToString().Contains("[global::System.Obsolete (@\"This is a field deprecated since 0!\")]"),writer.ToString());
604+
Assert.True(writer.ToString().Contains("[global::System.Obsolete (@\"This is a constructor deprecated since empty string!\")]"),writer.ToString());
605+
606+
// This should not have a message because the default "deprecated" message isn't useful
607+
Assert.True(writer.ToString().Contains("[global::System.Runtime.Versioning.ObsoletedOSPlatform (\"android25.0\")]"),writer.ToString());
608+
Assert.True(writer.ToString().Contains("[global::System.Runtime.Versioning.ObsoletedOSPlatform (\"android22.0\")]"),writer.ToString());
609+
610+
// This should use [Obsolete] because the 'deprecated-since' attribute could not be parsed
611+
Assert.True(writer.ToString().Contains("[global::System.Obsolete (@\"This method has an invalid deprecated-since!\")]"),writer.ToString());
612+
}
613+
534614
[Test]
535615
[NonParallelizable]// We are setting a static property on Report
536616
publicvoidWarnIfTypeNameMatchesNamespace()

‎tools/generator/CodeGenerationOptions.cs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ public SymbolTable SymbolTable {
6666
publicboolSupportNestedInterfaceTypes{get;set;}
6767
publicboolSupportNullableReferenceTypes{get;set;}
6868
publicboolUseShallowReferencedTypes{get;set;}
69+
publicboolUseObsoletedOSPlatformAttributes{get;set;}
6970
publicboolRemoveConstSugar=>BuildingCoreAssembly;
7071

7172
bool?buildingCoreAssembly;

‎tools/generator/CodeGenerator.cs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ static void Run (CodeGeneratorOptions options, DirectoryAssemblyResolver resolve
8383
SupportDefaultInterfaceMethods=options.SupportDefaultInterfaceMethods,
8484
SupportNestedInterfaceTypes=options.SupportNestedInterfaceTypes,
8585
SupportNullableReferenceTypes=options.SupportNullableReferenceTypes,
86+
UseObsoletedOSPlatformAttributes=options.UseObsoletedOSPlatformAttributes,
8687
};
8788
varresolverCache=newTypeDefinitionCache();
8889

‎tools/generator/CodeGeneratorOptions.cs‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ public CodeGeneratorOptions ()
5353
publicboolSupportNestedInterfaceTypes{get;set;}
5454
publicboolSupportNullableReferenceTypes{get;set;}
5555
publicboolUseLegacyJavaResolver{get;set;}
56+
publicboolUseObsoletedOSPlatformAttributes{get;set;}
5657

5758
publicXmldocStyleXmldocStyle{get;set;}=XmldocStyle.IntelliSense;
5859

@@ -102,12 +103,13 @@ public static CodeGeneratorOptions Parse (string[] args)
102103
"SDK Platform {VERSION}/API level.",
103104
v =>opts.ApiLevel=v},
104105
{"lang-features=",
105-
"For internal use. (Flags: interface-constants,default-interface-methods,nullable-reference-types)",
106+
"For internal use. (Flags: interface-constants,default-interface-methods,nested-interface-types,nullable-reference-types,obsoleted-platform-attributes)",
106107
v =>{
107108
opts.SupportInterfaceConstants=v?.Contains("interface-constants")==true;
108109
opts.SupportDefaultInterfaceMethods=v?.Contains("default-interface-methods")==true;
109110
opts.SupportNestedInterfaceTypes=v?.Contains("nested-interface-types")==true;
110111
opts.SupportNullableReferenceTypes=v?.Contains("nullable-reference-types")==true;
112+
opts.UseObsoletedOSPlatformAttributes=v?.Contains("obsoleted-platform-attributes")==true;
111113
}},
112114
{"preserve-enums",
113115
"For internal use.",

‎tools/generator/Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ public static Ctor CreateCtor (GenBase declaringType, XElement elem, CodeGenerat
155155
ApiAvailableSince=declaringType.ApiAvailableSince,
156156
CustomAttributes=elem.XGetAttribute("customAttributes"),
157157
Deprecated=elem.Deprecated(),
158+
DeprecatedSince=elem.XGetAttributeAsIntOrNull("deprecated-since"),
158159
GenericArguments=elem.GenericArguments(),
159160
Name=elem.XGetAttribute("name"),
160161
Visibility=elem.Visibility()
@@ -200,6 +201,7 @@ public static Field CreateField (GenBase declaringType, XElement elem, CodeGener
200201
varfield=newField{
201202
ApiAvailableSince=declaringType.ApiAvailableSince,
202203
DeprecatedComment=elem.XGetAttribute("deprecated"),
204+
DeprecatedSince=elem.XGetAttributeAsIntOrNull("deprecated-since"),
203205
IsAcw=true,
204206
IsDeprecated=elem.XGetAttribute("deprecated")!="not deprecated",
205207
IsDeprecatedError=elem.XGetAttribute("deprecated-error")=="true",
@@ -237,6 +239,7 @@ public static Field CreateField (GenBase declaringType, XElement elem, CodeGener
237239
publicstaticGenBaseSupportCreateGenBaseSupport(XElementpkg,XElementelem,CodeGenerationOptionsopt,boolisInterface)
238240
{
239241
varsupport=newGenBaseSupport{
242+
DeprecatedSince=elem.XGetAttributeAsIntOrNull("deprecated-since"),
240243
IsAcw=true,
241244
IsDeprecated=elem.XGetAttribute("deprecated")!="not deprecated",
242245
IsGeneratable=true,
@@ -348,6 +351,7 @@ public static Method CreateMethod (GenBase declaringType, XElement elem, CodeGen
348351
ArgsType=elem.Attribute("argsType")?.Value,
349352
CustomAttributes=elem.XGetAttribute("customAttributes"),
350353
Deprecated=elem.Deprecated(),
354+
DeprecatedSince=elem.XGetAttributeAsIntOrNull("deprecated-since"),
351355
ExplicitInterface=elem.XGetAttribute("explicitInterface"),
352356
EventName=elem.Attribute("eventName")?.Value,
353357
GenerateAsyncWrapper=elem.Attribute("generateAsyncWrapper")!=null,

‎tools/generator/Java.Interop.Tools.Generator.ObjectModel/Field.cs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ public class Field : ApiVersionsSupport.IApiAvailability, ISourceLineInfo
99
publicstringAnnotation{get;set;}
1010
publicintApiAvailableSince{get;set;}
1111
publicstringDeprecatedComment{get;set;}
12+
publicint?DeprecatedSince{get;set;}
1213
publicboolIsAcw{get;set;}
1314
publicboolIsDeprecated{get;set;}
1415
publicboolIsDeprecatedError{get;set;}

‎tools/generator/Java.Interop.Tools.Generator.ObjectModel/GenBase.cs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,8 @@ public bool ContainsProperty (string name, bool check_ifaces, bool check_base_if
251251

252252
publicstringDeprecatedComment=>support.DeprecatedComment;
253253

254+
publicint?DeprecatedSince=>support.DeprecatedSince;
255+
254256
IEnumerable<GenBase>Descendants(IList<GenBase>gens)
255257
{
256258
foreach(vardirectDescendantsingens.Where(x =>x.BaseGen==this)){

‎tools/generator/Java.Interop.Tools.Generator.ObjectModel/GenBaseSupport.cs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ public class GenBaseSupport
77
publicboolIsAcw{get;set;}
88
publicboolIsDeprecated{get;set;}
99
publicstringDeprecatedComment{get;set;}
10+
publicint?DeprecatedSince{get;set;}
1011
publicboolIsGeneratable{get;set;}
1112
publicboolIsGeneric{get;set;}
1213
publicboolIsObfuscated{get;set;}

‎tools/generator/Java.Interop.Tools.Generator.ObjectModel/MethodBase.cs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ protected MethodBase (GenBase declaringType)
1818
publicstringAssemblyName{get;set;}
1919
publicGenBaseDeclaringType{get;}
2020
publicstringDeprecated{get;set;}
21+
publicint?DeprecatedSince{get;set;}
2122
publicGenericParameterDefinitionListGenericArguments{get;set;}
2223
publicboolIsAcw{get;set;}
2324
publicboolIsValid{get;privateset;}

0 commit comments

Comments
 (0)