20

I'm a total newbie with Ivy, and have been trying it out very simply, for fetching commonly used libraries such as Guava and Gson that are available in the central Maven repository:

<ivy-module version="2.0">
    <info organisation="com.company" module="foobar"/>    
    <dependencies>
        <dependency org="com.google.guava" name="guava" rev="10.0.1"/>
        <dependency org="com.google.code.gson" name="gson" rev="2.0"/>
    </dependencies>    
</ivy-module>

On Windows, by default, Ivy stores its files in %USERPROFILE%\.ivy2\cache\; on Unix-y systems, they are downloaded under $HOME/.ivy2/.

I suppose this is pretty basic question: how to tell Ivy to download both binaries and sources, and to put the binary jars in one (arbitrary) directory and source jars in another directory?

For example, I'd like Ivy to put all downloaded binary jars in [project_home]/WebContent/WEB-INF/lib.

Note that I'm using Ivy via Ant, along the following lines, not an IDE plugin.

<project xmlns:ivy="antlib:org.apache.ivy.ant" name="ivy" default="resolve" > 
    <target name="resolve" description="retrieve dependencies with ivy">
        <ivy:retrieve/>
    </target>

    <path id="ivy.lib.path">
        <fileset dir="tools/buildlibs" includes="*.jar"/>
    </path>
    <taskdef resource="org/apache/ivy/ant/antlib.xml" uri="antlib:org.apache.ivy.ant" classpathref="ivy.lib.path"/>        
</project>
Dave Jarvis
  • 30,436
  • 41
  • 178
  • 315
Jonik
  • 80,077
  • 70
  • 264
  • 372

3 Answers3

19

Another SO answer describes how configurations are used to keep groups of dependencies separate. This problem however might require the dependencies to be declared more than once to fit into different ivy configurations.

Try the following:

ivy.xml

<ivy-module version="2.0">
    <info organisation="com.company" module="foobar"/>    
    <configurations>
        <conf name="sources"  description="Source jars"/>
        <conf name="binaries" description="binary jars"/>
    </configurations>
    <dependencies>
        <dependency org="com.google.guava" name="guava" rev="10.0.1" conf="sources->sources"/>
        <dependency org="com.google.code.gson" name="gson" rev="2.0" conf="sources->sources"/>

        <dependency org="com.google.guava" name="guava" rev="10.0.1" conf="binaries->default"/>
        <dependency org="com.google.code.gson" name="gson" rev="2.0" conf="binaries->default"/>
    </dependencies>    
</ivy-module>

build.xml

<project xmlns:ivy="antlib:org.apache.ivy.ant" name="hello-ivy" default="resolve">

    <target name="resolve" description="retrieve dependencies with ivy">
        <ivy:retrieve conf="sources" pattern="lib/[conf]/[artifact](-[classifier]).[ext]"/>
        <ivy:retrieve conf="binaries" pattern="lib/[conf]/[artifact](-[classifier]).[ext]"/>
    </target>

    <target name="clean" description="Remove build directories">
        <delete dir="lib"/>
    </target>

    <target name="clean-all" depends="clean" description="clean ivy cache">
        <ivy:cleancache />
    </target>

</project>

Note: Updated to add target which purges ivy cache.

Build is run as follows, to ensure artifacts are freshly downloaded:

$ ant clean-all resolve

Result

$ find . -type f
./build.xml
./ivy.xml
./lib/sources/gson-sources.jar
./lib/sources/guava-sources.jar
./lib/binaries/gson.jar
./lib/binaries/jsr305.jar
./lib/binaries/guava.jar

Proof that the source artifact contains java files:

$ unzip -t ./lib/sources/gson-sources.jar
Archive:  ./lib/sources/gson-sources.jar
    testing: META-INF/                OK
    testing: META-INF/MANIFEST.MF     OK
    testing: com/                     OK
    testing: com/google/              OK
    testing: com/google/gson/         OK
    testing: com/google/gson/annotations/   OK
    testing: com/google/gson/internal/   OK
    testing: com/google/gson/internal/bind/   OK
    testing: com/google/gson/reflect/   OK
    testing: com/google/gson/stream/   OK
    testing: com/google/gson/annotations/Expose.java   OK
    testing: com/google/gson/annotations/package-info.java   OK
    testing: com/google/gson/annotations/SerializedName.java   OK
    testing: com/google/gson/annotations/Since.java   OK
    testing: com/google/gson/annotations/Until.java   OK
    testing: com/google/gson/AnonymousAndLocalClassExclusionStrategy.java   OK
    testing: com/google/gson/Cache.java   OK
    testing: com/google/gson/CamelCaseSeparatorNamingPolicy.java   OK
    testing: com/google/gson/CompositionFieldNamingPolicy.java   OK
    testing: com/google/gson/DefaultTypeAdapters.java   OK
    testing: com/google/gson/DisjunctionExclusionStrategy.java   OK
    testing: com/google/gson/ExclusionStrategy.java   OK
    testing: com/google/gson/ExposeAnnotationDeserializationExclusionStrategy.java   OK
    testing: com/google/gson/ExposeAnnotationSerializationExclusionStrategy.java   OK
    testing: com/google/gson/FieldAttributes.java   OK
    testing: com/google/gson/FieldNamingPolicy.java   OK
    testing: com/google/gson/FieldNamingStrategy.java   OK
    testing: com/google/gson/FieldNamingStrategy2.java   OK
    testing: com/google/gson/FieldNamingStrategy2Adapter.java   OK
    testing: com/google/gson/Gson.java   OK
    testing: com/google/gson/GsonBuilder.java   OK
    testing: com/google/gson/GsonToMiniGsonTypeAdapterFactory.java   OK
    testing: com/google/gson/InnerClassExclusionStrategy.java   OK
    testing: com/google/gson/InstanceCreator.java   OK
    testing: com/google/gson/internal/$Gson$Preconditions.java   OK
    testing: com/google/gson/internal/$Gson$Types.java   OK
    testing: com/google/gson/internal/bind/ArrayTypeAdapter.java   OK
    testing: com/google/gson/internal/bind/BigDecimalTypeAdapter.java   OK
    testing: com/google/gson/internal/bind/BigIntegerTypeAdapter.java   OK
    testing: com/google/gson/internal/bind/CollectionTypeAdapterFactory.java   OK
    testing: com/google/gson/internal/bind/DateTypeAdapter.java   OK
    testing: com/google/gson/internal/bind/ExcludedTypeAdapterFactory.java   OK
    testing: com/google/gson/internal/bind/JsonElementReader.java   OK
    testing: com/google/gson/internal/bind/JsonElementWriter.java   OK
    testing: com/google/gson/internal/bind/MapTypeAdapterFactory.java   OK
    testing: com/google/gson/internal/bind/MiniGson.java   OK
    testing: com/google/gson/internal/bind/ObjectTypeAdapter.java   OK
    testing: com/google/gson/internal/bind/Reflection.java   OK
    testing: com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java   OK
    testing: com/google/gson/internal/bind/SqlDateTypeAdapter.java   OK
    testing: com/google/gson/internal/bind/StringToValueMapTypeAdapterFactory.java   OK
    testing: com/google/gson/internal/bind/TimeTypeAdapter.java   OK
    testing: com/google/gson/internal/bind/TypeAdapter.java   OK
    testing: com/google/gson/internal/bind/TypeAdapterRuntimeTypeWrapper.java   OK
    testing: com/google/gson/internal/bind/TypeAdapters.java   OK
    testing: com/google/gson/internal/ConstructorConstructor.java   OK
    testing: com/google/gson/internal/LazilyParsedNumber.java   OK
    testing: com/google/gson/internal/ObjectConstructor.java   OK
    testing: com/google/gson/internal/package-info.java   OK
    testing: com/google/gson/internal/Pair.java   OK
    testing: com/google/gson/internal/ParameterizedTypeHandlerMap.java   OK
    testing: com/google/gson/internal/Primitives.java   OK
    testing: com/google/gson/internal/Streams.java   OK
    testing: com/google/gson/internal/UnsafeAllocator.java   OK
    testing: com/google/gson/JavaFieldNamingPolicy.java   OK
    testing: com/google/gson/JsonArray.java   OK
    testing: com/google/gson/JsonDeserializationContext.java   OK
    testing: com/google/gson/JsonDeserializer.java   OK
    testing: com/google/gson/JsonDeserializerExceptionWrapper.java   OK
    testing: com/google/gson/JsonElement.java   OK
    testing: com/google/gson/JsonElementVisitor.java   OK
    testing: com/google/gson/JsonIOException.java   OK
    testing: com/google/gson/JsonNull.java   OK
    testing: com/google/gson/JsonObject.java   OK
    testing: com/google/gson/JsonParseException.java   OK
    testing: com/google/gson/JsonParser.java   OK
    testing: com/google/gson/JsonPrimitive.java   OK
    testing: com/google/gson/JsonSerializationContext.java   OK
    testing: com/google/gson/JsonSerializer.java   OK
    testing: com/google/gson/JsonStreamParser.java   OK
    testing: com/google/gson/JsonSyntaxException.java   OK
    testing: com/google/gson/LongSerializationPolicy.java   OK
    testing: com/google/gson/LowerCamelCaseSeparatorNamingPolicy.java   OK
    testing: com/google/gson/LowerCaseNamingPolicy.java   OK
    testing: com/google/gson/LruCache.java   OK
    testing: com/google/gson/ModifierBasedExclusionStrategy.java   OK
    testing: com/google/gson/ModifyFirstLetterNamingPolicy.java   OK
    testing: com/google/gson/package-info.java   OK
    testing: com/google/gson/RecursiveFieldNamingPolicy.java   OK
    testing: com/google/gson/reflect/package-info.java   OK
    testing: com/google/gson/reflect/TypeToken.java   OK
    testing: com/google/gson/SerializedNameAnnotationInterceptingNamingPolicy.java   OK
    testing: com/google/gson/stream/JsonReader.java   OK
    testing: com/google/gson/stream/JsonScope.java   OK
    testing: com/google/gson/stream/JsonToken.java   OK
    testing: com/google/gson/stream/JsonWriter.java   OK
    testing: com/google/gson/stream/MalformedJsonException.java   OK
    testing: com/google/gson/stream/StringPool.java   OK
    testing: com/google/gson/SyntheticFieldExclusionStrategy.java   OK
    testing: com/google/gson/UpperCamelCaseSeparatorNamingPolicy.java   OK
    testing: com/google/gson/UpperCaseNamingPolicy.java   OK
    testing: com/google/gson/VersionConstants.java   OK
    testing: com/google/gson/VersionExclusionStrategy.java   OK
No errors detected in compressed data of ./lib/sources/gson-sources.jar.
Community
  • 1
  • 1
Mark O'Connor
  • 76,015
  • 10
  • 139
  • 185
  • Got it working, thanks! Too bad some redundancy is needed (separate for binaries and sources) but oh well. Btw, I left out `conf="binaries->default"` in ivy.xml and `conf="binaries"` in build.xml as it works the same without them. – Jonik Jan 04 '12 at 08:43
  • No, wait, it doesn't work for sources :/ I thought it actually downloaded the source jars, but in fact it got the binary jars twice and put those under the source dir too. Any ideas? – Jonik Jan 04 '12 at 10:06
  • My example works. In order to control the contents of each configuration you need to specify the explicit mapping between local and remote configuration ("scope" in Maven parlance). Using conf="binaries" will not work. Maven doesn't support a scope called "binaries". – Mark O'Connor Jan 08 '12 at 15:09
  • To clarify, fetching **source** jars does not work with above config. Try it: look inside e.g. the gson-sources.jar file and see if it actually contains .java files (instead of .class files). If you know how to make that work, please let me know. – Jonik Jan 09 '12 at 08:54
  • Again works for me (Answer has been expanded). I'm using ANT 1.8.2 and ivy 2.2.0 – Mark O'Connor Jan 09 '12 at 20:36
