public
Last active

  • Download Gist
.gitignore
1 2 3 4 5 6
.gradle
gradle-wrapper.*
gradlew*
**/build
gradle-run.dump
**/*$py.class
README.textile
Textile

Migrating from Maven to Gradle

This project shows a minimal working version of a Gradle project. I’m coming from a Maven background, so a couple of gotchas happened along the path because of that. They are documented in the code and here (if I took the time to write them down).
Trying to use Gradle as new build system, because Maven is a mess with the slightly out of ordinary build requirements for this project.

Architecture

Although this is an minimalist project, the problem solved is not so easy. The requirements are the following.

  • Have a Java ‘java-stuff’ project (pretty standard, nothing fancy)
  • Have another project that is based on Jython ‘jython-stuff’, that depends on the artifacts from ‘java-stuff’.
  • ‘jython-stuff’ can only be executed if Jython is actually present (the ‘jython-stuff’-build runs primarily on Jython itself).
  • ‘jython-stuff’ has additional dependencies, which are resolved via zc.buildout
  • Allow the following interaction with ‘jython-stuff’
    • shell: Open a shell in the project (this compiles/resolves all required dependencies (including zc.buildout) and opens a Jython shell with an appropriate classpath and sys.path)
    • script: Alternatively run a script with the same conditions as when opening a shell.

Current Issues

  • Run zc.buildout (the Gradle/Maven for Python) from within the build
  • Adding local Maven cache to help with dependency resolution (${user.home}/.m2/repo)

Gotchas

  • Transitive dependencies in Maven can be used as compilation dependencies. This doesn’t work with Gradle. Meaning that if you have a dependency on IMPL.jar, which implements (and transitively includes) an API.jar, then you cannot reference the code in API.jar unless you have a dependency declared on it. Makes sense, but is not what Maven gives you.
  • All downloads should happen with Ivy. Ivy is extremely flexible and can handle most cases. Use that to download, it will save you trouble in the longtime.
  • One problem I discovered is when the actual dependencies downloaded with Ivy are not the ones that you want to depend on. See the ‘downloadJython.gradle’ for a clutch, a proper solution is still outstanding.
  • it makes sense to have the local maven repository (~/.m2/repository) added to the lookup path for Ivy, but take care to make it portable (see TODO).
build.gradle
Groovy
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
/*
* Parent build.
*/
 
//dependsOnChildren()
 
allprojects {
group="com.coolstuff"
version="0.1-SNAPSHOT"
}
 
subprojects {
repositories {
// local packager resolver:
// - enables to resolve arbitrary download locations (specified in the module)
// - will only resolve for declared modules (or packagers). This avoids false lookups (ever tried to open
// a 404.html as a .jar ? Doesn't work, trust me.)
 
def packagerResolverBase = file("$rootDir/local-ivy-roundup").toURI().toString()
addFirst(new org.apache.ivy.plugins.resolver.packager.PackagerResolver()) {
name = "local-ivy-roundup"
 
buildRoot = file("${System.properties['java.io.tmpdir']}/ivypackager-${System.properties['user.name']}")
resourceCache = file("${System.properties['user.home']}/.ivy2/packager/cache")
 
restricted = true
 
addIvyPattern("$packagerResolverBase[module]-[revision]-ivy.xml")
addArtifactPattern("$packagerResolverBase[module]-[revision]-packager.xml")
 
preserveBuildDirectories = true // for debugging
}
}
}
 
// helper from src/samples/userguide/tutorial/projectCoreProperties/build.gradle
task check << {
description = 'output some data about the projects'
allprojects {
println "project path $path"
println " project name = $name"
println " project dir = '${toPath(projectDir)}'"
println " build file = '${toPath(buildFile)}'"
println " build dir = '${toPath(buildDir)}'"
}
}
 
def toPath(File file) {
rootProject.relativePath(file).path.replaceAll(java.util.regex.Pattern.quote(File.separator), , '/')
}
 
// Used to build the wrapper (.gradlew)
//
// generated files are not committed because
// gradle-wrapper.jar is way too big (1.1Mb).
task wrapper(type: Wrapper) {
gradleVersion = '0.6'
}
java-stuff/build.gradle
Groovy
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
usePlugin('java')
 
repositories {
mavenRepo name:'localRepo', urls:'file:${user.home}/.m2/repository'
mavenCentral()
}
 
dependencies {
// note: _all_ dependencies needed for compilation have to be declared.
// even if they are included transitive in Maven...
compile(
[group: 'org.apache.activemq', name:'activemq-core', version: '5.2.0'],
[group: 'org.apache.geronimo.specs', name:'geronimo-jms_1.1_spec', version: '1.1.1']
) { transitive = true }
}
java-stuff/src/main/java/com/coolstuff/Fantastic.java
Java
1 2 3 4 5 6 7 8 9 10 11
package com.coolstuff;
 
import javax.jms.Connection;
import org.apache.activemq.ActiveMQConnectionFactory;
 
