9
votes

I'm looking into using Ivy to manage dependencies but wow - that thing really likes to make multiple copies of jars! It spreads like the ivy in my back yard and is just as undesirable!

Is it possible to have Ivy simply define a classpath (for a specified profile) that references the resolved dependencies so my javac can reference them directly in the ivy repository (or cache?).

I've read the reference docs buy only see the option to set up symbolic links to the repository cache. I guess this will suffice, but it seems like a waste. Also, I'm not sure that a "war" task can build the war from symbolic links... but I guess I'll find out when I give it a try.

Any better suggestions?

3

3 Answers

15
votes

Here's my standard Java build file that creates an executable jar.

The objective is to manage project specific stuff via a combination of ANT properties and an ivy.xml file for the 3rd-party dependencies.

<project xmlns:ivy="antlib:org.apache.ivy.ant" name="demo" default="build">

  <property name="src.dir" location="src"/>
  <property name="build.dir" location="build"/>
  <property name="dist.dir" location="dist"/>
  <property name="dist.jar" location="${dist.dir}/${ant.project.name}.jar"/>
  <property name="dist.main.class" value="HelloWorld"/>

  <target name="retrieve">
    <ivy:resolve/>
    <ivy:cachepath pathid="build.path" conf="build"/>
    <ivy:cachepath pathid="runtime.path" conf="runtime"/>
  </target>

  <target name="compile" depends="retrieve">
    <mkdir dir="${build.dir}/classes"/>
    <javac srcdir="${src.dir}" destdir="${build.dir}/classes" classpathref="build.path"/>
  </target>

  <target name="build" depends="compile">
    <ivy:retrieve pattern="${dist.dir}/lib/[artifact].[ext]"/>

    <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>

  <target name="clean">
    <delete dir="${build.dir}"/>
    <delete dir="${dist.dir}"/>
  </target>

</project>

As you've discovered in the Ivy docu, the cachepath Ivy task is used to manage two ANT paths. One for the build dependencies the other for the run-time dependencies of the executable jar.

The real power of Ivy is in something called configurations. I found it difficult to grasp initially until I realised it was simple a logical grouping of jars that I can define for my project. This example has two configurations:

  • build
  • runtime

Here's the ivy file demonstrating how dependencies can be associated with configurations:

<ivy-module version="2.0">
    <info organisation="com.myspotontheweb" module="demo"/>
    <configurations>
        <conf name="build" description="Libraries needed to for compilation"/>
        <conf name="runtime" extends="build" description="Libraries that need to be included with project jar" />
    </configurations>
    <dependencies>
        <dependency org="commons-lang" name="commons-lang" rev="2.0" conf="build->default"/>
        <dependency org="commons-cli" name="commons-cli" rev="1.0" conf="runtime->default"/>
    </dependencies>
</ivy-module>

In conclusion I hope this example helps in understanding Ivy. I like the way it concentrates on only one thing, the management of 3rd-party dependencies.

3
votes

After battling through the badly written Ivy documentation (sigh - what is wrong with these people? - did they not attend high-school literacy classes in any language?), I see there is a post-resolve task called cachepath that will construct an ant path to the resolved dependency artifacts instead of copying files to a lib directory. This might be just what I'm looking for!

3
votes

Just to augment @Mark's answer.

Note that cachepath result can also be directly used in build without the need to copy jars with retrieve:

<target name="build" depends="compile">
    <jar destfile="${dist.ear}">
        <mappedresources>
            <resources refid="runtime.path"/>
            <chainedmapper>
                <flattenmapper/>
                <globmapper from="*" to="lib/*"/>
            </chainedmapper>
        </mappedresources>
    </jar>
</target>