Gradle projects depending on artifacts created by sibling projects
Asked Answered
N

2

14

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)
}
Nyaya answered 26/7, 2013 at 7:37 Comment(0)
D
14

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.

Derision answered 26/7, 2013 at 8:1 Comment(7)
Thanks! 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).Nyaya
Also - 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?Nyaya
Reaching 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") }.Derision
Excellent, 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.Nyaya
Should work exactly the same as my code for "frontend" above.Derision
What 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.Nyaya
It's safer to make` mergeFrontAndBack` a Zip task, as otherwise you might end up with some duplicated War behavior (e.g. duplicated descriptors). Another improvement is to make mergeFrontAndBack depend on configurations.merge, rather than on individual tasks.Derision
E
0

Using this post and ideas from elsewhere I made a github project - https://github.com/istomisgood/WarNode

Ethelinda answered 16/11, 2017 at 8:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.