Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Xamarin.Android.Build.Tasks] Set .NET6 defaults for bindings projects. #6066

Merged
merged 4 commits into from
Jul 14, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
32 changes: 3 additions & 29 deletions Documentation/guides/OneDotNet.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,37 +22,11 @@ See the [Target Framework Names in .NET 5][net5spec] spec for details.

[net5spec]: https://github.com/dotnet/designs/blob/5e921a9dc8ecce33b3195dcdb6f10ef56ef8b9d7/accepted/2020/net5/net5.md

## Consolidation of binding projects
## Bindings projects

In .NET 6, there will no longer be 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.
Changes for bindings projects are documented [here][binding].

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

```xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0-android</TargetFramework>
</PropertyGroup>
</Project>
```

Along with the file structure:

Transforms/
Metadata.xml
foo.jar

`Transforms\*.xml` files are automatically included as a
`@(TransformFile)` item, and `.jar` files are automatically included
as a `@(AndroidLibrary)` item.

This will bind C# types for the Java types found in `foo.jar` using
the metadata fixups from `Transforms\Metadata.xml`.

[binding]: https://docs.microsoft.com/xamarin/android/platform/binding-java-library/
[binding]: OneDotNetBindingProjects.md

## .NET Configuration Files

Expand Down
221 changes: 221 additions & 0 deletions Documentation/guides/OneDotNetBindingProjects.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
# .NET 6 - Xamarin.Android Binding Migration

## Consolidation of binding projects

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.

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

```xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0-android</TargetFramework>
</PropertyGroup>
</Project>
```

**Note:** It is still recommended that you create separate project(s) for binding your
libraries, just that the project file will look the same as an application.

[binding]: https://docs.microsoft.com/xamarin/android/platform/binding-java-library/

## Unsupported Legacy Options

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.

### `<AndroidClassParser>`

`jar2xml` is no longer a valid option for `<AndroidClassParser>`. As `class-parse` is
now the only valid option, this setting will no longer affect anything, and `class-parse`
will always be used.

`class-parse` takes advantage of many new modern features not available in `jar2xml`, such as:

* Automatic parameter names for class methods (if your Java code is compiled with `javac -parameters`)
* Kotlin support
* Static/Default interface member (DIM) support
* Java Nullable reference type (NRT) annotations support

### `<AndroidCodegenTarget>`

`XamarinAndroid` is no longer a valid option for `<AndroidCodegenTarget>`. `XAJavaInterop1`
is now the default and only supported option.

If you have hand-bound code in your `Additions` files that interacts with the generated binding
plumbing (which is rare), it may need to be updated to be compatible with `XAJavaInterop1`.

## Default file inclusion

File structure:

Transforms/
Metadata.xml
foo.jar

`Transforms\*.xml` files are automatically included as a
`@(TransformFile)` item, and `.jar`/`.aar` files are automatically included
as a `@(AndroidLibrary)` item.

This will bind C# types for the Java types found in `foo.jar` using
the metadata fixups from `Transforms\Metadata.xml`.

Default Android related file globbing behavior is defined in [AutoImport.props][default-items].
This behavior can be disabled for Android items by setting `$(EnableDefaultAndroidItems)` to `false`, or
all default item inclusion behavior can be disabled by setting `$(EnableDefaultItems)` to `false`.

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

## Migration Considerations

There are several new features set by default to help produce new bindings that better match their
Java counterparts. However, if you are migrating an existing binding project, these features may create
bindings that are not API compatible with your existing released bindings. In order to maintain
compatibility, you may wish to disable or modify these new features.

### Interface Constants

Traditionally, C# has not allowed constants to be declared in an `interface`, which is a common pattern
in Java code:

```java
public interface Foo {
public static int BAR = 1;
}
```

This pattern was previously supported by creating an alternative `class` that contains the constant(s):

```csharp
public abstract class Foo : Java.Lang.Object
{
public static int Bar = 1;
}
```

With C# 8, we can now put these constants on the `interface` just like Java:

```csharp
public interface IFoo {
public static int Bar = 1;
}
```

However this means we no longer generate the alternative class that existing code may depend on.

Setting `<AndroidBoundInterfacesContainConstants>false</AndroidBoundInterfacesContainConstants>` will revert to the legacy behavior.

### Nested Interface Types

Traditionally, C# has not allowed nested types to be declared in an `interface`, which is allowed
in Java code:

```java
public interface Foo {
public class Bar { }
}
```

This pattern was supported by moving the nested type to a top-level type with a generated name composed
of the interface and nested type name:

```csharp
public interface IFoo { }

public class IFooBar : Java.Lang.Object { }
```

