Skip to content

BuildArchive incremental build bug: Java resources alternate between present and missing in APK on consecutive publishes #11543

@tms320

Description

@tms320

Android framework version

net10.0-android

Affected platform version

VS 2026 18.6.2

Description

When running dotnet publish for an Android project, the resulting APK alternates between a "good" state (with Java/JAR resources) and a "bad" state (without them) on every consecutive build, even when no source files change.

Bad APK contains 438 files — missing all Java resources (kotlin/, commonMain/, nativeMain/, classes.jar, etc.)
Good APK contains 770 files — includes all expected Java resources.

The pattern is deterministic:

  1. Build → bad APK
  2. Build → good APK
  3. Build → bad APK
  4. (repeats forever)

Steps to Reproduce

  1. Create an Avalonia Android project targeting net10.0-android with AndroidPackageFormat=apk
  2. Run dotnet publish -c Release -f net10.0-android -p:RuntimeIdentifier=android-arm64
  3. Inspect the APK contents
  4. Run the same publish command again (no source changes)
  5. Inspect the APK contents — the Java resources will be different

Did you find any workaround?

Environment

Component Version
OS Windows 11 (10.0.26200)
.NET SDK 10.0.300
.NET Runtime 10.0.8
MSBuild 18.6.3
Android workload 36.1.53/10.0.100
Android SDK Platform android-36
Android Build Tools 36.0.0
JDK OpenJDK 17.0.19 (Microsoft)
Avalonia 12.0.4
Xamarin.AndroidX.Core.SplashScreen 1.0.1.15

Root Cause Analysis

The issue is in the BuildArchive task used by the _BuildApkEmbed target in Microsoft.Android.Sdk.Windows\36.1.53\tools\Xamarin.Android.Common.targets.

BuildArchive uses an incremental approach: it copies the previous intermediate APK (obj\Release\android\bin\<package>.apk) as the base, then adds/updates files from FilesToAddToArchive.

The bug manifests as follows:

Good build (previous APK was bad = missing Java resources)

The previous APK does NOT contain Java resources, so BuildArchive successfully adds them:

Adding META-INF/androidx.versionedparcelable_versionedparcelable.version from lp\61\jl\classes.jar as the archive file is out of date.
Adding META-INF/androidx.tracing_tracing.version from lp\62\jl\classes.jar as the archive file is out of date.
Adding commonMain/default/manifest from ... as the archive file is out of date.
...
(400 entries added, 37 duplicates "Failed to add")

Bad build (previous APK was good = already has Java resources)

The previous APK already contains all Java resources, so every add attempt fails:

Failed to add jar entry META-INF/androidx.versionedparcelable_versionedparcelable.version from classes.jar: the same file already exists in the apk
Failed to add jar entry META-INF/androidx.tracing_tracing.version from classes.jar: the same file already exists in the apk
Failed to add jar entry commonMain/default/manifest from ... the same file already exists in the apk
...
(369 entries "Failed to add", only 68 entries added)

The key observation: when an entry "already exists" and fails to add, BuildArchive treats these files as if they were no longer needed by the current build and removes them from the APK. This is incorrect — if a file is both present in the APK AND in FilesToAddToArchive, it should be treated as retained, not removed.

This creates a feedback loop:

  • Good APK → all adds fail as duplicates → files removed → Bad APK
  • Bad APK → adds succeed → files present → Good APK
  • (repeats)

Files Missing from Bad APK

The bad APK is missing 332 Java resource files compared to the good APK, including:

  • commonMain/ — Kotlin/Native klib data (manifests, linkdata, .knm files)
  • nativeMain/ — native platform klib data
  • concurrentMain/, unixMain/, webMain/, etc. — platform-specific klib data
  • kotlin/ — Kotlin builtins (kotlin_builtins files)
  • classes.jar — merged Java classes
  • META-INF/*.version — AndroidX library version files
  • META-INF/androidx/**/LICENSE.txt — license files
  • META-INF/proguard/*.pro — ProGuard rules
  • META-INF/services/* — service loader registrations
  • META-INF/kotlin-project-structure-metadata.json
  • META-INF/maven/** — Maven POM files
  • R.txt, proguard.txt

Attached Logs

  • log_bad_buildarchive.txt — BuildArchive task messages from a bad build (448 lines)
  • log_good_buildarchive.txt — BuildArchive task messages from a good build (448 lines)
  • diff_apk_files.txt — diff between bad and good APK file listings (332 files missing)

Workaround

Deleting the intermediate APK before _BuildApkEmbed forces BuildArchive to build from scratch, avoiding the incremental bug:

<Target Name="_CleanIntermediateApk" BeforeTargets="_BuildApkEmbed">
  <Delete Files="$(ApkFileIntermediate)" Condition="Exists('$(ApkFileIntermediate)')" />
</Target>

This has been verified to produce consistent APKs across consecutive builds.

Expected Fix

The BuildArchive task should track which files it attempted to add (regardless of whether the add succeeded due to the file already existing). Files that exist in both the previous APK AND FilesToAddToArchive should be treated as retained and must NOT be removed by the incremental cleanup logic.

diff_apk_files.txt
log_bad_buildarchive.txt
log_good_buildarchive.txt

Relevant log output

Metadata

Metadata

Labels

Area: App+Library BuildIssues when building Library projects or Application projects.needs-triageIssues that need to be assigned.

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions