Gradle projects depending on artifacts created by sibling projects
The local Maven repository (and Gradle's install
task) should only be used when exchanging artifacts with Maven builds. It's not meant to be used for exchanging artifacts between projects of a Gradle build, and installing into the local Maven repository won't happen automatically.
Instead, merger
needs to declare project dependencies on the other two projects. For example:
configurations {
merge
}
dependencies {
merge project(":frontend"), project(":backend")
}
task merge(type: Zip) {
from { configurations.merge.collect { zipTree(it) } }
}
This assumes that frontend
and backend
correctly declare their artifacts. (This may happen automatically, for example if the war
plugin is used.)
You'll find much more on this in the Gradle User Guide, in particular the multi-project builds chapter.
Related videos on Youtube
stolsvik
Merge KEEP! Java coder. Check LinkedIn page for more info.
Updated on June 04, 2022Comments
-
stolsvik about 2 years
I have this Gradle setup with four projects, a parent with three children, where a Java Servlet JSON 'backend' is built into a war-file, and then a static HTML5 'frontend' consuming this is built into a zip. Both these "installs" their artifcats to the local maven repo.
The third sibling project 'merger' depends on these two artifacts, to build a "merged" war by simply "zipping them together".
However, once I had this up and running as intended, I obviously had to test the bootstrap-scenario by deleting the artifacts from the local repo.
Now I suddenly get "Artifact 'no.company:frontend:1.0-SNAPSHOT@zip' not found".
Is it not possible to depend on artifacts which will be produced by the current build?
Edit:
Based on another idea (and the reply from Peter discouraging this Maven logic), this version looks promising, not traversing Maven (note: it works!):
// ## From frontend's build.gradle: task zipFrontend(dependsOn: 'buildFrontend', type: Zip) { from ('dist/') } // ## From backend's build.gradle: apply plugin: 'war' // ## From merger's build.gradle: task mergeFrontAndBack(dependsOn: [':backend:war', ':frontend:zipFrontend'], type: War) { from zipTree(project(':frontend').tasks['zipFrontend'].archivePath) from zipTree(project(':backend').tasks['war'].archivePath) destinationDir(buildDir) }
Edit 2:
Based upon Peter's comment about not reaching into siblings' project structure and his concrete suggestions, here's the resulting piece (note: it works!):
// ## From frontend's build.gradle: task zipFrontend(dependsOn: 'buildFrontend', type: Zip) { from ('dist/') } configurations { zip } artifacts { zip zipFrontend } // ## From backend's build.gradle: apply plugin: 'war' configurations { jsonOnlyWar } artifacts { jsonOnlyWar war } // ## From merger's build.gradle: configurations { merge } dependencies { merge project(path: ':backend', configuration: 'jsonOnlyWar') merge project(path: ':frontend', configuration: 'zip') } task mergeFrontAndBack(dependsOn: configurations.merge, type: War) { from { configurations.merge.collect { zipTree(it) } } destinationDir(buildDir) }
-
stolsvik almost 11 yearsThanks! Okay, because this was how I did it with Maven (and I do install the artifacts to local Maven repo - this works!). However, I've tried the project dependency route you here suggest, but then I only get the JAR from the java-war-project (not the war), and not the zip from the static HTML-files project at all. Also, if I do the merge-task you define, won't I also merge in all the jar's from the java-war project? See, I only want the WAR-structure merged with the ZIP file of static HTML files (the resulting beast is servlet-container-deployable as a proper war due to the nature of war's).
-
stolsvik almost 11 yearsAlso - now I am trying another route: In the merger-project, I simply define a type:War task, which sets from-properties to the output of the "war" task in backend, and on the "zipFrontend" task of frontend. Is this possible? How? The tasks-property doesn't seem to hold sibling references?
-
Peter Niederwieser almost 11 yearsReaching out into the project model of other projects is discouraged. Instead, declare the necessary artifacts for the other two projects, e.g.
configurations { zip }; artifacts { zip taskThatProducesZip }
. Then adapt the project dependencies (e.g.dependencies { merge project(path: ":frontend", configuration: "zip") }
. -
stolsvik almost 11 yearsExcellent, this approach seems nice! How do I now get hold of the war from the 'backend' project with the same logic? There is already a war-task, since I apply the war plugin. But apparently that is not a "configuration", and I have problems defining it as such.
-
Peter Niederwieser almost 11 yearsShould work exactly the same as my code for "frontend" above.
-
stolsvik almost 11 yearsWhat I just realized when reading that, was that I could maybe hack it. Because this doesn't cut it: configurations { war }; artifacts { war war } and then dependencies { merge project(path: ":backend", configuration: "war") } (I actually expected the "war" to be an "implicit configuration", so that only the dependency would be needed..). But then I tried configurations { theWar }; artifacts { theWar war } and dep on "theWar", and this works! The question is updated with the results.
-
Peter Niederwieser almost 11 yearsIt's safer to make` mergeFrontAndBack` a
Zip
task, as otherwise you might end up with some duplicatedWar
behavior (e.g. duplicated descriptors). Another improvement is to makemergeFrontAndBack
depend onconfigurations.merge
, rather than on individual tasks.