With C# 8, we can now put these nested type in the `interface` just like Java:

```csharp
public interface IFoo {
public class Bar : Java.Lang.Object { }
}
```

However this means we no longer generate the top-level class that existing code may depend on.

Setting `<AndroidBoundInterfacesContainTypes>false</AndroidBoundInterfacesContainTypes>` will globally
revert to the old behavior.

If you wish to use a hybrid approach, for example, to keep existing nested types moved to a top-level
type, but allow any future nested types to remain nested, you can specify this at the `interface` level using
`metadata` to set the `unnest` attribute.

Setting it to `true` will result in "un-nesting" any nested types (legacy behavior):

```xml
<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):

```xml
<attr path="/api/package[@name='my.package']/interface[@name='Foo']" name="unnest">false</attr>
```

Using this approach, you could leave `<AndroidBoundInterfacesContainTypes>` as `true` and set `unnest` to
`true` for every `interface` with nested types you have **today**. These will always remain top-level
types, while any new nested types introduced later will be nested.

### Static and Default Interface Members (DIM)

Traditionally, C# has not allowed interfaces to contain `static` members and `default` methods.

```java
public interface Foo {
public static void Bar () { ... }
public default void Baz () { ... }
}
```

Static members on interfaces has been supported by moving them to a sibling `class`:

```csharp
public interface IFoo { }

public class Foo {
public static void Bar () { ... }
}
```

`default` interface methods have traditionally not been bound, since they are not required and there
wasn't a C# construct to support them.

With C# 8, `static` and `default` members are supported on interfaces, mirroring the Java interface:

```csharp
public interface IFoo {
public static void Bar () { ... }
public default void Baz () { ... }
}
```

However this means the alternative sibling `class` containing `static` members will no longer be generated.

Setting `<AndroidBoundInterfacesContainStaticAndDefaultInterfaceMethods>false</AndroidBoundInterfacesContainStaticAndDefaultInterfaceMethods>` will globally
revert to the old behavior.


### Nullable Reference Types

Support for Nullable Reference Types (NRT) was added in [Xamarin.Android 11.0][11-0-release-notes].

This continues to be enabled/disabled using the same mechanism as all .NET projects:

```xml
<PropertyGroup>
<Nullable>enable</Nullable>
</PropertyGroup>
```

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

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


[11-0-release-notes]: https://docs.microsoft.com/en-us/xamarin/android/release-notes/11/11.0
29 changes: 29 additions & 0 deletions Documentation/guides/building-apps/build-properties.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,32 @@ implements a .NET type or interface in terms of Java types, for example

Added in Xamarin.Android 10.2.

## AndroidBoundInterfacesContainConstants

A boolean property that
determines whether binding constants on interfaces will be supported,
or the workaround of creating an `IMyInterfaceConsts` class
will be used.

Defaults to `True` in .NET 6 and `False` for legacy.

## AndroidBoundInterfacesContainStaticAndDefaultInterfaceMethods

A boolean property that
whether default and static members on interfaces will be supported,
or old workaround of creating a sibling class containing static
members like `abstract class MyInterface`.

Defaults to `True` in .NET 6 and `False` for legacy.

## AndroidBoundInterfacesContainTypes

A boolean property that
whether types nested in interfaces will be supported, or the workaround
of creating a non-nested type like `IMyInterfaceMyNestedClass`.

Defaults to `True` in .NET 6 and `False` for legacy.

## AndroidBuildApplicationPackage

A boolean value that
Expand Down Expand Up @@ -263,6 +289,9 @@ The benefits of `XAJavaInterop1` include:

The default value is `XAJavaInterop1`.

Support for `XamarinAndroid` is obsolete, and support for `XamarinAndroid` will be removed
as part of .NET 6.

## AndroidCreatePackagePerAbi

A boolean property that determines if a *set* of files -- on per ABI
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ It is shared between "legacy" binding projects and .NET 5 projects.
ToolPath="$(MonoAndroidToolsDirectory)"
ToolExe="$(BindingsGeneratorToolExe)"
LangVersion="$(LangVersion)"
EnableInterfaceMembersPreview="$(_EnableInterfaceMembers)"
EnableBindingStaticAndDefaultInterfaceMethods="$(AndroidBoundInterfacesContainStaticAndDefaultInterfaceMethods)"
EnableBindingNestedInterfaceTypes="$(AndroidBoundInterfacesContainTypes)"
EnableBindingInterfaceConstants="$(AndroidBoundInterfacesContainConstants)"
Nullable="$(Nullable)"
/>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,24 @@
<GenerateDependencyFile Condition=" '$(GenerateDependencyFile)' == '' ">false</GenerateDependencyFile>
<CopyLocalLockFileAssemblies Condition=" '$(CopyLocalLockFileAssemblies)' == '' ">false</CopyLocalLockFileAssemblies>
<ComputeNETCoreBuildOutputFiles Condition=" '$(ComputeNETCoreBuildOutputFiles)' == '' ">false</ComputeNETCoreBuildOutputFiles>
<!-- jar2xml is not supported -->
<AndroidClassParser>class-parse</AndroidClassParser>
<!-- mono-symbolicate is not supported -->
<MonoSymbolArchive>false</MonoSymbolArchive>
<AndroidCodegenTarget Condition=" '$(AndroidCodegenTarget)' == '' ">XAJavaInterop1</AndroidCodegenTarget>
<!--
Disable @(Content) from referenced projects
See: https://github.com/dotnet/sdk/blob/955c0fc7b06e2fa34bacd076ed39f61e4fb61716/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Publish.targets#L16
-->
<_GetChildProjectCopyToPublishDirectoryItems>false</_GetChildProjectCopyToPublishDirectoryItems>
<UseMonoRuntime Condition=" '$(UseMonoRuntime)' == '' ">true</UseMonoRuntime>

<!-- Bindings properties -->
<!-- jar2xml is not supported -->
<AndroidClassParser>class-parse</AndroidClassParser>
<!-- Anything other than XAJavaInterop1 is not supported -->
<AndroidCodegenTarget>XAJavaInterop1</AndroidCodegenTarget>
<AndroidBoundInterfacesContainStaticAndDefaultInterfaceMethods Condition=" '$(AndroidBoundInterfacesContainStaticAndDefaultInterfaceMethods)' == '' ">true</AndroidBoundInterfacesContainStaticAndDefaultInterfaceMethods>
<AndroidBoundInterfacesContainTypes Condition=" '$(AndroidBoundInterfacesContainTypes)' == '' ">true</AndroidBoundInterfacesContainTypes>
<AndroidBoundInterfacesContainConstants Condition=" '$(AndroidBoundInterfacesContainConstants)' == '' ">true</AndroidBoundInterfacesContainConstants>

<!-- Mono components -->
<AndroidEnableProfiler Condition=" '$(AndroidEnableProfiler)' == ''">false</AndroidEnableProfiler>
</PropertyGroup>
Expand Down
12 changes: 9 additions & 3 deletions src/Xamarin.Android.Build.Tasks/Tasks/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ public class BindingsGenerator : AndroidDotnetToolTask

public string LangVersion { get; set; }

public bool EnableInterfaceMembersPreview { get; set; }
public bool EnableBindingStaticAndDefaultInterfaceMethods { get; set; }
public bool EnableBindingNestedInterfaceTypes { get; set; }
public bool EnableBindingInterfaceConstants { get; set; }
public string Nullable { get; set; }

public ITaskItem[] TransformFiles { get; set; }
Expand Down Expand Up @@ -172,10 +174,14 @@ protected override string GenerateCommandLineCommands ()
if (SupportsCSharp8) {
var features = new List<string> ();

if (EnableInterfaceMembersPreview) {
if (EnableBindingInterfaceConstants)
features.Add ("interface-constants");

if (EnableBindingNestedInterfaceTypes)
features.Add ("nested-interface-types");

if (EnableBindingStaticAndDefaultInterfaceMethods)
features.Add ("default-interface-methods");
}

if (string.Equals (Nullable, "enable", StringComparison.OrdinalIgnoreCase))
features.Add ("nullable-reference-types");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ Copyright (C) 2012 Xamarin Inc. All rights reserved.
<_AndroidIsBindingProject>True</_AndroidIsBindingProject>
</PropertyGroup>

<!-- Convert legacy DIM preview flag into new Enable* flags -->
<PropertyGroup Condition="'$(_EnableInterfaceMembers)' == 'True'">
<AndroidBoundInterfacesContainStaticAndDefaultInterfaceMethods Condition=" '$(AndroidBoundInterfacesContainStaticAndDefaultInterfaceMethods)' == '' ">True</AndroidBoundInterfacesContainStaticAndDefaultInterfaceMethods>
<AndroidBoundInterfacesContainTypes Condition=" '$(AndroidBoundInterfacesContainTypes)' == '' ">True</AndroidBoundInterfacesContainTypes>
<AndroidBoundInterfacesContainConstants Condition=" '$(AndroidBoundInterfacesContainConstants)' == '' ">True</AndroidBoundInterfacesContainConstants>
</PropertyGroup>

<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />

<PropertyGroup>
Expand Down