Skip to content

[Xamarin.Android.Build.Tasks] Add Support for AndroidJavaSource to Bindings #5926

New issue

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

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

Already on GitHub? # to your account

Merged
merged 10 commits into from
May 12, 2022
Merged
4 changes: 1 addition & 3 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
{
"nxunitExplorer.modules": [
"bin/TestDebug/MSBuildDeviceIntegration/net472/MSBuildDeviceIntegration.dll",
"bin/TestDebug/net472/Xamarin.Android.Build.Tests.dll",
"bin/TestRelease/MSBuildDeviceIntegration/net472/MSBuildDeviceIntegration.dll",
"bin/Test*/**/net472/*.dll",
],
"cmake.configureOnOpen": false,
"dotnetCoreExplorer.searchpatterns": "bin/Test{Debug,Release}/**/net6.0/{Xamarin.Android.Build.Tests,MSBuildDeviceIntegration}.dll",
Expand Down
103 changes: 103 additions & 0 deletions Documentation/guides/BindingJavaSource.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
---
id:
title: "Binding Java Source"
dateupdated: 2021-05-21
---

# Overview

With .net 6 Bindings can be done on not only `.jar` and `.aar` files
but also raw `.java` code. This will allow developers of bindings
or applications to write custom API's for underlying bindings
and expose them easily to the developer.

Applications already had the ability to include
[`AndroidJavaSource`](https://docs.microsoft.com/en-us/xamarin/android/deploy-test/building-apps/build-items#androidjavasource)
items in them, these would then be compiled into the final `classes.dex`.
However this custom java source code was not "bound" and if users wanted
to call these classes they would need to do that manually.

Starting with .NET 7, all `*.java` files within the project directory are
automatically added to the `AndroidJavaSource` item group. Additionally,
`AndroidJavaSource` now supports `Bind` item metadata. This will instruct
the build system to not only compile the code but also produce a C# API for it.

The `%(Bind)` attribute metadata is `True` by default. If you do not want
to bind the Java code, set `%(Bind)` to `False`.

```xml
<ItemGroup>
<AndroidJavaSource Update="MyClass.java" Bind="False" />
</ItemGroup>
```

# Example

Consider the following Java code.

```java
package com.xamarin.test;

class MyClass {
public boolean isDave (String name)
{
return name.equals ("Dave");
}
}
```

We want to bind this into a C# API in our app project. By default
the app project will pick up ALL `.java` files and add them to
the `AndroidJavaSource` ItemGroup. The `Bind` attribute is `true`
by default as well so it will be automatically bound.

```xml
<!-- No changes needed in .NET 7; `MyClass.java` is automatically included. -->
```

When we build the app, the java code will be compiled into a
`.jar` file which matches the application project name.
This `.jar` will then be bound and the generated C# API will end
up being something like this.

```csharp
using System;

namespace Com.Xamarin.Test {
public class MyClass : Java.Lang.Object {
public virtual bool IsDave (string name) => …;
}
}
```

All this will happen before the main C# code is compiled. The binding
code will be included in the C# compile process, so you can use the
code directly in your app.

```csharp
public class MainActivity : Activity {
public override void OnCreate()
{
var c = new MyClass ();
c.IsDave ("Bob");
}
}
```


# Limitations

This feature is only available in .NET 7.

You will be limited to standard java types and any types that
are available in a `.jar` or `.aar` which you reference.

You *should not* use Java Generics. The Binding process currently does not
really support Java Generic very well. Stick to primitive types
or normal classes as much as possible, so that you don't need to use
[`metadata.xml`](https://docs.microsoft.com/en-us/xamarin/android/platform/binding-java-library/customizing-bindings/java-bindings-metadata)
to alter the API of the Java class.

Because of the point at which the Java compilation happens
(before the `<Csc/>` task), you will not be able to access any of the
`R.*` Resource types either. This may be addressed in a later release.
9 changes: 8 additions & 1 deletion Documentation/guides/building-apps/build-items.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ ms.assetid: 5EBEE1A5-3879-45DD-B1DE-5CD4327C2656
ms.technology: xamarin-android
author: jonpryor
ms.author: jopryo
ms.date: 03/29/2022
ms.date: 05/11/2022
---

# Build Items
Expand Down Expand Up @@ -89,6 +89,13 @@ package.
Files with a Build action of `AndroidJavaSource` are Java source code which
will be included in the final Android package.

Starting with .NET 7, all `**\*.java` files within the project directory
automatically have a Build action of `AndroidJavaSource`, *and* will be
bound prior to the Assembly build. This allows C# code to easily use
types and members present within the `**\*.java` files.

Set `%(AndroidJavaSource.Bind)` to False to disable this behavior.

## AndroidLibrary

**AndroidLibrary** is a new build action for simplifying how
Expand Down
1 change: 1 addition & 0 deletions build-tools/installers/create-installers.targets
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@
<_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.DX.targets" ExcludeFromAndroidNETSdk="true" />
<_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.EmbeddedResource.targets" />
<_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.FSharp.targets" ExcludeFromAndroidNETSdk="true" />
<_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.Javac.targets" />
<_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.Legacy.targets" ExcludeFromAndroidNETSdk="true" />
<_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.PCLSupport.props" ExcludeFromAndroidNETSdk="true" />
<_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.PCLSupport.targets" ExcludeFromAndroidNETSdk="true" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ This item group populates the Build Action drop-down in IDEs.
<Bind>true</Bind>
<Pack Condition=" '$(UsingAndroidNETSdk)' == 'true' ">true</Pack>
</AndroidLibrary>
<AndroidJavaSource>
<Bind Condition=" '$(UsingAndroidNETSdk)' == 'true' ">true</Bind>
</AndroidJavaSource>
<AndroidAarLibrary>
<!-- NOTE: .aar items should skip %(AndroidSkipResourceProcessing) by default -->
<AndroidSkipResourceProcessing>true</AndroidSkipResourceProcessing>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ This file is only used by binding projects.
<FileWrites Include="$(IntermediateOutputPath)class-parse.rsp" />
<!-- Created by <BindingGenerator /> -->
<FileWrites Condition="Exists ('$(IntermediateOutputPath)java-resolution-report.log')" Include="$(IntermediateOutputPath)java-resolution-report.log" />
</ItemGroup>
</ItemGroup>
</Target>

<Target Name="_GetJavaSourceJarJavadocFiles"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ It is shared between "legacy" binding projects and .NET 5 projects.
<GeneratedOutputPath Condition=" '$(GeneratedOutputPath)' == '' ">$(IntermediateOutputPath)generated\</GeneratedOutputPath>
<AndroidJavadocVerbosity Condition=" '$(AndroidJavadocVerbosity)' == '' ">intellisense</AndroidJavadocVerbosity>
<ApiOutputFile Condition=" '$(ApiOutputFile)' == '' ">$(IntermediateOutputPath)api.xml</ApiOutputFile>
<_GeneratorStampFile>$(IntermediateOutputPath)generator.stamp</_GeneratorStampFile>
<_GeneratorStampFile>$(_AndroidStampDirectory)generator.stamp</_GeneratorStampFile>
</PropertyGroup>

<PropertyGroup Condition=" '$(_UseLegacyJavadocImport)' != 'true' ">
Expand All @@ -29,7 +29,7 @@ It is shared between "legacy" binding projects and .NET 5 projects.
</PropertyGroup>

<Target Name="_SetAndroidGenerateManagedBindings"
Condition=" '@(InputJar->Count())' != '0' Or '@(EmbeddedJar->Count())' != '0' Or '@(LibraryProjectZip->Count())' != '0' ">
Condition=" '@(InputJar->Count())' != '0' Or '@(EmbeddedJar->Count())' != '0' Or '@(LibraryProjectZip->Count())' != '0' Or '@(_JavaBindingSource->Count())' != '0' ">
<PropertyGroup>
<!-- Used throughout to determine if C# binding-related targets should skip -->
<_AndroidGenerateManagedBindings>true</_AndroidGenerateManagedBindings>
Expand Down Expand Up @@ -115,15 +115,17 @@ It is shared between "legacy" binding projects and .NET 5 projects.
<Delete Files="$(_GeneratorStampFile)" />
<Delete Files="@(IntermediateAssembly->'$(OutputPath)%(filename).xml')" />
<Delete Files="@(IntermediateAssembly->'$(IntermediateOutputPath)%(filename).xml')" />
<Delete Files="$(_AndroidIntermediateBindingClassesZip)" />
<RemoveDirFixed Directories="$(GeneratedOutputPath)" />
<RemoveDirFixed Directories="$(_AndroidStampDirectory)" />
<RemoveDirFixed Directories="$(IntermediateOutputPath)docs" />
<RemoveDirFixed Directories="$(IntermediateOutputPath)javadocs" />
<RemoveDirFixed Directories="$(IntermediateOutputPath)javasources" />
<RemoveDirFixed Directories="$(_AndroidIntermediateBindingJavaClassDirectory)" />
</Target>

<Target Name="CleanLibraryProjectIntermediaries">
<Delete Files="$(IntermediateOutputPath)__AndroidLibraryProjects__.zip" />
<Delete Files="$(_AndroidStampDirectory)_ResolveLibraryProjectImports.stamp" />
<Delete Files="$(_AndroidLibraryImportsCache)" />
<Delete Files="$(_AndroidLibraryProjectImportsCache)" />
<Delete Files="$(_AndroidLibrayProjectAssemblyMapFile)" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
<!--
***********************************************************************************************
Xamarin.Android.Javac.targets

This file contains the core MSBuild logic for Xamarin.Android Java Invocations.

It is shared between "legacy" binding projects and .NET 7+ projects.

***********************************************************************************************
-->

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

<UsingTask TaskName="Xamarin.Android.Tasks.AdjustJavacVersionArguments" AssemblyFile="Xamarin.Android.Build.Tasks.dll" />
<UsingTask TaskName="Xamarin.Android.Tasks.DetermineJavaLibrariesToCompile" AssemblyFile="Xamarin.Android.Build.Tasks.dll" />
<UsingTask TaskName="Xamarin.Android.Tasks.Javac" AssemblyFile="Xamarin.Android.Build.Tasks.dll" />

<PropertyGroup>
<_AndroidIntermediateBindingJavaClassDirectory>$(IntermediateOutputPath)binding\bin\classes\</_AndroidIntermediateBindingJavaClassDirectory>
<_AndroidIntermediateBindingJavaSourceDirectory>$(IntermediateOutputPath)binding\src</_AndroidIntermediateBindingJavaSourceDirectory>
<_AndroidIntermediateBindingClassesZip>$(IntermediateOutputPath)binding\bin\$(MSBuildProjectName).jar</_AndroidIntermediateBindingClassesZip>
<_AndroidCompileJavaStampFile>$(_AndroidStampDirectory)_CompileJava.stamp</_AndroidCompileJavaStampFile>
<_AndroidCompileBindingJavaStampFile>$(_AndroidStampDirectory)_CompileBindingJava.stamp</_AndroidCompileBindingJavaStampFile>
</PropertyGroup>

<Target Name="_AdjustJavacVersionArguments">
<AdjustJavacVersionArguments
Condition=" '$(JavacTargetVersion)' == '' or '$(JavacSourceVersion)' == '' "
JdkVersion="$(_JdkVersion)"
DefaultJdkVersion="$(_DefaultJdkVersion)"
SkipJavacVersionCheck="$(AndroidSkipJavacVersionCheck)"
EnableProguard="$(AndroidEnableProguard)"
EnableMultiDex="$(AndroidEnableMultiDex)">
<Output TaskParameter="TargetVersion" PropertyName="JavacTargetVersion" />
<Output TaskParameter="SourceVersion" PropertyName="JavacSourceVersion" />
</AdjustJavacVersionArguments>
</Target>

<Target Name="_DetermineBindingJavaLibrariesToCompile" Condition=" '@(_JavaBindingSource->Count())' != '0' ">
<DetermineJavaLibrariesToCompile
MonoPlatformJarPaths="$(MonoPlatformJarPath);$(_RuntimeJar)"
JavaSourceFiles="@(_JavaBindingSource)"
JavaLibraries="@(AndroidJavaLibrary);@(EmbeddedJar)"
ExternalJavaLibraries="@(AndroidExternalJavaLibrary)"
LibraryProjectJars="@(ExtractedJarImports)"
DoNotPackageJavaLibraries="@(_ResolvedDoNotPackageAttributes)"
EnableInstantRun="$(_InstantRunEnabled)">
<Output TaskParameter="JavaLibrariesToCompile" ItemName="_BindingJavaLibrariesToCompile" />
<Output TaskParameter="ReferenceJavaLibraries" ItemName="_ReferenceJavaLibs" />
</DetermineJavaLibrariesToCompile>
</Target>

<Target Name="_DetermineJavaLibrariesToCompile">

<DetermineJavaLibrariesToCompile
MonoPlatformJarPaths="$(MonoPlatformJarPath);$(_RuntimeJar)"
JavaSourceFiles="@(AndroidJavaSource)"
JavaLibraries="@(AndroidJavaLibrary)"
ExternalJavaLibraries="@(AndroidExternalJavaLibrary)"
LibraryProjectJars="@(ExtractedJarImports)"
DoNotPackageJavaLibraries="@(_ResolvedDoNotPackageAttributes)"
EnableInstantRun="$(_InstantRunEnabled)">
<Output TaskParameter="JavaLibrariesToCompile" ItemName="_JavaLibrariesToCompile" />
<Output TaskParameter="ReferenceJavaLibraries" ItemName="_ReferenceJavaLibs" />
</DetermineJavaLibrariesToCompile>

<ItemGroup>
<_JavaLibrariesToCompileForApp Include="@(_JavaLibrariesToCompile)" Condition=" '$(_InstantRunEnabled)' != 'True' " />
<AndroidExternalJavaLibrary Include="@(_JavaLibrariesToCompile)" Condition=" '$(_InstantRunEnabled)' == 'True' " />
</ItemGroup>
</Target>

<Target Name="_CollectJavaSourceForBinding">
<ItemGroup>
<_JavaBindingSource Include="@(AndroidJavaSource)" Condition=" '%(AndroidJavaSource.Bind)' == 'True' " />
</ItemGroup>
</Target>

<Target Name="_CollectJavaSource">
<ItemGroup>
<_JavaSource Include="@(AndroidJavaSource)" Condition=" '%(AndroidJavaSource.Bind)' != 'True' " />
</ItemGroup>
</Target>

<Target Name="_CompileBindingJava"
Condition=" '@(_JavaBindingSource->Count())' != '0' "
DependsOnTargets="$(_CompileBindingJavaDependsOnTargets)"
Inputs="@(_AndroidMSBuildAllProjects);$(MonoPlatformJarPath);@(_JavaBindingSource)"
Outputs="$(_AndroidCompileBindingJavaStampFile)">

<!-- remove existing <Javac /> outputs, since *.class files and classes.zip could contain old files -->
<RemoveDirFixed Directories="$(_AndroidIntermediateBindingJavaClassDirectory)" Condition="Exists('$(_AndroidIntermediateBindingJavaClassDirectory)')" />
<Delete Files="$(_AndroidIntermediateBindingClassesZip)" Condition="Exists('$(_AndroidIntermediateBindingClassesZip)')" />
<!-- Compile java code -->
<Javac
Condition=" '@(_JavaBindingSource->Count())' != '0' "
JavaPlatformJarPath="$(JavaPlatformJarPath)"
ClassesOutputDirectory="$(_AndroidIntermediateBindingJavaClassDirectory)"
ClassesZip="$(_AndroidIntermediateBindingClassesZip)"
StubSourceDirectory="$(_AndroidIntermediateBindingJavaSourceDirectory)"
JavaSourceFiles="@(_JavaBindingSource)"
ToolPath="$(JavacToolPath)"
ToolExe="$(JavacToolExe)"
Jars="@(_BindingJavaLibrariesToCompile);@(_ReferenceJavaLibs)"
JavacTargetVersion="$(JavacTargetVersion)"
JavacSourceVersion="$(JavacSourceVersion)"
/>
<Touch Files="$(_AndroidCompileBindingJavaStampFile)" AlwaysCreate="true" />
<ItemGroup>
<EmbeddedJar Include="$(_AndroidIntermediateBindingClassesZip)" Condition="Exists('$(_AndroidIntermediateBindingClassesZip)')" />
<AndroidJavaLibrary Include="$(_AndroidIntermediateBindingClassesZip)" Condition="Exists('$(_AndroidIntermediateBindingClassesZip)')" />
<FileWrites Include="$(_AndroidIntermediateBindingClassesZip)" Condition="Exists('$(_AndroidIntermediateBindingClassesZip)')" />
</ItemGroup>
</Target>

<Target Name="_CompileJava"
DependsOnTargets="$(_CompileJavaDependsOnTargets);_CollectJavaSource"
Inputs="@(_AndroidMSBuildAllProjects);$(MonoPlatformJarPath);@(_JavaStubFiles);@(_JavaSource)"
Outputs="$(_AndroidCompileJavaStampFile)">

<!-- remove existing <Javac /> outputs, since *.class files and classes.zip could contain old files -->
<RemoveDirFixed Directories="$(_AndroidIntermediateJavaClassDirectory)" />
<Delete Files="$(_AndroidIntermediateClassesZip)" />

<!-- Compile java code -->
<Javac
JavaPlatformJarPath="$(JavaPlatformJarPath)"
ClassesOutputDirectory="$(_AndroidIntermediateJavaClassDirectory)"
ClassesZip="$(_AndroidIntermediateClassesZip)"
StubSourceDirectory="$(_AndroidIntermediateJavaSourceDirectory)"
JavaSourceFiles="@(_JavaSource)"
ToolPath="$(JavacToolPath)"
ToolExe="$(JavacToolExe)"
Jars="@(_JavaLibrariesToCompile);@(_InstantRunJavaReference);@(_ReferenceJavaLibs)"
JavacTargetVersion="$(JavacTargetVersion)"
JavacSourceVersion="$(JavacSourceVersion)"
/>

<Touch Files="$(_AndroidCompileJavaStampFile)" AlwaysCreate="true" />
</Target>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ https://github.com/dotnet/designs/blob/4703666296f5e59964961464c25807c727282cae/
<AndroidLibrary Include="**\*.aar" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" />
<AndroidNativeLibrary Include="**\*.so" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" />
<JavaSourceJar Include="$(_DefaultJavaSourceJarPattern)" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" />
<AndroidJavaSource Include="**\*.java" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,8 @@ projects.
<Touch Files="$(_AndroidStampDirectory)_ExtractAar.stamp" AlwaysCreate="true" />
</Target>

<Target Name="_CleanAarCache">
<Delete Files="$(_AarCacheFile)" />
</Target>

</Project>
Loading