0

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?

mike rodent
  • 14,126
  • 11
  • 103
  • 157
  • Would it be possible for ProjectA to publish all JARs to a directory from which ProjectB consumes them as follows? https://stackoverflow.com/a/32192840/6505250 – howlger Dec 30 '18 at 15:04
  • Thanks... Yes, "publish" on the local system, in other words? I'm not familiar with fileTree ... but it appears that the first difficulty is to create an "artifact" (.jar?) which stipulates its own dependencies ... and then somehow loads them when it itself is included as a dependency in another project. I don't have a clue how to do that. – mike rodent Dec 30 '18 at 15:42
  • Either ProjectA is published as a _fat JAR_ (one JAR that contains, in addition, all dependencies) or [copies its dependencies to a directory](https://stackoverflow.com/q/26697999/6505250) from where ProjectB consumes it. Instead of a directory also a real repository (local Maven repository, Nexus, Artifactory, etc.) can be used with the advantage to have metadata (version, dependencies) for the ProjectA JAR. – howlger Dec 30 '18 at 16:00
  • Thanks again. I don't want to create a fat jar and I don't want to copy dependencies. I want to use the same mechanism for dependency supply as used by "the big boys" ("real repositories"): i.e. these should be downloaded as required (if absent) to GRADLE_USER_HOME when ProjectB does "./gradlew build". Are you suggesting publishing ProjectA to a real repository? I'd really like just to see an example of how that is done & how this situation is best handled... – mike rodent Dec 30 '18 at 16:09
  • As far as I know, Gradle has a local cache and no repository like Maven. I guess the easiest way is probably a local Maven repository. But I'm not a Maven or Gradle expert and haven't done anything like this yet. – howlger Dec 30 '18 at 16:15

2 Answers2

0

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... :)

mike rodent
  • 14,126
  • 11
  • 103
  • 157
0

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

emilles
  • 1,104
  • 7
  • 14