I've been having this issue on and off for the past few weeks after beginning to learn how to use Gradle.
I added the dependency to my build.gradle file
compile group: 'org.apache.xmlgraphics', name: 'batik-all', version: '1.12', ext: 'pom'
I used VSCode's command palette to "refresh", cleaned the server workspace and ran gradle build but the new dependency does not show up in my "Project and External Dependencies" and I cannot import org.apache.batik.*
Hoping to find a fix for this in VSCode as I have seen some other IDE specific fixes.
So the issue was that in the map notation you were specifying the ext: 'pom' part. This effectively told Gradle to only import the POM file and not treat the dependency as a regular one.
When you moved to the different notation, you did not keep that pom element in the coordinates and so you got the right behaviour.
Note: compile has been replaced by implementation for a while now and is even deprecated in Gradle 6.x. Have a look at the documentation for more information on this.
I have no idea why gradle is doing this to me. I have a multiproject that looks like this:
|Parent
|client
|common
|service
Parent is just an empty project and client, common and service are gradle java projects:
I have some classes that are used in both client and service, therefore I wanted to create a common project and build a jar that I would later use in both service and client. I can build the common jar, but whenever i try to do 'add dependency on common' and then try to 'refresh gradle', it removes the dependency and fails to build!
This is what I do:
Then I press this because I want to build it:
And it just removes the dependency!!!
This is build.gradle file from client project:
plugins {
id 'org.springframework.boot' version '2.2.5.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
id 'java'
}
group = 'sot.rest'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
repositories {
mavenCentral()
}
dependencies {
compile group: 'org.springframework', name: 'spring-web', version: '5.2.4.RELEASE'
implementation 'org.springframework.boot:spring-boot-starter'
// https://mvnrepository.com/artifact/com.google.code.gson/gson
compile group: 'com.google.code.gson', name: 'gson', version: '2.8.6'
}
Please help, Im desperate!!
Check out my answer on your previous question. It should give you an idea how to structure and declare the multi-module gradle projects.
I think that when you add the dependency on module with IntelliJ, it just adds it to the project structure through project settings in IntelliJ. And later, when you hit refresh, IntelliJ configures the project based on Gradle files.
To make it working, the Parent project should also be a Gradle project. If it isn't just add build.gradle and settings.gradle under the Parent directory.
Then in settings.gradle add the subprojects like:
rootProject.name = 'Parend'
include('common') //this adds the module
include('client')
include('service')
And later, in build.gradle files of client and service modules add the common module as dependency with:
dependencies {
implementation project(':common')
//...
}
If you are going to work more with Gradle, you could take a look at this article about overall insight on Gradle.
Edit:
(I understood that when you use implementation it doesn't throw errors)
To work with multimodule project with Gradle, the root project also needs to be a Gradle project. The root might or might not contain any source code, but it needs to have its own Gradle files.
So if your project structure needs to be like:
Root project 'parent'
+--- Project ':client'
+--- Project ':common'
\--- Project ':service'
Then the parent and submodule projects need to be set as Gradle projects.
To make it the parent project needs to have at least a settings.gradle file and declared includes for submodules, similarly to:
rootProject.name = 'parent'
include 'common'
include 'client'
include 'service'
Each of modules (client,common,service) must have a build.gradle files under its directory. Submodules using common, so the service and client must add the common as dependency in their own build.gradle files, like:
dependencies {
implementation project(':common')
//and rest of required dependencies
//testCompile group: 'junit', name: 'junit', version: '4.12'
}
Then you should be able to import public classes from common in those submodules and rebuild or reimport project without error.
Since the parent project doesn't contain any source code, then it doesn't need its own build script, but then build file of all of the submodules needs to declare the java plugin on top of the build file:
plugins {
id 'java'
}
Since you are working with IntelliJ and your project could have different structure previously, then the project structure in IntelliJ setting could be messed up now.
To fix it you could go to File->Project Structure->Modules and remove/reimport the parent module again.
If you don't have many classes now, I'd recommend you to create a new project. In the "New Project" window pick Gradle and uncheck Java in "Additional Libraries and Frameworks". This will create blank Gradle project.
After the project is generated, do a right mouse click on parent, and select New->Module. In new module window again pick Gradle and leave the Java checked (since the submodules will contain source code).
With that, the IntelliJ will automatically include created module to the settings.gradle file of root/parent project and the build file of that module will contain the basic configuration (e.g. the java plugin).
But you still add the dependency of one module in another in the build.gradle file of that module.
When calling gradle idea, external dependencies are ordered first in the class path relatively to local Jar inclusions. As such :
dependencies {
compile fileTree(dir: 'libs', include:['*.jar'])
compile group: 'foo', name:'bar', version:'1.0.0'
}
will include my local jars last. This is a problem in my project as these jars' purpose is to partially overwrite the external library.
The same behavior is observed when specifying the repository as a source of dependencies using flatDir and loading the jar without fileTree. It is put last in the classpath.
I have found several mentions of the problem when researching, such as https://discuss.gradle.org/t/gradle-messes-up-the-classpath-order-in-generated-projects-when-there-are-mixed-dependency-types/13130, but no workarounds.
I suppose these exist, gradle being very customisable, but being very new to it my attempts to make one fail. How to proceed?
I'm not using IntelliJ on a regular basis but tried it in the context of this question and my impression is that gradle's idea plugin and IntelliJ's gradle plugin don't go well together. That is you should either use the idea gradle plugin and import as plain Java project or import as gradle project using IntelliJ's gradle plugin. Main reason is that the idea plugin and the IntelliJ plugin are generating slightly different iml-files (those files are holding the project dependencies - amongst others) which leads to lot of confusion when using both plugins together. As you specifically asked for the gradle idea plugin, I used this plugin and imported into IntelliJ as plain java project.
But to answer your question I found no evidence that the order of libraries on the classpath differs from the order as declared in the dependencies section of the gradle file, when using a flatDir repo. When using compile fileTree(dir: 'libs', include:['*.jar']) the order was actually broken as described in your question. That is, you should stick to using a flatDir repo.
I'm using gradle 4.9 and IntelliJ 2018.2.
This is my gradle file
apply plugin: 'java'
apply plugin: 'idea'
repositories {
jcenter()
flatDir {
dirs 'libs'
}
}
dependencies {
compile 'zzz:zzz-0.0.0'
compile 'aaa:aaa-0.0.0'
compile 'com.google.guava:guava:24.0-jre'
compile group: 'javax.websocket', name: 'javax.websocket-api', version: '1.1'
}
task wrapper(type: Wrapper) {
gradleVersion = '4.9'
distributionUrl = "http://services.gradle.org/distributions/gradle-${gradleVersion}-bin.zip"
}
In my libs folder there are two jars aaa-0.0.0.jar and zzz-0.0.0.jar both are copies of guava-24.0-jre.jar. That is all guava classes are present in both jars as well. As zzz:zzz-0.0.0 is the first dependency in the gradle file, the expectation would be that guava classes are being loaded from zzz-0.0.0.jar instead of guava-24.0-jre.jar or aaa-0.0.0.jar. I used the following main class to test this:
package test;
import com.google.common.math.LongMath;
public class Test {
public static void main(String[] args) throws Exception {
System.out.println(LongMath.class.getProtectionDomain().getCodeSource().getLocation().toURI());
}
}
And the output when running it from IntelliJ is
file:/C:/ws/gradle-idea-test/libs/zzz-0.0.0.jar
That is the com.google.common.math.LongMath class is indeed being loaded from the local libs/zzz-0.0.0.jar instead of the guava-24.0-jre.jar.
I noticed that the list of external dependencies in IntelliJ doesn't show the local libraries. And even more confusing the libraries are ordered alphabetically and don't reflect the actual order on the classpath which might be quite confusing:
To get the actual order of elements on the classpath you will have to look in the module dependencies section in the module settings ("Open Module Settings" > "Project" > "Modules" > "Dependencies Tab") which looks like this:
As you can see the dependencies are listed in correct order and include the local libraries as well. The order of libs in this dialog is basically the same as in the generated iml-file.
When using the IntelliJ gradle plugin instead of gradle's idea plugin, IntelliJ basically behaved the same way but the generated iml-file looked different and the external libraries were displayed in a different format. But there was no difference regarding the classpath order.
I maintain at least 2 Java Gradle projects. Let's say that I have a common project named A and another project B that depends on A. A is technically common to many other projects, but that isn't a minor detail to my problem. Anyways, the current situation is that B declares a dependency on A as a plain-old external dependency in its build.gradle file, like so:
compile group:'com.example', name: 'A', version: '0.1'
We have code specific to tests in A that we share with B by placing it in src/main/java. This code has to stay in A instead of B because there are other projects depending on A that use this test code. I'd like to avoid putting the code in this directory because it has no purpose being deployed to production, due to its test-only nature. Also, simply moving the code from A to B wouldn't be possible because other projects in my organization depend on the same code and I want to avoid code duplication. I would rather move this test code in A to src/test/java, but then it won't be published to A's JAR file.
Thus, I am trying to pursue a solution where this code lives in src/test/java in A and is deployed in a test-only JAR file. There are posts that discuss possible solutions, such as this blog post or SOF posts such as Multi-project test dependencies with gradle and Multi-project test dependencies with gradle. HOWEVER, I continuous run into a problem caused by the fact that I am not maintaining a multi-project Gradle build, but rather two completely separate single-project Gradle builds, and this cannot change.
Where I am at right now is that I am configuring build.gradle for A in the exact way suggested in the blog post, like so:
configurations {
testOutput.extendsFrom testCompile
}
task jarTest (type: Jar, dependsOn: testClasses) {
from sourceSets.test.output
classifier = 'test'
}
artifacts {
testOutput jarTest
}
And then I have A declared as both compile and testCompile dependencies in build.gradle of B:
compile(group: 'com.example', name: 'A', version: '0.1')
testCompile(group: 'com.example', name: 'A', version: '0.1', configuration: 'testOutput')
This doesn't work. I am not getting any of the testCompile dependencies visible in the test classpath for B; these testCompile dependencies are not being reported by gradle dependencies nor are recognized by my IDE's code search (IntelliJ). I threw up a Hail Mary and even tried to replace configuration: 'testOutput' with classifier: 'test', but to no avail. Gradle documentation doesn't quite seem to help either, as it seems that my use case isn't really covered in their intended use cases.
Any way I can achieve my desired usage of Gradle, or am I stuck with exporting test-only code in the main JAR file of A? All help much appreciated.
I would treat code that is only needed during testing exactly the same as libraries that are only used during testing (e.g. JUnit or Mockito): The code should be in a separate module with its own name.
So I suggest that you split A into two modules (of the same multi-project build):
A
A-test-support
The dependencies of A would have to change to:
dependencies {
testCompile project(':A-test-support')
}
In B you have to use these dependencies:
dependencies {
compile(group: 'com.example', name: 'A', version: '0.1')
testCompile(group: 'com.example', name: 'A-test-support', version: '0.1')
}
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