Skip to content

Instantly share code, notes, and snippets.

@narkisr
Created February 24, 2010 21:11
Show Gist options
  • Save narkisr/313851 to your computer and use it in GitHub Desktop.
Save narkisr/313851 to your computer and use it in GitHub Desktop.
/*
* Copyright 2007-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* This script obtains the effective pom of the current project, reads its dependencies
* and generates build.gradle scripts. It also generates settings.gradle for multimodule builds. <br/>
*
* It currently supports both single-module and multi-module POMs, inheritance, dependency management, properties - everything.
*
* @author Antony Stubbs <[email protected]>
* @author Baruch Sadogursky <[email protected]>
*/
// debug - print out our current working directory
println "Working path: ${new File('.').absolutePath}"
def pomContents = getEffectivePomContents()
if (pomContents != null && !pomContents.empty) {
def root = new XmlSlurper().parseText(pomContents)
String build
def reactorProjects = []
def multimodule = root.name() == "projects"
def contents = new Contents()
if (multimodule) {
println "This is multi-module project.\n"
def allProjects = root.project
print "Configuring Dependencies... "
def dependencies = [:]
allProjects.each {
if (it.packaging == "pom") {
reactorProjects.add(it)
}
dependencies.put(it.artifactId.toString(), getDependencies(it, reactorProjects, allProjects))
}
println "Done."
print "Generating settings.gradle... "
generateSettings(allProjects)
println "Done."
build = contents.initialMultiBuild(allProjects, dependencies)
reactorProjects.each {
it.modules.module.each {
String moduleName = it;
def submodule = allProjects.find {
new File(it.build.directory.toString()).parentFile.name == moduleName && !it.packaging.toString().equals("pom")
}
if (!submodule.isEmpty()) {
File submoduleDir = new File(submodule.build.directory.toString()).parentFile
def submoduleId = submodule.artifactId.toString()
String moduleDependencies = dependencies.get(submoduleId)
boolean warPack = submodule.packaging.toString().equals("war")
def hasDependencies = !(moduleDependencies == null || moduleDependencies.isEmpty())
if (warPack || hasDependencies) {
print "Generating build.gradle for module ${submoduleId}... "
File submoduleBuildFile = new File(submoduleDir, "build.gradle")
String moduleBuild = ""
if (warPack) {
moduleBuild += "usePlugin 'war'\n\n"
}
if (hasDependencies) {
moduleBuild += moduleDependencies
}
if (submoduleBuildFile.exists()) {
print "(backing up existing one)... "
submoduleBuildFile.renameTo(new File("build.gradle.bak"))
}
submoduleBuildFile.text = moduleBuild
println "Done."
}
}
}
}
//TODO deployment
} else {//simple
println "This is single module project."
build = "usePlugin 'java'\n" +
"usePlugin 'maven'\n\n" +
contents.getArtifactData(root) + "\n" +
"configurations.compile.transitive = true\n\n" +
contents.data.getCompilerSettings(root) + "\n"
print "Configuring Maven repositories... "
String repos = "repositories {\n"
Set<String> repoSet = new LinkedHashSet<String>();
getRepositoriesForModule(root, repoSet)
repoSet.each {
repos = "${repos} ${it}\n"
}
build += "${repos}}\n"
println "Done."
print "Configuring Dependencies... "
String dependencies = getDependencies(root, reactorProjects, null)
build += dependencies
println "Done."
}
build += "\n${contents.pomReplacementPart}"
print "Generating main build.gradle... "
def buildFile = new File("build.gradle")
if (buildFile.exists()) {
print "(backing up existing one)... "
buildFile.renameTo(new File("build.gradle.bak"))
}
buildFile.text = build
println "Done."
}
private String getDependencies(project, reactorProjects, allProjects) { // use GPath to navigate the object hierarchy and retrieve the collection of dependency nodes.
def dependencies = project.dependencies.dependency
def (compileTimeScope, runTimeScope, testScope, providedScope, systemScope) = [[], [], [], [], []]
//cleanup duplicates from parent
// using Groovy Looping and mapping a Groovy Closure to each element, we collect together all
// the dependency nodes into corresponding collections depending on their scope value.
dependencies.each() {
if (!duplicateDependency(it, project, allProjects)) {
def scope = (elementHasText(it.scope)) ? it.scope : "compile"
switch (scope) {
case "compile": compileTimeScope.add(it); break
case "test": testScope.add(it); break
case "provided": providedScope.add(it); break
case "runtime": runTimeScope.add(it); break
case "system": systemScope.add(it); break
}
}
}
/**
* print function then checks the exclusions node to see if it exists, if
* so it branches off, otherwise we call our simple print function
*/
def createGradleDep = {scope, sb, iterator ->
if (!reactorProjects.isEmpty() && iterator.groupId == project.groupId && projectContainsModule(reactorProjects, iterator.artifactId)) {
createProjectDependency(iterator, sb, scope)
} else {
def exclusions = iterator.exclusions.exclusion
if (exclusions.size() > 0) {
createComplexDependency(iterator, sb, scope)
} else {
createBasicDependency(iterator, sb, scope)
}
}
}
StringBuilder build = new StringBuilder()
if (!compileTimeScope.isEmpty() || !runTimeScope.isEmpty() || !testScope.isEmpty() || !providedScope.isEmpty() || !systemScope.isEmpty()) {
build.append("\tdependencies {").append("\n")
// for each collection, one at a time, we take each element and call our print function
if (!compileTimeScope.isEmpty()) compileTimeScope.each() { createGradleDep("compile", build, it) }
if (!runTimeScope.isEmpty()) runTimeScope.each() { createGradleDep("runtime", build, it) }
if (!testScope.isEmpty()) testScope.each() { createGradleDep("testCompile", build, it) }
if (!providedScope.isEmpty()) providedScope.each() { createGradleDep("provided", build, it) }
if (!systemScope.isEmpty()) systemScope.each() { createGradleDep("system", build, it) }
build.append("\t}\n")
}
return build.toString();
}
def projectContainsModule(def reactorProjects, def artifactId) {
def contains = false;
reactorProjects.each {
it.modules.module.each() {
if (it == artifactId) contains = true;
}
}
return contains;
}
/**
* complex print statement does one extra task which is
* iterate over each
* �exclusion� node and print out the artifact id.
*/
private def createComplexDependency(it, build, scope) {
build.append("\t${scope}(\"${contructSignature(it)}\") {\n")
it.exclusions.exclusion.each() {
build.append("\t\texclude(module: '${it.artifactId}')\n")
}
build.append("\t\t}\n")
}
/**
* Print out the basic form og gradle dependency
*/
private def createBasicDependency(it, build, String scope) {
def classifier = contructSignature(it)
build.append("\t${scope} \"${classifier}\"\n")
}
/**
* Print out the basic form og gradle dependency
*/
private def createProjectDependency(it, build, String scope) {
build.append("\t${scope} project(':${it.artifactId}')\n")
}
/**
* Construct and return the signature of a dependency, including it's version and
* classifier if it exists
*/
private def contructSignature(it) {
def gradelDep = "${it.groupId.text()}:${it.artifactId.text()}:${it?.version?.text()}"
def classifier = elementHasText(it.classifier) ? gradelDep + ":" + it.classifier.text().trim() : gradelDep
return classifier
}
/**
* Check to see if the selected node has content
*/
private boolean elementHasText(it) {
return it.text().length() != 0
}
private String getEffectivePomContents() {
String pomContents = null;
//let's try to get effective pom first
//TODO work on output stream, without writing to file
def effectivePomFileName = "effective.pom"
print "Wait, obtaining effective pom... "
def ant = new AntBuilder() // create an antbuilder
ant.exec(outputproperty: "cmdOut", errorproperty: "cmdErr", resultproperty: "cmdExit", failonerror: "true",
executable: (System.properties['os.name']).toLowerCase().contains("win") ? "mvn.bat" : "mvn") {
arg(line: """-Doutput=${effectivePomFileName} help:effective-pom""")
}
//print the output if verbose flag is on
println((args.any {it.equals("-verbose")}) ? "\n ${ant.project.properties.cmdOut}" : "Done.")
if ("0".equals(ant.project.properties.cmdExit)) {
// read in the effective pom file
File pomFile = new File(effectivePomFileName);
// get it's text into a string
pomContents = pomFile.text
if (!args.any {it.equals("-keepFile")}) {
pomFile.deleteOnExit()
}
} else {
println "Error obtaining effective pom: ${ant.project.properties.cmdErr}"
}
return pomContents
}
private def generateSettings(Object projects) {
Map<String, String> settingsTree = new LinkedHashMap<String, String>();
projects.each {
if (it.packaging == "pom") {
it.modules.module.each { //let's figure out if this is a top level module
if (new File(it.toString()).isDirectory()) { //top level
settingsTree.put(it.toString(), "'${it}'")
} else {
//this is module of subproject, let's find out which
String moduleName = it;
def submodule = projects.find {new File(it.build.directory.toString()).parentFile.name == moduleName}
File submoduleDir = new File(submodule.build.directory.toString()).parentFile
def parentModuleName = submoduleDir.parentFile.name
def existingPart = settingsTree.get(parentModuleName)
settingsTree.put(parentModuleName, "${existingPart}, '${parentModuleName}:${submoduleDir.name}'")
}
}
}
}
def values = settingsTree.values().toString()
File settingsFile = new File("settings.gradle")
if (settingsFile.exists()) {
print "(backing up existing one)... "
settingsFile.renameTo(new File("settings.gradle.bak"))
}
settingsFile.text = "include " + values.substring(1, values.length() - 1)
}
private boolean duplicateDependency(dependency, project, allProjects) {
def parentTag = project.parent
if (allProjects == null || parentTag.isEmpty()) {//simple project or no parent
return false;
} else {
def parent = allProjects.find {
it.groupId.equals(parentTag.groupId) && it.artifactId.equals(parentTag.artifactId)
}
def duplicate = parent.dependencies.dependency.any {
it.groupId.equals(dependency.groupId) && it.artifactId.equals(dependency.artifactId)
}
if (duplicate) {
return true;
} else {
duplicateDependency(dependency, parent, allProjects)
}
}
}
class Contents {
def data = new ProjectData()
def pomReplacementPart = {multimodule ->
"""
task replacePoms(dependsOn: install) << {
${multimodule ? ' subprojects.each {project ->' : ''}
def pomsDir = new File(project.buildDir, 'poms')
def defaultPomName = 'pom-default.xml'
def defaultPom = new File(pomsDir, defaultPomName)
if (defaultPom.exists()) {
File pomFile = new File(project.projectDir, 'pom.xml')
if (pomFile.exists()) {
pomFile.renameTo(new File(project.projectDir, 'pom.xml.bak'))
}
project.copy {
from(pomsDir) {
include defaultPomName
rename defaultPomName, 'pom.xml'
}
into(project.projectDir)
}
}
${multimodule ? ' }' : ''}
}""".trim()
}
def initialMultiBuild = {allProjects, dependencies ->
"""
allprojects {
usePlugin('java')
usePlugin('maven')\n
${getArtifactData(allProjects[0])}
configurations.compile.transitive = true\n
}\n\n
subprojects {\n
${data.getCompilerSettings(allProjects[0])}
${data.getRepositoriesForProjects(allProjects)}
${dependencies.get(allProjects[0].artifactId.toString())}
}
dependsOnChildren()\n"""
}
def getArtifactData(project) { "\tgroup = '${project.groupId}'\n \tversion = '${project.version}'\n" }
}
class ProjectData {
def getCompilerSettings(project) {
def plugin = project.build.plugins.plugin.find { it.artifactId.toString().equals("maven-compiler-plugin") }
def sourceCompatibility = plugin.configuration.source.toString().trim() ? plugin.configuration.source : "1.5";
def targetCompatibility = plugin.configuration.target.toString().trim() ? plugin.configuration.target : "1.5";
return "\tsourceCompatibility = ${sourceCompatibility}\n\ttargetCompatibility = ${targetCompatibility}\n"
}
def getRepositoriesForProjects(projects) {
print "Configuring Repositories... "
String repos = "\trepositories {\n"
Set<String> repoSet = new LinkedHashSet<String>();
projects.each {getRepositoriesForModule(it, repoSet)}
repoSet.each {repos = "${repos} ${it}\n"}
repos = "${repos}\t}\n"
println "Done."
repos
}
private getRepositoriesForModule(module, repoSet) {
module.repositories.repository.each {
repoSet.add("\t\tmavenRepo urls: [\"${it.url}\"]")
} //No need to include plugin repos - who cares about maven plugins?
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment