Issue in gradle custom task using snakeyml

I am using snake yaml inside my custom gradle task .

def map= new org.yaml.snakeyaml.Yaml().load( new File(“test.yml”).newInputStream())

I have added snakeyaml dependency in build.gradle of the custom gradle plugin

dependencies {
compile group : ‘org.yaml’, name: ‘snakeyaml’, version: ‘1.9’
}

When i use this custom gradle plugin in one of the application , it is giving me error as below

FAILURE: Build failed with an exception.

  • What went wrong:
    Execution failed for task ‘:’.

org/yaml/snakeyaml/Yaml

Try running Gradle with flags like -iS to see the full stacktrace.

Snakeyaml is a transitive dependency to many other plugins (like Spring Boot), and there are no classpath isolation for plugins. This means if your plugin requires a specific version of this dependency but another plugin requires another version, and those versions happen to be incompatible, you will run into problems. For example, again for Spring Boot, you need a relative recent version or you will get issues like this one.

Do you really need such an old version of Snakeyaml? The newest is 1.25.

Thanks for the quick response. I tried using 1.24 as well, but no luck. Below is the stacktrace

  • Exception is:
    org.gradle.api.tasks.TaskExecutionException: Execution failed for task ‘:’.
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:110)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:77)
    at org.gradle.api.internal.tasks.execution.OutputDirectoryCreatingTaskExecuter.execute(OutputDirectoryCreatingTaskExecuter.java:51)
    at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:59)
    at org.gradle.api.internal.tasks.execution.ResolveTaskOutputCachingStateExecuter.execute(ResolveTaskOutputCachingStateExecuter.java:54)
    at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:59)
    at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:101)
    at org.gradle.api.internal.tasks.execution.FinalizeInputFilePropertiesTaskExecuter.execute(FinalizeInputFilePropertiesTaskExecuter.java:44)
    at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:91)
    at org.gradle.api.internal.tasks.execution.ResolveTaskArtifactStateTaskExecuter.execute(ResolveTaskArtifactStateTaskExecuter.java:62)
    at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:59)
    at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:54)
    at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43)
    at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:34)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.run(EventFiringTaskExecuter.java:51)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:301)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:293)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:175)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:91)
    at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:46)
    at org.gradle.execution.taskgraph.LocalTaskInfoExecutor.execute(LocalTaskInfoExecutor.java:42)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareWorkItemExecutor.execute(DefaultTaskExecutionGraph.java:277)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareWorkItemExecutor.execute(DefaultTaskExecutionGraph.java:262)
    at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$ExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:135)
    at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$ExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:130)
    at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$ExecutorWorker.execute(DefaultTaskPlanExecutor.java:200)
    at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$ExecutorWorker.executeWithWork(DefaultTaskPlanExecutor.java:191)
    at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$ExecutorWorker.run(DefaultTaskPlanExecutor.java:130)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
    at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
    at java.lang.Thread.run(Thread.java:748)
    Caused by: java.lang.NoClassDefFoundError: org/yaml/snakeyaml/Yaml
    at com.zzz.zzz.zzz.zzz(zzz.groovy:93)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:73)
    at org.gradle.api.internal.project.taskfactory.StandardTaskAction.doExecute(StandardTaskAction.java:46)
    at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:39)
    at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:26)
    at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:801)
    at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:768)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$1.run(ExecuteActionsTaskExecuter.java:131)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:301)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:293)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:175)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:91)
    at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:120)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:99)
    … 34 more

The relevant part of the stacktrace this:

Is your class really named com.zzz.zzz.zzz.zzz? :grinning:

Anyway, this probably means your plugin dependencies did not get picked up in the consumer project. How are you applying the plugin? Through the Gradle plugin portal, through an included build or through buildscript dependencies?

Class name is something else :wink: Anyways coming back to your question , I am adding the snake yaml dependency in my custom plugin build.gradle as
dependencies {
compile group : ‘org.yaml’, name: ‘snakeyaml’, version: ‘1.9’
}

And i am applying my custom plugin like this in the app build.gradle .
dependencies {
classpath fileTree(include: [’*.jar’], dir: ‘libs’)
}
My custom plugin jar is inside the lib folder of the app

Alright, so I guess the problem is that you are just grabbing the plugin’s jar file, but that jar file does not have references it’s required dependencies. There are a few other ways to get the plugin correctly on the classpath of the consumer project.

You can make a “fat jar” of the plugin which includes all dependencies. This is a rather ugly solution though as it can’t be used in dependency resolution rules.

If this is a plugin you are only using for that particular project, and you have the source code for the plugin is available with the project (e.g. in the same repository), you can also include it as a composite project. To do this, you will put a reference to the plugin in your settings.gradle file for the consumer project like this: includeBuild("../path/to/plugin").

Another solution is to publish the the plugin as a Maven distribution, and then depend the Maven coordinates in the consumer project. You could possibly just publish it to a local maven repository (e.g. the “.m2” folder in your home) to keep it simple.

Lastly, you can also just supply the dependencies directly to the consumer project in the same dependencies block that you have already.

Would any of those approaches work?

Thanks for the detailed reply. I believe none of the approaches work coz

  1. fat.jar → not possible since it is ugly not standard.
  2. both the projects are om different repos.
  3. I will do this eventually but before releasing the plugin, initial testing has led to this. So until the code works I cannot publish into maven distribution
  4. I will not able to force the plugin in consumer project coz this will affect number of the other projects across.

My question after all this , can I not use something in the gradle custom task to add the dependency on fly ? Something like this → task.add.depedency ?

You can add dependencies on the fly, but it requires you to declare a custom configuration, resolve it and then use a classloader to work with the classes in it (or use the Worker API). But that seems wrong unless you were trying to get around the version incompatibilities I mentioned in the beginning of the thread.

I am not sure I understand your reason for “forcing the plugin”. If you are applying the plugin with something like

buildscript {
  dependencies {
     classpath fileTree(include: [’*.jar’], dir: ‘libs’)
  }
}
apply plugin: "..."

Why can’t you expand the classpath dependencies block to this:

  dependencies {
     classpath fileTree(include: [’*.jar’], dir: ‘libs’)
     classpath group : ‘org.yaml’, name: ‘snakeyaml’, version: ‘1.24’
  }

?

It might not be very maintainable, but you are only in the testing phase, and if you want to publish it to the Gradle plugin portal later, it is only a temporary solution.

Sorry if I have misunderstood how you have structured your project. But if this is how you do it, I can’t think of any better ideas than either creating a pom file for your plugin, or configure its dependencies up front in the buildscript. Maybe someone else can come up with other approaches though.

As alluded to already Snakeyaml can be problematic in Gradle plugins. You might even find that the version bundled in the Gradle distribution pollute your classpath under circumstances.

Unfortunately the only things you can do is to either use one or or a combination of

  • a separate configuration
  • a separate classloader
  • run with a Gradle worker using classpath isolation
  • run with javaexec.

There is usually no trivial solution to this. From bitter experience I can tell you that even if you run a Gradle worker, the moment you test it with TestKit there is a good chance that Gradle’s own snakeyaml will end up on the classpath.

Yes Got your point @Bjorn_Vester . Ya forcing the plugin on the cosumer side is the only option left. Will be trying that out. Thanks for your responses @Bjorn_Vester

Yep. As suggsted by @Bjorn_Vester , will be going with forcing the plugin on cosumer side. Thanks for your reply @Schalk_Cronje