Ivy, everyone’s favorite “it’s not Maven” dependency manager, has a unique, idiosyncratic feature I was fighting with today: artifact tags to override which jars/artifacts you use from upstream projects.
While Maven has only project-to-project dependencies (i.e. you get all the jars in each upstream project whether you want them or not), Ivy adds the concept of configurations to select subsets of artifacts within your dependencies. E.g., in an ivy.xml
file:
<dependency org="upstream" name="project" rev="1.0"
conf="default->someSubsetConf" />
By using someSubsetConf
, you can select a specific set of artifacts (only those that its own ivy.xml
file declares as part of it’s someSubsetConf
) from the upstream project.
Ivy goes further, and that if the upstream project doesn’t have a configuration in its ivy.xml
file that suits your needs, you can request specific artifacts directly, e.g.:
<dependency org="upstream" name="project" rev="1.0">
<artifact name="onlyTheOneYouWant"/>
</dependency>
While this flexibility, which is very characteristic of Ivy, is often times nice, I do sometimes wonder whether Maven’s non-flexibility and hence simplicity in not having confs and artifact overrides is the better choice in the long run.
Nonetheless, I use Ivy, and have used artifact
tags on several occasions, but they never worked the way I thought they would, so I finally sat down and tried to systematically understand their semantics.
The Short Version
-
Prefer to not use
artifact
tags—if possible, change the upstream project’s configurations to match your needs and just rely on configuration-to-configuration mapping. It’s more intuitive, less boilerplate.If you must use
artifact
tags, then: -
Always add a
conf
attribute to yourartifact
tag, and -
Always add a
conf
attribute to the parentdependency
tag that is the union of all of the confs used byartifact
tags within that dependency.
For example:
<dependency org="upstream" name="project"
rev="1.0" conf="conf1,conf2">
<artifact name="artifact1" conf="conf1"/>
<artifact name="artifact2" conf="conf2"/>
</dependency>
Will put artifact1
in your conf1
and artifact2
in your conf2
, just as you would expect. Simple.
Be warned: if you don’t include these conf
attributes exactly as I’ve described, things will very likely not work they way you expect.
If you don’t care why, that’s fine, this template should serve you well. If you do want to understand the incantation, that’s what the next section is about.
The Long Version
This section describes each of the variations of dependency
and artifact
tags I used to derive Ivy’s semantics and decipher the docs.
Note that I used Ivy 2.3.0-rc1, which as of this writing is current, and the springframework
dependency comes from Bizo’s internal Ivy repository, so it doesn’t match the Maven central namespace or anything like that. Substitute your own dependency with a non-trivial amount of artifacts if you want to follow along.
The base ivy.xml
file I was using looked like:
...
<configurations>
<conf name="default"/>
<conf name="buildtime" visibility="private"/>
<conf name="test" visibility="private"/>
<conf name="sources"/>
</configurations>
<dependencies
defaultconfmapping="sources->sources();%->default"
defaultconf="default;sources">
...
</dependencies>
And now, each of the variations:
-
Just a regular
dependency
tag:<dependency org="springframework" name="spring" rev="3.0.6.RELEASE"/>
Pulls in:
- default: all
default
spring artifacts (web, servlets, test, etc.) - buildtime, test: no spring artifacts
- sources: all
sources
spring artifacts (web, servlets, test, etc.)
As expected, given our
ivy.xml
file’sdefaultconf="default;sources"
setting.This is actually fine, and shows the intuitiveness of configuration-to-configuration mapping.
But the idea is that the Spring
default
conf (in our internal repository) anyway pulls in a lot of various Spring jars we don’t need, so we’re going to try and useartifact
to cut that down some. - default: all
-
Add just an
artifact
tag:<dependency org="springframework" name="spring" rev="3.0.6.RELEASE"> <artifact name="org.springframework.web" /> </dependency>
Pulls in:
- default: web jar
- buildtime, test: no spring artifacts
- sources: web jar (not sources)
Wtf? Ivy docs on artifact:
“By default, if no (artifact) configuration is specified, artifacts specification applies to all master configurations.”
What is a “master configuration? Ivy docs on dependency:
“This mapping (
conf
attribute ofdependency
tag) indicates which configurations of the dependency are required in which configurations of the current module, also called master configurations.”So, due to
defaultconf="default;sources"
, those are our “master configurations” for this artifact, and the web jar artifact is put in both the default and sources confs.Take away: Leaving off
artifact
’sconf
attribute puts the artifact in any master configuration, which is probably not what you want. Add aconf
. -
Add an
artifact
tag withconf
attribute:<dependency org="springframework" name="spring" rev="3.0.6.RELEASE"> <artifact name="org.springframework.web" conf="default"/> </dependency>
Pulls in:
- default: web jar
- buildtime, test: no spring artifacts
- sources: all spring sources
Wtf? Ivy docs on artifact:
“do not forget that if you do not specify any specification for a particular configuration (none of the artifact’s confs include the master configuration) then no specification will apply for this configuration and it will be resolved not taking into account any specification.”
Since we used
conf=default
but had noconf=sources
anywhere, sources fell back to using the default mapping, and pulled in all sources artifacts.Take away: If you add an
artifact
conf
, set the parentdependency
conf
to include only those confs that you’ve mapped. -
Add an
artifact
tag withconf
attribute anddependency
conf
:<dependency org="springframework" name="spring" rev="3.0.6.RELEASE" conf="default"> <artifact name="org.springframework.web" conf="default"/> </dependency>
Pulls in:
- default: web jar
- buildtime, test: no spring artifacts
- sources: no spring artifacts
As expected.
-
Add two
artifact
tags withconf
attributes:<dependency org="springframework" name="spring" rev="3.0.6.RELEASE"> <artifact name="org.springframework.web" conf="default"/> <artifact name="org.springframework.web-sources" type="sources" ext="jar" conf="sources"/> </dependency>
Pulls in:
- default: web jar
- buildtime, test: no spring artifacts
- sources: web sources
As expected. All master configurations had
artifact
tag overrides, so none of them (master
orsources
) fell back to their default mapping. -
Add two
artifact
tags with one non-”master”conf
attribute:<dependency org="springframework" name="spring" rev="3.0.6.RELEASE"> <artifact name="org.springframework.web" conf="default"/> <artifact name="org.springframework.test" conf="test"/> </dependency>
Pulls in:
- default: web jar
- buildtime: no spring artifacts
- test: no spring artifacts
- sources: all spring sources
Wtf? Ivy docs on artifact:
conf
attribute is “comma separated list of the master configurations in which this artifact should be included.”test
is not a master configuration, so it is essentially ignored.Take away: if you add an
artifact
conf
, ensure it is a master configuration, or just add it to thedependency
conf
to make sure -
Two
artifact
tags, both with “master”conf
attributes:<dependency org="springframework" name="spring" rev="3.0.6.RELEASE" conf="default,test"> <artifact name="org.springframework.web" conf="default"/> <artifact name="org.springframework.test" conf="test"/> </dependency>
Pulls in:
- default: web jar
- buildtime: no spring artifacts
- test: test jar
- sources: no spring sources
As expected, given the behavior discovered so far.
Note on Transitive Dependencies
Just a quick note, but while artifact
let’s you pick apart an upstream project’s artifacts for only those you want, this doesn’t have any affect on the transitive dependencies you inherit from the project—those are still based strictly on configurations.
Usually this just means you’ll end up pulling in more transitive dependencies than you need, but I think it re-enforces the notion the artfact
override is a hack and that finding a way to do configuration-to-configuration mappings is a better way to do things with Ivy.
Conclusion
Don’t use artifact
tags. If you do, always specify both the dependency
conf
and the artifact
conf
.