Skip to content

Commit 4696bf3

Browse files
authored
[Xamarin.Android.Build.Tasks] Enable POM verification features. (#8649)
Fixes: #4528 Context: 3659766 Context: dotnet/java-interop@1c9c8c9 Commit 3659766 mentioned: > There are 2 parts to #4528: > > 1. Downloading artifacts from Maven > 2. Ensuring all dependencies specified in the POM file are met > > Only (1) is currently addressed. (2) will be addressed in the future. Implement support for (2): use `Java.Interop.Tools.Maven.dll` from dotnet/java-interop@1c9c8c9c to download and parse [Maven POM files][0] and ensure that all required Java dependencies are fulfilled. POM files are downloaded into `$(MavenCacheDirectory)` (3659766). Java dependency verification is a critical step that users often miss or make mistakes, resulting in non-functional bindings. Consider a classlib project with the following snippet: <ItemGroup> <AndroidMavenLibrary Include="com.squareup.okhttp3:okhttp" Version="4.9.3" /> </ItemGroup> With the new POM verification features, the above snippet will produce the following errors: error XA4242: Java dependency 'com.squareup.okio:okio:2.8.0' is not satisfied. Microsoft maintains the NuGet package 'Square.OkIO' that could fulfill this dependency. error XA4242: Java dependency 'org.jetbrains.kotlin:kotlin-stdlib:1.4.10' is not satisfied. Microsoft maintains the NuGet package 'Xamarin.Kotlin.StdLib' that could fulfill this dependency. This is accomplished by automatically downloading the POM file (and any needed parent/imported POM files) for the specified artifact and using them to determine all required dependencies. There are many documented ways of fixing these errors, the best way is using the latest versions of the suggested NuGet packages: <ItemGroup> <PackageReference Include="Square.OkIO" Version="3.6.0.1" /> <PackageReference Include="Xamarin.Kotlin.StdLib" Version="1.9.22" /> </ItemGroup> Note this commit replaces our previous usage of `MavenNet` with `Java.Interop.Tools.Maven`. Thus it removes the `MavenNet` TPN. Some future concerns: - Can we automatically determine which dependencies are met by a `@(ProjectReference)`? Today, the user must manually add the metadata `%(ProjectReference.JavaArtifact)` and `%(ProjectReference.JavaVersion)` to specify this. - We use the link https://aka.ms/ms-nuget-packages to download [`microsoft-packages.json`][1], which is used to provide NuGet suggestions. We need to figure out a permanent home for this file and a process for generating it. Luckily we can ship a preview for now and change the `aka.ms` link to point to the permanent home later. [0]: https://maven.apache.org/pom.html [1]: https://raw.githubusercontent.com/jpobst/Prototype.Android.MavenBindings/main/microsoft-packages.json
1 parent e206c52 commit 4696bf3

35 files changed

+2333
-290
lines changed

Documentation/guides/AndroidMavenLibrary.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ Note: This feature is only available in .NET 9+.
1717
</ItemGroup>
1818
```
1919

20-
This will do two things at build time:
20+
This will do several things at build time:
2121
- Download the Java [artifact](https://central.sonatype.com/artifact/com.squareup.okhttp3/okhttp/4.9.3) with group id `com.squareup.okhttp3`, artifact id `okhttp`, and version `4.9.3` from [Maven Central](https://central.sonatype.com/) to a local cache (if not already cached).
2222
- Add the cached package to the .NET Android bindings build as an [`<AndroidLibrary>`](https://github.com/xamarin/xamarin-android/blob/main/Documentation/guides/building-apps/build-items.md#androidlibrary).
23+
- Download the Java artifact's POM file (and any needed parent/imported POM files) to enable [Java Dependency Verification](JavaDependencyVerification.md). To opt out of this feature, add `VerifyDependencies="false"` to the `<AndroidMavenLibrary>` item.
2324

2425
Note that only the requested Java artifact is added to the .NET Android bindings build. Any artifact dependencies are not added. If the requested artifact has dependencies, they must be fulfilled individually.
2526

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# Java Dependency Verification
2+
3+
Note: This feature is only available in .NET 9+.
4+
5+
## Description
6+
7+
A common problem when creating Java binding libraries for .NET Android is not providing the required Java dependencies. The binding process ignores API that requires missing dependencies, so this can result in large portions of desired API not being bound.
8+
9+
Unlike .NET assemblies, a Java library does not specify its dependencies in the package. The dependency information is stored in external files called POM files. In order to consume this information to ensure correct dependencies an additional layer of files must be added to a binding project.
10+
11+
Note: the preferred way of interacting with this system is to use [`<AndroidMavenLibrary>`](AndroidMavenLibrary.md) which will automatically download any needed POM files.
12+
13+
For example:
14+
15+
```xml
16+
<AndroidMavenLibrary Include="com.squareup.okio:okio" Version="1.17.4" />
17+
```
18+
19+
automatically gets expanded to:
20+
21+
```xml
22+
<AndroidLibrary
23+
Include="<MavenCacheDir>/Central/com.squareup.okio/okio/1.17.4/com.squareup.okio_okio.jar"
24+
Manifest="<MavenCacheDir>/Central/com.squareup.okio/okio/1.17.4/com.squareup.okio_okio.pom"
25+
JavaArtifact="com.squareup.okio:okio"
26+
JavaVersion="1.17.4" />
27+
28+
<AndroidAdditionalJavaManifest
29+
Include="<MavenCacheDir>/Central/com.squareup.okio/okio-parent/1.17.4/okio-parent-1.17.4.pom"
30+
JavaArtifact="com.squareup.okio:okio-parent"
31+
JavaVersion="1.17.4" />
32+
33+
etc.
34+
```
35+
36+
However it is also possible to manually opt in to Java dependency verification using the build items documented here.
37+
38+
## Specification
39+
40+
To manually opt in to Java dependency verification, add the `Manifest`, `JavaArtifact`, and `JavaVersion` attributes to an `<AndroidLibrary>` item:
41+
42+
```xml
43+
<!-- JavaArtifact format is {GroupId}:{ArtifactId} -->
44+
<ItemGroup>
45+
<AndroidLibrary
46+
Include="my_binding_library.jar"
47+
Manifest="my_binding_library.pom"
48+
JavaArtifact="com.example:mybinding"
49+
JavaVersion="1.0.0" />
50+
</ItemGroup>
51+
```
52+
53+
Building the binding project now should result in verification errors if `my_binding_library.pom` specifies dependencies that are not met.
54+
55+
For example:
56+
57+
```
58+
error : Java dependency 'androidx.collection:collection' version '1.0.0' is not satisfied.
59+
```
60+
61+
Seeing these error(s) or no errors should indicate that the Java dependency verification is working. Follow the [Resolving Java Dependencies](ResolvingJavaDependencies.md) guide to fix any missing dependency errors.
62+
63+
## Additional POM Files
64+
65+
POM files can sometimes have some optional features in use that make them more complicated than the above example.
66+
67+
That is, a POM file can depend on a "parent" POM file:
68+
69+
```xml
70+
<parent>
71+
<groupId>com.squareup.okio</groupId>
72+
<artifactId>okio-parent</artifactId>
73+
<version>1.17.4</version>
74+
</parent>
75+
```
76+
77+
Additionally, a POM file can "import" dependency information from another POM file:
78+
79+
```xml
80+
<dependencyManagement>
81+
<dependencies>
82+
<dependency>
83+
<groupId>com.squareup.okio</groupId>
84+
<artifactId>okio-bom</artifactId>
85+
<version>3.0.0</version>
86+
<type>pom</type>
87+
<scope>import</scope>
88+
</dependency>
89+
</dependencies>
90+
</dependencyManagement>
91+
```
92+
93+
Dependency information cannot be accurately determined without also having access to these additional POM files, and will results in an error like:
94+
95+
```
96+
error : Unable to resolve POM for artifact 'com.squareup.okio:okio-parent:1.17.4'.
97+
```
98+
99+
In this case, we need to provide the POM file for `com.squareup.okio:okio-parent:1.17.4`:
100+
101+
```xml
102+
<!-- JavaArtifact format is {GroupId}:{ArtifactId} -->
103+
<ItemGroup>
104+
<AndroidAdditionalJavaManifest
105+
Include="com.square.okio.okio-parent.1.17.4.pom"
106+
JavaArtifact="com.squareup.okio:okio-parent"
107+
JavaVersion="1.17.4" />
108+
</ItemGroup>
109+
```
110+
111+
Note that as "Parent" and "Import" POMs can themselves have parent and imported POMs, this step may need to be repeated until all POM files can be resolved.
112+
113+
Note also that if using `<AndroidMavenLibrary>` this should all be handled automatically.
114+
115+
At this point, if there are dependency errors, follow the [Resolving Java Dependencies](ResolvingJavaDependencies.md) guide to fix any missing dependency errors.
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# Resolving Java Dependencies
2+
3+
Note: This feature is only available in .NET 9+.
4+
5+
## Description
6+
7+
Once Java dependency verification has been enabled for a bindings project, either automatically via `<AndroidMavenLibrary>` or manually via `<AndroidLibrary>`, there may be errors to resolve, such as:
8+
9+
```
10+
error : Java dependency 'androidx.collection:collection' version '1.0.0' is not satisfied.
11+
```
12+
13+
These dependencies can be fulfilled in many different ways.
14+
15+
## `<PackageReference>`
16+
17+
In the best case scenario, there is already an existing binding of the Java dependency available on NuGet.org. This package may be provided by Microsoft or the .NET community. Packages maintained by Microsoft may be surfaced in the error message like this:
18+
19+
```
20+
error : Java dependency 'androidx.collection:collection' version '1.0.0' is not satisfied. Microsoft maintains the NuGet package 'Xamarin.AndroidX.Collection' that could fulfill this dependency.
21+
```
22+
23+
Adding the `Xamarin.AndroidX.Collection` package to the project should automatically resolve this error, as the package provides metadata to advertise that it provides the `androidx.collection:collection` dependency. This is done by looking for a specially crafted NuGet tag. For example, for the AndroidX Collection library, the tag looks like this:
24+
25+
```xml
26+
<!-- artifact={GroupId}:{ArtifactId}:{Java Library Version} -->
27+
<PackageTags>artifact=androidx.collection:collection:1.0.0</PackageTags>
28+
```
29+
30+
However there may be NuGet packages which fulfill a dependency but have not had this metadata added to it. In this case, you will need to explicitly specify which dependency the package contains with `JavaArtifact` and `JavaVersion`:
31+
32+
```xml
33+
<PackageReference
34+
Include="Xamarin.Kotlin.StdLib"
35+
Version="1.7.10"
36+
JavaArtifact="org.jetbrains.kotlin:kotlin-stdlib"
37+
JavaVersion="1.7.10" />
38+
```
39+
40+
With this, the binding process knows the Java dependency is satisfied by the NuGet package.
41+
42+
> Note: NuGet packages specify their own dependencies, so you will not need to worry about transitive dependencies.
43+
44+
## `<ProjectReference>`
45+
46+
If the needed Java dependency is provided by another project in your solution, you can annotate the `<ProjectReference>` to specify the dependency it fulfills:
47+
48+
```xml
49+
<ProjectReference
50+
Include="..\My.Other.Binding\My.Other.Binding.csproj"
51+
JavaArtifact="my.other.binding:helperlib"
52+
JavaVersion="1.0.0" />
53+
```
54+
55+
With this, the binding process knows the Java dependency is satisfied by the referenced project.
56+
57+
> Note: Each project specifies their own dependencies, so you will not need to worry about transitive dependencies.
58+
59+
## `<AndroidLibrary>`
60+
61+
If you are creating a public NuGet package, you will want to follow NuGet's "one library per package" policy so that the NuGet dependency graph works. However, if creating a binding for private use, you may want to include your Java dependencies directly inside the parent binding.
62+
63+
This can be done by adding additional `<AndroidLibrary>` items to the project:
64+
65+
```xml
66+
<ItemGroup>
67+
<AndroidLibrary Include="mydependency.jar" JavaArtifact="my.library:dependency-library" JavaVersion="1.0.0" />
68+
</ItemGroup>
69+
```
70+
71+
To include the Java library but not produce C# bindings for it, mark it with `Bind="false"`:
72+
73+
```xml
74+
<ItemGroup>
75+
<AndroidLibrary Include="mydependency.jar" JavaArtifact="my.library:dependency-library" JavaVersion="1.0.0" Bind="false" />
76+
</ItemGroup>
77+
```
78+
79+
Alternatively, `<AndroidMavenLibrary>` can be used to retrieve a Java library from a Maven repository:
80+
81+
```xml
82+
<ItemGroup>
83+
<AndroidMavenLibrary Include="my.library:dependency-library" Version="1.0.0" />
84+
<!-- or, if the Java library doesn't need to be bound -->
85+
<AndroidMavenLibrary Include="my.library:dependency-library" Version="1.0.0" Bind="false" />
86+
</ItemGroup>
87+
```
88+
89+
> Note: If the dependency library has its own dependencies, you will be required to ensure they are fulfilled.
90+
91+
## `<AndroidIgnoredJavaDependency>`
92+
93+
As a last resort, a needed Java dependency can be ignored. An example of when this is useful is if the dependency library is a collection of Java annotations that are only used at compile type and not runtime.
94+
95+
Note that while the error message will go away, it does not mean the package will magically work. If the dependency is actually needed at runtime and not provided the Android application will crash with a `Java.Lang.NoClassDefFoundError` error.
96+
97+
```xml
98+
<ItemGroup>
99+
<AndroidIgnoredJavaDependency Include="com.google.errorprone:error_prone_annotations" Version="2.15.0" />
100+
</ItemGroup>
101+
```

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

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,30 @@ ms.date: 07/26/2022
1414
Build items control how a Xamarin.Android application
1515
or library project is built.
1616

17+
## AndroidAdditionalJavaManifest
18+
19+
`<AndroidAdditionalJavaManifest>` is used in conjunction with [Java Dependency Resolution](../JavaDependencyVerification.md).
20+
21+
It is used to specify additional POM files that will be needed to verify dependencies.
22+
These are often parent or imported POM files referenced by a Java library's POM file.
23+
24+
```xml
25+
<ItemGroup>
26+
<AndroidAdditionalJavaManifest Include="mylib-parent.pom" JavaArtifact="com.example:mylib-parent" JavaVersion="1.0.0" />
27+
</ItemGroup>
28+
```
29+
30+
The following MSBuild metadata are required:
31+
32+
- `%(JavaArtifact)`: The group and artifact id of the Java library matching the specifed POM
33+
file in the form `{GroupId}:{ArtifactId}`.
34+
- `%(JavaVersion)`: The version of the Java library matching the specified POM file.
35+
36+
See the [Java Dependency Resolution documentation](../JavaDependencyVerification.md)
37+
for more details.
38+
39+
This build action was introduced in .NET 9.
40+
1741
## AndroidAsset
1842

1943
Supports [Android Assets](https://developer.android.com/guide/topics/resources/providing-resources#OriginalFiles),
@@ -115,6 +139,30 @@ Files with a Build action of `AndroidJavaLibrary` are Java
115139
Archives ( `.jar` files) that will be included in the final Android
116140
package.
117141

142+
## AndroidIgnoredJavaDependency
143+
144+
`<AndroidIgnoredJavaDependency>` is used in conjunction with [Java Dependency Resolution](../JavaDependencyVerification.md).
145+
146+
It is used to specify a Java dependency that should be ignored. This can be
147+
used if a dependency will be fulfilled in a way that Java dependency resolution
148+
cannot detect.
149+
150+
```xml
151+
<!-- Include format is {GroupId}:{ArtifactId} -->
152+
<ItemGroup>
153+
<AndroidIgnoredJavaDependency Include="com.google.errorprone:error_prone_annotations" Version="2.15.0" />
154+
</ItemGroup>
155+
```
156+
157+
The following MSBuild metadata are required:
158+
159+
- `%(Version)`: The version of the Java library matching the specified `%(Include)`.
160+
161+
See the [Java Dependency Resolution documentation](../JavaDependencyVerification.md)
162+
for more details.
163+
164+
This build action was introduced in .NET 9.
165+
118166
## AndroidJavaSource
119167

120168
Files with a Build action of `AndroidJavaSource` are Java source code that
@@ -218,6 +266,17 @@ hosted in Maven.
218266
<AndroidMavenLibrary Include="com.squareup.okhttp3:okhttp" Version="4.9.3" />
219267
</ItemGroup>
220268
```
269+
270+
The following MSBuild metadata are supported:
271+
272+
- `%(Version)`: Required version of the Java library referenced by `%(Include)`.
273+
- `%(Repository)`: Optional Maven repository to use. Supported values are `Central` (default),
274+
`Google`, or an `https` URL to a Maven repository.
275+
276+
The `<AndroidMavenLibrary>` item is translated to an
277+
[`<AndroidLibrary>`](https://github.com/xamarin/xamarin-android/blob/main/Documentation/guides/building-apps/build-items.md#androidlibrary)
278+
item, so any metadata supported by `<AndroidLibrary>` like `Bind` or `Pack` are also supported.
279+
221280
See the [AndroidMavenLibrary documentation](../AndroidMavenLibrary.md)
222281
for more details.
223282

Documentation/guides/messages/README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,19 @@ Either change the value in the AndroidManifest.xml to match the $(SupportedOSPla
186186
+ XA4230: Error parsing XML: {exception}
187187
+ [XA4231](xa4231.md): The Android class parser value 'jar2xml' is deprecated and will be removed in a future version of Xamarin.Android. Update the project properties to use 'class-parse'.
188188
+ [XA4232](xa4232.md): The Android code generation target 'XamarinAndroid' is deprecated and will be removed in a future version of Xamarin.Android. Update the project properties to use 'XAJavaInterop1'.
189+
+ [XA4234](xa4234.md): '<{item}>' item '{itemspec}' is missing required attribute '{name}'.
190+
+ [XA4235](xa4235.md): Maven artifact specification '{artifact}' is invalid. The correct format is 'group_id:artifact_id'.
191+
+ [XA4236](xa4236.md): Cannot download Maven artifact '{group}:{artifact}'. - {jar}: {exception} - {aar}: {exception}
192+
+ [XA4237](xa4237.md): Cannot download POM file for Maven artifact '{artifact}'. - {exception}
193+
+ [XA4239](xa4239.md): Unknown Maven repository: '{repository}'.
194+
+ [XA4241](xa4241.md): Java dependency '{artifact}' is not satisfied.
195+
+ [XA4242](xa4242.md): Java dependency '{artifact}' is not satisfied. Microsoft maintains the NuGet package '{nugetId}' that could fulfill this dependency.
196+
+ [XA4243](xa4243.md): Attribute '{name}' is required when using '{name}' for '{element}' item '{itemspec}'.
197+
+ [XA4244](xa4244.md): Attribute '{name}' cannot be empty for '{element}' item '{itemspec}'.
198+
+ [XA4245](xa4245.md): Specified POM file '{file}' does not exist.
199+
+ [XA4246](xa4246.md): Could not parse POM file '{file}'. - {exception}
200+
+ [XA4247](xa4247.md): Could not resolve POM file for artifact '{artifact}'.
201+
+ [XA4248](xa4248.md): Could not find NuGet package '{nugetId}' version '{version}' in lock file. Ensure NuGet Restore has run since this <PackageReference> was added.
189202
+ XA4300: Native library '{library}' will not be bundled because it has an unsupported ABI.
190203
+ [XA4301](xa4301.md): Apk already contains the item `xxx`.
191204
+ [XA4302](xa4302.md): Unhandled exception merging \`AndroidManifest.xml\`: {ex}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
---
2+
title: .NET Android error XA4234
3+
description: XA4234 error code
4+
ms.date: 02/26/2024
5+
---
6+
# .NET Android error XA4234
7+
8+
## Example message
9+
10+
```
11+
error XA4234: '<AndroidMavenLibrary>' item 'com.example:mylib' is missing required attribute 'Version'.
12+
```
13+
14+
## Issue
15+
16+
The specified MSBuild XML item requires the specified XML attribute.
17+
18+
For example the following item is missing the required 'Version' attribute:
19+
20+
```xml
21+
<ItemGroup>
22+
<AndroidMavenLibrary Include="com.example:mylib" />
23+
</ItemGroup>
24+
```
25+
26+
## Solution
27+
28+
To resolve this error, ensure that the specified XML contains the specified attribute:
29+
30+
```xml
31+
<ItemGroup>
32+
<AndroidMavenLibrary Include="com.example:mylib" Version="1.0.0" />
33+
</ItemGroup>
34+
```

0 commit comments

Comments
 (0)