Status Update
Comments
de...@squareup.com <de...@squareup.com> #2
Another key detail I should have noted is that minification is not enabled, and regardless adding a keep rule to the proguard rules to keep the Thing
class has no effect.
dy...@google.com <dy...@google.com>
ju...@google.com <ju...@google.com>
ac...@google.com <ac...@google.com> #3
app/build/intermediates/apk/debug/app-debug.apk
feature/build/intermediates/apk/debug/feature-debug.apk
Neither contain the definition for "Lcom/example/manifestboom/library/Thing;"
The deployment part looks WAI so I am going to to hand it over to the build model team to see if they can tell what's wrong.
de...@squareup.com <de...@squareup.com> #4
After debugging a build this is what appears to be happening within AGP:
- Dex transformations happen on the included dependency, and this works as expected! dex files are produced for both the main and generated kapt classes. All of the classes can be found in these dex files, like normal.
- Later on, the
DexMergingTask
resolves dex artifacts for the dynamic feature APK but the main dex files are inexplicably missing. The kapt dex files are there though and are merged correctly. This results in those main class being absent in the final APK.
No idea why that happens.
de...@squareup.com <de...@squareup.com> #5
Upon further investigation, it appears to be caused by the same issue described here:
In FilteringSpec
, the initFilteredArtifacts()
method uses artifacts
to get a list of all the dex files (requested by DexMergingTask
) to be used in dex merging. Due to
If artifactFiles
was used instead, all of the expected dex files would be present.
I tested this on an affected build and added some logging to compare the outputs from artifacts
and artifactFiles
in FilteringSpec
to compare the files that are included for the affected library module.
// In `FilteringSpec.initFilteredArtifacts
println("artifactFiles:")
artifacts.artifactFiles.files.filter { it.toString().contains("observable-cache") }.forEach(::println)
println("artifacts:")
artifacts.artifacts.filter { it.toIdString().contains("observable-cache") }.forEach { println(it.file) }
Output:
artifactFiles:
/.../util/observable-cache/build/.transforms/435e66ff23438be50a57e5a98bae247d/transformed/main
/.../util/observable-cache/build/.transforms/6827180ca030ff7eb90fc7aa6d9d5f16/transformed/main
/.../util/observable-cache/build/.transforms/24d8af9c3ce84a1bd0f4c371267edf9f/transformed/main
artifacts:
/.../util/observable-cache/build/.transforms/435e66ff23438be50a57e5a98bae247d/transformed/main
de...@squareup.com <de...@squareup.com> #6
I was able to patch the problem locally by appending a hash of the input file's path to the output file's name to ensure that they are all unique after the dexing transform.
On this line:
- val outputDir = outputs.dir(Files.getNameWithoutExtension(input.name))
+ val outputDir = outputs.dir("${Files.getNameWithoutExtension(input.name)}-${Hashing.sha256().hashString(input.absolutePath, StandardCharsets.UTF_8)}")
bi...@google.com <bi...@google.com> #7
Thanks for submitting a fix, actually the problem could have been fixed recently, can you confirm if you still see this error in AGP 8.0 alpha02?
de...@squareup.com <de...@squareup.com> #8
I tried building the affected project using AGP 8.0-alpha06 and unfortunately the issue still seems to be present.
From my understanding of the issue (above), the same broken ArtifactCollection.artifacts
property is still in use in AGP 8.0's FilteringSpec
implementation.
bi...@google.com <bi...@google.com>
ga...@google.com <ga...@google.com> #9
Thanks for filing the issue. We have indeed fixed SubtractingArtifactCollection
awhile back, but we also need to fix FilteringSpec
.
bingran@ we need to stop relying on artifacts
for filtering, but we need to use artifactFiles
to figure out if a file belongs to file collection. We should rewrite it to something like
override fun isSatisfiedBy(file: File): Boolean {
if (excluded.value.isEmpty()) return true
return file in filteredFileCollection.value
}
bi...@google.com <bi...@google.com> #10
The issue is related to the one of fixing SubtractingArtifactCollection
, here is what we got after running library:outgoingVariants
Variant runtimeElements
--------------------------------------------------
Elements of runtime for main.
Capabilities
- Manifest Boom:library:unspecified (default capability)
Attributes
- org.gradle.category = library
.......
- org.jetbrains.kotlin.platform.type = jvm
Artifacts
- build/libs/library.jar (artifactType = jar)
Secondary Variants (*)
--------------------------------------------------
Secondary Variant classes
--------------------------------------------------
Directories containing compiled class files for main.
Attributes
- org.gradle.category = library
........
- org.jetbrains.kotlin.platform.type = jvm
Artifacts
- build/classes/java/main (artifactType = java-classes-directory)
- build/classes/kotlin/main (artifactType = java-classes-directory)
- build/tmp/kapt3/classes/main (artifactType = java-classes-directory)
I believe the problem is gradle couldn't differentiate transformed artifacts with file name main
from artifacts build/classes/java/main
, build/classes/kotlin/main
and build/tmp/kapt3/classes/main
. The challenge of fixing this issue is we use componentId to exclude artifacts. If we compute artifactFiles
first and we want to exclude artifacts with certain componentId, it is hard for us to tell which file to be excluded because we cannot get componentId from FileCollection
.
(BTW The temporary work around for users is to add android.enableDexingArtifactTransform=false
or set minifyEnabled to true for release build)
de...@squareup.com <de...@squareup.com> #11
I noticed that this has been pushed back until AGP 8.1. If we're able to do the work of implementing the proper fix for this issue, would you be open to accepting a patch to get this in for 8.0?
We're currently monkey-patching this locally using that patch to DexingTransform.kt
I submitted a while back which is proving to be an inconvenience for us to maintain as we upgrade AGP. So, if we're able to help expedite this fix by submitting a proper patch then that'd be great.
ga...@google.com <ga...@google.com> #12
We are actually discussing internally and with Gradle how to address this.
Current options are (not in any particular order):
- replace the filtering mechanism entirely: publish actual configuration from the base module that contains artifacts that need to be removed, and switch to something like
SubtractingArtifactCollection
that we already have - your proposal from #6: while it ensures different identity, I'm concerns about getting different output files on different machines, and ending up with multiple entries in the remote build cache. Consumers that care about file name or artifact identity will start getting cache misses.
- similar to 2), but using the input file content hash and appending it to the output file name. This ensures that identity and output file remain the same across different machines. Also, it is a bit tricky as we need to snapshot dirs.
- changing dex transforms from
java-classes-directory
->dex
withjar
->dex
in dynamic features. We already do this when desugaring classpath is requested, so with this we'll just lose incremental dexing for API 24+ in dynamic features for local subprojects.
My current recommendation is 4).
bi...@google.com <bi...@google.com> #13
Fixed with Ia029820623ada6a24b4d74024c962925b0387b53
hu...@google.com <hu...@google.com> #14
We decided to go with option 4: Disabling incremental dexing transforms for dynamic features (
This will likely slow down builds for dynamic features, so I've filed
Description
DESCRIBE THE ISSUE IN DETAIL:
With the following project structure:
app
modulelibrary
with some classes defined that also uses kapt/dagger2feature
which depends onlibrary
, and makes use of code fromlibrary
.Launching an activity from the
feature
module causes aNoClassDefFoundError
when trying to instantiate the class(es) defined in thelibrary
module.This only seems to happen when Dagger2 is applied to the
library
module and when code is generated via kapt. If all dagger-related code is removed from thelibrary
module then the class loading works fine. When dagger2 is applied it is actually also possible to instantiate the classes that are generated by dagger, despite the manually-defined classes being absent from the dex path.A project that reproduces this error is available here:https://github.com/dellisd/manifest-boom/tree/dagger-wtf (on that particular branch).
Another behaviour of this issue is that changing the app deploy method from "Default APK" to "APK from app bundle" fixes this issue in that project, however the same problem has been observed in other larger projects when using "APK from app bundle". It's been difficult to reproduce in a smaller project, but presumably it is related to the issue described here.
STEPS TO REPRODUCE:
ATTACH SCREENSHOTS/RECORDINGS OF THE ISSUE
ATTACH LOG FILES (Select Help > Show Log in Files, or Show Log in Finder on a Mac)
The output from these lines of code:https://github.com/dellisd/manifest-boom/blob/dagger-wtf/feature/src/main/java/com/example/manifestboom/feature/FirstFragment.kt#L26-L36
IMPORTANT: Please readhttps://developer.android.com/studio/report-bugs.html carefully and supply
all required information.
Studio Build: 2021.3.1 RC 1, #AI-213.7172.25.2113.8913347 Version of Gradle Plugin: 7.3.0-rc01 (also observed on 7.2.2) Version of Gradle: 7.4 Version of Java: 11 OS: macOS