Circular POM dependency breaks Licenses task
romtsn opened this issue · 5 comments
Describe the bug
Circular dependencies in .pom
files causes the {Variant}OssLicensesTask
to fail. This is the same problem AGP faced and was fixed in version 7.2.1. https://issuetracker.google.com/issues/232075280
To Reproduce
Here's the reproducer https://github.com/d-emrani/Sentry-Bug
In the project both oss-licenses-plugin:0.10.5
and sentry-android-gradle-plugin:3.1.2
are used. The sentry plugin will add a io.sentry:sentry-android-fragment
dependency to the POM file of androidx.fragment:fragment
. Because io.sentry:sentry-android-fragment
itself depends on androidx.fragment:fragment
this creates a circle and causes the task to fail.
Expected behavior
The task should handle circular dependencies in POM files gracefully.
Additional context
Here's the stacktrace:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task 'releaseOssLicensesTask'.
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.lambda$executeIfValid$1(ExecuteActionsTaskExecuter.java:147)
at org.gradle.internal.Try$Failure.ifSuccessfulOrElse(Try.java:282)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:145)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:133)
at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:77)
at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:51)
at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:56)
at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
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:73)
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:74)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:333)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:320)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:313)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:299)
at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.lambda$run$0(DefaultPlanExecutor.java:143)
at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:227)
at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.executeNextNode(DefaultPlanExecutor.java:218)
at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:140)
at org.gradle.execution.plan.DefaultPlanExecutor.process(DefaultPlanExecutor.java:72)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph.executeWithServices(DefaultTaskExecutionGraph.java:144)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph.execute(DefaultTaskExecutionGraph.java:129)
at org.gradle.execution.SelectedTaskExecutionAction.execute(SelectedTaskExecutionAction.java:42)
at org.gradle.execution.DryRunBuildExecutionAction.execute(DryRunBuildExecutionAction.java:51)
at org.gradle.execution.BuildOperationFiringBuildWorkerExecutor$ExecuteTasks.call(BuildOperationFiringBuildWorkerExecutor.java:54)
at org.gradle.execution.BuildOperationFiringBuildWorkerExecutor$ExecuteTasks.call(BuildOperationFiringBuildWorkerExecutor.java:43)
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:73)
at org.gradle.execution.BuildOperationFiringBuildWorkerExecutor.execute(BuildOperationFiringBuildWorkerExecutor.java:40)
at org.gradle.internal.build.DefaultBuildLifecycleController.lambda$executeTasks$7(DefaultBuildLifecycleController.java:165)
at org.gradle.internal.model.StateTransitionController.doTransition(StateTransitionController.java:238)
at org.gradle.internal.model.StateTransitionController.lambda$tryTransition$8(StateTransitionController.java:174)
at org.gradle.internal.work.DefaultSynchronizer.withLock(DefaultSynchronizer.java:44)
at org.gradle.internal.model.StateTransitionController.tryTransition(StateTransitionController.java:174)
at org.gradle.internal.build.DefaultBuildLifecycleController.executeTasks(DefaultBuildLifecycleController.java:165)
at org.gradle.internal.build.DefaultBuildWorkGraphController$DefaultBuildWorkGraph.runWork(DefaultBuildWorkGraphController.java:142)
at org.gradle.composite.internal.DefaultBuildController.doBuild(DefaultBuildController.java:231)
at org.gradle.internal.Factories$1.create(Factories.java:31)
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:270)
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:119)
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:124)
at org.gradle.composite.internal.DefaultBuildController.doRun(DefaultBuildController.java:204)
at org.gradle.composite.internal.DefaultBuildController.access$000(DefaultBuildController.java:51)
at org.gradle.composite.internal.DefaultBuildController$BuildOpRunnable.run(DefaultBuildController.java:264)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
Caused by: java.lang.StackOverflowError
at org.gradle.internal.operations.MultipleBuildOperationFailures.format(MultipleBuildOperationFailures.java:39)
at org.gradle.internal.operations.MultipleBuildOperationFailures.<init>(MultipleBuildOperationFailures.java:28)
at org.gradle.internal.operations.DefaultBuildOperationQueue.markFinished(DefaultBuildOperationQueue.java:157)
at org.gradle.internal.operations.DefaultBuildOperationQueue.waitForWorkToComplete(DefaultBuildOperationQueue.java:127)
at org.gradle.internal.operations.DefaultBuildOperationQueue.waitForCompletion(DefaultBuildOperationQueue.java:106)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.executeInParallel(DefaultBuildOperationExecutor.java:143)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.runAll(DefaultBuildOperationExecutor.java:102)
at org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ParallelResolveArtifactSet$VisitingSet.visit(ParallelResolveArtifactSet.java:66)
at org.gradle.api.internal.artifacts.DefaultResolvedDependency.sort(DefaultResolvedDependency.java:129)
at org.gradle.api.internal.artifacts.DefaultResolvedDependency.getModuleArtifacts(DefaultResolvedDependency.java:107)
at org.gradle.api.internal.artifacts.DefaultResolvedDependency.getAllModuleArtifacts(DefaultResolvedDependency.java:113)
at org.gradle.api.internal.artifacts.DefaultResolvedDependency.getAllModuleArtifacts(DefaultResolvedDependency.java:115)
at org.gradle.api.internal.artifacts.DefaultResolvedDependency.getAllModuleArtifacts(DefaultResolvedDependency.java:115)
at org.gradle.api.internal.artifacts.DefaultResolvedDependency.getAllModuleArtifacts(DefaultResolvedDependency.java:115)
at org.gradle.api.internal.artifacts.DefaultResolvedDependency.getAllModuleArtifacts(DefaultResolvedDependency.java:115)
...
We're also seeing another StackOverflowError in plugin version 4.3.13, with a different stack trace (but potentially similar root cause).
It may not be directly related, but in this case there is a library dependency that uses a strictly("[17.0,)")
version constraint on firebase-messaging.
It also has a platform/BOM dependency that is used for version alignment (the BOM references a constraint on the library, and the library adds the platform() dependency on the BOM project, so they are cross-referenced, as recommended in the Gradle documentation)
And finally, the app module enables Android viewBinding (hence the dataBindingMergeDependencyArtifacts*
task):
android {
buildFeatures {
viewBinding = true
}
}
Gradle: 7.3
Android Gradle Plugin: 7.1.0
play-services-plugin: 4.3.13, 4.3.12
The failed task console output:
:app:dataBindingMergeDependencyArtifactsDebug FAILED
Dependency resolved to an incompatible version: Dependency(fromArtifactVersion=ArtifactVersion(groupId=com.example, artifactId=lib-push-fcm, version=1.2.3), toArtifact=Artifact(groupId=com.google.firebase, artifactId=firebase-messaging), toArtifactVersionString=[[17.0,)])
And the stack trace:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:dataBindingMergeDependencyArtifactsDebug'.
at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:38)
•••
Caused by: org.gradle.internal.event.ListenerNotificationException: Failed to notify dependency resolution listener.
at org.gradle.internal.event.AbstractBroadcastDispatch.dispatch(AbstractBroadcastDispatch.java:89)
•••
Caused by: java.lang.StackOverflowError: (No message provided)
at com.google.android.gms.dependencies.ArtifactDependencyManager.getDependencies(DataObjects.kt:127)
at com.google.android.gms.dependencies.DependencyAnalyzer.getNode(DependencyAnalyzer.java:82)
at com.google.android.gms.dependencies.DependencyAnalyzer.getNode(DependencyAnalyzer.java:90)
at com.google.android.gms.dependencies.DependencyAnalyzer.getNode(DependencyAnalyzer.java:90)
...
...
It appears that DependencyAnalyzer and ArtifactDependencyManager do not handle the potential for circular dependencies, similar to what is described above. I cannot explain why the stack trace is different, for what appears to be the same root cause.
NOTE: the issue does not occur with com.google.gms.google-services
plugin version 4.3.10
. Versions 4.3.12
and 4.3.13
are affected (there is no 4.3.11
published at maven.google.com).
I cloned https://github.com/d-emrani/Sentry-Bug and ran ./gradlew assembleRelease
without error.
The second report with incompatible versions is unrelated to the oss licenses plugin and should a new/separate issue.
I cloned d-emrani/Sentry-Bug and ran ./gradlew assembleRelease without error.
I'm not sure what's wrong here, but I just did the same (cloning + running assembleRelease
) and it still fails:
So this is a bit of a weird one. With a clean Docker image with tools installed:
git clone https://github.com/d-emrani/Sentry-Bug.git
cd Sentry-Bug
./gradlew assemble
# Fails with stack overflow
kill `pgrep -f "GradleDaemon"`
./gradlew assemble
# OK
./gradlew assembleRelease
# OK
I accidentally did this the first time because I had JAVA_HOME
set to JDK8 and the first build failed, but failed after a point where it had done something with the artifact cache I suppose. When I fixed JAVA_HOME
to be a JDK 11 install, Gradle restarted the daemon when I ran ./gradlew assemble
again and all subsequent builds succeeded including ./gradlew assembleRelease
. Doing it in any other order or skipping directly to assembleRelease
doesn't work. It took me a minute to figure out that scenario from my command history, but I made exactly the right mistakes on the first go round.
Maybe caching related to the sentry plugin run for the non-release builds is touching a shared cache file for release builds?
I'm not convinced the OSS licenses plugin can solve this since the entire stack is org.gradle.* code. The plugin already has cycle detection. This stack overflow is happening from ParallelResolveArtifactSet
. I think the only way it could avoid it is skipping artifact resolution, but it needs to obtain POM files to get license info.
Having them remove that cycle injection would be ideal.
Alright, thanks for investigating. Yeah, we removed the cycle already, not sure if anything else can be done here, in case there are others who uses this trick with the pom file.