Pergunta

I've been working on having Gradle generate an IntelliJ WAR artifact definition as part of the 'idea' task as per this blog post. This has worked pretty successfully so far although my code is quite dirty so I'm trying to clean it up. As far as I'm concerned, the war task should contain all the information required to create the necessary tags in the IntelliJ. For example, given a Gradle Project object, say gradleProject, I can do something like:

def artifactManager = project.component.find { it.@name == 'ArtifactManager' } as Node
if (artifactManager) {
  Node artifact = artifactManager.artifact.find { it.@type == 'exploded-war' }
  if (artifact) {
    artifactManager.remove(artifact)
  }
} else {
  artifactManager = project.appendNode('component', [name: 'ArtifactManager']);
}
def builder = new NodeBuilder();
def artifact = builder.artifact(type: 'exploded-war', name: "${gradleProject.name} exploded war") {
  'output-path'("${gradleProject.buildDir}/${gradleProject.name}-exploded.war")
  root(id: 'root') {
    element(id: 'directory', name: 'WEB-INF') {
      // copy web.xml
      element(id: 'file-copy', path: gradleProject.war.webXml)
      Set<String> excludeJars = [] as Set
      element(id: 'directory', name: 'classes') {
        // copy classes
        element(id: 'module-output', name: gradleProject.name)
        // copy depending projects module output (classes)
        gradleProject.configurations.runtime.allDependencies.each {
          if (it.hasProperty('dependencyProject')) { // TODO: make it functional?!?
            if (it.dependencyProject.hasProperty('jar'))
              excludeJars.add(it.dependencyProject.jar.archiveName)
            element(id: 'module-output', name: it.dependencyProject.name)
          }
        }
      }
      // copy dependency jars excluding possible existing module jars (not sure it's necessary)
      element(id: 'directory', name: 'lib') {
        // TODO: this should handle ALL kinds of dependencies
        gradleProject.configurations.runtime.each {
          if (!excludeJars.contains(it.name))
            element(id: 'file-copy', path: it)
        }
      }
    }
  }
}
artifactManager.append artifact

to modify or create my IntelliJ .ipr file. Now I have added some files to my WAR artifact in the Gradle script as in:

war{
  from project(':rest').file('resource/some.properties')
  from project(':some_project').fileTree('some_folder')
  metaInf {
    from parent.file('build/config/website/' + environment + '/context.xml')
  }
}

and I'd like to add some Groovy code to pick up those files and create the correct XML tags to have IntelliJ copy them in the right locations. What's the simplest way to do this? I tried playing with the following:

gradleProject.war.source.each { // ... create tags etc.  }

but the list of source files includes all the files, regardless of their destination (all libs, web.xml, classes, etc.).

How can I achieve what I'm trying to do?

Foi útil?

Solução

I finally figured it out with the help of Gradle docs and a little trial and error. I got the desired result using the following code:

def artifactManager = project.component.find { it.@name == 'ArtifactManager' } as Node
if (artifactManager) {
  Node artifact = artifactManager.artifact.find { it.@type == 'exploded-war' }
  if (artifact) {
    artifactManager.remove(artifact)
  }
} else {
  artifactManager = project.appendNode('component', [name: 'ArtifactManager']);
}
def builder = new NodeBuilder();
def artifact = builder.artifact(type: 'exploded-war', name: "${gradleProject.name} exploded war") {
  'output-path'("${gradleProject.buildDir}/${gradleProject.name}-exploded.war")
  root(id: 'root') {
    println "Adding files to root"
    gradleProject.war.rootSpec.children.each {
      if (it.destPath.pathString.equals("")) {
        add(builder, it, "")
      }
    }
    println "Adding files to META-INF"
    element(id: 'directory', name: 'META-INF') {
      gradleProject.war.rootSpec.children.each {
        if (it.destPath.pathString.equals("META-INF")) {
          add(builder, it, "META-INF")
        }
      }
    }
    println "Adding files to WEB-INF"
    element(id: 'directory', name: 'WEB-INF') {
      element(id: 'file-copy', path: gradleProject.war.webXml)
      gradleProject.war.rootSpec.children.each {
        if (it.destPath.pathString.equals("WEB-INF")) {
          add(builder, it, "WEB-INF")
        }
      }
      Set<String> excludeJars = [] as Set
      element(id: 'directory', name: 'classes') {
        element(id: 'module-output', name: gradleProject.name)
        gradleProject.configurations.runtime.allDependencies.each {
          if (it.hasProperty('dependencyProject')) { 
            if (it.dependencyProject.hasProperty('jar'))
              excludeJars.add(it.dependencyProject.jar.archiveName)
            element(id: 'module-output', name: it.dependencyProject.name)
          }
        }
      }
      element(id: 'directory', name: 'lib') {
        gradleProject.configurations.runtime.each {
          if (!excludeJars.contains(it.name))
            element(id: 'file-copy', path: it)
        }
      }
    }
  }
}
artifactManager.append artifact

with the add function defined as:

def add(NodeBuilder builder, Object s, String dest, String prefix = "") {
  println(prefix + "current destination: [" + dest + "]")
  if (s instanceof File) {
    println(prefix + "-- file " + s.toString())
    builder.element(id: 'file-copy', path: s)
  } else if (s instanceof FileTree) {
    println(prefix + "-- folder " + s.dir.toString())
    builder.element(id: 'dir-copy', path: s.dir)
  } else if (s instanceof CopySpecInternal) {
    print(prefix + "-- copy spec " + s.toString() + " with destination path: [" + s.destPath.pathString + "]; ")
    if (dest.equals(s.destPath.pathString)) {
      println("destination matches current folder")
      s.sourcePaths.each { path ->
          add(builder, path, dest, prefix + "  ")
      }
      s.children.each { child -> add(builder, child, dest, prefix + "  ") }
    } else {
      println("")
      if (s.destPath.segments != null && s.destPath.segments.length > 0 && s.destPath.segments[0].equals(dest)) {
        String relDest = new RelativePath(false, Arrays.copyOfRange(s.destPath.segments, 1, s.destPath.segments.length)).pathString
        builder.element(id: 'directory', name: relDest) {
          s.sourcePaths.each { path ->
            add(builder, path, relDest, prefix + "  ")
          }
          s.children.each { child -> add(builder, child, relDest, prefix + "  ") }
        }
      } else {
        println(prefix + "!! destination path is not relative to [" + dest + "]: [" + s.destPath.pathString + "]")
      }
    }
  } else println(prefix + "?? unknown " + s.toString())
}

I basically reverse engineered the war task properties and dug into the tree of files to copy. As far as I can tell, this task doesn't expose an interface to do this kind of things so I had to resort to a 'hacky' solution, which is not my favorite. I'd be happy if anyone can chip in with some cleaner code.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top