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)
}
stolsvik
  • 5,253
  • 7
  • 43
  • 52

2 Answers2

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.

Peter Niederwieser
  • 121,412
  • 21
  • 324
  • 259
  • 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). – stolsvik Jul 26 '13 at 08:31
  • 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? – stolsvik Jul 26 '13 at 08:39
  • 1
    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") }`. – Peter Niederwieser Jul 26 '13 at 09:13
  • 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. – stolsvik Jul 26 '13 at 11:31
  • Should work exactly the same as my code for "frontend" above. – Peter Niederwieser Jul 26 '13 at 11:49
  • 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. – stolsvik Jul 26 '13 at 12:13
  • 1
    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. – Peter Niederwieser Jul 26 '13 at 12:33
0

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

Thomas Newman
  • 164
  • 1
  • 6