public class Fantastic {
public static void main(String[] args) throws Exception {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(args[0], args[1], args[2]);
Connection connection = connectionFactory.createConnection();
}
}
jython-stuff/build.gradle
Groovy
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
/*
* A Jython project.
*/
 
//defaultTasks 'bootstrap'
defaultTasks 'tryout'
 
task clean
 
configurations {
jythonInstaller {
description = 'the Jython installer, to bootstrap a local Jython installation'
visible = false
}
virtualenv {
description = 'the virtualenv tar.gz, to setup a virtualenv and bootstrap from there'
}
// the actual runtime dependencies
jython {
description = 'jython interpreter and jython dist lib in the installation directory'
}
runtime {
description = 'runtime classpath' // there is no compile.
extendsFrom jython
}
}
 
def jythonDep
 
dependencies {
jythonDep = jython(group: 'org.jython', name: 'jython', version: '2.5rc3', configuration: 'jython-standalone')
 
jythonInstaller group: 'org.jython', name: 'jython', version: "${jythonDep.version}", configuration: 'installer'
 
virtualenv group: 'org.python.pypi', name: 'virtualenv', version: '1.3.3'
 
//runtime project(':java-stuff') // TODO
}
 
/*repositories {
// TODO make it the right dir
// TODO need to insert before the local-roundup resolver.
//addFirst(flatDir(dirs: "$projectDir/jython")
}
*/
 
task tryout(dependsOn: 'installJythonLocal') {
}
 
def jythonHome = file("$projectDir/jython")
 
task installJythonLocal {
enabled = !jythonHome.exists() // only execute if necessary
if (enabled) // so clean works..
clean << { ant.delete(dir: jythonHome) }
} << {
 
File installerFile = resolveToFile(configurations.jythonInstaller)
 
// execute the installer & install to $jythonHome
ant {
java(jar: installerFile.path, fork: true) {
arg(value: '--silent') // console based, no GUI
arg(value: '--directory') // destination dir
arg(value: jythonHome)
 
arg(value: '--type') // install everything
arg(value: 'all')
}
// TODO do we also need another jython-standalone.jar, so we can
// build the classpath without $jythonHome/jython (shellscript)?
}
}
 
def sandbox = file("$projectDir/sandbox")
 
task installVirtualenv(dependsOn: 'installJythonLocal') {
enabled = !sandbox.exists() // only execute if necessary
if (enabled) // so clean works..
clean << { ant.delete(dir: sandbox) }
} << {
 
File virtualenvArchive = resolveToFile(configurations.virtualenv)
 
print virtualenvArchive
 
ant {
// get virtualenv.py out of the archive
unzip(src: virtualenvArchive, dest: projectDir) {
patternset {
include name: "virtualenv.py"
}
}
 
// Big moment: Here we launch into jython and install virtualenv.
// This actually resolves a couple of files from the net (it's going to take some time)
exec(executable: "$jythonHome/jython", dir: projectDir) {
arg(value: "$projectDir/virtualenv.py")
//arg(value: "")
arg(value: projectDir)
//arg(value: "sandbox")
}
}
}
 
/**
* Helper, so that the line can be understood.
*/
def resolveToFile(configuration) {
return configuration.resolve().iterator().next()
}
 
 
def bootstrapPy = new File(projectDir,"bootstrap.py")
 
task bootstrap << {
/*if (bootstrapPy.exists()) {
throw new StopExecutionException() // abort this task, since we already have
}
*/
// TODO does it make sense to download and install only when it's not present?
ant.get(
src: "http://svn.zope.org/*checkout*/zc.buildout/trunk/bootstrap/bootstrap.py",
dest: bootstrapPy.path,
verbose: true,
usetimestamp: true
)
 
// find the jython dependency on the disk
File interpreterFile = configurations.jython.iterator().next()
 
ant.java(jar: interpreterFile, fork: true) {
arg(value: '-S')
arg(value: bootstrapPy)
}
}
 
task buildout(dependsOn: 'bootstrap') << {
/*ant.task(cmd: buildout) {
}
*/// TODO rig classpath and sys.path
}
 
task test(dependsOn: 'buildout') << {
ant.task(cmd: buildout) {
arg(value: 'test')
}
}
 
task shell(dependsOn: 'buildout') << {
description = "Can be used to interactively explore the code and/or execute something"
// is this on the same
ant.task(cmd: buildout) {
arg(value: 'test')
}
}
local-ivy-roundup/jython-2.5rc3-ivy.xml
XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
<?xml version="1.0" encoding="UTF-8"?>
 
<!--
Copyright 2009 YOUR NAME HERE
 
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.
-->
<ivy-module version="2.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation=
"http://ant.apache.org/ivy/schemas/ivy.xsd">
<info organisation="org.jython"
module="jython"
revision="2.5rc3"
status="release"
publication="20090529120000">
<license name=
"Python Software Foundation License, Version 2.0"
url="http://www.opensource.org/licenses/PythonSoftFoundation.php"/>
<repository
name="sourceforge"
url="http://downloads.sourceforge.net/"
pattern="[organisation]/[module]/[name]-[revision].[ext]"
ivys="false"
artifacts="true"/>
<description homepage="http://www.jython.org/">
Jython is an implementation of the high-level, dynamic, object-oriented language Python written in 100% Pure Java, and seamlessly integrated with the Java platform. It thus allows you to run Python on any Java platform.
Jython, lest you do not know of it, is the most compelling weapon the Java platform has for its survival into the 21st century :-)
—Sean McGrath, CTO, Propylon
</description>
</info>
 
