Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Documentation/docs-mobile/messages/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,8 @@ Either change the value in the AndroidManifest.xml to match the $(SupportedOSPla
+ [XA4235](xa4249.md): Maven artifact specification '{artifact}' is invalid. The correct format is 'group_id:artifact_id:version'.
+ [XA4250](xa4250.md): Manifest-referenced type '{type}' was not found in any scanned assembly. It may be a framework type.
+ [XA4252](xa4252.md): Insecure HTTP Maven repository URL '{url}' is not allowed. Use an HTTPS URL, or set AllowInsecureHttp="true" metadata on the item to override this check.
+ [XA4253](xa4253.md): Trimmable type map Java source input directory '{input}' and output directory '{output}' must be different.
+ [XA4254](xa4254.md): Generated trimmable type map Java source '{path}' was not found.
+ XA4300: Native library '{library}' will not be bundled because it has an unsupported ABI.
+ [XA4301](xa4301.md): Apk already contains the item `xxx`.
+ [XA4302](xa4302.md): Unhandled exception merging \`AndroidManifest.xml\`: {ex}
Expand Down
25 changes: 25 additions & 0 deletions Documentation/docs-mobile/messages/xa4253.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
title: .NET for Android error XA4253
description: XA4253 error code
ms.date: 05/20/2026
f1_keywords:
- "XA4253"
---

# .NET for Android error XA4253

## Example message

```text
error XA4253: Trimmable type map Java source input directory 'obj/Release/net11.0-android/typemap/java' and output directory 'obj/Release/net11.0-android/typemap/java' must be different.
```

## Issue

The trimmable type map build tried to clean the Java source output directory, but the configured input and output directories resolved to the same path.

Cleaning the output directory in this configuration would delete the input Java sources before they can be copied.

## Solution

This error indicates an internal build configuration problem. File an issue at <https://github.com/dotnet/android/issues> and include the full build log.
27 changes: 27 additions & 0 deletions Documentation/docs-mobile/messages/xa4254.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
title: .NET for Android error XA4254
description: XA4254 error code
ms.date: 05/20/2026
f1_keywords:
- "XA4254"
---

# .NET for Android error XA4254

## Example message

```text
error XA4254: Generated trimmable type map Java source 'obj/Release/net11.0-android/typemap/java/my/app/MainActivity.java' was not found.
```

## Issue

The post-trim trimmable type map scan expected to copy a generated Java source file from the pre-trim Java source directory, but the file was missing.

This can happen if intermediate build outputs are stale or if the generated Java source list no longer matches the files on disk.

## Solution

Delete the project's `obj` and `bin` directories, then rebuild.

If the error persists after a clean rebuild, file an issue at <https://github.com/dotnet/android/issues> and include the full build log.
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ static bool IsUnconditionalEntry (JavaPeerInfo peer)

// User-defined ACW types (not MCW bindings, not interfaces) are unconditional
// because Android can instantiate them from Java at any time.
if (!peer.DoNotGenerateAcw && !peer.IsInterface) {
if (!peer.IsFrameworkAssembly && !peer.DoNotGenerateAcw && !peer.IsInterface) {
return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ public sealed record JavaPeerInfo
/// </summary>
public required string AssemblyName { get; init; }

/// <summary>
/// True when the type belongs to a framework assembly supplied by the Android SDK.
/// Framework ACWs are generated by the SDK and can be trimmed like bindings unless
/// another rule explicitly roots them.
/// </summary>
public bool IsFrameworkAssembly { get; set; }

/// <summary>
/// JNI name of the base Java type, e.g., "android/app/Activity" for a type
/// that extends Activity. Null for java/lang/Object or types without a Java base.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ public TrimmableTypeMapResult Execute (
ManifestConfig? manifestConfig = null,
XDocument? manifestTemplate = null,
string? packageNamingPolicy = null,
int maxArrayRank = 0)
int maxArrayRank = 0,
bool generateTypeMapAssemblies = true)
{
_ = assemblies ?? throw new ArgumentNullException (nameof (assemblies));
_ = systemRuntimeVersion ?? throw new ArgumentNullException (nameof (systemRuntimeVersion));
Expand All @@ -48,12 +49,15 @@ public TrimmableTypeMapResult Execute (
logger.LogNoJavaPeerTypesFound ();
return new TrimmableTypeMapResult ([], [], allPeers);
}
MarkFrameworkAssemblyPeers (allPeers, frameworkAssemblyNames);

RootManifestReferencedTypes (allPeers, PrepareManifestForRooting (manifestTemplate, manifestConfig));
PropagateDeferredRegistrationToBaseClasses (allPeers);
PropagateCannotRegisterToDescendants (allPeers);

var generatedAssemblies = GenerateTypeMapAssemblies (allPeers, systemRuntimeVersion, useSharedTypemapUniverse, maxArrayRank);
var generatedAssemblies = generateTypeMapAssemblies
? GenerateTypeMapAssemblies (allPeers, systemRuntimeVersion, useSharedTypemapUniverse, maxArrayRank)
: [];
var jcwPeers = allPeers.Where (p =>
!frameworkAssemblyNames.Contains (p.AssemblyName)
|| p.JavaName.StartsWith ("mono/", StringComparison.Ordinal)).ToList ();
Expand Down Expand Up @@ -188,6 +192,15 @@ List<GeneratedAssembly> GenerateTypeMapAssemblies (List<JavaPeerInfo> allPeers,
return generatedAssemblies;
}

static void MarkFrameworkAssemblyPeers (List<JavaPeerInfo> allPeers, HashSet<string> frameworkAssemblyNames)
{
foreach (var peer in allPeers) {
if (frameworkAssemblyNames.Contains (peer.AssemblyName)) {
peer.IsFrameworkAssembly = true;
}
}
}

/// <summary>
/// Groups peers by assembly, merging cross-assembly aliases into a single group.
/// When the same JNI name appears in multiple assemblies (e.g. <c>Java.Lang.Object</c>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,18 @@
<_TrimmableRuntimeProviderJavaName Condition=" '$(_TrimmableRuntimeProviderJavaName)' == '' ">mono.MonoRuntimeProvider</_TrimmableRuntimeProviderJavaName>
</PropertyGroup>

<PropertyGroup>
<_GenerateProguardAfterTargets Condition=" '$(_GenerateProguardAfterTargets)' == '' ">ILLink</_GenerateProguardAfterTargets>
</PropertyGroup>

<!-- Add TypeMap DLLs to ILLink input. ILLink natively understands TypeMapAttribute<T>. -->
<Target Name="_AddTrimmableTypeMapToLinker"
BeforeTargets="PrepareForILLink;_RunILLink"
DependsOnTargets="_GenerateTrimmableTypeMap">
<ItemGroup>
<ManagedAssemblyToLink Include="$(_TypeMapOutputDirectory)*.dll">
<DestinationSubPath>%(Filename)%(Extension)</DestinationSubPath>
<IsTrimmable>true</IsTrimmable>
</ManagedAssemblyToLink>
</ItemGroup>
</Target>
Expand All @@ -20,9 +25,73 @@
<Target Name="_ConfigureTrimmableTypeMapForLinker"
BeforeTargets="PrepareForILLink;_RunILLink"
DependsOnTargets="_GenerateTrimmableTypeMap">
<ItemGroup>
Comment on lines 25 to +28
<_TrimmableTypeMapEntryAssemblies Include="$(_TypeMapOutputDirectory)*.TypeMap.dll" />
</ItemGroup>
<PropertyGroup>
<_ExtraTrimmerArgs>--typemap-entry-assembly $(_TypeMapAssemblyName) $(_ExtraTrimmerArgs)</_ExtraTrimmerArgs>
<_ExtraTrimmerArgs>@(_TrimmableTypeMapEntryAssemblies->'--typemap-entry-assembly %(Filename)', ' ') $(_ExtraTrimmerArgs)</_ExtraTrimmerArgs>
</PropertyGroup>
<ItemGroup>
<_TrimmableTypeMapEntryAssemblies Remove="@(_TrimmableTypeMapEntryAssemblies)" />
</ItemGroup>
</Target>

<Target Name="_PrepareLinkedAssembliesForProguard"
AfterTargets="$(_GenerateProguardAfterTargets)"
Condition=" '$(PublishTrimmed)' == 'true' and '$(_ProguardProjectConfiguration)' != '' ">
<ItemGroup>
<_LinkedAssemblyForProguard Include="@(ResolvedFileToPublish)" Condition=" '%(Extension)' == '.dll' " />
</ItemGroup>
</Target>

<Target Name="_GenerateProguardConfiguration"
AfterTargets="_PrepareLinkedAssembliesForProguard"
Condition=" '$(PublishTrimmed)' == 'true' and '$(_ProguardProjectConfiguration)' != '' "
Inputs="@(_LinkedAssemblyForProguard)"
Outputs="$(_ProguardProjectConfiguration)">
<GenerateProguardConfiguration
LinkedAssemblies="@(_LinkedAssemblyForProguard)"
OutputFile="$(_ProguardProjectConfiguration)" />
</Target>

<Target Name="_GeneratePostTrimTrimmableTypeMapJavaSources"
Condition=" '$(_AndroidTypeMapImplementation)' == 'trimmable' and '$(PublishTrimmed)' == 'true' and Exists('$(IntermediateOutputPath)linked/Link.semaphore') "
AfterTargets="ILLink"
BeforeTargets="_GenerateJavaStubs;_CompileJava;_CompileToDalvik"
Inputs="$(IntermediateOutputPath)linked/Link.semaphore"
Outputs="$(_PostTrimTrimmableTypeMapJavaStamp)">
<ItemGroup>
<_PostTrimTrimmableTypeMapInputAssemblies Include="@(ResolvedFileToPublish)"
Condition=" '%(Extension)' == '.dll' " />
</ItemGroup>

<GenerateTrimmableTypeMap
ResolvedAssemblies="@(_PostTrimTrimmableTypeMapInputAssemblies)"
FrameworkAssemblyNames="@(ResolvedFrameworkAssemblies->'%(Filename)')"
OutputDirectory="$(_TypeMapOutputDirectory)"
JavaSourceOutputDirectory="$(_PostTrimTypeMapJavaOutputDirectory)"
JavaSourceInputDirectory="$(_TypeMapJavaOutputDirectory)"
TargetFrameworkVersion="$(TargetFrameworkVersion)"
PackageNamingPolicy="$(_TrimmableTypeMapPackageNamingPolicy)"
MaxArrayRank="$(_AndroidTrimmableTypeMapMaxArrayRank)"
GenerateTypeMapAssemblies="false"
CleanJavaSourceOutputDirectory="true"
AcwMapOutputFile="$(IntermediateOutputPath)acw-map.txt"
ApplicationRegistrationOutputFile="$(IntermediateOutputPath)android/src/net/dot/android/ApplicationRegistration.java">
<Output TaskParameter="GeneratedJavaFiles" ItemName="_PostTrimGeneratedJavaFiles" />
</GenerateTrimmableTypeMap>

<MakeDir Directories="$([System.IO.Path]::GetDirectoryName('$(_PostTrimTrimmableTypeMapJavaStamp)'))" />
<Touch Files="$(_PostTrimTrimmableTypeMapJavaStamp)" AlwaysCreate="true" />

<ItemGroup>
<FileWrites Include="@(_PostTrimGeneratedJavaFiles)" />
<FileWrites Include="$(IntermediateOutputPath)acw-map.txt" />
<FileWrites Include="$(IntermediateOutputPath)android/src/net/dot/android/ApplicationRegistration.java" />
<FileWrites Include="$(_PostTrimTrimmableTypeMapJavaStamp)" />
<_PostTrimTrimmableTypeMapInputAssemblies Remove="@(_PostTrimTrimmableTypeMapInputAssemblies)" />
<_PostTrimGeneratedJavaFiles Remove="@(_PostTrimGeneratedJavaFiles)" />
</ItemGroup>
</Target>

<!-- Add TypeMap DLLs to assembly store with per-ABI metadata.
Expand All @@ -47,9 +116,10 @@
</_TrimmableTypeMapAbi>
</ItemGroup>

<!-- Try linked/ first (trimmed by ILLink in inner builds) -->
<!-- Try linked/ first (trimmed by ILLink). Single-RID builds put linked output directly
under $(IntermediateOutputPath); plural-RID builds put it under the per-RID directory. -->
<ItemGroup>
<_LinkedTypeMapDlls Include="$(IntermediateOutputPath)%(_TrimmableTypeMapAbi.RuntimeIdentifier)/linked/_*.TypeMap.dll;$(IntermediateOutputPath)%(_TrimmableTypeMapAbi.RuntimeIdentifier)/linked/_Microsoft.Android.TypeMap*.dll">
<_LinkedTypeMapDlls Include="$(IntermediateOutputPath)linked/_*.TypeMap.dll;$(IntermediateOutputPath)linked/_Microsoft.Android.TypeMap*.dll;$(IntermediateOutputPath)%(_TrimmableTypeMapAbi.RuntimeIdentifier)/linked/_*.TypeMap.dll;$(IntermediateOutputPath)%(_TrimmableTypeMapAbi.RuntimeIdentifier)/linked/_Microsoft.Android.TypeMap*.dll">
<Abi>%(_TrimmableTypeMapAbi.Identity)</Abi>
<RuntimeIdentifier>%(_TrimmableTypeMapAbi.RuntimeIdentifier)</RuntimeIdentifier>
<DestinationSubPath>%(_TrimmableTypeMapAbi.Identity)/%(_LinkedTypeMapDlls.Filename)%(_LinkedTypeMapDlls.Extension)</DestinationSubPath>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<UsingTask TaskName="Xamarin.Android.Tasks.GenerateTrimmableTypeMap" AssemblyFile="$(_XamarinAndroidBuildTasksAssembly)" />
<UsingTask TaskName="Xamarin.Android.Tasks.GenerateEmptyTypemapStub" AssemblyFile="$(_XamarinAndroidBuildTasksAssembly)" />
<UsingTask TaskName="Xamarin.Android.Tasks.GenerateNativeAotBootstrapSources" AssemblyFile="$(_XamarinAndroidBuildTasksAssembly)" />
<UsingTask TaskName="Xamarin.Android.Tasks.GenerateProguardConfiguration" AssemblyFile="$(_XamarinAndroidBuildTasksAssembly)" />

<Import Project="$(MSBuildThisFileDirectory)Microsoft.Android.Sdk.TypeMap.Trimmable.CoreCLR.targets"
Condition=" '$(_AndroidRuntime)' == 'CoreCLR' " />
Expand All @@ -22,11 +23,17 @@
<_TypeMapBaseOutputDir>$(_TypeMapBaseOutputDir.Replace('\','/'))</_TypeMapBaseOutputDir>
<_TypeMapOutputDirectory>$(_TypeMapBaseOutputDir)typemap/</_TypeMapOutputDirectory>
<_TypeMapJavaOutputDirectory>$(_TypeMapBaseOutputDir)typemap/java</_TypeMapJavaOutputDirectory>
<_PostTrimTypeMapJavaOutputDirectory>$(_TypeMapBaseOutputDir)typemap/linked-java</_PostTrimTypeMapJavaOutputDirectory>
<_TypeMapJavaStubsSourceDirectory Condition=" '$(_TypeMapJavaStubsSourceDirectory)' == '' and '$(_AndroidRuntime)' == 'CoreCLR' and '$(PublishTrimmed)' == 'true' ">$(_PostTrimTypeMapJavaOutputDirectory)</_TypeMapJavaStubsSourceDirectory>
<_TypeMapJavaStubsSourceDirectory Condition=" '$(_TypeMapJavaStubsSourceDirectory)' == '' ">$(_TypeMapJavaOutputDirectory)</_TypeMapJavaStubsSourceDirectory>
<_PostTrimTrimmableTypeMapJavaStamp>$(_TypeMapBaseOutputDir)stamp/_GeneratePostTrimTrimmableTypeMapJavaSources.stamp</_PostTrimTrimmableTypeMapJavaStamp>
<_TrimmableJavaSourceStamp Condition=" '$(_TrimmableJavaSourceStamp)' == '' and '$(_AndroidRuntime)' == 'CoreCLR' and '$(PublishTrimmed)' == 'true' ">$(_PostTrimTrimmableTypeMapJavaStamp)</_TrimmableJavaSourceStamp>
<_TrimmableJavaSourceStamp Condition=" '$(_TrimmableJavaSourceStamp)' == '' ">$(_TypeMapOutputDirectory)$(_TypeMapAssemblyName).dll</_TrimmableJavaSourceStamp>

<!-- Max array rank for __ArrayMapRank{N} sentinel emission. Defaults to 3 for NativeAOT
(PublishAot=true, dynamic code is unavailable, so array creation uses the typemap path);
<!-- Max array rank for __ArrayMapRank{N} sentinel emission. Defaults to 3 when
dynamic code is unavailable, so array creation uses the typemap path;
defaults to 0 otherwise, where dynamic code can use Array.CreateInstance directly. -->
<_AndroidTrimmableTypeMapMaxArrayRank Condition=" '$(_AndroidTrimmableTypeMapMaxArrayRank)' == '' and '$(PublishAot)' == 'true' ">3</_AndroidTrimmableTypeMapMaxArrayRank>
<_AndroidTrimmableTypeMapMaxArrayRank Condition=" '$(_AndroidTrimmableTypeMapMaxArrayRank)' == '' and ('$(PublishAot)' == 'true' or '$(DynamicCodeSupport)' == 'false') ">3</_AndroidTrimmableTypeMapMaxArrayRank>
<_AndroidTrimmableTypeMapMaxArrayRank Condition=" '$(_AndroidTrimmableTypeMapMaxArrayRank)' == '' ">0</_AndroidTrimmableTypeMapMaxArrayRank>
</PropertyGroup>

Expand Down Expand Up @@ -78,6 +85,7 @@

<GenerateTrimmableTypeMap
ResolvedAssemblies="@(_TypeMapInputAssemblies)"
FrameworkAssemblyNames="@(ResolvedFrameworkAssemblies->'%(Filename)')"
OutputDirectory="$(_TypeMapOutputDirectory)"
JavaSourceOutputDirectory="$(_TypeMapJavaOutputDirectory)"
TargetFrameworkVersion="$(TargetFrameworkVersion)"
Expand Down Expand Up @@ -182,16 +190,23 @@
-->
<Target Name="_GenerateJavaStubs"
DependsOnTargets="_SetLatestTargetFrameworkVersion;_CleanIntermediateIfNeeded;_PrepareAssemblies;_GetGenerateJavaStubsInputs;_DefineBuildTargetAbis;_PrepareNativeAssemblySources"
Inputs="$(_TypeMapOutputDirectory)$(_TypeMapAssemblyName).dll;@(_EnvironmentFiles)"
Inputs="$(_TrimmableJavaSourceStamp);@(_EnvironmentFiles)"
Outputs="$(_AndroidStampDirectory)_GenerateJavaStubs.stamp">

<ItemGroup>
<_TypeMapJavaFiles Include="$(_TypeMapJavaOutputDirectory)/**/*.java" />
<_TypeMapJavaFiles Include="$(_TypeMapJavaStubsSourceDirectory)/**/*.java" />
<_TypeMapJavaDestinationFiles Include="@(_TypeMapJavaFiles->'$(IntermediateOutputPath)android/src/%(RecursiveDir)%(Filename)%(Extension)')" />
<_ExistingTypeMapJavaDestinationFiles Include="$(IntermediateOutputPath)android/src/mono/**/*.java;$(IntermediateOutputPath)android/src/android/runtime/**/*.java" />
<_StaleTypeMapJavaDestinationFiles Include="@(_ExistingTypeMapJavaDestinationFiles)" Exclude="@(_TypeMapJavaDestinationFiles)" />
</ItemGroup>
<Copy SourceFiles="@(_TypeMapJavaFiles)" DestinationFolder="$(IntermediateOutputPath)android/src/%(RecursiveDir)" />
<Delete Files="@(_StaleTypeMapJavaDestinationFiles)" />
<Copy
SourceFiles="@(_TypeMapJavaFiles)"
DestinationFiles="@(_TypeMapJavaDestinationFiles)"
SkipUnchangedFiles="true" />

<ItemGroup>
<FileWrites Include="@(_TypeMapJavaFiles->'$(IntermediateOutputPath)android/src/%(RecursiveDir)%(Filename)%(Extension)')" />
<FileWrites Include="@(_TypeMapJavaDestinationFiles)" />
</ItemGroup>

<!-- NativeAOT bootstrap Java sources depend on environment files, so include them as inputs -->
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions src/Xamarin.Android.Build.Tasks/Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1111,6 +1111,17 @@ To use a custom JDK path for a command line build, set the 'JavaSdkDirectory' MS
<value>Insecure HTTP Maven repository URL '{0}' is not allowed. Use an HTTPS URL, or set AllowInsecureHttp="true" metadata on the item to override this check.</value>
<comment>The following are literal names and should not be translated: HTTP, HTTPS, Maven, AllowInsecureHttp
{0} - The insecure HTTP URL</comment>
</data>
<data name="XA4253" xml:space="preserve">
<value>Trimmable type map Java source input directory '{0}' and output directory '{1}' must be different.</value>
<comment>The following are literal names and should not be translated: Trimmable type map, Java.
{0} - Full path to the Java source input directory
{1} - Full path to the Java source output directory</comment>
</data>
<data name="XA4254" xml:space="preserve">
<value>Generated trimmable type map Java source '{0}' was not found.</value>
<comment>The following are literal names and should not be translated: trimmable type map, Java.
{0} - Full path to the generated Java source file</comment>
</data>
<data name="XA0142" xml:space="preserve">
<value>Command '{0}' failed.\n{1}</value>
Expand Down
Loading