Skip to content

Commit

Permalink
[Xamarin.Android.Build.Tasks] Add AndroidEnableRestrictToAttributes (#…
Browse files Browse the repository at this point in the history
…7990)

Context: dotnet/java-interop#1081
Context: dotnet/java-interop@b274a67
Context: xamarin/AndroidX#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 xamarin/AndroidX#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
  • Loading branch information
jpobst authored and jonathanpeppers committed Aug 15, 2023
1 parent 465e338 commit 4f41349
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 7 deletions.
52 changes: 45 additions & 7 deletions Documentation/guides/OneDotNetBindingProjects.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# .NET 6 - Xamarin.Android Binding Migration
# .NET 6+ - Xamarin.Android Binding Migration

## Consolidation of binding projects

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

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

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

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

### `<AndroidClassParser>`

Expand Down Expand Up @@ -70,6 +70,44 @@ all default item inclusion behavior can be disabled by setting `$(EnableDefaultI

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

## Embedded `.jar`./`.aar`

In Xamarin.Android Classic, the Java `.jar` or `.aar` was often embedded into the binding `.dll`
as an Embedded Resource.

However, this led to slow builds, as each `.dll` must be opened and scanned for Java code. If
found, it must be extracted to disk to be used.

In .NET 6+, Java code is no longer embedded in the `.dll`. The application build process will
automatically include any `.jar` or `.aar` files it finds in the same directory as a referenced `.dll`.

If a project references a binding via `<PackageReference>` or `<ProjectReference>` then everything
just works and no additional considerations are needed.

However if a project references a binding via `<Reference>`, the `.jar`/`.aar` must be located next
to the `.dll`.

That is, for a reference like this:

```xml
<Reference Include="MyBinding.dll" />
```

A directory like this will not work:

```
\lib
- MyBinding.dll
```

The directory must contain the native code as well:

```
\lib
- MyBinding.dll
- mybinding.jar
```

## Migration Considerations

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

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

```xml
<attr path="/api/package[@name='my.package']/interface[@name='Foo']" name="unnest">false</attr>
Expand Down Expand Up @@ -213,7 +251,7 @@ This continues to be enabled/disabled using the same mechanism as all .NET proje
</PropertyGroup>
```

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

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

Expand All @@ -222,7 +260,7 @@ Use `enable` as shown above to enable NRT support.
### `Resource.designer.cs`

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

One example of a failure from this change, is if your binding generates a class named `Resource`
Expand Down
32 changes: 32 additions & 0 deletions Documentation/guides/building-apps/build-properties.md
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,38 @@ Support for this property was added in Xamarin.Android 9.4.

This property is `False` by default.


## AndroidEnableRestrictToAttributes

An enum-style property with valid values of `obsolete` and `disable`.

When set to `obsolete`, types and members that are marked with the Java annotation
`androidx.annotation.RestrictTo` *or* are in non-exported Java packages will
be marked with an `[Obsolete]` attribute in the C# binding.

This `[Obsolete]` attribute has a descriptive message explaining that the
Java package owner considers the API to be "internal" and warns against its use.

This attribute also has a custom warning code `XAOBS001` so that it can be suppressed
independently of "normal" obsolete API.

When set to `disable`, API will be generated as normal with no additional
attributes. (This is the same behavior as before .NET 8.)

Adding `[Obsolete]` attributes instead of automatically removing the API was done to
preserve API compatibility with existing packages. If you would instead prefer to
*remove* members that have the `@RestrictTo` annotation *or* are in non-exported
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
this property to prevent these types from being bound:

```xml
<remove-node path="//*[@annotated-visibility]" />
```

Support for this property was added in .NET 8.

This property is set to `obsolete` by default.

## AndroidEnableSGenConcurrent

A boolean property that
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ It is shared between "legacy" binding projects and .NET 5 projects.
EnableBindingStaticAndDefaultInterfaceMethods="$(AndroidBoundInterfacesContainStaticAndDefaultInterfaceMethods)"
EnableBindingNestedInterfaceTypes="$(AndroidBoundInterfacesContainTypes)"
EnableBindingInterfaceConstants="$(AndroidBoundInterfacesContainConstants)"
EnableRestrictToAttributes="$(AndroidEnableRestrictToAttributes)"
Nullable="$(Nullable)"
UseJavaLegacyResolver="$(_AndroidUseJavaLegacyResolver)"
NamespaceTransforms="@(AndroidNamespaceReplacement)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
<AndroidBoundInterfacesContainStaticAndDefaultInterfaceMethods Condition=" '$(AndroidBoundInterfacesContainStaticAndDefaultInterfaceMethods)' == '' ">true</AndroidBoundInterfacesContainStaticAndDefaultInterfaceMethods>
<AndroidBoundInterfacesContainTypes Condition=" '$(AndroidBoundInterfacesContainTypes)' == '' ">true</AndroidBoundInterfacesContainTypes>
<AndroidBoundInterfacesContainConstants Condition=" '$(AndroidBoundInterfacesContainConstants)' == '' ">true</AndroidBoundInterfacesContainConstants>
<AndroidEnableRestrictToAttributes Condition=" '$(AndroidEnableRestrictToAttributes)' == '' ">obsolete</AndroidEnableRestrictToAttributes>

<!-- Mono components -->
<AndroidEnableProfiler Condition=" '$(AndroidEnableProfiler)' == ''">false</AndroidEnableProfiler>
Expand Down
4 changes: 4 additions & 0 deletions src/Xamarin.Android.Build.Tasks/Tasks/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ public class BindingsGenerator : AndroidDotnetToolTask
public bool EnableBindingStaticAndDefaultInterfaceMethods { get; set; }
public bool EnableBindingNestedInterfaceTypes { get; set; }
public bool EnableBindingInterfaceConstants { get; set; }
public string EnableRestrictToAttributes { get; set; }
public string Nullable { get; set; }

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

if (string.Equals (EnableRestrictToAttributes, "obsolete", StringComparison.OrdinalIgnoreCase))
features.Add ("restrict-to-attributes");

if (string.Equals (Nullable, "enable", StringComparison.OrdinalIgnoreCase))
features.Add ("nullable-reference-types");

Expand Down

0 comments on commit 4f41349

Please sign in to comment.