I'm using the latest version (7.1.2) of the ShadowJar (Gradle) plugin to build the application's JAR. I'm trying to exclude some dependencies from the resultant JAR, so I have configured the plugin like:
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
...
tasks.withType(ShadowJar) {
final def attributes = [
// ...all the attributes here
]
dependencies {
// Dependencies that are already provided in the lib/ folder of Apache Flink
exclude(dependency("org.apache.flink:flink-clients_2.12"))
exclude(dependency("org.apache.flink:flink-java"))
exclude(dependency("org.apache.flink:flink-streaming-java_2.12"))
}
manifest.attributes(attributes)
mergeServiceFiles()
minimize()
setZip64(true)
}
It's not clear to me what dependencies are actually begin excluded/included — when unzipping and visually analyzing the generated ZIP file(s). I've been trying to compare the generated (ZIP) -shadow file with the one generated by the distZip task, but the -shadow one doesn't have a single JAR on it...only classes.
So I'm wondering if there is a way to print the effective group of dependencies that are going to be part of the final JAR file.
Based on the ShadowJar's documentation, the default configuration is to merge all dependencies from the project's runtimeClasspath, but ./gradlew dependencies --configuration runtimeClasspath doesn't account for the excluded ones.
I have a Java Gradle project that uses an OpenAPI specified API. I've used the org.openapi.generator plugin which generates sources as well as a complete Gradle module.
I expect that there's a way to define the generate, compile, jar steps such that I can have other modules depend on the generated module.
I.e.
# api/build.gradle:
plugins {
id 'java'
id "org.openapi.generator" version "5.0.0"
}
repositories {
mavenCentral()
}
dependencies {
testImplementation group: 'junit', name: 'junit', version: '4.12'
}
compileJava.dependsOn "openApiGenerate"
openApiGenerate {
generatorName = "java"
inputSpec = "$projectDir/src/main/openapi/spec.yaml".toString()
outputDir = "$buildDir/generated"
apiPackage = "com.example.api"
invokerPackage = "com.example.api.invoker"
modelPackage = "com.example.api.model"
configOptions = [
dateLibrary: "java8",
library : "native"
]
groupId = "com.example"
id = "api"
}
gradlew api:openApiGenerate generates (extraneous files elided):
api/build/generated/
├── build.gradle
├── pom.xml
├── settings.gradle
└── src
├── main/java/...
└── test/java/...
Is there some way I can delegate-to, include, or depend on this generated module from other modules in the project? The generated module has a reliable group:artifact:version coordinate.
I.e. I'd like to be able to specify com.example:api:1.0 elsewhere in the project.
I've had a read through of https://docs.gradle.org/current/userguide/composite_builds.html as that seemed to be close to what I expect, but I am new to Gradle and it was a little to deep.
I've tried overriding the main and test source sets in api/build.gradle but I dislike having to copy and paste the dependencies from the api/build/generated/build.gradle.
I found https://docs.gradle.org/current/userguide/declaring_dependencies.html#sec:dependency-types which includes a tantalizing example but falls down as it is a source-only dependency.
dependencies {
implementation files("$buildDir/classes") {
builtBy 'compile'
}
}
I looked at this example but how do I depend on a project (api/build/generated/) that does not exist yet?
dependencies {
implementation project(':shared')
}
Great question! I don’t have a perfect answer but hopefully the following will still help.
Suggested Approach
I would keep the builds of the modules that depend on the generated API completely separate from the build that generates the API. The only connection between such builds should be a dependency declaration. That means, you’ll have to manually make sure to build the API generating project first and only build the dependent projects afterwards.
By default, this would mean to also publish the API module before the dependent projects can be built. An alternative to this default would indeed be composite builds – for example, to allow you to test a newly generated API locally first before publishing it. However, before creating/running the composite build, you would have to manually run the API generating build each time that the OpenAPI document changes.
Example
Let’s say you have project A depending on the generated API. Its Gradle build would contain something like this:
dependencies {
implementation 'com.example:api:1.0'
}
Before running A’s build, you’d first have to run
./gradlew openApiGenerate from your api project.
./gradlew publish from the api/build/generated/ directory.
Then A’s build could fetch the published dependency from the publishing repository.
Alternatively, you could drop step 2 locally and run A’s build with an additional Gradle CLI option:
./gradlew --include-build $path_to/api/build/generated/ …
Idea for Less Manual Work
I have thought quite a bit about this but didn’t come up with any complete solution – hence my imperfect suggestion above. Let me still summarize my idea for how this could work.
You would have a Gradle build which generates the API – similar to your api project. That build would also be committed to your VCS.
That build would publish the generated API, even if it wouldn’t produce it itself. Instead, it would somehow delegate to the Gradle build generated by the openApiGenerate task. The delegation would have to happen via a GradleBuild task.
Here lies the crux: all information on dependencies and published artifacts would effectively have to be retrieved via the Gradle CLI. I doubt that that’s currently possible.
Projects that dependend on the API could then include the api-like Gradle project in a composite build without requiring the manual hassle from the approach above.
To expand on #Chriki's answer with what I've actually used:
Define api/ as it's own project with an empty api/settings.gradle file.
This tells gradle that it is a self-contained project.
Define the api module with:
# api/build.gradle
plugins {
id 'java'
id "org.openapi.generator" version "5.0.0"
}
repositories {
mavenCentral()
}
openApiGenerate {
generatorName = "java"
inputSpec = "$projectDir/src/main/openapi/specification.yaml"
outputDir = "$buildDir/generated"
apiPackage = "com.example.api"
invokerPackage = "com.example.api.invoker"
modelPackage = "com.example.api.model"
configOptions = [
dateLibrary: "java8",
library : "native"
]
groupId = "com.example"
id = "api"
version = "1.0.0"
}
Note the group and id (and version) explicitly define its maven coordinate.
Include the build with a substitution so that the dependents can just use its maven coordinate:
# settings.gradle
includeBuild('api/build/generated') {
dependencySubstitution {
substitute module('com.example:api') with project(':')
}
}
... and in some other module:
# app/build.gradle
dependencies {
implementation group: 'com.example', name: 'api'
}
The main advantage of this over ./gradlew --include-build api/build/generated is that [my] IDE will 'link' it all up too.
Generate the API library:
./gradlew --project-dir api/ openApiGenerate
Build/run the main project:
./gradlew build
./gradlew run
I have a huge legacy EAR project. Current build process uses Ant and I'm trying to convert to gradle.
The old Ant build uses a single source folder and everything is in there (EJB and WAR code together); then Ant uses different tasks to build EJB-JAR and WAR artifacts filtering by the package of interest (my.web.* for the WAR and my.ejb.* for the EJB).
It goes without saying that EJB and WAR heavily reference each other and I guess that's why they are compiled together even though separate artifacts are generated.
I have tried creating a parent EAR project, then separating my.ejb.* in an EJB subproject as well as my.web.* in a WAR subproject but gradle immediately complained about circular dependency and I haven't found a way around.
If above is not possible, then I am looking for recommendations on how to generate the EJB-JAR and WAR artifacts off the same codebase that would be compiled altogether, then include them in the EAR file as 'deploy' dependencies.
I have googled around but I am not very bright at gradle (obviously). Any help will be greatly appreciated.
I think you are falling into the trap that many of us have when trying to convert an existing ant project to Gradle for the first time - you are thinking of things the same 'ol ant way.
Instead, let Gradle do the work for you and it will be easier and the resulting build.gradle file much smaller and more concise.
Thinking in terms of having a parent ear project and a separate war project is good thinking however. But, you should take advantage of the plugins Gradle has to offer to simplify the task of constructing the archives.
Use the 'ear plugin':
https://docs.gradle.org/current/userguide/war_plugin.html
For example:
apply plugin: 'ear'
apply plugin: 'java'
repositories { mavenCentral() }
dependencies {
// The following dependencies will be the ear modules and
// will be placed in the ear root
deploy project(':war')
// The following dependencies will become ear libs and will
// be placed in a dir configured via the libDirName property
earlib group: 'log4j', name: 'log4j', version: '1.2.15', ext: 'jar'
}
ear {
appDirName 'src/main/app' // use application metadata found in this folder
// put dependent libraries into APP-INF/lib inside the generated EAR
libDirName 'APP-INF/lib'
deploymentDescriptor { // custom entries for application.xml:
// fileName = "application.xml" // same as the default value
// version = "6" // same as the default value
applicationName = "customear"
initializeInOrder = true
displayName = "Custom Ear" // defaults to project.name
// defaults to project.description if not set
description = "My customized EAR for the Gradle documentation"
// libraryDirectory = "APP-INF/lib" // not needed, above libDirName setting does this
// module("my.jar", "java") // won't deploy as my.jar isn't deploy dependency
// webModule("my.war", "/") // won't deploy as my.war isn't deploy dependency
securityRole "admin"
securityRole "superadmin"
withXml { provider -> // add a custom node to the XML
provider.asNode().appendNode("data-source", "my/data/source")
}
}
}
Then for your war, use the 'war plugin'. You can create the war as a separate project:
configurations {
moreLibs
}
repositories {
flatDir { dirs "lib" }
mavenCentral()
}
dependencies {
compile module(":compile:1.0") {
dependency ":compile-transitive-1.0#jar"
dependency ":providedCompile-transitive:1.0#jar"
}
providedCompile "javax.servlet:servlet-api:2.5"
providedCompile module(":providedCompile:1.0") {
dependency ":providedCompile-transitive:1.0#jar"
}
runtime ":runtime:1.0"
providedRuntime ":providedRuntime:1.0#jar"
testCompile "junit:junit:4.12"
moreLibs ":otherLib:1.0"
}
war {
from 'src/rootContent' // adds a file-set to the root of the archive
webInf { from 'src/additionalWebInf' } // adds a file-set to the WEB-INF dir.
classpath fileTree('additionalLibs') // adds a file-set to the WEB-INF/lib dir.
classpath configurations.moreLibs // adds a configuration to the WEB-INF/lib dir.
webXml = file('src/someWeb.xml') // copies a file to WEB-INF/web.xml
}
There is another helpful post on creating them as a single project, but that's definitely a bit more 'dicey':
https://discuss.gradle.org/t/single-project-with-jar-ejb-war-and-ear/5874
I have a Gradle project which depends on sub-projects. I would like to create a "fat jar" containing all my sub-projects, and externel dependencies as external jars.
build.gradle:
dependencies {
compile project(':MyDep1')
compile project(':MyDep2')
compile 'com.google.guava:guava:18.0'
}
I would like to be able to generate the following output:
MyProject.jar -> Includes MyDep1 & MyDep2
libs/guavaXXX.jar -> Guava as external lib
I don't know how I could do this.
Use different configurations to hold your internal and external dependencies and package only one of those configurations into your project artifact.
configurations{
internalCompile
externalCompile
}
//add both int and ext to compile
configurations.compile.extendsFrom(internalCompile)
configurations.compile.extendsFrom(externalCompile)
dependencies{
internalCompile project(':MyDep1')
internalCompile project(':MyDep2')
externalCompile 'com.google.guava:guava:18.0'
}
in your fat jar task, include only from internalCompile
I finally made it work with this solution:
jar {
subprojects.each {
from files(it.sourceSets.main.output)
}
}
distributions {
main {
contents {
exclude subprojects.jar.archivePath.name
}
}
}
In my project's jar, I include the content of all subprojects ouputs. In the distribution, I exclude the jar from subprojects (so it only contains dependencies). This is probably not the best way, but it's simple and it works.
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