- Documentation (2.6.0-local-20230820130639)
- Release Notes
- Tutorials
- Reference
- Introduction
- System Properties
- Settings Files
- Ivy Files
- Ant Tasks
- artifactproperty
- artifactreport
- buildlist
- buildnumber
- buildobr
- cachefileset
- cachepath
- checkdepsupdate
- cleancache
- configure
- convertmanifest
- convertpom
- deliver
- dependencytree
- findrevision
- fixdeps
- info
- install
- listmodules
- makepom
- post resolve tasks
- publish
- report
- repreport
- resolve
- resources
- retrieve
- settings
- var
- Using standalone
- OSGi
- Developer doc
Using Ivy Module Configurations
This tutorial introduces the use of module configurations in Ivy files. Ivy module configurations are indeed a very important concept. Someone even told me one day that using Ivy without using configurations is like eating a good cheese without touching the glass of Chateau Margaux 1976 you have just poured :-)
More seriously, configurations in Ivy can be better understood as views on your module, and you will see how they can be used effectively here.
Introduction
Source code is available in src/example/configurations/multi-projects
.
We have two projects:
-
filter-framework is a library that defines an API to filter String arrays and two implementations of this API.
-
myapp is a very small app that uses filter-framework.
The filter-framework library project produces 3 artifacts:
-
the API jar,
-
an implementation jar with no external dependencies,
-
a second implementation jar that needs commons-collections to perform.
The application only needs the API jar to compile and can use either of the two implementations at runtime.
The library project
The first project we’ll look at in this tutorial is filter-framework. In order to have a fine-grained artifact publication definition, we defined several configurations, each of which maps to a set of artifacts that other projects can make use of.
The ivy.xml file
<ivy-module version="1.0">
<info organisation="org.apache" module="filter-framework"/>
<configurations>
<conf name="api" description="only provide filter framework API"/>
<conf name="homemade-impl" extends="api" description="provide a home made implementation of our API"/>
<conf name="cc-impl" extends="api" description="provide an implementation that use apache common collection framework"/>
<conf name="test" extends="cc-impl" visibility="private" description="for testing our framework"/>
</configurations>
<publications>
<artifact name="filter-api" type="jar" conf="api" ext="jar"/>
<artifact name="filter-hmimpl" type="jar" conf="homemade-impl" ext="jar"/>
<artifact name="filter-ccimpl" type="jar" conf="cc-impl" ext="jar"/>
</publications>
<dependencies>
<dependency org="commons-collections" name="commons-collections" rev="3.1" conf="cc-impl->default"/>
<dependency org="junit" name="junit" rev="3.8" conf="test->default"/>
</dependencies>
</ivy-module>
Explanation
As you can see, we defined 4 configurations, with 3 being public and 1 private (the JUnit dependency for testing). The 2 implementation configurations, homemade-impl and cc-impl extend the api configuration so that all artifacts defined in api will also be part of the extending configuration.
In the publications tag, we defined the artifacts we produce (jars in this case) and we assign them to a configuration. When others use our library they will have a flexible way to ask for what they need.
See it in action
The filter-framework project is built using Ant. Open a shell in the root directory of the project and type ant
.
[ivy@apache:/ivy/configurations/multi-projects/filter-framework]$ ant
Buildfile: /ivy/configurations/multi-projects/filter-framework/build.xml
clean:
resolve:
[ivy:retrieve] :: Apache Ivy 2.6.0-local-20230820130639 - 20230820130639 :: https://ant.apache.org/ivy/ ::
[ivy:retrieve] :: loading settings :: url = jar:file://home/ivy/ivy.jar!/org/apache/ivy/core/settings/ivysettings.xml
[ivy:retrieve] :: resolving dependencies :: org.apache#filter-framework;working@apache
[ivy:retrieve] confs: [api, homemade-impl, cc-impl, test]
[ivy:retrieve] found org.apache.commons#commons-collections4;4.1 in public
[ivy:retrieve] found junit#junit;4.12 in public
[ivy:retrieve] found org.hamcrest#hamcrest-core;1.3 in public
[ivy:retrieve] downloading https://repo1.maven.org/maven2/org/apache/commons/commons-collections4/4.1/commons-collections4-4.1.jar ...
[ivy:retrieve] ................................................ (733kB)
[ivy:retrieve] .. (0kB)
[ivy:retrieve] [SUCCESSFUL ] org.apache.commons#commons-collections4;4.1!commons-collections4.jar (145ms)
[ivy:retrieve] downloading https://repo1.maven.org/maven2/junit/junit/4.12/junit-4.12.jar ...
[ivy:retrieve] ..................... (307kB)
[ivy:retrieve] .. (0kB)
[ivy:retrieve] [SUCCESSFUL ] junit#junit;4.12!junit.jar (118ms)
[ivy:retrieve] downloading https://repo1.maven.org/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar ...
[ivy:retrieve] .... (43kB)
[ivy:retrieve] .. (0kB)
[ivy:retrieve] [SUCCESSFUL ] org.hamcrest#hamcrest-core;1.3!hamcrest-core.jar (75ms)
[ivy:retrieve] :: resolution report :: resolve 1490ms :: artifacts dl 345ms
---------------------------------------------------------------------
| | modules || artifacts |
| conf | number| search|dwnlded|evicted|| number|dwnlded|
---------------------------------------------------------------------
| api | 0 | 0 | 0 | 0 || 0 | 0 |
| homemade-impl | 0 | 0 | 0 | 0 || 0 | 0 |
| cc-impl | 1 | 1 | 1 | 0 || 1 | 1 |
| test | 3 | 3 | 3 | 0 || 3 | 3 |
---------------------------------------------------------------------
[ivy:retrieve] :: retrieving :: org.apache#filter-framework
[ivy:retrieve] confs: [api, homemade-impl, cc-impl, test]
[ivy:retrieve] 4 artifacts copied, 0 already retrieved (1818kB/11ms)
build:
[mkdir] Created dir: /ivy/configurations/multi-projects/filter-framework/build
[mkdir] Created dir: /ivy/configurations/multi-projects/filter-framework/distrib
[javac] Compiling 4 source files to /ivy/configurations/multi-projects/filter-framework/build
[javac] Note: /ivy/configurations/multi-projects/filter-framework/src/filter/FilterProvider.java uses or overrides a deprecated API.
[javac] Note: Recompile with -Xlint:deprecation for details.
[jar] Building jar: /ivy/configurations/multi-projects/filter-framework/distrib/filter-api.jar
[jar] Building jar: /ivy/configurations/multi-projects/filter-framework/distrib/filter-hmimpl.jar
[jar] Building jar: /ivy/configurations/multi-projects/filter-framework/distrib/filter-ccimpl.jar
test:
[mkdir] Created dir: /ivy/configurations/multi-projects/filter-framework/build/test-report
[mkdir] Created dir: /ivy/configurations/multi-projects/filter-framework/build/test-classes
[javac] /ivy/configurations/multi-projects/filter-framework/build.xml:82: warning: 'includeantruntime' was not set, defaulting to build.sysclasspath=last; set to false for repeatable builds
[javac] Compiling 3 source files to /ivy/configurations/multi-projects/filter-framework/build/test-classes
[junit] Running filter.ccimpl.CCFilterTest
[junit] Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0,015 sec
[junit] Running filter.hmimpl.HMFilterTest
[junit] Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0,01 sec
publish:
[ivy:publish] :: delivering :: org.apache#filter-framework;working@apache :: 1.3 :: release :: Sun Aug 20 13:06:56 CEST 2023
[ivy:publish] delivering ivy file to /ivy/configurations/multi-projects/filter-framework/distrib/ivy.xml
[ivy:publish] :: publishing :: org.apache#filter-framework
[ivy:publish] published filter-api to /home/ivy/.ivy2/local/org.apache/filter-framework/1.3.part/jars/filter-api.jar
[ivy:publish] published filter-hmimpl to /home/ivy/.ivy2/local/org.apache/filter-framework/1.3.part/jars/filter-hmimpl.jar
[ivy:publish] published filter-ccimpl to /home/ivy/.ivy2/local/org.apache/filter-framework/1.3.part/jars/filter-ccimpl.jar
[ivy:publish] published ivy to /home/ivy/.ivy2/local/org.apache/filter-framework/1.3.part/ivys/ivy.xml
[ivy:publish] publish committed: moved /home/ivy/.ivy2/local/org.apache/filter-framework/1.3.part
[ivy:publish] to /home/ivy/.ivy2/local/org.apache/filter-framework/1.3
[echo] project filter-framework released with version 1.3
BUILD SUCCESSFUL
Total time: 3 seconds
The Ant default target is publish. This target uses Ivy to publish our library binaries to a local repository. Since we do not specify any repository path, the default one is used. (${home.dir}/.ivy2/local/org.apache/filter-framework/
) At this point, we are ready to use our library.
The application project
Now that we have shipped (published) our fantastic filter library, we want to use it! The tutorial comes with a sample application called myapp.
The ivy.xml
file
<ivy-module version="1.0">
<info organisation="org.apache" module="myapp"/>
<configurations>
<conf name="build" visibility="private" description="compilation only need API jar"/>
<conf name="noexternaljar" description="use only company jar"/>
<conf name="withexternaljar" description="use company jar and third party jars"/>
</configurations>
<dependencies>
<dependency org="org.apache" name="filter-framework" rev="latest.integration" conf="build->api; noexternaljar->homemade-impl; withexternaljar->cc-impl"/>
</dependencies>
</ivy-module>
Explanation
We create 3 configurations that define the different ways we want to use the application. The build configuration defines the compile-time dependencies, and thus only needs the api conf from the filter-framework project. The other two configurations define runtime dependencies. One will only use our "home-made" jar, and the other will use an external jar.
We also defined a dependency on our previously built library. In this dependency, we use configuration mappings to match ours with the dependency’s configurations. You can find more information about configuration mapping here
-
build→api : here we tell Ivy that our build configuration depends on the api configuration of the dependency
-
noexternaljar→homemade-impl : here we tell Ivy that our noexternaljar configuration depends on the homemade-impl configuration of the dependency.
-
withexternaljar→cc-impl : here we tell Ivy that our withexternaljar configuration depends on the cc-impl configuration of the dependency
Note that we never declare any of the dependency’s artifacts we need in each configuration: it’s the dependency module’s Ivy file that declares the published artifacts which should be used in each configuration.
In the Ant build.xml
file, we defined a 'resolve' target as follow:
<target name="resolve" description="--> retrieve dependencies with ivy">
<ivy:retrieve pattern="${ivy.lib.dir}/[conf]/[artifact].[ext]"/>
</target>
When we call this target, Ivy will do a resolve using our ivy.xml
file in the root folder and then retrieve all the artifacts. The artifacts retrieved are kept in separate folders according to the configurations they belong to. Here is how your lib directory should look after a call to this target:
Repertoire de D:\ivy\src\example\configurations\multi-projects\myapp\lib
01/24/2006 11:19 AM <REP> build
01/24/2006 11:19 AM <REP> noexternaljar
01/24/2006 11:19 AM <REP> withexternaljar
0 fichier(s) 0 octets
Repertoire de D:\ivy\src\example\configurations\multi-projects\myapp\lib\build
01/24/2006 10:53 AM 1,174 filter-api.jar
1 fichier(s) 1,174 octets
Repertoire de D:\ivy\src\example\configurations\multi-projects\myapp\lib\noexternaljar
01/24/2006 10:53 AM 1,174 filter-api.jar
01/24/2006 10:53 AM 1,030 filter-hmimpl.jar
2 fichier(s) 2,204 octets
Repertoire de D:\ivy\src\example\configurations\multi-projects\myapp\lib\withexternaljar
01/24/2006 10:53 AM 559,366 commons-collections.jar
01/24/2006 10:53 AM 1,174 filter-api.jar
01/24/2006 10:53 AM 1,626 filter-ccimpl.jar
3 fichier(s) 562,166 octets
As you can see, we have a set of jars for each configuration now.
Let’s try to launch our app.
See it in action
Use Ant to run the application. The default Ant target is run-cc and will launch the application using the Apache commons-collections implementation.
[ivy@apache:/ivy/configurations/multi-projects/myapp]$ ant
Buildfile: /ivy/configurations/multi-projects/myapp/build.xml
resolve:
[ivy:retrieve] :: Apache Ivy 2.6.0-local-20230820130639 - 20230820130639 :: https://ant.apache.org/ivy/ ::
[ivy:retrieve] :: loading settings :: url = jar:file://home/ivy/ivy.jar!/org/apache/ivy/core/settings/ivysettings.xml
[ivy:retrieve] :: resolving dependencies :: org.apache#myapp;working@apache
[ivy:retrieve] confs: [build, noexternaljar, withexternaljar]
[ivy:retrieve] found org.apache#filter-framework;1.3 in local
[ivy:retrieve] [1.3] org.apache#filter-framework;latest.integration
[ivy:retrieve] found org.apache.commons#commons-collections4;4.1 in public
[ivy:retrieve] downloading /home/ivy/.ivy2/local/org.apache/filter-framework/1.3/jars/filter-hmimpl.jar ...
[ivy:retrieve] .. (1kB)
[ivy:retrieve] .. (0kB)
[ivy:retrieve] [SUCCESSFUL ] org.apache#filter-framework;1.3!filter-hmimpl.jar (2ms)
[ivy:retrieve] downloading /home/ivy/.ivy2/local/org.apache/filter-framework/1.3/jars/filter-api.jar ...
[ivy:retrieve] .. (1kB)
[ivy:retrieve] .. (0kB)
[ivy:retrieve] [SUCCESSFUL ] org.apache#filter-framework;1.3!filter-api.jar (2ms)
[ivy:retrieve] downloading /home/ivy/.ivy2/local/org.apache/filter-framework/1.3/jars/filter-ccimpl.jar ...
[ivy:retrieve] .. (1kB)
[ivy:retrieve] .. (0kB)
[ivy:retrieve] [SUCCESSFUL ] org.apache#filter-framework;1.3!filter-ccimpl.jar (1ms)
[ivy:retrieve] :: resolution report :: resolve 82ms :: artifacts dl 9ms
---------------------------------------------------------------------
| | modules || artifacts |
| conf | number| search|dwnlded|evicted|| number|dwnlded|
---------------------------------------------------------------------
| build | 1 | 1 | 1 | 0 || 1 | 1 |
| noexternaljar | 1 | 1 | 1 | 0 || 2 | 2 |
| withexternaljar | 2 | 1 | 1 | 0 || 3 | 2 |
---------------------------------------------------------------------
[ivy:retrieve] :: retrieving :: org.apache#myapp
[ivy:retrieve] confs: [build, noexternaljar, withexternaljar]
[ivy:retrieve] 6 artifacts copied, 0 already retrieved (740kB/7ms)
build:
[mkdir] Created dir: /ivy/configurations/multi-projects/myapp/build
[javac] Compiling 1 source file to /ivy/configurations/multi-projects/myapp/build
run-cc:
[java] Filtering with:class filter.ccimpl.CCFilter
[java] Result :[two, tree]
BUILD SUCCESSFUL
Total time: 0 seconds
Launching the application using the homemade implementation is also straightforward.
type ant run-hm
[ivy@apache:/ivy/configurations/multi-projects/myapp]$ ant run-hm
Buildfile: /ivy/configurations/multi-projects/myapp/build.xml
resolve:
[ivy:retrieve] :: Apache Ivy 2.6.0-local-20230820130639 - 20230820130639 :: https://ant.apache.org/ivy/ ::
[ivy:retrieve] :: loading settings :: url = jar:file://home/ivy/ivy.jar!/org/apache/ivy/core/settings/ivysettings.xml
[ivy:retrieve] :: resolving dependencies :: org.apache#myapp;working@apache
[ivy:retrieve] confs: [build, noexternaljar, withexternaljar]
[ivy:retrieve] found org.apache#filter-framework;1.3 in local
[ivy:retrieve] [1.3] org.apache#filter-framework;latest.integration
[ivy:retrieve] found org.apache.commons#commons-collections4;4.1 in public
[ivy:retrieve] :: resolution report :: resolve 57ms :: artifacts dl 4ms
---------------------------------------------------------------------
| | modules || artifacts |
| conf | number| search|dwnlded|evicted|| number|dwnlded|
---------------------------------------------------------------------
| build | 1 | 0 | 0 | 0 || 1 | 0 |
| noexternaljar | 1 | 0 | 0 | 0 || 2 | 0 |
| withexternaljar | 2 | 0 | 0 | 0 || 3 | 0 |
---------------------------------------------------------------------
[ivy:retrieve] :: retrieving :: org.apache#myapp
[ivy:retrieve] confs: [build, noexternaljar, withexternaljar]
[ivy:retrieve] 0 artifacts copied, 6 already retrieved (0kB/5ms)
build:
run-hm:
[java] Filtering with:class filter.hmimpl.HMFilter
[java] Result :[two, tree]
BUILD SUCCESSFUL
Total time: 0 seconds
Nice! We got the same result, but we can see that the implementation classes are different.
Conclusion
You should use configurations as often as possible. Configurations are a very important concept in Ivy. They allow you to group artifacts and give the group a meaning. When you write Ivy files for projects that are intended for use by others, use configurations to allow people to get only what they need, without having to specify them one by one in their own dependency list.