<configurations defaultconfmapping="default->default">
<conf name="default" description="Jython engine"/>
<conf name="standalone" description="Jython engine plus standard library"/>
<conf name="installer" description="The original Jython installer"/>
<conf name="dev" description="The original Jython installer"/>
</configurations>
 
<publications>
<artifact name="jython" conf="default"/>
<artifact name="jython-standalone" conf="standalone"/>
<artifact name="source" type="source" conf="dev" ext="zip"/>
<artifact name="javadoc" type="javadoc" conf="dev" ext="zip"/>
<artifact name="jython_installer" type="installer" conf="installer" ext="jar"/>
</publications>
 
<dependencies>
</dependencies>
 
</ivy-module>
local-ivy-roundup/jython-2.5rc3-packager.xml
XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
<?xml version="1.0" encoding="UTF-8"?>
 
<!--
Copyright 2009 Daniel Mueller
 
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.
-->
 
<packager-module version="1.0">
 
<property name="sfproject" value="jython"/>
<property name="name" value="${ivy.packager.module}"/>
<property name="revision" value="${ivy.packager.revision}"/>
<property name="installername" value="${name}_installer-${revision}"/>
 
<resource url="http://downloads.sourceforge.net/${sfproject}/${installername}.jar"
sha1="8b84682647742ecf942f3bb3092db6db7cdb13c6"
tofile="archive/${installername}.jar">
 
</resource>
<build>
<unzip src="archive/${installername}.jar" dest="archive/${installername}"/>
 
<!-- jython_installer -->
<mkdir dir="artifacts/installers"/>
<copy file="archive/${installername}.jar" tofile="artifacts/installers/${name}_installer.jar"/>
<!-- jython -->
<move file="archive/${installername}/${name}.jar" todir="artifacts/jars"/>
<!-- jython-standalone -->
<jar destfile="artifacts/jars/${name}.jar" update="true">
<fileset dir="archive/${installername}" includes="Lib/**/*"/>
</jar>
<!-- sources -->
<zip destfile="artifacts/sources/source.zip">
<fileset dir="archive/${installername}/src" includes="**/*.java"/>
<fileset dir="archive/${installername}" includes="grammar/**/*"/>
</zip>
<!-- JavaDocs -->
<zip destfile="artifacts/javadocs/javadoc.zip">
<fileset dir="archive/${installername}/Doc/javadoc"/>
</zip>
</build>
 
</packager-module>
local-ivy-roundup/virtualenv-1.3.3-ivy.xml
XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
<?xml version="1.0" encoding="UTF-8"?>
 
<!--
Copyright 2009 YOUR NAME HERE
 
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.
-->
<ivy-module version="2.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation=
"http://ant.apache.org/ivy/schemas/ivy.xsd">
<info organisation="org.python.pypi"
module="virtualenv"
revision="1.3.3"
status="release"
publication="20090529120000">
<license name=
"MIT-style permissive license"
url="http://svn.colorstudy.com/virtualenv/trunk/docs/license.txt"/>
<repository
name="sourceforge"
url="http://pypi.python.org/packages/source/"
pattern="v/[name]-[revision].[ext]"
ivys="false"
artifacts="true"/>
<description homepage="http://pypi.python.org/pypi/virtualenv">
virtualenv is a tool to create isolated Python environments.
</description>
</info>
 
<configurations>
<conf name="default" description="The virtualenv archive"/>
</configurations>
 
<publications>
<artifact name="virtualenv" type="python-egg" conf="default" ext="zip"/>
</publications>
 
</ivy-module>
local-ivy-roundup/virtualenv-1.3.3-packager.xml
XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
<?xml version="1.0" encoding="UTF-8"?>
 
<!--
Copyright 2009 Daniel Mueller
 
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.
-->
 
<packager-module version="1.0">
 
<property name="name" value="${ivy.packager.module}"/>
<property name="revision" value="${ivy.packager.revision}"/>
<property name="archive" value="${name}-${revision}"/>
 
<resource
url="http://pypi.python.org/packages/source/v/virtualenv/${name}-${revision}.tar.gz"
sha1="1907ab65de03b210ef350d7f54a3a2be7e488b39"
dest="archive">
 
</resource>
<build>
<!-- repacking as zip, because it's a lot friendlier to work with. -->
<mkdir dir="artifacts/python-eggs"/>
<zip basedir="archive/${archive}" destfile="artifacts/python-eggs/${name}.zip"/>
</build>
 
</packager-module>
settings.gradle
Groovy
1
include 'jython-stuff' //, 'java-stuff'

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.