Search code examples
antivy

ANT Ivy: How to retrieve a library and put its transitive dependencies in different folder


I am working on a project analysing libraries in Maven repo. I am using Ivy to retrieve the library(e.g. log4j) from Maven repo. But Ivy also downloads its transitive dependencies in the same folder! How do I separate them?


Solution

  • The secret is configuration mappings, a powerful concept in ivy. When downloading from an ivy repository, configurations allow you to configure different combinations of files that can be downloaded. For example you could specify you want just the module's published files, or the published files with all their dependencies. It's a very flexible capability.

    When ivy downloads from Maven repositories it used the ibiblio resolver. Maven repositories do not support configurations so ivy makes them up and has a standard set which I have documented in more detail here:

    Long story short when you specify a dependency the configuration mapping tells ivy which remote files to retrieve from the remote repository. I would advice you to always specify a configuration and gain more control. Once you understand configurations you understand ivy.

    Examples

    No configurations

    Without a mapping all files associated with the remote Maven module are retrieved.

    ├── build.xml
    └── lib
        ├── activation-1.1.jar
        ├── geronimo-jms_1.1_spec-1.0.jar
        ├── javaee-api-5.0-2.jar
        ├── log4j-1.2.17.jar
        ├── log4j-1.2.17-javadoc.jar
        ├── log4j-1.2.17-sources.jar
        └── mail-1.4.3.jar
    

    Build file:

    <project name="demo" default="resolve" xmlns:ivy="antlib:org.apache.ivy.ant">
    
      <target name="resolve">
        <ivy:retrieve pattern="lib/[artifact]-[revision](-[classifier]).[ext]">
          <dependency org="log4j" name="log4j" rev="1.2.17"/>
        </ivy:retrieve>
      </target>
    
    </project>
    

    Specify the "master" configuration

    When ivy reads a remote Maven module the "master" configuration is the remote module's file only, with no dependencies

    ├── build.xml
    └── lib
        └── log4j-1.2.17.jar
    

    Build file:

    <project name="demo" default="resolve" xmlns:ivy="antlib:org.apache.ivy.ant">
    
      <target name="resolve">
        <ivy:retrieve pattern="lib/[artifact]-[revision](-[classifier]).[ext]">
          <dependency org="log4j" name="log4j" rev="1.2.17" conf="master"/>
        </ivy:retrieve>
      </target>
    
    </project>
    

    Notes:

    • The conf="master" mapping tells ivy to retrieve from the remote master configuration

    Using an ivy file

    This is a more complex example that demonstrates the real power of ivy and how multiple configurations and mappings can be specified. Here I have two directories. One with just the log4j jar the second additionally containing the remote module's "optional" dependencies. If you look at the remote POM you'll see they have a different scope.

    ├── build.xml
    ├── ivy.xml
    ├── lib1
    │   └── log4j-1.2.17.jar
    └── lib2
        ├── activation-1.1.jar
        ├── geronimo-jms_1.1_spec-1.0.jar
        ├── log4j-1.2.17.jar
        └── mail-1.4.3.jar
    

    Build file

    <project name="demo" default="resolve" xmlns:ivy="antlib:org.apache.ivy.ant">
    
      <target name="resolve">
        <ivy:resolve/>
    
        <ivy:retrieve pattern="lib1/[artifact]-[revision](-[classifier]).[ext]" conf="noDependencies"/>
        <ivy:retrieve pattern="lib2/[artifact]-[revision](-[classifier]).[ext]" conf="withDependencies"/>
      </target>
    
    </project>
    

    ivy file

    <ivy-module version="2.0">
      <info organisation="com.myspotontheweb" module="demo"/>
    
      <configurations>
        <conf name="noDependencies" description="File grouping that has no transitive dependencies"/>
        <conf name="withDependencies" description="File grouping that contains dependencies"/>
      </configurations>
    
      <dependencies>
        <dependency org="log4j" name="log4j" rev="1.2.17" conf="noDependencies->master; withDependencies->master,optional"/>
      </dependencies>
    
    </ivy-module>