paparazzi
paparazzi copied to clipboard
lateinit property mergeResourcesTask has not been initialized
Description
We have several gradle modules that are pure compose. Our project default is android.library.defaults.buildfeatures.androidresources=false
.
If you run paparrazi on a module without androidResources, it crashes in the configuration phase.
Caused by: kotlin.UninitializedPropertyAccessException: lateinit property mergeResourcesTask has not been initialized
at com.android.build.gradle.internal.scope.MutableTaskContainer.getMergeResourcesTask(MutableTaskContainer.kt:67)
at com.android.build.gradle.internal.api.BaseVariantImpl.getMergeResourcesProvider(BaseVariantImpl.java:384)
at com.android.build.gradle.internal.api.LibraryVariantImpl_Decorated.getMergeResourcesProvider(Unknown Source)
at app.cash.paparazzi.gradle.PaparazziPlugin.setupPaparazzi$lambda-22(PaparazziPlugin.kt:68)
at org.gradle.configuration.internal.DefaultUserCodeApplicationContext$CurrentApplication$1.execute(DefaultUserCodeApplicationContext.java:123)
at org.gradle.api.internal.DefaultCollectionCallbackActionDecorator$BuildOperationEmittingAction$1.run(DefaultCollectionCallbackActionDecorator.java:110)
at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:47)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:68)
at org.gradle.api.internal.DefaultCollectionCallbackActionDecorator$BuildOperationEmittingAction.execute(DefaultCollectionCallbackActionDecorator.java:107)
at org.gradle.internal.ImmutableActionSet$SetWithFewActions.execute(ImmutableActionSet.java:285)
at org.gradle.api.internal.DefaultDomainObjectCollection.doAdd(DefaultDomainObjectCollection.java:262)
at org.gradle.api.internal.DefaultDomainObjectCollection.add(DefaultDomainObjectCollection.java:251)
at com.android.build.gradle.LibraryExtension.addVariant(LibraryExtension.kt:102)
at com.android.build.gradle.internal.ApiObjectFactory.create(ApiObjectFactory.java:115)
... 184 more
- Paparazzi Version: 1.0.0
- OS: Macos
- Compile SDK: 32
- Gradle Version: 7.4.2
- Android Gradle Plugin Version: 7.3.0-beta04
Ok, took a shot at this (branch), and it's a bit tricky.
We can read the buildFeatures.androidResources flag off the LibraryBuildFeatures extension and use that to:
- write a dummy sentinel value to resources.txt in place of the merged resources path, and
- ignore looking for the module-specific R class when non transitive R classes are enabled
but...if the view/composable under snapshot is part of a larger view/composable hierarchy which looks up resources, then things will still crash because of 1. Ultimately variant.mergeResourcesProvider is not present when androidResources = false, but we kinda need it, in order to use its output directory in order to read the other transitive resources.
So, I think:
- the workaround for now, is to not disable this flag. Hopefully running AAPT on a module without resources isn't really a huge speed slowdown.
- the real fix is not to use mergeResourcesProvider.outputDir, but rather the original resource source sets (or nothing, if the flag is enabled) as tracked in https://github.com/cashapp/paparazzi/issues/524.
Technically, this is now fixed by the work done in https://github.com/cashapp/paparazzi/issues/524, but let's move this and close in an upcoming release, once we deem the new mechanism stable and the old code deleted.
I do not see this working out-of-the-box with 1.3.1. Stacktrace is mostly the same
Caused by: kotlin.UninitializedPropertyAccessException: lateinit property mergeResourcesTask has not been initialized
at com.android.build.gradle.internal.scope.MutableTaskContainer.getMergeResourcesTask(MutableTaskContainer.kt:67)
at com.android.build.gradle.internal.api.BaseVariantImpl.getMergeResourcesProvider(BaseVariantImpl.java:394)
at com.android.build.gradle.internal.api.LibraryVariantImpl_Decorated.getMergeResourcesProvider(Unknown Source)
at app.cash.paparazzi.gradle.PaparazziPlugin$setupPaparazzi$1.invoke(PaparazziPlugin.kt:119)
at app.cash.paparazzi.gradle.PaparazziPlugin$setupPaparazzi$1.invoke(PaparazziPlugin.kt:115)
at app.cash.paparazzi.gradle.PaparazziPlugin.setupPaparazzi$lambda$7(PaparazziPlugin.kt:115)
at org.gradle.configuration.internal.DefaultUserCodeApplicationContext$CurrentApplication$1.execute(DefaultUserCodeApplicationContext.java:123)
at org.gradle.api.internal.DefaultCollectionCallbackActionDecorator$BuildOperationEmittingAction$1.run(DefaultCollectionCallbackActionDecorator.java:110)
at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:47)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:68)
at org.gradle.api.internal.DefaultCollectionCallbackActionDecorator$BuildOperationEmittingAction.execute(DefaultCollectionCallbackActionDecorator.java:107)
at org.gradle.internal.ImmutableActionSet$SetWithFewActions.execute(ImmutableActionSet.java:285)
at org.gradle.api.internal.DefaultDomainObjectCollection.doAdd(DefaultDomainObjectCollection.java:262)
at org.gradle.api.internal.DefaultDomainObjectCollection.add(DefaultDomainObjectCollection.java:251)
at com.android.build.gradle.LibraryExtension.addVariant(LibraryExtension.kt:102)
at com.android.build.gradle.internal.ApiObjectFactory.create(ApiObjectFactory.java:119)
... 188 more
Seems like the legacy codepath is only ignored at runtime and not within the plugin which still wires its folders through the task. So that's why we have to wait until the legacy codepath is fully deleted.
@PaulWoitaschek I delved deeper into this issue, and noticed currently we cannot support the module with androidresources=false
, even if deleting legacy codepath for resource loading.
The reason is today Paparazzi is using ComposeView
to render snapshot for compose and ComposeView
is using android resource internally
java.lang.NoClassDefFoundError: androidx/customview/poolingcontainer/R$id
at androidx.customview.poolingcontainer.PoolingContainer.<clinit>(PoolingContainer.kt:121)
at androidx.compose.ui.platform.ViewCompositionStrategy$DisposeOnDetachedFromWindowOrReleasedFromPool.installFor(ViewCompositionStrategy.android.kt:97)
at androidx.compose.ui.platform.AbstractComposeView.<init>(ComposeView.android.kt:125)
at androidx.compose.ui.platform.ComposeView.<init>(ComposeView.android.kt:418)
at androidx.compose.ui.platform.ComposeView.<init>(ComposeView.android.kt:414)
at app.cash.paparazzi.Paparazzi.snapshot(Paparazzi.kt:199)
at app.cash.paparazzi.Paparazzi.snapshot$default(Paparazzi.kt:198)
at app.cash.paparazzi.sample.HelloComposeTest.compose(HelloComposeTest.kt:25)
But there is no issue for compose preview in Android Studio, so we need to investigate further to update how compose is rendered in Paparazzi
cc @jrodbx @JakeWharton