Gradle multi project - sharing source code between projects - java

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
}

Related

How can I have separate debug and release versions of gradle project that pull in different versions of the same class?

I have two versions of the same Java class (same name / methods). Since it's Java, both .java files have the same name. I want to configure gradle in such a way that I can build a "debug" version of my application that pulls in one of these files, and a "production" version of my application that pulls in the other one. How would I go about doing this?
This class has only static methods. I don't ever want to make an instance of this class. I additionally don't want to add the overhead of an if statement in each of the methods on this class to check which version I'm in.
Following #JFabianMeier's answer you could use 4 projects:
with the production version class
with the debug version class
with code that uses either of the two, parameterized according to Migrating Maven profiles ... → Example 6. Mimicking the behavior of Maven profiles in Gradle. (I'm also a Maven guy and therefore can't tell you exactly how to do it in Gradle.)
a multi-project with 1./2./3. as sub[-]projects for building all of them in one go, maybe parameterized to build just 1.+ 3. or 2.+ 3.
Have you tried creating production and debug source directories/sets? According to the docs you can use multiple directories when specifying source directories in your source set. Try dropping the different versions of your class in their respective production/debug source directories.
I haven't tested myself (not sure about the syntax), but this is based on the Gradle's Java compilation documentation.
sourceSets {
// Could also name this production
main {
java {
srcDirs ['src/main/java', 'src/prod/java']
}
}
debug {
java {
srcDirs ['src/main/java', 'src/debug/java']
}
}
}
You could do the following:
Put the class into a separate project (so generate a separate jar from it)
Then you can have two different jars, for production and debugging
Then you can pull either one or the other jar in gradle depending on a parameter.
Alternatively, you could look into template engines like Velocity which allow you to generate source code during the build depending on variables and then compile it.
Android has a neat feature called Product Flavors. It lets you swap classes at compile time effortlessly and keep your project clean.
This post is very good to get a taste of it: https://android-developers.googleblog.com/2015/12/leveraging-product-flavors-in-android.html
And here is the full documentation: https://developer.android.com/studio/build/build-variants#product-flavors

Java, IntelliJ, maven project - importing issues

This might be a silly question, but I need to know. I'll delete it if it's too silly to answer.
In a maven project in IntelliJ, I have the following structure:
procedure
e2e
cucumber (same level as common)
src
test
java
e2e
support
File: ScenarioState.java
package e2e.support
common (same level as cucumber)
src
main
java
common
testdata
File: Case.java
package common.testdata
Is there any way to import the package e2e.support (where ScenarioState.java resides) into the file Case.java in the common.testdata package?
I've been playing around with maven imports, dependencies etc., but I haven't found a way to do it. I might have to redesign some classes to get around it, but that would impact other parts of the project and I'd like to avoid it if possible.
If you really want to do that (and I would strongly recommend to either leave the project alone or restructure it first), define an additional source directory as in
How to add an extra source directory for maven to compile and include in the build jar?
But beware that a project like this will haunt you till the end of time.
One could have in the common's pom.xml a dependency to cucumber with <type>test-jar</type>.
However this violates the concept of src/main for the final product, and src/test for the unit-tests (not incorporated in the product, separate test classes).
(In src/test there can be other classes, so maybe easiest would be for common to have a src/test instead.)
If ScenarioState has nothing to do in src/main, one could place it in a more low-level library cucumberbase in src/main. And make a dependency in cucumber to cucumberbase with <scope>test</scope>. In <common> a normal dependency to cucumberbase.
Keep this main-test separation as otherwise other developers risk insanity.

Cyclic reference when a java util library is tested with external mock util library also using the java util lib

Problem
In java, I have a a "Util project" using another "Mock project" when doing unit test.
My problem is that the "Mock Project" is as well using the "Util project" to build some of the Mock object.
When i use maven to build my projects, i can't build it cause one project miss the jar from the second and reverse case for the other project.
Example
As you can see in the example below, it make sense that both project needs each other and each piece of code is located in the right project, what is "Mock" is in "Mock" project, what is "Util" is in "Util" project.
public class TestProjectUtil
{
#Test myMethod()
{
//some code
GeneratedEntity obj = ProjectMockUtil.generateEntity();
}
}
public class ProjectMockUtil
{
public static EntityObj generateEntity()
{
//Some code
EntityObj obj = new EntityObj();
MethodList names = ProjectUtil.Reflection.getMethodList(obj);
//Some code
}
}
Question
How should you deal with this type of situation. I tried to force maven to still build my project and ignore failure but as soon as one class fail to compile then the generated jar does not include any class at all, so the jar is empty.
As well i do not believe that a refactoring is ultimately needed in my case, the different classes are in the right projects and i do not want to duplicate my code for the sake of having the same class in both project to satisfy maven and make it work.
What might be the best approach ?
option 1
Another way to do it is to build the first project as a JAR WITHOUT MAVEN, in this case your jar is usable by the second project when you run maven for the first time. (the JAR will need to be added as dependency in the POM).
After that you can build the first project normally with maven, then rebuild the second project again but this time change the JAR reference in the POM to use dependency from local repository (the one you just build with first project).
And for future build always use MAVEN as before, it will work properly.
Option 2
Another way to do so is to merge the 2 projects together, its not always logic to do so but in my case, it could be logic to merge 2 class util together and create a separation via package name, for instance first project under dev.helper.helperjse, then the second project dev.helper.helpermock
In this case we don't have the issue with circular reference since within a project circular reference are accepted and normal.
Option 3
Another way is to change the argument of the maven compilation plugin and pass argument to force compilation error .class to be added to the jar file and to not fail on error. (this one i did not find what are the arguments yet, Happy if someone knows).

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 Transitive Dependencies from Modules

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.

Categories