Skip to content

Commit 50521d9

Browse files
authored
[Xamarin.Android.Build.Tasks] Add AndroidEnableRestrictToAttributes (#7990)
Context: dotnet/java-interop#1081 Context: dotnet/java-interop@b274a67 Context: dotnet/android-libraries#690 Android libraries may use the [`androidx.annotation.RestrictTo`][0] annotation to mark a `public` Java type as "not public": // Java @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class DrawableWrapper extends Drawable { } Unfortunately, .NET Android didn't know about this annotation, so all such annotated types were bound as *`public`* types: // C# binding public class DrawableWrapper : Drawable { } This is a problem because Google doesn't maintain API compatibility for types with the `@RestrictTo` annotation. This can result in undesirable API breakage; see also dotnet/android-libraries#690. xamarin/java.interop#b274a67f updated `class-parse` to know about the `@RestrictTo` annotation; when present, an `//*/@annotated-visibility` attribute is present within `api.xml`: <class name="DrawableWrapper" … annotated-visibility="LIBRARY_GROUP_PREFIX" /> xamarin/java.interop#b274a67f also updated `generator` to support a `generator --lang-features=restrict-to-attributes`; when present, types with an `//*/@annotated-visibility` attribute will be marked as `[Obsolete]` (*not* removed!), in order to maintain API compatibility with existing ("broken") bindings, so that customers can migrate away from these types: // C# binding [Obsolete "While this type is 'public', Google considers it internal API and reserves the right to modify or delete it in the future. Use at your own risk.", DiagnosticId = "XAOBS001") partial class DrawableWrapper : Drawable { } The new `[Obsolete]` usage also specifies a custom warning code of `XAOBS001` so that the warning can be suppressed if desired. Add support for a new `$(AndroidEnableRestrictToAttributes)` MSBuild "enum-style" property. Supported values include: * `obsolete`: `generator --lang-features=restrict-to-attributes` will be used and the `[Obsolete]` custom attribute will be placed on bindings of Java types which have a `@RestrictTo` annotation. This is the default value. * `disable`: Java types with a `@RestrictTo` annotation will *not* be marked as `[Obsolete]`, and will be bound as a "normal" Java `public` type. <AndroidEnableRestrictToAttributes>disable</AndroidEnableRestrictToAttributes> If you would instead prefer that types with `@RestrictTo` not be bound at all, this can be achieved via Metadata, e.g. <remove-node path="//*[@annotated-visibility]" /> [0]: https://developer.android.com/reference/androidx/annotation/RestrictTo
1 parent e694ba5 commit 50521d9

File tree

5 files changed

+83
-7
lines changed

5 files changed

+83
-7
lines changed

Documentation/guides/OneDotNetBindingProjects.md

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
# .NET 6 - Xamarin.Android Binding Migration
1+
# .NET 6+ - Xamarin.Android Binding Migration
22

33
## Consolidation of binding projects
44

5-
In .NET 6, there is no longer a concept of a [binding
5+
In .NET 6+, there is no longer a concept of a [binding
66
project][binding] as a separate project type. Any of the MSBuild item
77
groups or build actions that currently work in binding projects will
8-
be supported through a .NET 6 Android application or library.
8+
be supported through a .NET 6+ Android application or library.
99

1010
For example, a binding library would be identical to a class library:
1111

@@ -26,7 +26,7 @@ libraries, just that the project file will look the same as an application.
2626

2727
The following legacy options are no longer supported. The supported alternatives
2828
have been available for several years, and the smoothest migration option is to update
29-
and test your current projects with these options **first** before migrating them to .NET 6.
29+
and test your current projects with these options **first** before migrating them to .NET 6+.
3030

3131
### `<AndroidClassParser>`
3232

@@ -91,6 +91,44 @@ Or exclude *all* files within that folder:
9191

9292
[default-items]: https://github.com/xamarin/xamarin-android/blob/main/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/Sdk/AutoImport.props
9393

94+
## Embedded `.jar`./`.aar`
95+
96+
In Xamarin.Android Classic, the Java `.jar` or `.aar` was often embedded into the binding `.dll`
97+
as an Embedded Resource.
98+
99+
However, this led to slow builds, as each `.dll` must be opened and scanned for Java code. If
100+
found, it must be extracted to disk to be used.
101+
102+
In .NET 6+, Java code is no longer embedded in the `.dll`. The application build process will
103+
automatically include any `.jar` or `.aar` files it finds in the same directory as a referenced `.dll`.
104+
105+
If a project references a binding via `<PackageReference>` or `<ProjectReference>` then everything
106+
just works and no additional considerations are needed.
107+
108+
However if a project references a binding via `<Reference>`, the `.jar`/`.aar` must be located next
109+
to the `.dll`.
110+
111+
That is, for a reference like this:
112+
113+
```xml
114+
<Reference Include="MyBinding.dll" />
115+
```
116+
117+
A directory like this will not work:
118+
119+
```
120+
\lib
121+
- MyBinding.dll
122+
```
123+
124+
The directory must contain the native code as well:
125+
126+
```
127+
\lib
128+
- MyBinding.dll
129+
- mybinding.jar
130+
```
131+
94132
## Migration Considerations
95133

96134
There are several new features set by default to help produce new bindings that better match their
@@ -173,7 +211,7 @@ Setting it to `true` will result in "un-nesting" any nested types (legacy behavi
173211
<attr path="/api/package[@name='my.package']/interface[@name='Foo']" name="unnest">true</attr>
174212
```
175213

176-
Setting it to `false` will result in nested types remaining nested in the `interface` (.NET 6 behavior):
214+
Setting it to `false` will result in nested types remaining nested in the `interface` (.NET 6+ behavior):
177215

178216
```xml
179217
<attr path="/api/package[@name='my.package']/interface[@name='Foo']" name="unnest">false</attr>
@@ -234,7 +272,7 @@ This continues to be enabled/disabled using the same mechanism as all .NET proje
234272
</PropertyGroup>
235273
```
236274

237-
As the default for .NET 6 is `disable`, the same applies for Xamarin Android projects.
275+
As the default for .NET 6+ is `disable`, the same applies for Xamarin Android projects.
238276

239277
Use `enable` as shown above to enable NRT support.
240278

@@ -243,7 +281,7 @@ Use `enable` as shown above to enable NRT support.
243281
### `Resource.designer.cs`
244282

245283
In Xamarin.Android, Java binding projects did not support generating a `Resource.designer.cs` file.
246-
Since binding projects are just class libraries in .NET 6, this file will be generated. This could
284+
Since binding projects are just class libraries in .NET 6+, this file will be generated. This could
247285
be a breaking change when migrating existing projects.
248286

249287
One example of a failure from this change, is if your binding generates a class named `Resource`

Documentation/guides/building-apps/build-properties.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,38 @@ Support for this property was added in Xamarin.Android 9.4.
485485

486486
This property is `False` by default.
487487

488+
489+
## AndroidEnableRestrictToAttributes
490+
491+
An enum-style property with valid values of `obsolete` and `disable`.
492+
493+
When set to `obsolete`, types and members that are marked with the Java annotation
494+
`androidx.annotation.RestrictTo` *or* are in non-exported Java packages will
495+
be marked with an `[Obsolete]` attribute in the C# binding.
496+
497+
This `[Obsolete]` attribute has a descriptive message explaining that the
498+
Java package owner considers the API to be "internal" and warns against its use.
499+
500+
This attribute also has a custom warning code `XAOBS001` so that it can be suppressed
501+
independently of "normal" obsolete API.
502+
503+
When set to `disable`, API will be generated as normal with no additional
504+
attributes. (This is the same behavior as before .NET 8.)
505+
506+
Adding `[Obsolete]` attributes instead of automatically removing the API was done to
507+
preserve API compatibility with existing packages. If you would instead prefer to
508+
*remove* members that have the `@RestrictTo` annotation *or* are in non-exported
509+
Java packages, you can use [Transform files](https://learn.microsoft.com/xamarin/android/platform/binding-java-library/customizing-bindings/java-bindings-metadata#metadataxml-transform-file) in addition to
510+
this property to prevent these types from being bound:
511+
512+
```xml
513+
<remove-node path="//*[@annotated-visibility]" />
514+
```
515+
516+
Support for this property was added in .NET 8.
517+
518+
This property is set to `obsolete` by default.
519+
488520
## AndroidEnableSGenConcurrent
489521

490522
A boolean property that

src/Xamarin.Android.Build.Tasks/MSBuild/Xamarin/Android/Xamarin.Android.Bindings.Core.targets

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ It is shared between "legacy" binding projects and .NET 5 projects.
9393
EnableBindingStaticAndDefaultInterfaceMethods="$(AndroidBoundInterfacesContainStaticAndDefaultInterfaceMethods)"
9494
EnableBindingNestedInterfaceTypes="$(AndroidBoundInterfacesContainTypes)"
9595
EnableBindingInterfaceConstants="$(AndroidBoundInterfacesContainConstants)"
96+
EnableRestrictToAttributes="$(AndroidEnableRestrictToAttributes)"
9697
Nullable="$(Nullable)"
9798
UseJavaLegacyResolver="$(_AndroidUseJavaLegacyResolver)"
9899
NamespaceTransforms="@(AndroidNamespaceReplacement)"

src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
<AndroidBoundInterfacesContainStaticAndDefaultInterfaceMethods Condition=" '$(AndroidBoundInterfacesContainStaticAndDefaultInterfaceMethods)' == '' ">true</AndroidBoundInterfacesContainStaticAndDefaultInterfaceMethods>
3737
<AndroidBoundInterfacesContainTypes Condition=" '$(AndroidBoundInterfacesContainTypes)' == '' ">true</AndroidBoundInterfacesContainTypes>
3838
<AndroidBoundInterfacesContainConstants Condition=" '$(AndroidBoundInterfacesContainConstants)' == '' ">true</AndroidBoundInterfacesContainConstants>
39+
<AndroidEnableRestrictToAttributes Condition=" '$(AndroidEnableRestrictToAttributes)' == '' ">obsolete</AndroidEnableRestrictToAttributes>
3940

4041
<!-- Mono components -->
4142
<AndroidEnableProfiler Condition=" '$(AndroidEnableProfiler)' == ''">false</AndroidEnableProfiler>

src/Xamarin.Android.Build.Tasks/Tasks/Generator.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ public class BindingsGenerator : AndroidDotnetToolTask
5151
public bool EnableBindingStaticAndDefaultInterfaceMethods { get; set; }
5252
public bool EnableBindingNestedInterfaceTypes { get; set; }
5353
public bool EnableBindingInterfaceConstants { get; set; }
54+
public string EnableRestrictToAttributes { get; set; }
5455
public string Nullable { get; set; }
5556

5657
public ITaskItem[] TransformFiles { get; set; }
@@ -216,6 +217,9 @@ protected override string GenerateCommandLineCommands ()
216217
if (EnableBindingStaticAndDefaultInterfaceMethods)
217218
features.Add ("default-interface-methods");
218219

220+
if (string.Equals (EnableRestrictToAttributes, "obsolete", StringComparison.OrdinalIgnoreCase))
221+
features.Add ("restrict-to-attributes");
222+
219223
if (string.Equals (Nullable, "enable", StringComparison.OrdinalIgnoreCase))
220224
features.Add ("nullable-reference-types");
221225

0 commit comments

Comments
 (0)