firebase-android-sdk icon indicating copy to clipboard operation
firebase-android-sdk copied to clipboard

`firebase-crashlytics-buildtools` has an undeclared dependency on `org.apache.httpcomponents:httpclient`

Open yogurtearl opened this issue 1 year ago • 7 comments

[READ] Step 1: Are you in the right place?

yes

[REQUIRED] Step 2: Describe your environment

  • Gradle version: 8.4
  • AGP version: 8.1.2
  • Firebase Component: Crashlytics gradle plugin
  • Component version: 2.9.9

[REQUIRED] Step 3: Describe the problem

Steps to reproduce:

Apply the firebase gradle plugin via a precompiled script plugin in build-logic/

i.e. at build-logic/src/main/kotlin/firebase.gradle.kts you would have:

plugins {
    id("com.google.firebase.crashlytics")
}

that plugin uses org.apache.http.client.methods.HttpRequestBase which comes from org.apache.httpcomponents:httpclient but the pom.xml does NOT declare this dependency.

This pom needs to add org.apache.httpcomponents:httpclient as a dependency: https://dl.google.com/android/maven2/com/google/firebase/firebase-crashlytics-buildtools/2.9.9/firebase-crashlytics-buildtools-2.9.9.pom

See error below.

workaround

Add this to your build-logic/build.gradle.kts :

plugins {
   `kotlin-dsl`
}

dependencies {
    // workaround for https://github.com/firebase/firebase-android-sdk/issues/5473
    implementation("org.apache.httpcomponents:httpclient:4.5.5")

    implementation("com.google.firebase.crashlytics:com.google.firebase.crashlytics.gradle.plugin:2.9.9")
}

ERROR

Caused by: java.lang.NoClassDefFoundError: org/apache/http/client/methods/HttpRequestBase
	at com.google.firebase.crashlytics.buildtools.Buildtools.createWebApi(Buildtools.java:64)
	at com.google.firebase.crashlytics.buildtools.Buildtools.getInstance(Buildtools.java:107)
	at com.google.firebase.crashlytics.buildtools.Buildtools$getInstance$0.call(Unknown Source)
	at com.google.firebase.crashlytics.buildtools.gradle.CrashlyticsPlugin.configureBuildtools(CrashlyticsPlugin.groovy:133)
	at com.google.firebase.crashlytics.buildtools.gradle.CrashlyticsPlugin.apply(CrashlyticsPlugin.groovy:73)
	at com.google.firebase.crashlytics.buildtools.gradle.CrashlyticsPlugin.apply(CrashlyticsPlugin.groovy)
	at org.gradle.api.internal.plugins.ImperativeOnlyPluginTarget.applyImperative(ImperativeOnlyPluginTarget.java:43)
	at org.gradle.api.internal.plugins.RuleBasedPluginTarget.applyImperative(RuleBasedPluginTarget.java:51)
	at org.gradle.api.internal.plugins.DefaultPluginManager.addPlugin(DefaultPluginManager.java:187)
	at org.gradle.api.internal.plugins.DefaultPluginManager.access$100(DefaultPluginManager.java:52)
	at org.gradle.api.internal.plugins.DefaultPluginManager$AddPluginBuildOperation.run(DefaultPluginManager.java:282)
	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:73)
	at org.gradle.api.internal.plugins.DefaultPluginManager.lambda$doApply$0(DefaultPluginManager.java:167)
	at org.gradle.configuration.internal.DefaultUserCodeApplicationContext.apply(DefaultUserCodeApplicationContext.java:44)
	at org.gradle.api.internal.plugins.DefaultPluginManager.doApply(DefaultPluginManager.java:166)
	at org.gradle.api.internal.plugins.DefaultPluginManager.apply(DefaultPluginManager.java:137)
	at org.gradle.plugin.use.internal.DefaultPluginRequestApplicator.lambda$applyPlugin$1(DefaultPluginRequestApplicator.java:181)
	at org.gradle.plugin.use.internal.DefaultPluginRequestApplicator.applyPlugin(DefaultPluginRequestApplicator.java:233)
	at org.gradle.plugin.use.internal.DefaultPluginRequestApplicator.applyPlugin(DefaultPluginRequestApplicator.java:179)
	at org.gradle.plugin.use.internal.DefaultPluginRequestApplicator.access$200(DefaultPluginRequestApplicator.java:63)
	at org.gradle.plugin.use.internal.DefaultPluginRequestApplicator$1$1.lambda$add$1(DefaultPluginRequestApplicator.java:156)
	at org.gradle.plugin.use.internal.DefaultPluginRequestApplicator.lambda$applyPlugins$0(DefaultPluginRequestApplicator.java:108)
	at org.gradle.plugin.use.internal.DefaultPluginRequestApplicator.applyPlugins(DefaultPluginRequestApplicator.java:108)
	at org.gradle.plugin.use.internal.DefaultPluginRequestApplicator.applyPlugins(DefaultPluginRequestApplicator.java:139)
	at org.gradle.kotlin.dsl.provider.plugins.precompiled.tasks.GeneratePrecompiledScriptPluginAccessorsKt.applyPlugins(GeneratePrecompiledScriptPluginAccessors.kt:512)
	at org.gradle.kotlin.dsl.provider.plugins.precompiled.tasks.GeneratePrecompiledScriptPluginAccessorsKt.access$applyPlugins(GeneratePrecompiledScriptPluginAccessors.kt:1)
	at org.gradle.kotlin.dsl.provider.plugins.precompiled.tasks.GeneratePrecompiledScriptPluginAccessors$projectSchemaFor$1$1$1.call(GeneratePrecompiledScriptPluginAccessors.kt:366)
	at org.gradle.kotlin.dsl.provider.plugins.precompiled.tasks.GeneratePrecompiledScriptPluginAccessors$projectSchemaFor$1$1$1.call(GeneratePrecompiledScriptPluginAccessors.kt:350)
	at org.gradle.internal.Try.ofFailable(Try.java:41)
	at org.gradle.kotlin.dsl.provider.plugins.precompiled.tasks.GeneratePrecompiledScriptPluginAccessors$projectSchemaFor$1$1.apply(GeneratePrecompiledScriptPluginAccessors.kt:350)
	at org.gradle.kotlin.dsl.provider.plugins.precompiled.tasks.GeneratePrecompiledScriptPluginAccessors$projectSchemaFor$1$1.apply(GeneratePrecompiledScriptPluginAccessors.kt:349)
	at org.gradle.internal.build.DefaultBuildLifecycleController.lambda$withSettings$0(DefaultBuildLifecycleController.java:123)
	at org.gradle.internal.model.StateTransitionController.lambda$notInState$3(StateTransitionController.java:132)
	at org.gradle.internal.work.DefaultSynchronizer.withLock(DefaultSynchronizer.java:44)
	at org.gradle.internal.model.StateTransitionController.notInState(StateTransitionController.java:128)
	at org.gradle.internal.build.DefaultBuildLifecycleController.withSettings(DefaultBuildLifecycleController.java:123)
	at org.gradle.internal.buildtree.DefaultBuildTreeLifecycleController.lambda$withEmptyBuild$3(DefaultBuildTreeLifecycleController.java:89)
	at org.gradle.internal.buildtree.DefaultBuildTreeLifecycleController.lambda$runBuild$4(DefaultBuildTreeLifecycleController.java:98)
	at org.gradle.internal.model.StateTransitionController.lambda$transition$6(StateTransitionController.java:169)
	at org.gradle.internal.model.StateTransitionController.doTransition(StateTransitionController.java:266)
	at org.gradle.internal.model.StateTransitionController.lambda$transition$7(StateTransitionController.java:169)
	at org.gradle.internal.work.DefaultSynchronizer.withLock(DefaultSynchronizer.java:44)
	at org.gradle.internal.model.StateTransitionController.transition(StateTransitionController.java:169)
	at org.gradle.internal.buildtree.DefaultBuildTreeLifecycleController.runBuild(DefaultBuildTreeLifecycleController.java:95)
	at org.gradle.internal.buildtree.DefaultBuildTreeLifecycleController.withEmptyBuild(DefaultBuildTreeLifecycleController.java:88)
	at org.gradle.kotlin.dsl.provider.plugins.precompiled.tasks.GeneratePrecompiledScriptPluginAccessors$projectSchemaFor$1.apply(GeneratePrecompiledScriptPluginAccessors.kt:349)
	at org.gradle.kotlin.dsl.provider.plugins.precompiled.tasks.GeneratePrecompiledScriptPluginAccessors$projectSchemaFor$1.apply(GeneratePrecompiledScriptPluginAccessors.kt:348)
	at org.gradle.composite.internal.RootOfNestedBuildTree$1.call(RootOfNestedBuildTree.java:137)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:199)
	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.call(DefaultBuildOperationRunner.java:53)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:78)
	at org.gradle.composite.internal.RootOfNestedBuildTree.run(RootOfNestedBuildTree.java:134)
	at org.gradle.composite.internal.DefaultNestedBuildTree.run(DefaultNestedBuildTree.java:80)
	at org.gradle.kotlin.dsl.provider.plugins.precompiled.tasks.GeneratePrecompiledScriptPluginAccessors.projectSchemaFor(GeneratePrecompiledScriptPluginAccessors.kt:348)
	at org.gradle.kotlin.dsl.provider.plugins.precompiled.tasks.GeneratePrecompiledScriptPluginAccessors.access$projectSchemaFor(GeneratePrecompiledScriptPluginAccessors.kt:85)
	at org.gradle.kotlin.dsl.provider.plugins.precompiled.tasks.GeneratePrecompiledScriptPluginAccessors$projectSchemaImpliedByPluginGroups$1$1.invoke(GeneratePrecompiledScriptPluginAccessors.kt:325)
	at org.gradle.kotlin.dsl.provider.plugins.precompiled.tasks.GeneratePrecompiledScriptPluginAccessors$projectSchemaImpliedByPluginGroups$1$1.invoke(GeneratePrecompiledScriptPluginAccessors.kt:323)
	at org.gradle.kotlin.dsl.provider.plugins.precompiled.tasks.GeneratePrecompiledScriptPluginAccessorsKt.withCapturedOutputOnError(GeneratePrecompiledScriptPluginAccessors.kt:531)
	... 131 more
Caused by: java.lang.ClassNotFoundException: org.apache.http.client.methods.HttpRequestBase
	at org.gradle.internal.classloader.VisitableURLClassLoader$InstrumentingVisitableURLClassLoader.findClass(VisitableURLClassLoader.java:186)
	... 201 more

yogurtearl avatar Oct 24 '23 16:10 yogurtearl

Hi @yogurtearl, thanks for reaching out. While we investigate this, any chance you could share a minimal reproducible example of the issue? Thanks!

argzdev avatar Oct 30 '23 09:10 argzdev

Are you intending to extend the plugin or something? I am curious why you apply it this was as a dep in your build-logic, instead of applied as a typical gradle plugin? The issue might have something to do with that, I can play with it but I want to understand the use case more please.

mrober avatar Oct 31 '23 19:10 mrober

Hey @yogurtearl. We need more information to resolve this issue but there hasn't been an update in 5 weekdays. I'm marking the issue as stale and if there are no new updates in the next 5 days I will close it automatically.

If you have more information that will help us get to the bottom of this, just add a comment!

google-oss-bot avatar Nov 07 '23 02:11 google-oss-bot

I have a precompiled script plugin that applies the firebase plugin, but because it is not declaring it's deps properly it fails when gradle tries to generate the accessors.

yogurtearl avatar Nov 07 '23 02:11 yogurtearl

This script will reproduce the issue on macOS, assuming you have gradle 8.x installed locally.

This uses the built in kotlin-dsl plugin to create a precompiled script plugin. https://docs.gradle.org/current/userguide/custom_plugins.html#sec:precompiled_plugins

#!/usr/bin/env zsh

tmpdir=$(/usr/bin/mktemp -d /tmp/repro_5473.XXXXXX)

cd "$tmpdir" || exit

mkdir -p "$tmpdir/src/main/kotlin/"

cat <<"BUILD_FILE" > "$tmpdir/build.gradle.kts"
plugins {
    `kotlin-dsl`
}

repositories {
    google()
    mavenCentral()
}

dependencies {
    implementation("com.google.firebase.crashlytics:com.google.firebase.crashlytics.gradle.plugin:2.9.9")
}
BUILD_FILE

cat <<"PLUGIN_FILE" > "$tmpdir/src/main/kotlin/myplugin.gradle.kts"
plugins {
    id("com.google.firebase.crashlytics")
}
PLUGIN_FILE

gradle build --stacktrace

echo "$tmpdir"

will result in:

Caused by: java.lang.NoClassDefFoundError: org/apache/http/client/methods/HttpRequestBase
        at com.google.firebase.crashlytics.buildtools.Buildtools.createWebApi(Buildtools.java:64)
        at com.google.firebase.crashlytics.buildtools.Buildtools.getInstance(Buildtools.java:107)
        at com.google.firebase.crashlytics.buildtools.Buildtools$getInstance$0.call(Unknown Source)
        at com.google.firebase.crashlytics.buildtools.gradle.CrashlyticsPlugin.configureBuildtools(CrashlyticsPlugin.groovy:133)
        at com.google.firebase.crashlytics.buildtools.gradle.CrashlyticsPlugin.apply(CrashlyticsPlugin.groovy:73)
        at com.google.firebase.crashlytics.buildtools.gradle.CrashlyticsPlugin.apply(CrashlyticsPlugin.groovy)
        at org.gradle.api.internal.plugins.ImperativeOnlyPluginTarget.applyImperative(ImperativeOnlyPluginTarget.java:43)
.
.
Caused by: java.lang.ClassNotFoundException: org.apache.http.client.methods.HttpRequestBase
        at org.gradle.internal.classloader.VisitableURLClassLoader$InstrumentingVisitableURLClassLoader.findClass(VisitableURLClassLoader.java:186)
        ... 196 more


BUILD FAILED in 1s
4 actionable tasks: 4 executed

This can be fixed by adding the httpclient dependency to your published pom file.

https://dl.google.com/android/maven2/com/google/firebase/firebase-crashlytics-buildtools/2.9.9/firebase-crashlytics-buildtools-2.9.9.pom

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.14</version>
</dependency>

yogurtearl avatar Nov 07 '23 16:11 yogurtearl

Is the source code for the firebase crashlytics gradle plugin open source?

yogurtearl avatar Nov 07 '23 16:11 yogurtearl

Unfortunately is it not open sourced. I am advocating for open sourcing our Gradle plugin, but it is not a high priority right now.

I have a hunch your issue is caused by a failure in the way we package the fat jar. We do include httpclient, but we also namespace it so it does not interfere with your other Gradle plugins that may depend on an incompatible version of httpclient. We validate it with the typical workflow, that is simply applying the plugin in a project. Your workflow is different, and we don't have a functional test for it. I will add one, and fix the fat jar, but it might not happen until other high priority issues are addressed first.

mrober avatar Feb 22 '24 20:02 mrober