Find hidden dependencies in Ivy - java

I'm using Apache Ivy + IvyDE for getting my project's dependencies, which are:
<dependency org="com.google.guava" name="guava" rev="r08" />
<!-- logging -->
<dependency org="org.slf4j" name="jcl-over-slf4j" rev="1.6.1" />
<dependency org="ch.qos.logback" name="logback-classic" rev="0.9.27" />
<!-- database -->
<dependency org="org.hibernate" name="hibernate-entitymanager" rev="3.6.2.Final" />
<dependency org="org.hibernate" name="hibernate-validator" rev="4.1.0.Final" />
<dependency org="org.hibernate" name="hibernate-c3p0" rev="3.6.2.Final" />
<dependency org="mysql" name="mysql-connector-java" rev="5.1.14" />
Sources are the Maven and JBoss (Hibernate) repositories.
As you can see I'm using logback+SLF4J for logging, but for some reason Ivy will download log4j and slf4j-log4j as well, which causes a few small problem in my application.
Is there a way to see why this happens, to see which of the dependencies above depend on log4j? Can I get a dependency graph/tree generated from Ivy/IvyDE?
And is there then a way to prevent this from happening?

We have an ant target that looks like this:
<target name="report" depends="init">
<mkdir dir="report" />
<!--
The type attribute is optional, we're using it to exlude other dependcy types we're not interested in.
Note that each resolve uses that list (via a property) in our build.
-->
<ivy:resolve type="jar,ejb,tld,bundle"/>
<ivy:report todir="report" />
</target>
Then it's just a call ant report and Ivy will generate the report as HTML in the given directory.
Take a look at the Ivy documentation for ivy:report.
Edit:
To prevent the inclusion of those artifacts/dependencies, you could try transitive="false" on the <dependency ..> element, or use <exclude>. For example, we use Hibernate 3 but don't want to have JTA 1.1, so our ivy.xml containts this: <exclude module="jta"/> to exclude all transitive JTA dependencies.

I'd like to build on Thomas' answer and recommend adding a "conf" declaration to the dependencies:
<dependency org="org.slf4j" name="jcl-over-slf4j" rev="1.6.1" conf="default"/>
<dependency org="ch.qos.logback" name="logback-classic" rev="0.9.27" conf="default"/>
This will reduce the transitive dependencies to the default sub-set, which in Maven terminology is the jars on the "compile" scope.
Without this setting you get all the dependencies which includes lot of optional stuff you won't need.

Related

Include a project JAR from filesystem in project using IVY fails to pull in sources and javadoc

So I've been trying this for the better part of a day, and I'm so close. I have two projects, and I want to publish one as a JAR to a shared file location and then pull it in to the other project including the source and javadoc .jar files that I am generating out. The actual ivy:publish is successful and the files get correctly created in the remote file system.
However when trying to include the JAR's in my 2nd project, I can only pull in the base JAR, not the JAR's that contain the sources and javadoc.
My files are named as follows: [projectname]-[version](-[classifier]) so at the remote location I get foo-1.0.0.jar, foo-1.0.0-sources.jar, foo-1.0.0-javadoc.jar & ivy-1.0.0.xml as well as .sha and .md5 files for each JAR, but my IVY is only pulling in the foo-1.0.0.jar.
I'd like to stress that I am successfully pulling in sources and javadocs off the mavenrepo remote repository automatically and that is works really well. I just don't get why it is refusing to pull down the source & javadoc JAR's into the IVY cache when it finds and pulls the class JAR down fine from the file system.
This is the resolver pattern that I am using to pull down the files off the remote file system is:
/path/to/remote/location/[organisation]/[module]/[revision]/[module]-[revision](-[classifier]).[ext]
and the pattern that I'm using to push the files to the shared location is:
/path/to/remote/location/[organisation]/[module]/[revision]/[artifact]-[revision](-[classifier]).[ext]
I've been trawling the documentation and examples that people have, but the documentation may as well be written in Klingon for all the sense it makes, and the examples are either from 2010 or don't fetch sources from the file system.
Thoughts:
Its not a permissions issue (if it was, IVY wouldn't be able to pull anything down)
Its not a connection issue (see above)
It might be a problem with how I am publishing my project (If you think this is the case, I can provide much more detail in what I am doing with that, upon request)
I'm suspicious that IVY isn't pulling down (or appearing to read) the ivy-1.0.0.xml file on the remote filesystem at all. After all, that is where I'm declaring that the sources and javadoc JAR's exist.
Its not just this project, I've tried to follow the same process for a third project, only to have IVY only grab the class .jar file from the remote file system.
I could be not creating/naming files in a way that IVY is expecting to read. I had a look at the file structure that is used in mavencentral and attempted to copy how they named and laid out their files, but it doesn't work.
What I've tried:
Just about every combination of IVY patterns that I can think of.
Repeatedly deleting the cached files to see if IVY correctly resolves all files from the remote filesystem (it doesn't, it just regrabs the class JAR and generates an ivy-1.0.0.xml file in the cache directory)
Happy to post config and clarify exactly what I'm doing if you want me to etc, just drop a comment and ask for what you want.
IVY Config in second project:
<ivy-conf>
<property name="ivy.shared.dir"
value="/path/to/remote/repo/maven-repo/shared" />
<property name="ibiblio-maven2-root" value="http://repo1.maven.org/maven2/"
override="false" />
<property name="ibiblio-spring-core-root" value="http://maven.springframework.org/"
override="false" />
<property name="local.pattern"
value="[organisation]/[module]/[revision]/[module]-[revision](-[classifier]).[ext]"/>
<property name="maven2.pattern"
value="[organisation]/[module]/[revision]/[module]-[revision]" />
<property name="spring.pattern" value="org/[organisation]/[module]/[revision]/" />
<property name="maven2.pattern.ext" value="${maven2.pattern}(-[classifier]).[ext]" />
<property name="spring.pattern.ext" value="${spring.pattern}(-[classifier]).[ext]" />
<settings defaultResolver="default" />
<resolvers>
<filesystem name="shared" m2compatible="true">
<artifact pattern="${ivy.shared.dir}/${local.pattern}" />
</filesystem>
<ibiblio name="maven2" root="${ibiblio-maven2-root}" pattern="${maven2.pattern.ext}"
m2compatible="true" />
<ibiblio name="spring" m2compatible="true" pattern="${spring.pattern.ext}"
root="${ibiblio-spring-core-root}" />
<chain name="internal">
<resolver ref="shared" />
</chain>
<chain name="external">
<resolver ref="maven2" />
<resolver ref="spring" />
</chain>
<chain name="default" returnFirst="true">
<chain ref="external" />
<chain ref="internal" />
</chain>
</resolvers>
<modules>
<module organisation="company.com.au" name="name"
resolver="default" />
</modules>
</ivy-conf>
The only difference between the two IVY files (between both projects) is this line which is the pattern to push to the remote server:
<property name="shared-repo"
value="[organisation]/[module]/[revision]/[artifact]-[revision](-[classifier]).[ext]" />
as well as changing the tag in the resolver to shared-repo.
IVY file in the remote location:
<ivy-module version="2.0" xmlns:m="http://ant.apache.org/ivy/maven" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd">
<info organisation="company" module="simple-email" revision="1.0.0" status="integration" publication="20140924154556">
</info>
<configurations defaultconfmapping="default">
<conf name="compile" visibility="private"/>
<conf name="test" extends="compile" visibility="private"/>
<conf name="master"/>
<conf name="runtime"/>
<conf name="default" extends="master,runtime"/>
<conf name="sources" visibility="public" description="this configuration contains the source artifact of this module, if any."/>
<conf name="javadoc" visibility="public" description="this configuration contains the javadoc artifact of this module, if any."/>
</configurations>
<publications>
<artifact name="simple-email" type="jar" ext="jar" conf="master"/>
<artifact name="simple-email" type="source" ext="jar" conf="sources" m:classifier="sources"/>
<artifact name="simple-email" type="javadoc" ext="jar" conf="javadoc" m:classifier="javadoc"/>
</publications>
<dependencies>
<!-- Unit Testing -->
<dependency org="junit" name="junit" rev="4.8.2" conf="test->default"/>
<!-- Log4j Logging -->
<dependency org="log4j" name="log4j" rev="1.2.13" conf="runtime->default;test->default;compile->default"/>
<!-- SLF Logging -->
<dependency org="org.slf4j" name="slf4j-simple" rev="1.6.1" conf="runtime->default;compile->default;test->default"/>
<dependency org="org.slf4j" name="slf4j-api" rev="1.6.1" conf="runtime->default;compile->default;test->default"/>
<dependency org="javamail" name="javamail" rev="1.4" conf="runtime->default;compile->default;test->default"/>
</dependencies>
</ivy-module>
IVY file that gets generated in the cache:
<ivy-module version="2.0">
<info organisation="company"
module="simple-email"
revision="1.0.0"
status="release"
publication="20140924162544"
default="true"
/>
<configurations>
<conf name="default" visibility="public"/>
</configurations>
<publications>
<artifact name="simple-email" type="jar" ext="jar" conf="default"/>
</publications>
</ivy-module>
Directory Structure on remote server:
Org
|--Module
|--Version
|--ivy-1.0.0.xml
|--ivy-1.0.0.md5
|--ivy-1.0.0.sha
|--simple-email-1.0.0-javadoc.jar
|--simple-email-1.0.0-javadoc.jar.md5
|--simple-email-1.0.0-javadoc.jar.sha
|--simple-email-1.0.0-sources.jar
|--simple-email-1.0.0-sources.jar.md5
|--simple-email-1.0.0-sources.jar.sha
|--simple-email-1.0.0.jar
|--simple-email-1.0.0.jar.md5
|--simple-email-1.0.0.jar.sha
Ivy Cache Directory structure:
Org
|--Module
|--ivy-1.0.0.xml
|--ivydata-1.0.0.properties
|--jars
|--simple-email-1.0.0.jar
Publish Task on the ANT script:
<target name="ivy-publish" depends="archive">
<ivy:publish resolver="shared" pubrevision="${project.jar.version}" overwrite="true">
<artifacts pattern="${dist.dir}/[artifact].[ext]"/>
</ivy:publish>
</target>
Explanation
Theory
First of all this setup is a bit complicated, but also familiar because it's how ivy translates Maven modules into ivy ones. The problem is understanding how Maven "scopes" are translated into ivy "configurations".
How are maven scopes mapped to ivy configurations by ivy
Your remote module
Addressing your specific question I think the issue is how you are downloading artefacts. Your remote module declares the following files:
<publications>
<artifact name="simple-email" type="jar" ext="jar" conf="master"/>
<artifact name="simple-email" type="source" ext="jar" conf="sources" m:classifier="sources"/>
<artifact name="simple-email" type="javadoc" ext="jar" conf="javadoc" m:classifier="javadoc"/>
</publications>
The magic part is the configurations. In this case you have a file associated with the following configurations:
master
sources
javadoc
Secondly the remote module has a rather complex set of configurations declared. Here are ones relevent to the "default" setup:
<configurations defaultconfmapping="default">
..
..
<conf name="master"/>
<conf name="runtime"/>
<conf name="default" extends="master,runtime"/>
..
..
</configurations>
So... Only the "master" artefact is included. This would explain why source code is excluded by default, which makes sense because normally users would want the compiled binaries.
How ivy downloads artefacts
Simple (I don't want to use configurations)
This is where we dig into the magic of dependency mappings. Most of the time users don't care about time. So to ignore them I normally recommend adding a conf="default" at the end of each dependency:
<dependency org="company" name="simple-email" rev="1.0.0" conf="default"/>
The creates the following relationship between me and the remote module:
<local "default" configuration> -> <remote "default" configuration>
In others words only give me defaults which are the compiled binaries excluding other more optional stuff like source and javadoc.
Using configurations
Once you understand configurations you're going to want to declare them locally. For example:
<configurations>
<conf name="compile" description="Required to compile application"/>
<conf name="sources" description="Source code"/>
</configurations>
We're stating that we have two buckets or logical groupings of dependencies.
Now we make our dependency declares more aware of our local configurations:
<dependency org="company" name="simple-email" rev="1.0.0" conf="compile->default;sources"/>
We now have 2 mappings:
<local "compile" configuration> -> <remote "default" configuration>
<local "sources" configuration> -> <remote "sources" configuration>
We can now reference or use these configurations separately in our ANT build file. For example create a classpath:
<ivy:cachepath pathid="compile.path" conf="compile"/>
Our put the sources jar inside the build directory using the retrieve task:
<ivy:retrieve pattern="build/src/[artifact]-[revision](-[classifier]).[ext]" conf="sources"/>
Example
In this contrived example I want to download the source jars into a build/src directory and the compile dependencies into a lib dir:
├── build
│   └── src
│   └── log4j-1.2.17-sources.jar
├── build.xml
├── ivy.xml
└── lib
└── log4j-1.2.17.jar
build.xml
<project name="demo" default="retrieve" xmlns:ivy="antlib:org.apache.ivy.ant">
<target name="retrieve">
<ivy:retrieve pattern="lib/[artifact]-[revision](-[classifier]).[ext]" conf="compile"/>
<ivy:retrieve pattern="build/src/[artifact]-[revision](-[classifier]).[ext]" conf="sources"/>
</target>
</project>
Note:
The files downloaded by ivy are grouped into "configurations". Each retrieve task has a different "conf" attribute.
Configurations are declared in the ivy file and mappings are defined on each dependency.
ivy.xml
<ivy-module version="2.0">
<info organisation="com.myspotontheweb" module="demo"/>
<configurations>
<conf name="compile" description="Required to compile application"/>
<conf name="sources" description="Source code"/>
</configurations>
<dependencies>
<dependency org="log4j" name="log4j" rev="1.2.17" conf="compile->default;sources" />
</dependencies>
</ivy-module>
Notes:
Ivy file creates 2 configurations
The magic is the "conf" attribute of the dependency. This creates the following mappings "compile->default" and "sources->sources". This means compile dependencies come from the remote default (usual setting) and the local sources come from the remote sources.

How to subtract ivy dependency sets

My goal is to demarcate project transitive dependencies into several not crossing sets:
system (jars already present in j2ee container; listed manually with explicit fixed versions)
provided (jars to be copied to j2ee container; listed manually)
ear (jars to be packed inside ear/lib, the rest)
My current solution listed below has some shortcomings:
have to exclude system and provided libraries from ear conf one by one
new third-party transitive deps that weren't already explicitly excluded could accidentally get to ear
sometimes have to add explicit override duplicating library name and version
Is there some approach possible to eliminate these shortcomings?
It would be nice to be able somehow define one conf as a result of dependency sets subtraction of the others (with graceful conflict resolution):
ear = runtime - system - provided.
Maybe <conf name="ear" extends="runtime,!system,!provided"/> notation could be supported when IVY-982 gets fixed.
Looking for an actual solution to apply.
Even willing to consider switching to gradle if it has a solution.
<?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="example.com" module="parent"/>
<configurations defaultconfmapping="compile->#;runtime->#;system->master;provided->runtime;ear->runtime;test->test(default)">
<conf name="compile"/>
<conf name="runtime" extends="compile"/>
<conf name="ear" extends="runtime" description="Libs to be packed inside ear"/>
<conf name="provided" description="Libs to copy to j2ee container"/>
<conf name="system" description="Libs already present in j2ee container"/>
<conf name="test" extends="ear,provided,system" description="Simulate container environment. Used by unit tests to catch dependency compatibility problems."/>
</configurations>
<dependencies>
<dependency org="log4j" name="log4j" rev="1.2.15" force="true" conf="system"/>
<dependency org="commons-collections" name="commons-collections" rev="3.1" force="true" conf="system"/>
<dependency org="commons-lang" name="commons-lang" rev="2.2" force="true" conf="system"/>
<dependency org="org.apache.velocity" name="velocity" rev="1.7" force="true" conf="provided"/>
<dependency org="org.slf4j" name="slf4j-api" rev="1.5.6" force="true" conf="provided"/>
<dependency org="org.slf4j" name="slf4j-log4j12" rev="1.5.6" force="true" conf="provided"/>
<!-- ... -->
<dependency name="module1" rev="latest.integration" conf="runtime,ear,provided,test"/>
<dependency name="module2" rev="latest.integration" conf="runtime,ear,provided,test"/>
<!-- ... -->
<exclude org="commons-collections" conf="ear,provided"/>
<exclude org="commons-lang" conf="ear,provided"/>
<exclude org="org.apache.velocity" conf="ear"/>
<!-- TODO: negation not working: https://issues.apache.org/jira/browse/IVY-982 -->
<!--<exclude org="org.slf4j" conf="*, !provided"/>-->
<exclude org="org.slf4j" conf="ear,test"/>
<!-- ... -->
<override org="org.slf4j" rev="1.5.6"/>
<override org="commons-collections" module="commons-collections" rev="3.1"/>
<override org="commons-lang" module="commons-lang" rev="2.2"/>
<!-- ... -->
</dependencies>
</ivy-module>
Sample project sources to experiment with can be found in IVY-1443 attachment.
While provided dependencies exclusion is possible with Maven and Gradle, it seems that currently there is no way to easily achieve it with ivy.
Update
In some cases the task can be worked around with intermediate induced module and negative regexp mask:
<dependency org="com.company" name="root.module" conf="ear" rev="latest.integration">
<exclude org="^(?!com.company).*$" matcher="regexp"/>
</dependency>
But we've already moved to Gradle as Ivy seems to be losing momentum.

system scope equivalent for ivy

I am looking for the equivalent of a system scope dependency in Maven for Ivy. In Maven, declaring a dependency with system scope means that Maven will not include the dependency in the output, which is what I want. How can I achieve the same thing with Ivy?
I suspect you're talking about the Maven provided scope, not system.
provided This is much like compile, but indicates you expect the JDK or a container to provide the dependency at runtime. For example, when building a web application for the Java Enterprise Edition, you would set the dependency on the Servlet API and related Java EE APIs to scope provided because the web container provides those classes. This scope is only available on the compilation and test classpath, and is not transitive.
Ivy's configurations are very flexible which means that are several ways to do this.
ivy.xml
I would model my configuration on the different types of jar that my build will use:
<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"/>
<conf name="provided" description="Additional compile time dependencies, implementation provided by target platform"/>
</configurations>
The dependencies are then assigned to each logical grouping using a "conf" mapping:
<!-- compile dependencies -->
<dependency org="org.slf4j" name="slf4j-api" rev="1.7.5" conf="compile->default"/>
Note this is where the "extends" attribute it useful, without it the following mapping would be required for a logging dependency:
<!-- compile dependencies -->
<dependency org="org.slf4j" name="slf4j-api" rev="1.7.5" conf="compile,runtime,test->default"/>
Dependencies provided by the target platform are special. This is why I create a standalone configuration for them:
<!-- compile dependencies -->
<dependency org="my.target.platform" name="makeitgo-api" rev="1.0" conf="provided->default"/>
For even more details on ivy configuration mappings see:
Ivy, what is the master configuration and why is it not pulling jvyaml?
build.xml
It's here where the classpaths are actually managed. (We could try and model the set relationships in various ivy configurations, but I'd argue this approach is simpler and gets the job done)
<target name="resolve" description="Use ivy to resolve classpaths">
<ivy:resolve/>
<ivy:report todir='${ivy.reports.dir}' graph='false' xml='false'/>
<ivy:cachepath pathid="compile.path" conf="compile,provided"/>
<ivy:cachepath pathid="test.path" conf="test,provided"/>
</target>
So just as the Maven documentation describes, add the provided dependencies to the compile and test path.
This means the "runtime" configuration only contains the dependencies that should be bundled:
<ivy:retrieve pattern="${build.dir}/WEB-INF/lib/[artifact]-[revision].[ext]" conf="runtime"/>
I guess that the only problem to deal with is to have a dependency which should be used at compile time but not at runtime, right ?
Here is an exemple of ivy.xml on how to deal with it:
<ivy-module version="2.0" xmlns:m="http://ant.apache.org/ivy/maven">
<info organisation="com.acme" module="mymodule" />
<configurations>
<conf name="default" description="runtime" />
<conf name="runtime" description="Runtime configuration"/>
<conf name="compile" description="Used only for compilation" />
<conf name="test" extends="compile,runtime" description="Unit testing configuration" visibility="private" />
</configurations>
<dependencies defaultconf="runtime,compile->default">
<!-- Compile and runtime -->
<dependency org="commons-lang" name="commons-lang" rev="2.6" />
<!-- Only compile -->
<dependency org="javax.servlet" name="servlet-api" rev="2.5" conf="compile->default" />
</dependencies>
</ivy-module>

Ivy Dependency (pull in directory of files)

Extreme edit to question to have it make more sense:
Let's assume that I need to use a local version of httpclient rather than one that I can just pull from an online repo (due to signing reasons). The way that I want to handle this is like so...
ivy.xml
<dependencies>
...Other dependencies here
<dependency org="com.apache" name="httpclient" rev="4.2.2" conf="compile->default" ext="jar" />
</dependencies>
ivysettings.xml
<settings defaultResolver="central"/>
<resolvers>
<url name="repo">
<ivy pattern="http://myServer:8080/Repo/[organisation]/[artifact]/[revision]/ivy.xml" />
<artifact pattern="http://myServer:8080/Repo/[organisation]/[artifact]/[revision]/[artifact].[ext]"/>
</url>
<url name="httpclient">
<artifact pattern="http://myServer:8080/Repo/com.apache/httpclient/4.2.2/[artifact].[ext]"/>
</url>
<modules>
<module organisation="com.apache" resolver="repo" />
<module organisation="com.httpclient" resolver="httpclient" />
</modules>
Now what I'm hoping for here (and haven't been having much luck with) is the com.apache resolver looking for myServer:8080/Repo/com.apache/httpclient/4.2.2/ivy.xml and reading that, here's the contents of that file:
ivy.xml (in myServer:8080/repo/... directory)
<dependency org="com.httpclient" name="commons-codec" rev="1.6" />
<dependency org="com.httpclient" name="commons-logging" rev="1.1.1" />
<dependency org="com.httpclient" name="fluent-hc" rev="4.2.2" />
<dependency org="com.httpclient" name="httpclient" rev="4.2.2" />
<dependency org="com.httpclient" name="httpclient-cache" rev="4.2.2" />
<dependency org="com.httpclient" name="httpcore" rev="4.2.2" />
<dependency org="com.httpclient" name="httpmime" rev="4.2.2"/>
The reasoning behind wanting to read the second xml file rather than including the markup in my first file is pretty obvious when you consider how many LOC that would add to something that we include frequently. It also makes all future includes easier as well.
Right now the error that I'm getting is:
Some projects fail to be resolved
Impossible to resolve dependencies of com.myCompany#myProgramt;working#CompName
unresolved dependency: com.apache#httpclient;4.2.2: not found
Thanks for your help on this matter.
Ivy expects to find all the dependencies of a given artifact in the same resolver. So, it finds the artifacts for com.apache in your repo resolver, and expects to find com.httpclient in there as well.
Ivy also will roll through your <ivy pattern.../> and <artifact pattern.../> statements in order within the same resolver declaration. You can use this to your advantage to create a single resolver which hits both repositories in the order you want:
<url name="amalgamation">
<ivy pattern="http://myServer:8080/Repo/[organisation]/[artifact]/[revision]/ivy.xml" />
<artifact pattern="http://myServer:8080/Repo/[organisation]/[artifact]/[revision]/[artifact].[ext]"/>
<artifact pattern="http://myServer:8080/Repo/com.apache/httpclient/4.2.2/[artifact].[ext]"/>
</url>
When you configure your build to use the following resolver
<ibiblio name="central" m2compatible="true"/>
You are telling ivy to download its dependencis from Maven Central
What is your objective here? To create a local ivy repo that functionally works like Maven Central? In that case the simplest solution would be to setup a Maven repository manager like: Nexus, Artifactory or Archiva. A maven repository manager can act like a smart cache and "proxy" jars stored in the Central Maven repo.
Configuring your build to use a local Maven Repository is easy:
<ibiblio name="central" m2compatible="true" root="http://hostname:portnum/MavenRepo/>
What server are you using for your remote JAR repository?
Both Nexus and Artifactory can be setup to pull jars stored locally on themselves before puling ones from the remote repository. This way, you don't have to munge your ivysettings.xml. Instead, you simply download your preferred versions of the jars on Artifactory/Nexus. And, both are free, open source, downloads. It's way easier to do what you want with Artifactory/Nexus than futzing with your Ivy settings.
By the way, I have a Ivy project in Github you might want to look at. You simply attach this project to your Ant project, and it has everything automatically configured for Ivy. This way, an entire site can use Ivy for all of their projects, and everything is centrally controlled.

Get ivy:retrieve to not copy a jar into two configurations?

I'm trying to set up my ant build so that the targets which run Ivy functions are completely separated from the ones that the continuous build and most developers run. I want one target to download updated dependencies, which I'll check in. Other targets will set up their classpath by including *.jar from the relevant directory.
I have two configurations:
<configurations>
<conf name="compile" />
<conf name="test" />
</configurations>
I have some dependencies:
<dependency
org="my.org"
name="some-lib"
rev="latest.release"
conf="compile->default" />
<dependency
org="my.org"
name="some-test-lib"
rev="latest.release"
conf="test->default" />
And I download those dependencies and their transitive dependencies using ivy:retrieve:
<ivy:retrieve
pattern="lib/[conf]/[type]/[artifact]-[revision].[ext]"
sync="true"
file="ivy.xml" />
The problem is that I'm seeing some duplicates jars between the compile and the test directories, which come from transitive dependencies. Since I want to check in all these jars and use them for creating classpaths, I'd like to avoid duplicates. Is this possible?
lib/compile/jar/some-lib-1.0.jar
lib/compile/jar/slf4j-api-1.5.11.jar
lib/test/jar/some-test-lib-1.0.jar
lib/test/jar/junit-4.7.jar
lib/test/jar/slf4j-api-1.5.11.jar
This is not duplication, each configuration is a separate set of jars and the ivy restrieve task is faithly creating each set....
Perhaps it would make more sense to create the classpaths directly, rather than populating a local lib directory.
Here's a snippet of my ANT build files:
<target name="get-dependencies">
<ivy:resolve/>
<ivy:cachepath pathid="compile.path" conf="compile" />
<ivy:cachepath pathid="test.path" conf="test" />
</target>
<target name="compile" depends="get-dependencies">
<javac srcdir="src" destdir="build/classes" classpathref="compile.path"/>
</target>
I normally only use the ivy retrieve task when I need to create a local copy of a set of jars, for example assembling a web app's directory:
<ivy:retrieve pattern="build/WEB_INF/lib/[artifact].[ext]" conf="runtime"/>
Update
Another alternative is to instruct ivy to exclude the slf4j module when downloading transient dependencies, as follows:
<dependency org="my.org" name="some-lib" rev="latest.release" conf="compile->default">
<exclude module="slf4j-api"/>
</dependency>
<dependency org="my.org" name="some-test-lib" rev="latest.release" conf="test->default">
<exclude module="slf4j-api"/>
</dependency>
If I can do this through Ivy directly, that would be best. For now I've solved the problem by just deleting duplicates using ant.
Try the following. Your test should extend compile
<dependency
org="my.org"
name="some-test-lib"
rev="latest.release"
conf="**test->compile**" />
If i am right IVY should find that test extends compile and would download slf4j only once.

Categories