Gradle Transitive Dependencies from Modules - java

Disclaimer:
I'm new to Gradle, have read a lot of docs, and I don't know whether my maven-style understanding is tripping me out, or whether it's the sleep dep (kids - don't ask), but I'm still not getting it.
Problem Background:
I have a project that consists of several modules.
One of the modules, let's call it data-structure defines a data structure
Another module, data-structure-fabsearch, defines an implementation for a data source for the data structure, and finally
A third module, fabsearch-common, defines some common data source classes (eg: connection management to a fabsearch data source etc).
The reason I've done it like this is because there's actually another module that also uses the fabsearch-common stuff.
Anyway, my data-structure-fabsearch build.gradle looks something like this:
dependencies {
compile project(:data-structure)
compile project(:fabsearch-common)
}
The fabsearch-common module declares depedencies for the fabsearch api (let's call it fabsearch-api-1.0.0).
So, the dependency tree for data-structure-fabsearch should look like this:
- data-structure-fabsearch
- data-structure
- fabsearch-common
- fabsearch-api-1.0.0
This was all working wonderfully last night. This morning I came to work and all of a sudden those dependencies don't resolve anymore. References to fabsearch-api-1.0.0 classes are no longer found.
What I've Tried
1. In the parent build.gradle:
project(':data-structure-fabsearch'){
apply plugin: 'java'
dependencies {
compile project(path: ':data-structure', configuration: 'compile')
compile project(path: ':fabsearch-common', configuration: 'compile')
}
}
I've tried this with and without the configuration setting.
2. In the data-structure-fabsearch build.gradle file, adding the configuration parameter.
3. Restarting IntelliJ
4. Clicking the refresh icon in the Gradle tool window (repeatedly)
5. Reading all about transitive dependencies in the Gradle user guides
6. Drinking tea (repeatedly)
None of the above work.
What I'm Expecting
I'm expecting that the fabsearch-common dependencies (the fabsearch-api jars) should also be included in the data-structure-fabsearch dependency tree. All references to fabsearch-api classes in data-structure-fabsearch should resolve etc etc.
My Question[s]
Whilst this is possible in Maven, is it possible in Gradle?
What do I have to do to get it to work?
How much sleep dep can you take without dying?
Many thanks for any help.

Turns out the problem wasn't gradle at all. The problem was IntelliJ.
It got it's knickers into a proper twist!
Solution:
1. Close the project in IntelliJ
2. Delete the .idea directory
3. Delete all .iml files and any other IntelliJ cra-useful files
4. Open project in IntelliJ, choose same directory. Problem disappears.

Related

Gradle + Eclipse : use class from existing project in a new project

I know there are a lot of questions that seem similar. I have also spent a few hours getting to grips with Gradle multiprojects. But I still don't understand what the best course of action is here. Incidentally I am using Groovy as my coding language, but explanations referencing Java would be just as good.
I have developed an Eclipse Gradle project, "ProjectA", which in particular has a class, IndexManager, which is responsible for creating and opening and querying Lucene indices.
Now I am developing a new Eclipse Gradle project, "ProjectB", which would like to use the IndexManager class from ProjectA.
This doesn't really mean that I would like both projects to be part of a multiproject. I don't want to compile the latest version of ProjectA each time I compile ProjectB - instead I would like ProjectB to be dependent on a specific version of ProjectA's IndexManager. With the option of upgrading to a new version at some future point. I.e. much as with the sorts of dependencies you get from Maven or JCenter...
Both projects have the application plugin, so ProjectA produces an executable .jar file whose name incorporates the version. But currently this contains only the .class files, the resource files, and a file called MANIFEST.MF containing the line "Manifest-Version: 1.0". Obviously it doesn't contain any of the dependencies (e.g. Lucene jar files) needed by the .class files.
The application plugin also lets you produce a runnable distribution: this consists of an executable file (2 in fact, one for *nix/Cygwin, one for Windows), but also all the .jar dependencies needed to run it.
Could someone explain how I might accomplish the task of packaging up this class, IndexManager (or alternatively all the classes in ProjectA possibly), and then including it in my dependencies clause of ProjectB's build.gradle... and then using it in a given file (Groovy or Java) of ProjectB?
Or point to some tutorial about the best course of action?
One possible answer to this which I seem to have found, but find a bit unsatisfactory, appears to be to take the class which is to be used by multiple projects, here IndexManager, and put it in a Gradle project which is specifically designed to be a Groovy library. To this end, you can kick it off by creating the project directory and then:
$ gradle init --type groovy-library
... possible to do from the Cygwin prompt, but not from within Eclipse as far as I know. So you then have to import it into Eclipse. build.gradle in this library project then has to include the dependencies needed by IndexManager, in this case:
compile 'org.apache.lucene:lucene-analyzers-common:6.+'
compile 'org.apache.lucene:lucene-queryparser:6.+'
compile 'org.apache.lucene:lucene-highlighter:6.+'
compile 'commons-io:commons-io:2.6'
compile 'org.apache.poi:poi-ooxml:4.0.0'
compile 'ch.qos.logback:logback-classic:1.2.1'
After this, I ran gradle jar to create the .jar which contains this IndexManager class, initially without any fancy stuff in the manifest (e.g. name, version). And I put this .jar file in a dedicated local directory.
Then I created another Gradle project to use this .jar file, the critical dependency here being
compile files('D:/My Documents/software projects/misc/localJars/XGradleLibExp.jar' )
The file to use this class looks like this:
package core
import XGradleLibExp.IndexManager
class Test {
public static void main( args ) {
println "hello xxx"
Printer printer = new Printer()
IndexManager im = new IndexManager( printer )
def result = im.makeIndexFromDbaseTable()
println "call result $result"
}
}
class Printer {
def outPS = new PrintStream(System.out, true, 'UTF-8' )
}
... I had designed IndexManager to use an auxiliary class, which had a property outPS. Groovy duck-typing means you just have to supply anything with such a property and hopefully things work.
The above arrangement didn't run: although you can do build and installdist without errors, the attempt to execute the distributed executable fails because the above 6 compile dependency lines are not present in build.gradle of the "consumer" project. When you put them in this "consumer" Gradle project's build.gradle, it works.
No doubt you can add the version to the generated .jar file, and thus keep older versions for use with "consumer" projects. What I don't understand is how you might harness the mechanism which makes the downloading and use of the dependencies needed by the .jar as automatic as we are used to for things obtained from "real repositories".
PS in the course of my struggles today I seem to have found that Gradle's "maven-publish" plugin is not compatible with Gradle 5.+ (which I'm using). This may or may not be relevant: some people have talked of using a "local Maven repository". I have no idea whether this is the answer to my problem... Await input from an über-Gradle-geek... :)
You should be able to update the Eclipse model to reflect this project-to-project dependency. It looks something like this (in ProjectB's build.gradle):
apply plugin: 'eclipse'
eclipse {
classpath.file.whenMerged {
entries << new org.gradle.plugins.ide.eclipse.model.ProjectDependency('/ProjectA')
}
project.file.whenMerged {
// add a project reference, which should show up in /ProjectB/.project's <projects> element
}
}
These changes may be to the running data model, so they may not actually alter the .classpath and .project files. More info can be found here: https://docs.gradle.org/current/dsl/org.gradle.plugins.ide.eclipse.model.EclipseModel.html
This issue is discussed here: http://gradle.1045684.n5.nabble.com/Gradle-s-Eclipse-DSL-and-resolving-dependencies-to-workspace-projects-td4856525.html and a bug was opened but never resolved here: https://issues.gradle.org/browse/GRADLE-1014

Gradle - one project, multiple packages, multiple jars

I'm asking you about a very basic question but I hope you can find the time to help me:
I'm trying to realise a java-project, that can spit out several different programs which partially have dependencies on other projects of mine.
In order to keep it simple, I want to have all the code in one project, run by Gradle, so if I make changes to a central library (the database connector for example) all the child-programs automatically recieve the changes.
An example could look like this:
project:
program_A
central_library
program_B
output:
program_A.jar (including central library)
program_B.jar (including central library)
Now I'm having serious troubles finding a correct buildscript for this and was wondering if someone here could help me out.
P.S. : Since I'm new to this, if I should realize this through different modules within the Gradleproject instead of different packages in the Gradleprojects sourcefile, feel free to tell me :)
One way to approach this is to have a root project, that holds the three other projects inside of it.
Specify the sub-projects inside its settings.gradle file:
rootProject.name = 'RootProject'
include 'program_A'
include 'central_library'
include 'program_B'
With this in place, program_a can depend on central_library by adding a dependency in its build.gradle:
dependencies {
compile project(':central_library')
}
I have a similar setup in one of my projects, although the "central library" is the root project and the submodules are test environments.
Create a root directory and put each library or program into its own sub-directory.
Create a gradle project in each subproject.
You can for example create a skeleton gradle project by running
gradle init --type=java-library
or
gradle init --type=java-application
Then in the root directory create a gradle multi-module project. Basically
run only
gradle init
and then create a settings.gradle and list all sub-projects there.
This is actually described very well in the gradle documentation:
https://guides.gradle.org/creating-multi-project-builds/
If I understand correctly, what you want to do is, when you change your local projects, you want other projects to see those details. For this you need to publish your projects to some kind of repo, like maven repo. You can do this from command line gradle publishToMavenLocal, or gradle build pTMl. You can also do this in build.gradle file with something like the following:
task sourceJar (type : Jar) {
classifier = constants.extSources
from sourceSets.main.allSource
}
publications {
mavenJava(MavenPublication) {
from components.java
artifact(sourceJar) {
classifier "sources" //classifier = constants.extSources
}
}
}

Gradle multi project - sharing source code between projects

In a gradle multi project (java), it is possible to share source code so that each project has access to it and compiles against it?
For example, sub projects 'mouse', 'cat', 'dog', containing a single java class in each - Mouse.java Cat.java, and Dog.java respectively. Each of these classes extends Animal.java.
A caveat is that when any given sub-project (e.g, 'mouse') is being built, it has some compile time dependencies defined (from maven central) and it requires that Animal.java gets compiled using those some dependencies. The same goes for the other projects. So, I can't compile and jar up Animal.java and then use that jar as a dependency of the 3 projects. Animal must get recompiled together with the source of the dependent project. The result should be mouse.jar, cat.jar, and dog.jar, each in their respective project build dir (as usual) and each also containing their version of Animal.class.
I understand that the example isn't very intuitive (as Animal should be generic enough to be compiled independently). It's the best I could find to simulate the hard to explain scenario I'm trying to solve.
Is that possible to do in Gradle?
Thank you.
solved this with sourceSets. Added the parent project main source set to the sub projects:
e.g, added to build.gradle of project 'mouse':
sourceSets {
main.java.srcDir project(':animal').sourceSets.main.java
}

Dynamically add JAR to Gradle dependencies

Currently, my build.gradle has a dependency on an external library built with Ant. To accomplish building the library, I followed the advice here and created a task which builds the external library, and copies it to the libs/ folder.
The task is called as part of a dependency:
build.gradle
dependencies {
compile fileTree('libs') {
include '*.jar'
builtBy 'myTask'
}
}
task myTask (type: GradleBuild) { GradleBuild antBuild ->
antBuild.buildFile('external-stub.gradle')
antBuild.tasks = ['clean', 'ivy.check', 'ivy.download', 'ivy.task', 'ivy',
'init', 'mergeCode', 'compile', 'jar', 'copyJarsToProject']
}
However, when the compile actually runs, the library I just built and copied in is not included in the dependencies, as evidenced by a whole lot of compilation errors.
Am I including the library the wrong way?
The full build.gradle and associated files are over at Github, and I've linked to the specific commit I'm having issues with: Source Repository
Alright, took me a while to get a build I was happy with. But, here's what was changed.
The actual build of the JAR was built using the same style, but moved to the external project (so that the main build project wasn't reaching across to it). I'll give an in-depth explanation below, but the commits are here and here. These are in order.
Basically, we export the jar as an artifact that other projects can depend on, rather than copying over the Jar ourselves. This way, the Ant build runs and other projects can see the Jar we just created. This is the end of the first commit. In the second commit, the task outputs are marked as needing to be regenerated only if the Jar does not exist. This was due to the fact that whenever I tried to build the app, it would take minutes to regen the Jar, and then have to repackage everything else as well. The code is below:
build.gradle External Project
configurations {
buildJSword
}
task doBuildJSword (type: GradleBuild) {
buildFile = 'jsword-stub.gradle'
tasks = ['clean', 'ivy.check', 'ivy.download', 'ivy.task', 'ivy',
'init', 'mergeCode', 'compile', 'jar'] //, 'copyJarsToMinimalBible']
ext.outputJar = file('distribution/jsword.jar')
outputs.upToDateWhen {
ext.outputJar.exists()
}
}
artifacts {
buildJSword(doBuildJSword.ext.outputJar) {
builtBy doBuildJSword
}
}
Then, the main project just has to add this project as a compile-time dependency:
build.gradle Main Project
compile project(path: ':jsword-minimalbible', configuration: 'buildJSword')
Hope this is helpful for anyone with a similar issue, let me know if you have questions!
Note: The build currently does not clean itself properly, so if you change any code in the external project, you need to delete the external Jar for everything to regenerate itself correctly.

Gradle - how to run the tests from a different gradle project and still get coverage data

Does anyone know how to run the tests from a different gradle project and still get emma coverage reporting data?
Here is my current layout:
Root/
settings.gradle (no explicit build.gradle - just defines all subprojects)
SubProjectA/
build.gradle
src/ (all real source is here)
SubProjectATest/
build.gradle
src/ (all testing code is here)
SubProjectB/ (similar structure as A)
SubProjectBTest/ (similar structure as ATest)
I am currently using the emma plugin, and I would like to build SubProjectA and run all the tests in SubProjectATest from within the build.gradle of SubProjectA.
Here are some things I tried inside the build.gradle of SubProjectA
testCompile project(':SubProjectATest').sourceSets.test.classes (as suggested by this article), but I got an error "Could not find property 'sourceSets' on project"
Just the straight-up testCompile project(':SubProjectATest'), but then I get "..SubProjectA/build/classes/test', not found" and also "Skipping task ':SubProjectA:compileTestJava' as it has no source files."
Simply adding a sourceSet like the following:
test {
java {
srcDir '../SubProjectATest/src'
}
}
Adding the source set in (option 3) is the only option that worked, but it seems sloppy to do it this way. Does anyone know how to do this using project dependencies?
Update #1
I also tried one of the answers below to use test.dependsOn and the tests do run, but the emma plugin reported the following: build/classes/test', not found
1. and 2. just add classes to the test compile class path. This doesn't have any effect on which tests are going to be executed.
3. is the wrong approach because you should not add sources from project X to project Y.
If what you want is that gradle :SubProjectA:test also executes :SubProjectATest:test, all you need to do is to add a task dependency:
SubProjectA/build.gradle:
test.dependsOn(":subProjectATest:test")
By the way, what is your motivation for putting the tests in a separate project?

Categories