Skip to content

Instantly share code, notes, and snippets.

@danmueller
Created May 25, 2009 14:55
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save danmueller/117570 to your computer and use it in GitHub Desktop.
Save danmueller/117570 to your computer and use it in GitHub Desktop.
.gradle
gradle-wrapper.*
gradlew*
**/build
gradle-run.dump
**/*$py.class

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).
/*
* 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'
}
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 }
}
/*
* 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')
}
}
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();
}
}
<?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>
<?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>
include 'jython-stuff' //, 'java-stuff'
<?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>
<?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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment