I'm trying to build a jar for a custom gradle plugin to be used by other gradle projects. I'm using java to write the plugin. I'm having a problem including dependencies in my jar. If I build the jar using the below build.gradle
plugins {
id 'groovy'
}
repositories{
mavenCentral()
}
dependencies {
compile gradleApi()
compile localGroovy()
compile 'com.google.guava:guava:27.0-jre'
testCompile 'junit:junit:4.12'
//compile 'org.apache.commons:commons-lang3:3.8.1'
}
group = 'com.mine'
version = '1.0'
I get a NoClassDefFound exception for guava classes when applying the plugin on a project. If I include a task to create a jar with dependencies like below in the build.gradle
jar {
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it)}
}
}
It says Plugin with Id 'my-plugin' not found. How do I include dependencies in a gradle plugin jar?
Your plugin project should be configured as a standalone Plugin project and then published to a maven repository, which will make dependencies resolution work; there is good documentation about writing custom plugin here, specially the following part : using Gradle plugin development plugin
There is also a good example of writing/publishing/consuming a custom Plugin in the Gradle examples here : https://github.com/gradle/gradle/tree/master/subprojects/docs/src/samples/plugins (see the two subprojects publishing and consuming )
And here is a working example with a plugin that has dependency on external library (commons-lang for example):
Plugin project
build.gradle
plugins {
id 'java-gradle-plugin'
id 'groovy'
id 'maven-publish'
}
group 'org.gradle.sample.plugin'
version '0.1'
// pugin metadata configuration
gradlePlugin {
plugins {
myplugin {
id = "org.gradle.sample.plugin.myplugin"
implementationClass = "org.gradle.sample.plugin.MyPlugin"
}
}
}
// publish to local maven repo for testing
publishing {
repositories {
maven {
url "../repos/maven-repo"
}
}
}
// repo for dependences resolution
repositories{
jcenter()
}
// dependencies of this plugin
dependencies {
compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.8.1'
}
Plugin implementation : src/main/groovy/org/gradle/sample/plugin/MyPLugin.groovy
package org.gradle.sample.plugin
import org.apache.commons.lang3.StringUtils
import org.gradle.api.Plugin
import org.gradle.api.Project
class MyPlugin implements Plugin<Project> {
#Override
void apply(final Project project) {
println "Applying custom plugin... "
project.tasks.create('testPlugin'){
doLast{
println " custom plugin task executing."
println "Result: " + StringUtils.capitalize("stringtotest")
}
}
}
}
Build and publish this plugin ./gradlew publish : the plugin jar and "plugin marker artefacts" will be published to local maven repo in ../repos/maven-repo
Consumer project
build.gradle
plugins {
id 'java'
// import/apply your custom plugin
id 'org.gradle.sample.plugin.myplugin' version '0.1'
}
group 'org.gradle.sample.plugin'
version '0.1'
repositories{
maven {
url "../repos/maven-repo"
}
jcenter()
}
To test the plugin, try to execute the plugin task testPlugin
> Task :testPlugin
custom plugin task executing.
Result: Stringtotest
Sorry to add this as an answer but I don't have enough points to comment (yes it is a bit late in coming but I found this in a search and it came so close, maybe this will help someone else).
The answer by #M.Ricciuti is correct, just missing one file, namely a settings.gradle in the referencing project (not the plugin) directory:
pluginManagement {
repositories {
maven {
url '../repos/maven-repo'
}
gradlePluginPortal()
ivy {
url '../repos/ivy-repo'
}
}
}
Many thanks, I have tried many things that didn't work before finding this, even the examples by gradle didn't work (or more likely I didn't run them correctly). Anyway I merged what I saw in the answers with M. Ricciuti's answer and saw that file in the sample.
My complete project is at https://github.com/reddierocket/sampleGradlePlugin
The readme has instructions to run it. (Note I did not include the wrapper but I am using gradle version 5.3.1.)
I'm using Antlr in a simple Kotlin/Gradle project, and while my Gradle build is generating Antlr sources, they are not available for importing into the project.
As you can see (on the left), the classes (Lexer/Parser, etc.) are being generated. I have also configured this generated-src/antlr/main directory as a Source Root. Most questions I see list this as a solution, but I've already done it.
The issue persists after multiple rebuilds (both in IDEA and on the CLI), and following all the usual "Invalidate Cache and Restart" issues.
Further, the import issue is listed in the Gradle build on the CLI so it doesn't seem isolated to IDEA.
What am I missing here?
Here's the build.gradle file produced by IDEA when I was creating the project initially, and which IDEA is using for project/workspace synchronization.
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.2.50'
}
group 'com.craigotis'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
apply plugin: 'antlr'
dependencies {
antlr "org.antlr:antlr4:4.5"
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.2.0'
}
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
Try adding this to your build.gradle:
sourceSets {
main.java.srcDirs += "${project.buildDir}/generated-src/antlr/main"
}
generateGrammarSource {
arguments += ["-visitor", "-package", "com.craigotis.sprint.core.antlr"]
outputDirectory = file("${project.buildDir}/generated-src/antlr/main/com/craigotis/sprint/core/antlr")
}
compileKotlin.dependsOn generateGrammarSource
Shouldn't it locate the compiled classes and not the sources? Do you see the antlr generated classes in the target directory?
Try this: first build the project without referencing or using any ANTLR generated classes, and only after the build is successful, then add the code that references them.
(In other words, what I think that happens, is that your ANTLR sources are compiled after the code that references them. They never have a chance to compile because build fails before)
Also if this is really the case, you can solve it also by splitting into two artifacts and make sure the ANTLR one is built before the one with the code that uses it
Try to add generated sources in idea module like this post from Daniel Dekany here:
apply plugin: "idea"
...
sourceSets.main.java.srcDir new File(buildDir, 'generated/javacc')
idea {
module {
// Marks the already(!) added srcDir as "generated"
generatedSourceDirs += file('build/generated/javacc')
}
}
I'm evaluating gradle for replacing an ant build script and I can't manage to find a solution for creating a standard build script that correctly manages dev/prod environment.
Than ant script (it's for a java project, not android) is structured in this way:
a common script with the standard tasks (compile, build-jar, build-war)
a specific project script that includes the first one and through some properties it defines where the war task should pick the correct files
Our project structure/taks allows to override entire directories in the final war. Let consider this example:
the dev configuration is the standard one and lays int the dir webcontent
there are multiple prod conf (one of each specific installation, we do not have more that 10 different prod configs) all under the prod dir (i.e. *prod/conf1*m prod/conf2, etc)
The ant build has the dev_build task as the prod_conf1_build one, the prod_conf2_build one ,etc
the XXX_build task do the same things:
specify the parent (it's a project property) dir that contains the env dir/files
call the same ant taks that build the war using the property specified in the calling task
I'm trying to do the same in gradle but it seems that even calling a taks from another one it creates some problem (i.e. the task is always up to date)
Here is the script (it's a working draft, I'm learning gradle) that tries to do the same but it's not working when I call war_prod the taks does nothing since it reports up-to-date
apply plugin: 'java'
apply plugin: 'war'
apply plugin: 'eclipse'
project.ext.envdir = ""
eclipse {
jdt {
sourceCompatibility = 1.8
targetCompatibility = 1.8
javaRuntimeName = "jdk-1.8.x"
}
}
// In this section you declare where to find the dependencies of your project
repositories {
maven {
url 'http://artifactory.zzzz.priv/artifactory/libs-release'
url 'http://artifactory.zzzz.priv/artifactory/libs-snapshot'
credentials {
username 'xxxx'
password 'yyyy'
}
}
}
// In this section you declare the dependencies for your production and test code
dependencies {
// The production code uses the SLF4J logging API at compile time
compile 'org.slf4j:slf4j-api:1.7.18'
// Declare the dependency for your favourite test framework you want to use in your tests.
// TestNG is also supported by the Gradle Test task. Just change the
// testCompile dependency to testCompile 'org.testng:testng:6.8.1' and add
// 'test.useTestNG()' to your build script.
testCompile 'junit:junit:4.12'
}
task war_prod {
project.ext.envdir='prod/conf1'
project.ext.envdir=project.ext.envdir.replaceAll('\\\\',File.pathSeparator)
project.ext.envdir=project.ext.envdir.replaceAll('/',File.pathSeparator)
tasks.war.execute()
}
war {
eachFile {
println 'endir' + project.ext.envdir
println 'evaluating' + it
FileTree tree = fileTree(dir: project.ext.envdir)
tree.visit { FileVisitDetails file->
if (!file.file.isDirectory()) {
println '\tFileVisitDetails relpath ' + file.relativePath
println '\tsourcepath ' + it.file.getAbsolutePath()
println '\tcontains ' + it.file.getAbsolutePath().contains(project.ext.envdir)
if (it.relativePath == file.relativePath && !it.file.getAbsolutePath().contains(project.ext.envdir)) {
it.exclude()
println '\texcluding ' + it
} else {
if (it!=null) {
//println '\tincluding ' + it
}
}
}
}
}
from 'prod/conf1'
}
Can anyone point me in the right direction for creating a correct gradle script?
Is there a specific gradle way to build war files with prod/dev configurations (where the configuration is represented by some dir and files)?
In such scenarios task rules might be very useful. Basic idea is to keep configurations in a structured way and use a general task to build a war file with a configuration defined. Please have a look at build.gradle below:
apply plugin: 'war'
repositories {
mavenCentral()
}
tasks.addRule("Pattern: buildWar<ENV>") { String taskName ->
if (taskName.startsWith('buildWar')) {
def env = (taskName - 'buildWar').toLowerCase()
if (env in ['dev', 'prod',]) {
task(taskName, type: War) {
println "Configuring env: $env"
from("src/main/conf/$env") {
into("conf")
}
}
} else {
println "Invalid env: $env, skipping."
}
}
}
The buildWarENV rule defined here is pretty self descriptive. It accepts two environments dev and prod and prepares war file by taking configuration from appropriate folder. You can find a demo here. In case of questions, just ask.
P.S. Gradle has a bit different working model than ant, start with the basics. And what's important, never run a task from within other task.
I am trying to move from Dagger 1.2.2 to Dagger 2.0.1 in AppEngine project (NOT Android one).
With Dagger 1.2.2 simple:
compile 'com.squareup.dagger:dagger-compiler:1.2.2'
compile 'com.squareup.dagger:dagger:1.2.2'
did the trick.
With Dagger 2.0.1:
compile 'com.google.dagger:dagger-compiler:2.0.1'
compile 'com.google.dagger:dagger:2.0.1'
does not work (source is generated but mixed up with *.class files in build/classes/main/..package../).
You can also do it without net.ltgt.apt plugin, (which by the way may conflict with lombok).
apply plugin: 'java'
apply plugin: 'idea'
def generatedMain = new File(buildDir, "generated/main")
compileJava {
doFirst {
generatedMain.mkdirs()
}
options.compilerArgs += ['-s', generatedMain]
}
idea.module.sourceDirs += generatedMain
dependencies {
compileOnly 'com.google.dagger:dagger-compiler:2.8'
compile 'com.google.dagger:dagger:2.8'
}
I've found a solution.
https://github.com/tbroyer/gradle-apt-plugin
buildscript {
repositories {
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath "net.ltgt.gradle:gradle-apt-plugin:0.3"
}
}
apply plugin: "net.ltgt.apt"
dependecies {
apt 'com.google.dagger:dagger-compiler:2.0.1'
compile 'com.google.dagger:dagger:2.0.1'
}
Additionally if you are using Intellij a following configuration is recommended:
When using the Gradle integration in IntelliJ IDEA however, rather
than the idea task, you'll have to manually enable annotation
processing: in Settings… → Build, Execution, Deployment → Compiler →
Annotation Processors, check Enable annotation processing and Obtain
processors from project classpath. To mimic the Gradle behavior and
generated files behavior, you can configure the production and test
sources directories to build/generated/source/apt/main and
build/generated/source/apt/test respectively and choose to Store
generated sources relative to: Module content root.
I've also had to remove Exclude from whole build directory and mark generated/source/apt/main directory as source.
I have a multi-project configuration and I want to use gradle.
My projects are like this:
Project A
-> src/main/java
-> src/test/java
Project B
-> src/main/java (depends on src/main/java on Project A)
-> src/test/java (depends on src/test/java on Project A)
My Project B build.gradle file is like this:
apply plugin: 'java'
dependencies {
compile project(':ProjectA')
}
The task compileJava work great but the compileTestJava does not compile the test file from Project A.
Deprecated - For Gradle 5.6 and above use this answer.
In Project B, you just need to add a testCompile dependency:
dependencies {
...
testCompile project(':A').sourceSets.test.output
}
Tested with Gradle 1.7.
This is now supported as a first class feature in Gradle. Modules with java or java-library plugins can also include a java-test-fixtures plugin which exposes helper classes and resources to be consumed with testFixtures helper. Benefit of this approach against artifacts and classifiers are:
proper dependency management (implementation/api)
nice separation from test code (separate source set)
no need to filter out test classes to expose only utilities
maintained by Gradle
Example
:modul:one
modul/one/build.gradle
plugins {
id "java-library" // or "java"
id "java-test-fixtures"
}
modul/one/src/testFixtures/java/com/example/Helper.java
package com.example;
public class Helper {}
:modul:other
modul/other/build.gradle
plugins {
id "java" // or "java-library"
}
dependencies {
testImplementation(testFixtures(project(":modul:one")))
}
modul/other/src/test/java/com/example/other/SomeTest.java
package com.example.other;
import com.example.Helper;
public class SomeTest {
#Test void f() {
new Helper(); // used from :modul:one's testFixtures
}
}
Further reading
For more info, see the documentation:
https://docs.gradle.org/current/userguide/java_testing.html#sec:java_test_fixtures
It was added in 5.6:
https://docs.gradle.org/5.6/release-notes.html#test-fixtures-for-java-projects
Simple way is to add explicit task dependency in ProjectB:
compileTestJava.dependsOn tasks.getByPath(':ProjectA:testClasses')
Difficult (but more clear) way is to create additional artifact configuration for ProjectA:
task myTestsJar(type: Jar) {
// pack whatever you need...
}
configurations {
testArtifacts
}
artifacts {
testArtifacts myTestsJar
}
and add the testCompile dependency for ProjectB
apply plugin: 'java'
dependencies {
compile project(':ProjectA')
testCompile project(path: ':ProjectA', configuration: 'testArtifacts')
}
I've come across this problem myself recently, and man is this a tough issues to find answers for.
The mistake you are making is thinking that a project should export its test elements in the same way that it exports its primary artifacts and dependencies.
What I had a lot more success with personally was making a new project in Gradle. In your example, I would name it
Project A_Test
-> src/main/java
I would put into the src/main/java the files that you currently have in Project A/src/test/java. Make any testCompile dependencies of your Project A compile dependencies of Project A_Test.
Then make Project A_Test a testCompile dependency of Project B.
It's not logical when you come at it from the perspective of the author of both projects, but I think it makes a lot of sense when you think about projects like junit and scalatest (and others. Even though those frameworks are testing-related, they are not considered part of the "test" targets within their own frameworks - they produce primary artifacts that other projects just happen to use within their test configuration. You just want to follow that same pattern.
Trying to do the other answers listed here did not work for me personally (using Gradle 1.9), but I've found that the pattern I describe here is a cleaner solution anyway.
I know it's an old question but I just had the same problem and spent some time figuring out what is going on. I'm using Gradle 1.9. All changes should be in ProjectB's build.gradle
To use test classes from ProjectA in tests of ProjectB:
testCompile files(project(':ProjectA').sourceSets.test.output.classesDir)
To make sure that sourceSets property is available for ProjectA:
evaluationDependsOn(':ProjectA')
To make sure test classes from ProjectA are actually there, when you compile ProjectB:
compileTestJava.dependsOn tasks.getByPath(':ProjectA:testClasses')
Please read the update bellow.
Similar problems described by JustACluelessNewbie occurs in IntelliJ IDEA. Problem is that dependency testCompile project(':core').sourceSets.test.output actually means: "depend on classes generated by gradle build task". So if you open clean project where classes are not generated yet IDEA won't recognise them and reports error.
To fix this problem you have to add a dependency on test source files next to dependency on compiled classes.
// First dependency is for IDEA
testCompileOnly files { project(':core').sourceSets.test.java.srcDirs }
// Second is for Gradle
testCompile project(':core').sourceSets.test.output
You can observe dependencies recognised by IDEA in Module Settings -> Dependencies (test scope).
Btw. this is not nice solution so refactoring is worth considering. Gradle itself does have special subproject containing test-support classes only. See https://docs.gradle.org/current/userguide/test_kit.html
Update 2016-06-05
More I am thinking about proposed solution less I like it. There are few problems with it:
It creates two dependencies in IDEA. One points to test sources another to compiled classes. And it is crucial in which order these dependencies are recognised by IDEA. You can play with it by changing dependency order in Module settings -> Dependencies tab.
By declaring these dependencies you are unnecessarily polluting dependency structure.
So what's the better solution? In my opinion it's creating new custom source set and putting shared classes into it. Actually authors of Gradle project did it by creating testFixtures source set.
To do it you just have to:
Create source set and add necessary configurations. Check this script plugin used in Gradle project: https://github.com/gradle/gradle/blob/v4.0.0/gradle/testFixtures.gradle
Declare proper dependency in dependent project:
dependencies {
testCompile project(path: ':module-with-shared-classes', configuration: 'testFixturesUsageCompile')
}
Import Gradle project to IDEA and use the "create separate module per source set" option while importing.
New testJar based (trnsitive dependancies supported) solution available as gradle plugin:
https://github.com/hauner/gradle-plugins/tree/master/jartest
https://plugins.gradle.org/plugin/com.github.hauner.jarTest/1.0
From documentation
In case you have a multi-project gradle build you may have test
dependencies between sub-projects (which probably is a hint that your
projects are not well structured).
For example assume a project where the sub-project Project B depends
on Project A and B does not only have a compile dependency on A but
also a test dependency. To compile and run the tests of B we need some
test helper classes from A.
By default gradle does not create a jar artifact from the test build
output of a project.
This plugin adds a testArchives configuration (based on testCompile)
and a jarTest task to create a jar from the test source set (with the
classifier test added to name of the jar). We can then depend in B on
the testArchives configuration of A (which will also include the
transitive dependencies of A).
In A we would add the plugin to build.gradle:
apply plugin: 'com.github.hauner.jarTest'
In B we reference the
testArchives configuration like this:
dependencies {
...
testCompile project (path: ':ProjectA', configuration: 'testArchives')
}
The Fesler's solution haven't worked for me, when i tried it to build an android project (gradle 2.2.0).
So i had to reference required classes manually :
android {
sourceSets {
androidTest {
java.srcDir project(':A').file("src/androidTest/java")
}
test {
java.srcDir project(':A').file("src/test/java")
}
}
}
Here if you are using Kotlin DSL, you should create your task like that according to Gradle documentation.
Like some previous answer, you need to create a special configuration inside the project that will share its tests class, so that you don't mix test and main classes.
Simple steps
In project A you would need to add in your build.gradle.kts :
configurations {
create("test")
}
tasks.register<Jar>("testArchive") {
archiveBaseName.set("ProjectA-test")
from(project.the<SourceSetContainer>()["test"].output)
}
artifacts {
add("test", tasks["testArchive"])
}
Then in your project B in the dependencies, you will need to add in your build.gradle.kts:
dependencies {
implementation(project(":ProjectA"))
testImplementation(project(":ProjectA", "test"))
}
I'm so late to the party (it is now Gradle v4.4) but for anyone else who finds this:
Assuming:
~/allProjects
|
|-/ProjectA/module-a/src/test/java
|
|-/ProjectB/module-b/src/test/java
Go to the build.gradle of project B (the one that needs some test classes from A) and add the following:
sourceSets {
String sharedTestDir = "${projectDir}"+'/module-b/src/test/java'
test {
java.srcDir sharedTestDir
}
}
or (assuming your project is named ProjectB)
sourceSets {
String sharedTestDir = project(':ProjectB').file("module-b/src/test/java")
test {
java.srcDir sharedTestDir
}
}
Voila!
Creating test-jar For Gradle 6.6.x
I know that there are many sources telling you, that is not OK, fe:
https://github.com/gradle/gradle/issues/11280
https://gradle.org/whats-new/gradle-6/#better-builds
But this is so damn simple and I just don't like the idea of having common test classes separately in testFixtures folder.
So in module A:
task jarTests(type: Jar, dependsOn: testClasses) {
classifier = 'tests'
from sourceSets.test.output
}
configurations {
tests {
extendsFrom testRuntime
}
}
artifacts {
tests jarTests
}
And in module B:
testImplementation project(':moduleA')
testImplementation project(path: ':moduleA', configuration: 'tests')
And it just works!
If you want to use artifact dependencies to have:
ProjectB's source classes depend on Project A's source classes
ProjectB's test classes depend on Project A's test classes
then ProjectB's dependencies section in build.gradle should look something like this:
dependencies {
compile("com.example:projecta:1.0.0")
testCompile("com.example:projecta:1.0.0:tests")
}
For this to work ProjectA needs to build a -tests jar and include it in the artifacts it produces.
ProjectA's build.gradle should contain configuration like this:
task testsJar(type: Jar, dependsOn: testClasses) {
classifier = 'tests'
from sourceSets.test.output
}
configurations {
tests
}
artifacts {
tests testsJar
archives testsJar
}
jar.finalizedBy(testsJar)
When ProjectA's artifacts are published to your artifactory they will include a -tests jar.
The testCompile in ProjectB's dependencies section will bring in the classes in the -tests jar.
If you want to includeFlat ProjectA's source and test classes in ProjectB for development purposes then the dependencies section in ProjectB's build.gradle would look like this:
dependencies {
compile project(':projecta')
testCompile project(path: ':projecta', configuration: 'tests')
}
If you have mock dependencies which you need to share between tests, you can create new project projectA-mock and then add it as test dependency to ProjectA and ProjectB:
dependencies {
testCompile project(':projectA-mock')
}
This is clear solution to share mock dependencies, but if you need to run tests from ProjectA in ProjectB use other solution.
The solution mentioned by Nikita for Android + Kotlin looks like this:
task jarTests(type: Jar, dependsOn: "assembleDebugUnitTest") {
getArchiveClassifier().set('tests')
from "$buildDir/tmp/kotlin-classes/debugUnitTest"
}
configurations {
unitTestArtifact
}
artifacts {
unitTestArtifact jarTests
}
Gradle for project that is going to use dependencies:
testImplementation project(path: ':shared', configuration: 'unitTestArtifact')
If you are struggling to adapt the solution to the Gradle Kotlin DSL this is the equivalent:
configurations {
register("testClasses") {
extendsFrom(testImplementation.get())
}
}
val testJar = tasks.register<Jar>("testJar") {
archiveClassifier.set("test")
from(sourceSets.test)
}
artifacts.add("testClasses", testJar)
Some of the other answers caused errors one way or another - Gradle did not detect test classes from other projects or Eclipse project had invalid dependencies when imported. If anyone has the same problem, I suggest going with:
testCompile project(':core')
testCompile files(project(':core').sourceSets.test.output.classesDir)
The first line forces the Eclipse to link the other project as dependency, so all sources are included and up to date. The second allows Gradle to actually see the sources, while not causing any invalid dependency errors like testCompile project(':core').sourceSets.test.output does.
in project B:
dependencies {
testCompile project(':projectA').sourceSets.test.output
}
Seems to work in 1.7-rc-2