Building with Ant Ivy, I'm trying to separate my jars into one configuration for 3rd party jars and another configuration for the jars I build and publish. ProjectA uses 3rd party jars and builds a jar that ProjectB depends on, but when I use Ant Ivy confs I can't get ProjectB to retrieve the ProjectA jar.
When I execute the ant script for ProjectB, it builds ProjectA fine. The ProjectA build publishes a jar to the local repository. ProjectB retrieves the necessary jars from the public repository with no problem, but when it tries to retrieve the ProjectA jar, it says UNRESOLVED DEPENDENCY: testproject#ProjectA;2.0.0: configuration not found in testproject#ProjectA;2.0.0: 'localjars'. It was required from testproject#ProjectB;2.0.0 localjars
If I remove all references to the 2nd configuration, localjars, and just use default for everything, it works fine. I really need to sort my jars into the different confs though.
I've successfully used a revision value passed from the ant script in place of "2.0.0" below and referenced with ${revision}, but the conf error is the same.
ProjectA ivy.xml (with a subset of dependencies for brevity):
<ivy-module version="2.0">
<info organisation="testproject" module="ProjectA" revision="2.0.0" status="release" publication="20160524124555"/>
<configurations>
<conf name="default" transitive="false" visibility="public"/>
<conf name="localjars" extends="default" visibility="public"/>
</configurations>
<publications>
<artifact name="projectA-jar-2.0.0" type="jar" conf="localjars" ext="jar"/>
</publications>
<dependencies>
<dependency org="commons-beanutils" name="commons-beanutils" rev="1.7.0" conf="default->master"/>
<dependency org="commons-collections" name="commons-collections" rev="3.1" conf="default->master"/>
</dependencies>
</ivy-module>
ProjectA build.xml publish target:
<target name="publish" depends="package"
description="--> compile test and publish this project in the local ivy repository">
<ivy:publish artifactspattern="${DEPLOY_DIR_LIB}/[artifact].[ext]"
resolver="local" pubrevision="2.0.0" status="release"
srcivypattern="${ivy.dep.file}" forcedeliver="true" overwrite="true" conf="localjars,default"/>
<echo message="project ${ant.project.name} released with version 2.0.0" />
</target>
ProjectB ivy.xml:
<ivy-module version="2.0">
<info organisation="testproject" module="ProjectB" revision="2.0.0" status="release" publication="20160524103113"/>
<configurations>
<conf name="default"/>
<conf name="localjars" extends="default"/>
</configurations>
<publications>
<artifact name="projectB-2.0.0" conf="localjars" type="jar" ext="jar"/>
</publications>
<dependencies>
<dependency org="testproject" name="ProjectA" rev="${revision}" transitive="true" conf="localjars->localjars; default->default"/>
</dependencies>
ProjectB Ant resolve target:
<target name="resolve" description="--> retrieve dependencies with ivy">
<ivy:retrieve pattern="${DEPLOY_DIR_LIB}/[artifact]-2.0.0.[ext]" revision="2.0.0" conf="localjars" />
</target>
Any idea what's wrong? Thanks!
Patrick
Not entirely certain why your configuration is not working. One thing I'd advise is not to disable transitive dependencies. You'll note I have a different approach in creating the "default" configuration in the working example below.
Example
Each project has its own local build and ivy file. Collaboration is via jars published to the "local" repository.
├── build.xml
├── ProjectA
│ ├── build.xml
│ ├── ivy.xml
│ └── src
│ └── Hello.txt
└── ProjectB
├── build.xml
├── ivy.xml
└── src
└── Hello.txt
build.xml
Master build file that builds all modules in the correct order, using the buildlist task.
Additionally I normally include an extra target for installing ivy.
<project name="main" default="publish" xmlns:ivy="antlib:org.apache.ivy.ant">
<available classname="org.apache.ivy.Main" property="ivy.installed"/>
<target name="install-ivy" unless="ivy.installed">
<mkdir dir="${user.home}/.ant/lib"/>
<get dest="${user.home}/.ant/lib/ivy.jar" src="http://search.maven.org/remotecontent?filepath=org/apache/ivy/ivy/2.4.0/ivy-2.4.0.jar"/>
<fail message="Ivy has been installed. Run the build again"/>
</target>
<target name="publish" depends="install-ivy">
<ivy:buildlist reference="build-path">
<fileset dir="." includes="*/build.xml"/>
</ivy:buildlist>
<subant target="publish" buildpathref="build-path"/>
</target>
<target name="clean">
<subant target="clean">
<fileset dir="." includes="*/build.xml"/>
</subant>
</target>
<target name="clean-all" depends="clean">
<ivy:cleancache/>
</target>
</project>
ProjectA/ivy.xml
The "master" configuration contains only artifacts. This naming convention mirrors the scopes used by Maven.
Note also how the "default" configuration extends both "master" and "runtime". This enables clients to pull down everything they will need.
<ivy-module version="2.0">
<info organisation="com.myspotontheweb" module="ProjectA"/>
<configurations>
<conf name="default" description="Master artifact and runtime dependencies" extends="master,runtime"/>
<conf name="master" description="Artifact published by this module"/>
<conf name="compile" description="Required to compile application"/>
<conf name="runtime" description="Additional run-time dependencies" extends="compile"/>
<conf name="test" description="Required for test only" extends="runtime"/>
</configurations>
<publications>
<artifact name="ProjectA" type="jar" ext="jar" conf="master"/>
</publications>
<dependencies>
<!-- compile dependencies -->
<dependency org="org.slf4j" name="slf4j-api" rev="1.7.5" conf="compile->default"/>
<!-- runtime dependencies -->
<dependency org="org.slf4j" name="slf4j-log4j12" rev="1.7.5" conf="runtime->default"/>
<!-- test dependencies -->
<dependency org="junit" name="junit" rev="4.11" conf="test->default"/>
</dependencies>
</ivy-module>
ProjectB/ivy.xml
Note how ProjectA is the only dependency and it maps the remote "default" configuration to the local "compile" configuration.
Another subtle issue is the use of the "latest.integration" dynamic revision. This means we don't need to hardcode the revision of ProjectA.
<ivy-module version="2.0">
<info organisation="com.myspotontheweb" module="ProjectB"/>
<configurations>
<conf name="default" description="Master artifact and runtime dependencies" extends="master,runtime"/>
<conf name="master" description="Artifact published by this module"/>
<conf name="compile" description="Required to compile application"/>
<conf name="runtime" description="Additional run-time dependencies" extends="compile"/>
<conf name="test" description="Required for test only" extends="runtime"/>
</configurations>
<publications>
<artifact name="ProjectB" type="jar" ext="jar" conf="master"/>
</publications>
<dependencies>
<dependency org="com.myspotontheweb" name="ProjectA" rev="latest.integration" conf="compile->default"/>
</dependencies>
</ivy-module>
ProjectA/build.xml
The revision to be published is set as a property, which can be overridden if necessary.
Note also how ivy configurations can be used to control classpaths within the build, using the cachepath task
<project name="ProjectA" default="build" xmlns:ivy="antlib:org.apache.ivy.ant">
<property name="build.dir" location="build"/>
<property name="pub.version" value="1.0"/>
<property name="pub.resolver" value="local"/>
<target name="resolve">
<ivy:resolve/>
<ivy:cachepath pathid="compile.path" conf="compile"/>
<ivy:cachepath pathid="test.path" conf="test"/>
</target>
<target name="build" depends="resolve">
<mkdir dir="${build.dir}"/>
<jar destfile="${build.dir}/${ant.project.name}.jar" basedir="src"/>
</target>
<target name="publish" depends="build">
<ivy:publish pubrevision="${pub.version}" resolver="${pub.resolver}" overwrite="true">
<artifacts pattern="${build.dir}/[artifact].[ext]"/>
</ivy:publish>
</target>
<target name="clean">
<delete dir="${build.dir}"/>
</target>
</project>
ProjectB/build.xml
Identical to the other project just having a different name attribute.
<project name="ProjectB" default="build" ....
Related
I use ant + ivy for a project. So lets assume I need to run <sql task for ant, thus I need to fetch jdbc driver 1st. Furthermore the driver is required during compiling the project. So I want to have 2 configuration:
default: to retrieve jdbc driver and other project dependencies
jdbc: to retrieve ONLY jdbc driver.
And then just run retrieve task with different configurations like that:
<!--Fetch all project dependencies, including jdbc driver-->
<ivy:retrieve pattern="${build.lib.home}/[artifact].[ext]" conf="default" />
<!-- Fetch only jdbc driver-->
<ivy:retrieve pattern="${build.lib.home}/[artifact].[ext]" conf="jdbc" />
ivy.xml
<?xml version="1.0" encoding="UTF-8"?>
<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="" module="notebook-ivy"/>
<configurations>
<conf name="default" visibility="public" extend="jdbc"/>
<conf name="jdbc" visibility="public"/>
</configurations>
<dependencies>
<dependency org="mysql" name="mysql-connector-java" rev="5.1.6" conf="jdbc->default"/>
<dependency org="org.apache.camel" name="camel-core" rev="2.15.1"/>
</dependencies>
</ivy-module>
I'm using public mavencentral so I can't change dependency configuration on server:
ivysettings.xml
<ivysettings>
<settings defaultResolver="chain"/>
<resolvers>
<chain name="chain">
<ibiblio name="central" m2compatible="true" root="http://central.maven.org/maven2/"/>
</chain>
</resolvers>
</ivysettings>
The configuration described above works. But It looks confusing, when default extends jdbc and jdbc extends default simultaneously. I'm new to ivy, so my question is: if this's the right way of using configurations for ivy.
The "extends" operation enables you to perform a union set operation on jars within an ivy configuration, so this would work fine.
My preference is to model configurations on my anticipated classpath requirements:
<configurations>
<conf name="compile" description="Dependencies required to build project"/>
<conf name="compile" description="Dependencies required to run project" extends="compile"/>
<conf name="test" description="Dependencies required to test project" extends="runtime"/>
<conf name="build" description="ANT build tasks"/>
</configurations>
The ivy cachepath task can then be used to create these paths within the build file:
<target name="resolve">
<ivy:resolve/>
<ivy:cachepath pathid="build.path" conf="build"/>
<ivy:cachepath pathid="compile.path" conf="compile"/>
<ivy:cachepath pathid="test.path" conf="test"/>
</target>
This approach means something like a jdbc jar would be mapped to a "compile" configuration, making it available for javac tasks:
<target name="compile" depends="resolve">
..
<javac ... classpathref="compile.path"/>
</target>
But also included in the "runtime" configuration that gets saved to disk as a dependency when building the jar package:
<target name="build" depends="compile">
<ivy:retrieve pattern="${dist.dir}/lib/[artifact].[ext]" conf="runtime"/>
<manifestclasspath property="jar.classpath" jarfile="${dist.jar}">
<classpath>
<fileset dir="${dist.dir}/lib" includes="*.jar"/>
</classpath>
</manifestclasspath>
<jar destfile="${dist.jar}" basedir="${build.dir}/classes">
<manifest>
<attribute name="Main-Class" value="${dist.main.class}"/>
<attribute name="Class-Path" value="${jar.classpath}"/>
</manifest>
</jar>
</target>
I want to make project with two modules. App and server. Server depends on app. When I compile server I want to include class files from app into the build. But it resolves classpath relatively to server and not to app because of import issues. How to make ant resolve app locations relatively to app and server locations relatively to server. I din't get how it's done in ant docs. Could you please explain in a more simple way? Code snippet to clarify question little bit.
App build.xml:
<project name="app">
<property name="app.build.dir" location="build"/>
<target name="compile">
<echo message="Compiling app to ${app.build.dir}"/>
</target>
</project>
Server build.xml:
<project name="server">
<property name="server.build.dir" location="build"/>
<include file="../app/build.xml"/>
<target name="compile" depends="app.compile">
<echo message="Compiling server to ${server.build.dir} using classpath: ${app.build.dir}"/>
</target>
</project>
Output:
Buildfile: D:\work\test\ant-test2\server\build.xml
app.compile:
[echo] Compiling to D:\work\test\ant-test2\server\build
compile:
[echo] Compiling server to D:\work\test\ant-test2\server\build using classpath: D:\work\test\ant-test2\server\build
BUILD SUCCESSFUL
Total time: 0 seconds
Desired output:
Buildfile: D:\work\test\ant-test2\server\build.xml
app.compile:
[echo] Compiling to D:\work\test\ant-test2\app\build
compile:
[echo] Compiling server to D:\work\test\ant-test2\server\build using classpath: D:\work\test\ant-test2\app\build
BUILD SUCCESSFUL
Total time: 0 seconds
Multi module builds are difficult because there are no standards, each build author have his own approach to solving this problem.
My personal preference is to emulate how Maven does it. Each module creates and publishes a jar file to the "local" repository. This jar file is then a dependency of the other modules that consume its classes. This approach creates clean separation between modules and means you don't need to build the entire project when working on one sub-module.
So how is this done using ANT? Well you'll need to embrace another Maven concept, dependency management. The ivy plugin provides this feature to ANT.
Example
My dummy project. A single module called "app" which is dependency of the "server" module
├── build.xml <-- Builds all modules in correct order
├── app
│ ├── build.xml
│ ├── ivy.xml <-- Describes module dependencies
│ └── src
| ..
└── server
├── build.xml
├── ivy.xml <-- Dependency on the "app" module
└── src
..
Unless you customize locations, ivy uses the following directories to store files:
~/.ivy2/cache <-- Downloaded 3rd party dependencies go here
~/.ivy2/local <-- Repository which is private to the user.
Creating alternative storage locations and leveraging Maven repository managers is beyond the scope of this question.
After running this example by build produces the following explicitly versioned files:
~/.ivy2/local/com.myspotontheweb/demo-app/1.0.0/jars/demo-app.jar
~/.ivy2/local/com.myspotontheweb/demo-server/1.0.0/wars/demo-server.war
build.xml
Builds all modules in the correct order. This is determined by the module dependencies documented in each module's ivy.xml file (See ivy buildlist task). This is a very useful feature when you have a large number of interdependent modules.
<project name="demo" default="build" xmlns:ivy="antlib:org.apache.ivy.ant">
<available classname="org.apache.ivy.Main" property="ivy.installed"/>
<target name="install-ivy" unless="ivy.installed">
<mkdir dir="${user.home}/.ant/lib"/>
<get dest="${user.home}/.ant/lib/ivy.jar" src="http://search.maven.org/remotecontent?filepath=org/apache/ivy/ivy/2.3.0/ivy-2.3.0.jar"/>
<fail message="Ivy has been installed. Run the build again"/>
</target>
<target name="build-list" depends="install-ivy">
<ivy:buildlist reference="build-path">
<fileset dir="." includes="**/build.xml" excludes="build.xml"/>
</ivy:buildlist>
</target>
<target name="build" depends="build-list">
<subant buildpathref="build-path">
<target name="clean"/>
<target name="publish"/>
</subant>
</target>
<target name="clean" depends="build-list">
<subant buildpathref="build-path">
<target name="clean"/>
</subant>
</target>
<target name="clean-all" depends="clean">
<ivy:cleancache/>
</target>
</project>
Notes:
Contains logic to ensure the ivy jar dependency is installed if missing
Ivy will cache downloaded 3rd party dependencies. The "clean-all" task is useful for ensuring the build is sweaky clean :-)
app/ivy.xml
Lists the 3rd party dependencies that the module has. This is a very useful Maven feature. Dependencies get downloaded automatically from Maven Central. No need to commit them into your source code repository.
<ivy-module version="2.0">
<info organisation="com.myspotontheweb" module="demo-app"/>
<configurations>
<conf name="compile" description="Required to compile application"/>
<conf name="runtime" description="Additional run-time dependencies" extends="compile"/>
<conf name="test" description="Required for test only" extends="runtime"/>
</configurations>
<publications>
<artifact name="demo-app"/>
</publications>
<dependencies>
<!-- compile dependencies -->
<dependency org="org.slf4j" name="slf4j-api" rev="1.7.5" conf="compile->default"/>
<!-- runtime dependencies -->
<dependency org="org.slf4j" name="slf4j-log4j12" rev="1.7.5" conf="runtime->default"/>
<!-- test dependencies -->
<dependency org="junit" name="junit" rev="4.11" conf="test->default"/>
</dependencies>
</ivy-module>
Note:
Ivy configurations are used to classify and group dependencies. Used later to populate classpaths
app/build.xml
Pretty standard build process. Code is compiled tested and packaged. Note how ivy configurations are used to control the classpaths.
The "publish" target is worthy of special note it pushes the built jar into a local location where it can be picked up by other module builds.
<project name="demo-app" default="build" xmlns:ivy="antlib:org.apache.ivy.ant">
<!--
================
Build properties
================
-->
<property name="src.dir" location="src/main/java"/>
<property name="resources.dir" location="src/main/resources"/>
<property name="test.src.dir" location="src/test/java"/>
<property name="build.dir" location="target"/>
<property name="dist.dir" location="${build.dir}/dist"/>
<property name="jar.main.class" value="org.demo.App"/>
<property name="jar.file" value="${dist.dir}/${ant.project.name}.jar"/>
<property name="pub.revision" value="1.0"/>
<property name="pub.resolver" value="local"/>
<!--
===========
Build setup
===========
-->
<target name="resolve" description="Use ivy to resolve classpaths">
<ivy:resolve/>
<ivy:report todir='${build.dir}/ivy-reports' graph='false' xml='false'/>
<ivy:cachepath pathid="compile.path" conf="compile"/>
<ivy:cachepath pathid="test.path" conf="test"/>
</target>
<!--
===============
Compile targets
===============
-->
<target name="resources" description="Copy resources into classpath">
<copy todir="${build.dir}/classes">
<fileset dir="${resources.dir}"/>
</copy>
</target>
<target name="compile" depends="resolve,resources" description="Compile code">
<mkdir dir="${build.dir}/classes"/>
<javac srcdir="${src.dir}" destdir="${build.dir}/classes" includeantruntime="false" debug="true" classpathref="compile.path"/>
</target>
<target name="compile-tests" depends="compile" description="Compile tests">
<mkdir dir="${build.dir}/test-classes"/>
<javac srcdir="${test.src.dir}" destdir="${build.dir}/test-classes" includeantruntime="false" debug="true">
<classpath>
<path refid="test.path"/>
<pathelement path="${build.dir}/classes"/>
</classpath>
</javac>
</target>
<!--
============
Test targets
============
-->
<target name="test" depends="compile-tests" description="Run unit tests">
<mkdir dir="${build.dir}/test-reports"/>
<junit printsummary="yes" haltonfailure="yes">
<classpath>
<path refid="test.path"/>
<pathelement path="${build.dir}/classes"/>
<pathelement path="${build.dir}/test-classes"/>
</classpath>
<formatter type="xml"/>
<batchtest fork="yes" todir="${build.dir}/test-reports">
<fileset dir="${test.src.dir}">
<include name="**/*Test*.java"/>
<exclude name="**/AllTests.java"/>
</fileset>
</batchtest>
</junit>
</target>
<!--
=====================
Build project
=====================
-->
<target name="build" depends="test" description="Create executable jar archive">
<ivy:retrieve pattern="${dist.dir}/lib/[artifact]-[revision](-[classifier]).[ext]" conf="runtime"/>
<manifestclasspath property="jar.classpath" jarfile="${jar.file}">
<classpath>
<fileset dir="${dist.dir}/lib" includes="*.jar"/>
</classpath>
</manifestclasspath>
<jar destfile="${jar.file}" basedir="${build.dir}/classes">
<manifest>
<attribute name="Main-Class" value="${jar.main.class}" />
<attribute name="Class-Path" value="${jar.classpath}" />
</manifest>
</jar>
</target>
<!--
=====================
Publish project
=====================
-->
<target name="publish" depends="build" description="Publish artifacts to shared repo">
<ivy:buildnumber organisation="${ivy.organisation}" module="${ivy.module}" revision="${pub.revision}"/>
<ivy:publish resolver="${pub.resolver}" pubrevision="${ivy.new.revision}">
<artifacts pattern="${build.dir}/dist/[artifact].[ext]"/>
</ivy:publish>
</target>
<!--
=============
Clean project
=============
-->
<target name="clean" description="Cleanup build files">
<delete dir="${build.dir}"/>
</target>
</project>
Notes:
The ivy buildnumber task is really useful for ensuring your build number is properly incremented each time you run a build. It looks at the files previously published.
server/ivy.xml
This module has a single dependency on the latest version of the "app" module. The actual revision number is determined at build time based on the files present in the local repository.
<ivy-module version="2.0">
<info organisation="com.myspotontheweb" module="demo-server"/>
<configurations>
<conf name="compile" description="Required to compile application"/>
<conf name="runtime" description="Additional run-time dependencies" extends="compile"/>
<conf name="test" description="Required for test only" extends="runtime"/>
</configurations>
<publications>
<artifact name="demo-server" type="war"/>
</publications>
<dependencies>
<!-- runtime dependencies -->
<dependency org="com.myspotontheweb" name="demo-app" rev="latest.integration" conf="runtime"/>
</dependencies>
</ivy-module>
server/build.xml
This build just packages up the libraries into a WAR file. What makes it noteworthy is it's use of the ivy retrieve task. It will pull the "app" module dependency and all its transitive dependencies. It can be difficult to keep track of these manually.
<project name="demo-server" default="build" xmlns:ivy="antlib:org.apache.ivy.ant">
<!--
================
Build properties
================
-->
<property name="build.dir" location="target"/>
<property name="dist.dir" location="${build.dir}/dist"/>
<property name="war.file" value="${dist.dir}/${ant.project.name}.war"/>
<property name="pub.revision" value="1.0"/>
<property name="pub.resolver" value="local"/>
<!--
===========
Build setup
===========
-->
<target name="resolve" description="Use ivy to resolve classpaths">
<ivy:resolve/>
<ivy:report todir='${build.dir}/ivy-reports' graph='false' xml='false'/>
</target>
<!--
=====================
Build project
=====================
-->
<target name="build" depends="resolve" description="Create executable jar archive">
<ivy:retrieve pattern="${build.dir}/lib/[artifact]-[revision](-[classifier]).[ext]" conf="runtime"/>
<war destfile="${war.file}" webxml="src/resources/web.xml">
<lib dir="${build.dir}/lib"/>
</war>
</target>
<!--
=====================
Publish project
=====================
-->
<target name="publish" depends="build" description="Publish artifacts to shared repo">
<ivy:buildnumber organisation="${ivy.organisation}" module="${ivy.module}" revision="${pub.revision}"/>
<ivy:publish resolver="${pub.resolver}" pubrevision="${ivy.new.revision}">
<artifacts pattern="${build.dir}/dist/[artifact].[ext]"/>
</ivy:publish>
</target>
<!--
=============
Clean project
=============
-->
<target name="clean" description="Cleanup build files">
<delete dir="${build.dir}"/>
</target>
</project>
A simple approach would be the following: In the build.xml for app instead of
<property name="app.build.dir" location="build"/>
use
<property name="app.build.dir" location="../app/build"/>
If you specify a property by location (and with a relative path), ant resolves the path relative to your current project. With this notation, ant first goes up a directory level and then down to the app dir, which is right from both of your projects.
A better approach would be to put the settings used by both build scripts in a separate property file and include this file from both builds.
I'm fairly new to Ivy, but have gotten it to work with jar dependencies. The problem is trying to set it up, so I can fetch javadocs and sources independently of jars.
I have a simple test project, but no matter what I'm doing, I'm fetching the jar with the class files in it.
I have the following ivy.xml file:
<ivy-module version="1.0">
<info
organisation="com.vegicorp"
module="test"
revision="1.0"
status="release"/>
<configurations>
<conf name="default" visibility="public" extends="runtime,master"/>
<conf name="master" visibility="public"/>
<conf name="compile" visibility="public"/>
<conf name="provided" visibility="public"/>
<conf name="runtime" visibility="public" extends="compile"/>
<conf name="test" visibility="private" extends="runtime"/>
<conf name="system" visibility="public"/>
<conf name="sources" visibility="public"/>
<conf name="javadoc" visibility="public"/>
<conf name="optional" visibility="public"/>
</configurations>
<dependencies>
<dependency org="commons-logging" name="commons-logging" rev="1.1.1"
conf="compile->default"/>
<dependency org="commons-logging" name="commons-logging" rev="1.1.1"
conf="sources->default">
<artifact name="commons-logging" type="sources" ext="jar"/>
</dependency>
<dependency org="commons-logging" name="commons-logging" rev="1.1.1"
conf="javadoc->default">
<artifact name="commons-logging" type="javadoc" ext="jar"/>
</dependency>
</dependencies>
</ivy-module>
And the following build.xml:
<project name="ivy-test" default="default" basedir="."
xmlns:ivy="http://ant.apache.org/ivy">
<property name="ivy.dir" value="${basedir}/ivy.dir"/>
<import file="${ivy.dir}/ivy.tasks.xml"/>
<property name="target.dir" value="${basedir}/lib"/>
<target name="-resolve">
<ivy:resolve/>
</target>
<target name="clean">
<delete dir="${target.dir}"/>
<ivy:cleancache/>
</target>
<target name="default"
depends="-resolve">
<fail message="ivy.conf is not defined">
<condition>
<not>
<isset property="ivy.conf"/>
</not>
</condition>
</fail>
<delete dir="${target.dir}"/>
<mkdir dir="${target.dir}"/>
<ivy:retrieve conf="${ivy.conf}"
pattern="${target.dir}/[artifact]-[revision].[ext]"/>
</target>
</project>
At the command line, I'll type:
$ ant -Divy.conf=compile
And, that should download the jarfile with the classes.
However if I type it this:
$ ant -Divy.conf=sources
I want the jar file that contains the sources and not the classes, and when I type this:
$ ant -Divy.conf=javadoc
I want the jar file that contains the javadoc and not the sources.
I'm pretty sure it's my ivy.xml that's not quite right. I originally tried this:
<dependencies>
<dependency org="commons-logging" name="commons-logging" rev="1.1.1">
<artifact name="commons-logging" type="jar" ext="jar" conf="compile->default"/>
<artifact name="commons-logging" type="sources" ext="jar" conf="sources->default"/>
<artifact name="commons-logging" type="javadoc" ext="jar" conf="javadoc->default"/>
</dependency>
That downloaded the jar, the sources, and javadoc, but all at once no matter which configuration I tried.
Okay, I think I've figured it out. I was over thinking this whole process. My <dependencies> section should look like this:
<dependencies>
<dependency org="commons-logging" name="commons-logging" rev="1.1.1"
conf="javadoc->javadoc;sources->sources;compile->default"/>
</dependencies>
This maps my javadoc to Maven's javadoc and my sources to Maven's sources. When I mapped conf="sources->default", it was mapping my sources to Maven's default which is the compile dependencies.
I can specify all the configurations in one line, and I don't need separate <artifact> entities.
Hi i am trying to build an ant script that copies a certain lib file based on a if condition. however it doesnt seem to work as i get this error:
build.xml:20: fileset doesn't support the nested "if" element.
this is the part where it fails:
<target name="resolve">
<delete dir="${lib.dir}">
<include name="*" />
</delete>
<copy todir="${lib.dir}">
<fileset dir="ext-libs" >
<if name="${release}" value="true">
<include name="hello-client-[^DEBUG]*.jar" />
</if>
<else>
<include name="hello-client-*DEBUG.*.jar" />
</else>
</fileset>
</copy>
</target>
#JoseK is right. ANT filesets do not support nested "if" statements. In fact the "if" statement is not part of core ANT the recommended approach is to use conditional targets (See example)
#slipset is on the right track. Ivy configurations can be used to selectively choose your dependencies.
Example
This example is designed to be invoked in one of two ways
$ ant clean build
$ tree
.
|-- build.xml
|-- ivy.xml
`-- lib
|-- slf4j-api-1.6.4.jar
`-- slf4j-simple-1.6.4.jar
Or
$ ant -Drelease=1 clean build
$ tree
.
|-- build.xml
|-- ivy.xml
`-- lib
|-- logback-classic-1.0.3.jar
|-- logback-core-1.0.3.jar
`-- slf4j-api-1.6.4.jar
build.xml
<project name="demo" default="build" xmlns:ivy="antlib:org.apache.ivy.ant">
<target name="resolve">
<ivy:resolve/>
</target>
<target name="retrieve-alt" depends="resolve" unless="release">
<ivy:retrieve pattern="lib/[artifact]-[revision](-[classifier]).[ext]" conf="altruntime"/>
</target>
<target name="retrieve-release" depends="resolve" if="release">
<ivy:retrieve pattern="lib/[artifact]-[revision](-[classifier]).[ext]" conf="runtime"/>
</target>
<target name="build" depends="retrieve-alt,retrieve-release"/>
<target name="clean">
<delete dir="lib"/>
</target>
</project>
Notes:
The if and unless clauses on the targets perform a conditional test on the existence of the "release" property.
The ivy retrieve task uses a configuration to decide which jars should be used to populate the "lib" directory.
The retrieve pattern includes a "classifier" pattern, just in case you ivy mapping pulls down additional Maven artifacts like the source or javadoc jars.
ivy.xml
<ivy-module version="2.0">
<info organisation="com.myspotontheweb" module="demo"/>
<configurations>
<conf name="compile" description="Required to compile application"/>
<conf name="runtime" description="Additional run-time dependencies" extends="compile"/>
<conf name="altruntime" description="Alternative 'runtime' configuration" extends="compile"/>
<conf name="test" description="Required for test only" extends="altruntime"/>
</configurations>
<dependencies>
<!-- compile dependencies -->
<dependency org="org.slf4j" name="slf4j-api" rev="1.6.4" conf="compile->default"/>
<!-- runtime dependencies -->
<dependency org="ch.qos.logback" name="logback-classic" rev="1.0.3" conf="runtime->default"/>
<!-- altruntime dependencies -->
<dependency org="org.slf4j" name="slf4j-simple" rev="1.6.4" conf="altruntime->default"/>
<!-- test dependencies -->
<dependency org="junit" name="junit" rev="4.10" conf="test->default"/>
</dependencies>
</ivy-module>
Note:
I would highly recommend always specifying a configuration mapping for each dependency. This will then map directly to how you intend to use the jars, for example populate a classpath.
Appendix
How to use ivy configurations
Ivy configurations can be used to emulate Maven scopes, but in fact an ivy configuration can represent any logical grouping of dependencies.
Here are the 3 standard classpaths required in any Java build:
<configurations>
<conf name="compile" description="Required to compile application"/>
<conf name="runtime" description="Additional run-time dependencies" extends="compile"/>
<conf name="test" description="Required for test only" extends="runtime"/>
</configurations>
Note the "extends" syntax that enables you to create larger sets. For example, the runtime set of jars also includes anything needed to compile the code your code.
Ivy configurations are difficult to understand until you realise that they can be used to selectively populate an ANT path:
<ivy:cachepath pathid="compile.path" conf="compile"/>
<javac ..... classpathref="compile.path"/>
Or used to selectively populate a directory
<ivy:retrieve pattern="build/WEB-INF/lib/[artifact].[ext]" conf="runtime"/>
Configuration mappings
Mappings are used to decide how groups of jars in your project relate to groups of jars in other projects.
This normally happens as follows:
<dependency org="org.slf4j" name="slf4j-api" rev="1.6.4" conf="compile->default"/>
Here our compile configuration is populate by the remote default configuration (normally the other modules compile dependencies)
Well, the apache docs are clear that you cannot nest an <if> within a <fileset>
You might have seen an example of the ant-contrib fileset task which does have the if as a conditional argument
You can download and use that instead.
I've done this a bit differently myself.
I've got
<target name="internal-resolve" description="retrieve dependencies with ivy">
<ivy:retrieve pattern = "${basedir}/lib/[conf]/[artifact].[ext]" conf = "${configuration}" />
</target>
Which puts the jars in a directory called lib/${configuration} where configuration is passed as a parameter in
<target name = "resolve-compile">
<antcall target = "internal-resolve">
<param name = "configuration" value = "compile"/>
</antcall>
</target>
So whenever resolve-compile is called, the jars are put in lib/compile.
This gives me:
<path id = "compile.class.path">
<fileset dir="${basedir}/lib/compile" includes="*.jar"/>
</path>
and finally
<target name="compile"
depends="init, resolve-compile"
description="Compile all Java-classes">
<javac deprecation="false"
encoding="utf8"
debug="true"
srcdir="${java.src.dir}"
destdir="${build.classes.dir}"
classpathref="compile.class.path"/>
</target>
Given this, you could easily created your own resolve-build which would drop your jars in lib/build
Granted, you'd then need to add a build configuration to your ivy.xml as
<configurations>
<conf name="compile" visibility="public" description="Whatever"/>
<conf name="build" visibility="public" description="Whatever"/>
</configuration>
Does someone happens to know if there is a way to retrieve the contents of jar libs into one single jar - so that it would be
jar 1 :
org
smth
new.class
jar 2 :
org
smth
also.class
jar 3 :
org
another
otheralso.class
that single jar:
org
another
one.class
smth
two class
result jar :
org
another
one.class
otheralso.class
smth
two class
also.class
new.class
with ivy and ant ) i have a cachepath or cachefileset )
Need to combine ivy with the groovy plug-in.
build.xml
<target name="resolve">
<ivy:resolve/>
<ivy:cachepath pathid="build.path" conf="build"/>
<ivy:cachefileset setid="jarfiles" conf="jars"/>
</target>
<target name="combine-jars" depends="resolve">
<taskdef name="groovy" classname="org.codehaus.groovy.ant.Groovy" classpathref="build.path"/>
<!--
Iterate thru each file expanding content into a temp directory
used to create a new jar
-->
<groovy>
project.references.jarfiles.each {
ant.unzip(src: it, dest:"build/tmp")
}
ant.jar(destfile:"build/newjar.jar", basedir:"build/tmp")
</groovy>
</target>
ivy.xml
Use ivy configurations to separate downloads into build dependencies and a collection
of files to be later combined within the build.
<ivy-module version="2.0">
<info organisation="org.myspotontheweb" module="demo"/>
<configurations>
<conf name="build" description="ANT tasks"/>
<conf name="jars" description="Files to be combined"/>
</configurations>
<dependencies>
<!-- build dependencies -->
<dependency org="org.codehaus.groovy" name="groovy-all" rev="1.8.2" conf="build->default"/>
<!-- jars dependencies -->
<dependency org="log4j" name="log4j" rev="1.2.16" conf="jars->default"/>
<dependency org="commons-lang" name="commons-lang" rev="2.6" conf="jars->default"/>
..
..
</dependencies>
</ivy-module>
I actully came to this decision
<target name="project.archive" depends="project.make, ivy-runtimecahe">
<mkdir dir="${project.build}\temp" />
<copy todir="${project.build}\temp">
<archives>
<zips >
<restrict >
<path refid="classpath.Runtime" />
<name name="*.jar" />
</restrict>
</zips>
</archives>
</copy>
<jar jarfile="${project.build}\${project.archive.name}-${project.version}.jar"
manifest="${basedir}\${project.maifest}">
<fileset dir="${project.build}\temp" includes="**\*.class" />
<fileset dir="${project.classes}" includes="**\*.class" />
<fileset dir="Btlserver" includes="**\*.xml" />
</jar>
then we delete temp - and voila.
Besides, may be you can suggest smth about multiple projects layout in eclipse with ivyDE and ant?? I asked this question, but no one has even posted any suggestions. Sad (