8

~/.ivy2 is just the ivy cache.

You have to set a pattern to ivy retrieve. This will define where dependencies are downloaded.

<ivy:retrieve pattern="${project_home}/WebContent/WEB-INF/lib/[artifact].[ext]" conf="jars"/>

And maybe a second retrieve for the sources:

<ivy:retrieve pattern="${project_home}/sources/[artifact].[ext]" conf="sources"/>

This will also work, and put the dependencies of sources and jars in different directories:

<ivy:retrieve pattern="${project_home}/[conf]/[artifact].[ext]" conf="sources, jars"/>

It depends on how sources/jars are designated in your repository.

And on a side note: the taskdef has to come before using a task.

And you should define the resolver as m2compatible:

<ibiblio name="maven2" m2compatible="true"/>
oers
  • 18,436
  • 13
  • 66
  • 75
  • Thanks! I'm trying this kind of setup, but I now get the error: "Report file '~/.ivy2/cache/com.company-foobar-jars.xml' does not exist". It seems strange if I need to manually create such file under the ivy cache dir... Is there still something missing in my Ivy config? – Jonik Jan 02 '12 at 08:23
  • @Jonik hard to tell, did you execute prior to retrieve? – oers Jan 02 '12 at 08:28
  • No; I tried adding but still get the same error. From the error messages, it seems I can't simply add `conf="jars"` in a call without defining the "jars" config somewhere: `asked configuration not found in com.company#foobar;working@jonik: jars`. But how to do that? – Jonik Jan 02 '12 at 08:36
  • @jonik jars and sources were just an example, you have to know what [**conf**](https://ant.apache.org/ivy/history/trunk/tutorial/conf.html) values are available for your dependency. Look into the ivy.xml of the dependency. Everything that is under publications has a conf, that you can use. If your dependency is in a maven repository take a look here: http://stackoverflow.com/questions/7104364/how-are-maven-scopes-mapped-to-ivy-configurations-by-ivy – oers Jan 02 '12 at 08:52
  • Yes, I'm first trying to use Ivy with common libs that are available in default Maven repos; the question shows everything I have. For binary jars, I got it working simply by removing `conf="jars"` altogether; the jars now go to WEB-INF/lib. I still don't know how to do this for sources. Looking at the accepted answer of that question, I thought "sources" might be a valid conf name to use, but no. Apparently I need to declare/map the confs somehow in ivy.xml; I'm looking at [this blog post](http://lightguard-jp.blogspot.com/2009/04/ivy-configurations-when-pulling-from.html) to learn how. – Jonik Jan 02 '12 at 09:19
1

I had similar problems, but with a little more tricky situation for which the provided solution didn't work. I had a main ivy.xml file that defined no configuration, so inherited the default configuration that gets created without you even notice it (see doc). Applying a freshly made "main" conf on all dependencies just broke the build. I had no choice but to define a new module in another file, say ivy_extra.xml, and load it in a new target, simple as that!

Example file:

<!-- ivy_extra.xml -->
<ivy-module version="2.0">
    <info organisation="com.myorg" module="mymodule"/>
    <configurations>
        <conf name="download" visibility="private" />
    </configurations>
    <dependencies>
        <dependency org="com.google.guava" name="guava" rev="10.0.1" conf="download->default"/>
    </dependencies>
</ivy-module>

Loaded in your build.xml like that:

<target name="download-guava" description="Retrieve guava">
    <echo>Retrieving guava</echo>
    <ivy:resolve file="ivy_agent.xml"/>
    <ivy:retrieve conf="download" type="jar" pattern="guava/[artifact]-[revision].jar"/>
</target>
Jean
  • 642
  • 5
  • 9