War task related question


(dumi_daniliuc) #1

Hi,

We need to build multiple distributions of our products and we’ve decided to have each project build multiple .war files. The .war files are quite similar; mostly, we need to apply some distribution-specific changes to some files before adding them to the .war file. So we’ve defined our own custom War task that looks like this:

public class OurWar extends War {
   String cssPath = "/some/default/css/path"
   String jsPath = "/some/default/js/path"
     OurWar() {
      super()
        copyAction.rootSpec.into('css') {
         from cssPath
      }
        copyAction.rootSpec.into('js') {
         from jsPath
      }
        // Other stuff
   }
}

and in our build.gradle we have something like this:

task platformXWar(type: OurWar) {
   // Use default paths
}
  task platformYWar(type: OurWar) {
   cssPath = tasks.transformCssForPlatformY.destDir
}

Unfortunately, this doesn’t work, because the copy actions for platformYWar are configured before we have a chance to set ‘cssPath’ to something else. We tried moving the copy actions out of the OurWar constructor into a doFirst() block. It resulted in those files not being included in the war files at all. So finally we had to change OurWar to look like this:

public class OurWar extends War {
   String cssPath = /some/default/css/path
   String jsPath = /some/default/js/path
     OurWar() {
      super()
        // Other stuff
   }
     void setupStandardCopyActions() {
      copyAction.rootSpec.into('css') {
         from cssPath
      }
        copyAction.rootSpec.into('js') {
         from jsPath
      }
   }
}

and we had to change our build.gradle to:

task platformXWar(type: OurWar) {
   // Use default paths
   setupStandardCopyActions()
}
  task platformYWar(type: OurWar) {
   cssPath = tasks.transformCssForPlatformY.destDir
   setupStandardCopyActions()
}

Now everything works. However, it seems very ugly/hacky to have to call setupStandardCopyAction() at the end of every war task. Basically, it seems like we could really use a post-config (or rather “end-of-config”) hook on the Task class. Does anything like this exist? What’s the best way to do this?

Thanks!


(Peter Niederwieser) #2

A straightforward solution is to use ‘from { cssPath }’ and ‘from { jsPath }’ in the constructor. This will defer the evaluation of ‘cssPath’ and ‘jsPath’ until execution time.


(dumi_daniliuc) #3

Thanks Peter!

I tried

from {
   cssPath
   into 'css'
}

and Gradle complained about a cyclic dependency (it tells me that my war task depends on itself) – not sure why. However, this worked:

into('css') {
   from {
      cssPath
   }
}

Thanks!


(René Groeschke) #4

can you post the whole war task definition. This might help us to find a reason for the cyclic dependency.

regards, René


(dumi_daniliuc) #5

Our set up looks pretty much like this:

class OurWar extends War {
   OurWar() {
      super()
        // Copied from WarPlugin
      description = "Description for our War task"
      group = BasePlugin.BUILD_GROUP
      project.extensions.getByType(DefaultArtifactPublicationSet.class).addCandidate(new ArchivePublishArtifact(this))
        appName = "ourAppName"
      into('WEB-INF/classes') {
         from {
            "/path/to/our/standard/templates/commons-logging.properties"
         }
         expand(appName: appName)
      }
   }
     @Override
   FileCollection getClasspath() {
      def ourClasspath = project.files(project.tasks.jar.archivePath, project.configurations.runtime)
      return ourClasspath - project.configurations.providedRuntime - project.configurations.providedCompile
   }
}
  class OurExtendedWar extends OurWar {
   String propDir = 'WEB-INF/classes'
     String commonCssDir = '/default/path/to/our/common/css/dir'
     String productCssDir = '/default/path/to/our/product/specific/css/dir'
     String generatedCssDir = '/default/path/to/our/generated/css/dir'
     // Some similar paths to img files, js files and other resources
     OurExtendedWar() {
      super()
        archiveName = "${project.someProperty}.war"
        destinationDir = "${project.buildDir}/war"
        into(propDir) {
         from {
            "/path/to/our/standard/templates/autogen.properties.template"
         }
         expand(prop1: project.prop1, prop2: project.prop2)
         rename { it.replace('.template', '') }
      }
        into('css/common') {
         from {
            from commonCssDir
         }
      }
        into('css/product') {
         from {
            from productCssDir
         }
      }
        into('css/include') {
         from {
            from generatedCssDir
         }
      }
        // Similar blocks for other resource files.
        doFirst {
         destinationDir.mkdirs()
      }
        doFirst {
         // Make sure that all critical files are in the right location.
               }
   }
}

And then we have a war.gradle plugin that looks like this:

apply from: "/path/to/our/plugins/plugin1"
apply from: "/path/to/our/plugins/plugin2"
  buildscript {
   dependencies {
      classpath files("/path/to/jar/file/with/out/custom/war/tasks/custom-tasks.jar")
   }
}
  task war(type: OurExtendedWar) {
   dependsOn taskFromPlugin1
   dependsOn taskFromPlugin2
     from (webAppDirName) {
      into ''
   }
}

And then the project build.gradle file looks like this:

apply from: "/path/to/our/plugins/war.gradle"
  // Non-war related stuff
  tasks.withType(War.class).all { warTask ->
   warTask.configure {
      from('/path/specific/to/this/project') {
         into 'project/specific/path'
      }
   }
}

And for some reason, if I replace the “into { from { } }” blocks in the task OurExtendedWar constructor with

from {
   'path'
   into 'warPath'
}

I get the cyclic dependency.


(dumi_daniliuc) #6

I have a typo: the blocks that I posted as “into(path) { from { from path } }” are really “into(path) { from { path } }”


(Peter Niederwieser) #7

The latter declaration is invalid. It effectively passes ‘{ into ‘warPath’ }’ as the ‘from’ (whatever that means), with ‘path’ getting lost. Instead, it would have to be ‘from(‘path’) { into ‘warPath’ }’. Or, to get lazyness for the ‘from’ part, ‘from({ ‘path’ }) { into ‘warPath’ }’.