Overview

What is it?

This is a custom plugin for Gradle for retrieving source-code dependencies and other dependencies consisting of multiple zip files. It also supports publishing artifacts as collections of zip files.

  • Default dependency settings

    • Fail on version conflict by default (configurable)

  • Configurations

    • Create sets of related configurations following standard patterns.

  • Source code dependencies

    • Retrieving source code repositories from SVN and Mercurial

    • Caching credentials for SVN and Mercurial

    • Gradle configuration-based dependency mapping

  • Packed dependencies

    • Retrieving dependencies consisting of many files into your workspace

    • Central cache for unpacked dependencies & link from workspace to central cache

    • Configurable links from consumer module to multiple locations in unpacked dependency

    • Support for using multiple versions of the same component

  • Publishing

    • Support for creating multiple packages (i.e. zip files)

    • Uploading to Gradle format repository with Ivy metadata in ivy.xml file

Example build script

Here is an example build script:

buildscript {
    gplugins.use "intrepid-plugin:7.7.2"
}
gplugins.apply()

group = "com.example-corp.teamA"
version = System.getenv("NEXT_VERSION_NUMBER") ?: Project.DEFAULT_VERSION

repositories.ivy {
    url "http://artifactory-server/artifactory/libs-release"
    credentials {
        username my.username("Artifactory")
        password my.password("Artifactory")
    }
}

// dependenciesSettings.defaultFailOnVersionConflict = false

configurationsSets {
    main { type configurationSetTypes.DLL_64 }
    test {
        type configurationSetTypes.EXE_64
        prefix "test"
    }
}

sourceDependencies {
    framework {
        // Assume the following repo contains a build.gradle setting
        // group "com.example-corp.teamA" and version "1.0.3.987".
        svn "http://svn-server/path/to/framework"
        configurationSet configurationSets.main, configurationSetTypes.DLL_64
    }
    "another-lib" {
        hg "http://hg-server/scm/path/to/another-lib"
    }
}

packedDependencies {
    RenderingLib {
        dependency "com.example-corp.rendering:RenderingLib:2012a2"
        configurationSet configurationSets.main, configurationSetTypes.LIB_64
    }
    "NUnit <version> {
        dependency "org.nunit:NUnit:2.5.10"
        // This is an example of setting up configuraton mappings to a module which
        // does not use the same configuration naming scheme, so we can't map to a
        // configurationSetType.
        def testRuntimeConfs = configurationSets.test.configurationNamesMap.findAll { k, v ->
            k[stage] == 'runtime'
        }
        configuration "${testRuntimeConfs.join(',')}->bin"
        unpackToCache = false
    }
}

packageArtifacts {
    import_common {
        include "src/**/*.h"
    }
    configurationSets.main.axes['Configuration'].each { conf ->
        "import_x64_${conf}" {
            include "lib/${conf}/*.lib"
        }
        "runtime_x64_${conf}" {
            include "bin/${conf}/*.dll"
        }
        "debugging_x64_${conf}" {
            include "bin/${conf}/*.pdb"
        }
    }
}

publishPackages {
    repositories.ivy {
        credentials {
            username my.username("Artifactory")
            password my.password("Artifactory")
        }
        url "http://artifactory-server/artifactory/my-integration-repo/"
    }
}

This script does the following:

  • Makes use of a specific version of the intrepid plugin.

  • Describes which repository to use for satisfying dependencies, including credentials.

  • Defines configurations for this module using the configurationSets DSL, suitable for this module packaging a DLL, and having unit test executables which may have different dependencies.

  • Defines framework as a source code dependency:

    • When dependencies are fetched, an SVN checkout operation will fetch the framework source code to <workspace>/framework.

    • When this module is published, the source-code dependency will be converted to a published module dependency, using the group, name, and version defined by the build.gradle in that source dependency.

  • Defines another-lib as a source code dependency:

    • When dependencies are fetched, an Hg clone operation will fetch the source code to <workspace>/another-lib.

    • When this module is published, the source-code dependency will not be mentioned.

  • Defines RenderingLib as a dependency:

    • When dependencies are fetched, all packages belonging to the specified configurations will be fetched from the repository.

    • When this module is published, the ivy.xml will contain a module dependency for that module, with appropriate configuration mappings from the configurations in the "main" configuration set.

    • The packages will be unpacked to a central cache. (The cache settings can be configured.)

    • A link will be created from the workspace to the unpacked copy of RenderingLib.

  • Defines NUnit as a dependency:

    • When dependencies are fetched, all packages belonging to the bin configuration will be fetched from the repository and unpacked directly into <workspace>/NUnit 2.5.10.

    • When this module is published, the ivy.xml will contain a module dependency for that module, with appropriate configuration mappings from the configurations in the "test" configuration set.

  • Packages are defined for this module:

    • These will be created when the publish task is invoked.

    • Each package belongs to exactly one configuration (although configurations can extend one another).

    • It is possible to name a package differently from the configuration (not shown here).

    • Some Groovy logic helps to define the packages import_x64_Debug, import_x64_Release, and so on.

  • Publishing settings are configured, including:

    • which repository to publish to;

    • the credentials for the repository;

    • the group (set at the top of the file), name (implicit from the containing folder name, or set in settings.gradle), and version (also at the top of the file, using an environment variable).

For the above script the intrepid plugin makes the following tasks available (plus others described later):

Dependencies tasks
------------------
fetchAllDependencies - Retrieves all 'packedDependencies' and 'sourceDependencies', and sets up necessary links.
rebuildLinks - Rebuild all links.
deleteLinks - Delete all links.

Publishing tasks
----------------
packageEverything - Creates all zip packages for project 'example1'.
publish - Publishes all publications to all repositories.
publishIvyPublicationToIvyRepository - Publishes Ivy publication 'ivy' to Ivy repository 'ivy'.

When published, the Ivy module description for this example module will look like this:

<?xml version="1.0" encoding="UTF-8"?>
<ivy-module version="2.0">
  <info organisation="com.example-corp.teamA" module="example" revision="1.2.3.4" status="release" publication="20121115134228"/>
  <configurations>
    <conf name="build" visibility="private"/>
    <conf name="import_common" visibility="public"/>
    <conf name="import_x64_Release" visibility="public" extends="import_common"/>
    <conf name="import_x64_Debug" visibility="public" extends="import_common"/>
    <conf name="runtime_x64_Release" visibility="public"/>
    <conf name="runtime_x64_Debug" visibility="public"/>
    <conf name="debugging_x64_Release" visibility="public"/>
    <conf name="debugging_x64_Debug" visibility="public"/>
    <conf name="test_import_common" visibility="public"/>
    <conf name="test_import_x64_Release" visibility="public" extends="test_import_common"/>
    <conf name="test_import_x64_Debug" visibility="public" extends="test_import_common"/>
    <conf name="test_runtime_x64_Release" visibility="public"/>
    <conf name="test_runtime_x64_Debug" visibility="public"/>
    <conf name="test_debugging_x64_Release" visibility="public"/>
    <conf name="test_debugging_x64_Debug" visibility="public"/>
  </configurations>
  <publications>
    <artifact name="example-import_common" type="zip" ext="zip" conf="import_common"/>
    <artifact name="example-import_x64_Release" type="zip" ext="zip" conf="import_x64_Release"/>
    <artifact name="example-import_x64_Debug" type="zip" ext="zip" conf="import_x64_Debug"/>
    <artifact name="example-runtime_x64_Release" type="zip" ext="zip" conf="runtime_x64_Release"/>
    <artifact name="example-runtime_x64_Debug" type="zip" ext="zip" conf="runtime_x64_Debug"/>
    <artifact name="example-debugging_x64_Release" type="zip" ext="zip" conf="debugging_x64_Release"/>
    <artifact name="example-debugging_x64_Debug" type="zip" ext="zip" conf="debugging_x64"/>
  </publications>
  <dependencies>
    <dependency org="com.example-corp.teamA" name="framework" rev="1.0.3.987" conf="import_x64_Release->import_x64_Release;import_x64_Debug->import_x64_Debug;runtime_x64_Release->runtime_x64_Release;runtime_x64_Debug->runtime_x64_Debug;debugging_x64_Release->debugging_x64_Release;debugging_x64_Debug->debugging_x64_Debug"/>
    <dependency org="com.example-corp.rendering" name="RenderingLib" rev="2012a2" conf="import_x64_Release->import_x64_Release;import_x64_Debug->import_x64_Debug;runtime_x64_Release->runtime_x64_Release;runtime_x64_Debug->runtime_x64_Debug;debugging_x64_Release->debugging_x64_Release;debugging_x64_Debug->debugging_x64_Debug"/>
    <dependency org="org.nunit" name="NUnit" rev="2.5.10" conf="test_runtime_x64_Release,test_runtime_x64_Debug->bin"/>
  </dependencies>
</ivy-module>

DSL Guide

The intrepid plugin adds several domain-specific language (DSL) elements.

repositories

This DSL element isn’t actually part of the intrepid plugin, but will likely need to be configured in your build script, and usually appears near the top of the file. (See repositories in the Gradle documentation.) This element should be placed in the root of your build script—that means, outside of any other DSL elements such as sourceDependencies). For example:

repositories.ivy {
    url "http://artifactory-server/artifactory/my-repo"
    credentials {
        username "user"
        password "password"
    }
}

This repositories block will configure sourceDependencies and packedDependencies. You can configure it using any of the methods in methods in the Gradle RepositoryHandler (e.g. Maven, Ivy or file-system) but the publishing of packages using intrepid only supports Ivy so you’ll probably only have artifacts available in an Ivy repository.

configurationSetTypes

This DSL block is used in combination with the configurationSets block to provide a convenient way to define several related configurations in your project, and their mapping to configurations in other modules, following common patterns. For background information about configurations and configuration mapping, see the Gradle Module Concepts overview and the Configurations section of the Workflow With Gradle and Artifactory page.

A configuration set type defines a pattern for related named configurations and a pattern for mapping those configurations to corresponding configurations in other modules for packedDependencies and sourceDependencies. (A configuration set actually creates those configurations in a project, optionally extending the naming pattern, so that a project can have several different parts following the same pattern—for example, client and server executables.)

Often you will not need to use this block in your build file, because the Holy Gradle automatically adds several configuration set types which are commonly useful for web and Windows native applications. You can however remove or replace those types or add your own.

Tip
You can print a given configuration set type such as configurationSetTypes.LIB using the normal Groovy println method to see information about what it defines.

Pre-defined Configuration Set Types

This section describes the types pre-defined by the Holy Gradle.

Naming Pattern

The naming pattern is based on the idea that there are several concepts relevant to the files which make up a typical non-Java module. One word is used for each such concept or "axis" and, unlike Java which uses camelCaseWords, the axes are separated by underscores. This is so that it is easy for non-Java build tools to construct configuration names from pieces; but camelCase may be used within the value for some axis.

Some axes are optional, and the Platform and Configuration axes also have a "combined" value common, for files such as C++ headers, which are the same across Platform and Configuration.

[Testing "_"]
[Subcomponent "_"]
Stage
[ { ["_" Platform] ["_" BuildType] | "_common" } ]

For example, import_x64_Release, or test_server_runtime_x64_Release.

Testing

This axis is related to the testing of the module:

  • No value for this axis means the configuration covers the default, production use of the module.

  • test: Parts used for unit testing the module.

  • integTest: Parts used for integration testing of the module.

Subcomponent

This axis indicates that your module contains several separate parts, for example matching client and server libraries. There are no suggested standard values—you can use any word which describes the subcomponent. If you need more than one word, use camelCase.

Stage

This refers to stages of the software lifecycle.

  • import: Files needed from module when you want to build some other module which uses it.

  • runtime: Files needed from the module at runtime.

  • debugging: Files only needed if/when you want to debug a running application (or a crash dump).

Platform

This axis covers the Visual Studio / MSBuild "Platform": "Win32" or "x64". Non-C++ and other platform-neutral modules would omit this axis.

Configuration

For modules built with Visual Studio / MSBuild, this covers the "Configuration": "Debug" or "Release". Non-C++ modules will usually still include this axis; for example, a JavaScript library would have minified JS in its "Release" built type, whereas "Debug" would have the non-minified JS.

These configuration set types also define a private configuration called build. Its purpose is to map to configurations in other modules in cases where those files are needed to build this module, but are not needed by other modules which use this one. Typically it maps to

  • build tools;

  • import_* in other modules, if the interface of that module should not be exposed from this module.

Default Visual Studio Types

There are several configuration set types for Visual Studio solutions which are mainly C++-style projects. These types have a naming pattern which defines the Stage, Platform, and Configuration axes; the Testing and Subcomponent axes can be added by a specific configuration set.

  • LIB: static libraries, or similar things which do not themselves have runtime files.

  • DLL: dynamic libraries, or similar things which have runtime files and are used by other libraries or executables

  • EXE: dynamic libraries, or similar things which have runtime files and are executable themselves, instead of being used by other modules.

These configuration set types define configuration name parts, "extends from" relationships, and contents as follows.

Name Extends Contains

build

nothing

nothing, because this configuration is private

import_common

nothing

Platform- and Configuration-independent files which other modules need to build against this one.

import_x64_Release
import_x64_Debug
import_Win32_Release
import_Win32_Debug

import_common

Platform- and Configuration-specific files which other modules need to build against this one.

runtime_x64_Release
runtime_x64_Debug
runtime_Win32_Release
runtime_Win32_Debug

nothing

Platform- and Configuration-specific files which other modules need at runtime to use this one.

debugging_x64_Release
debugging_x64_Debug
debugging_Win32_Release
debugging_Win32_Debug

nothing

Platform- and Configuration-specific files which other modules need to debug against this one.

LIB

This type is for static libraries built with the typical Visual Studio Platform values of x64 and Win32, and Configuration values of Debug and Release. There are also types called

  • LIB_RELEASE, which does not include any of the names with Debug in them;

  • LIB_64, which does not include any of the names with Win32 in them;

  • LIB_64_RELEASE, which does not include any of the names with Win32 or Debug in them.

The configurations are intended to contain the following kinds of files when your module is published, as controlled by the packageArtifacts block.

Name Contains

build

nothing, because this configuration is private

import_common

For a Windows static library, this configuration might include

  • header files

  • Visual Studio property sheets (for setting up include/link paths)

import_x64_Release
import_x64_Debug
import_Win32_Release
import_Win32_Debug

.lib files, and also .pdb files (because PDBs for static libraries are linked into the PDBs for DLLs/EXEs which use them, and are not needed at debugging time)

runtime_x64_Release
runtime_x64_Debug
runtime_Win32_Release
runtime_Win32_Debug

nothing

debugging_x64_Release
debugging_x64_Debug
debugging_Win32_Release
debugging_Win32_Debug

nothing

DLL

This type is for dynamic libraries built with the typical Visual Studio Platform values of x64 and Win32, and Configuration values of Debug and Release. There are also types called

  • DLL_RELEASE, which does not include any of the names with Debug in them;

  • DLL_64, which does not include any of the names with Win32 in them;

  • DLL_64_RELEASE, which does not include any of the names with Win32 or Debug in them.

These types are also suitable for executables which are used like libraries, in that your module needs some kind of header to use them and/or they are not run independently. For example, COM singletons are build as EXEs but can be used by libraries, which need ".idl" files to access them. The configurations are intended to contain the following kinds of files when your module is published, as controlled by the packageArtifacts block.

Name Contains

build

nothing, because this configuration is private

import_common

For a Windows dynamic library, this configuration might include

  • header files

  • .idl files

  • Visual Studio property sheets (for setting up include/link paths)

import_x64_Release
import_x64_Debug
import_Win32_Release
import_Win32_Debug

.lib files

runtime_x64_Release
runtime_x64_Debug
runtime_Win32_Release
runtime_Win32_Debug

.dll files, and any .exe or other "plugin-style" executable files

debugging_x64_Release
debugging_x64_Debug
debugging_Win32_Release
debugging_Win32_Debug

.pdb files

EXE

This type is for executables built with the typical Visual Studio Platform values of x64 and Win32, and Configuration values of Debug and Release. There are also types called

  • EXE_RELEASE, which does not include any of the names with Debug in them;

  • EXE_64, which does not include any of the names with Win32 in them;

  • EXE_64_RELEASE, which does not include any of the names with Win32 or Debug in them.

These types are also suitable for executables which are used like libraries, in that your module needs some kind of header to use them and/or they are not run independently. For example, COM singletons are build as EXEs but can be used by libraries, which need ".idl" files to access them. The configurations are intended to contain the following kinds of files when your module is published, as controlled by the packageArtifacts block.

Name Contains

build

nothing, because this configuration is private

import_common

nothing

import_x64_Release
import_x64_Debug
import_Win32_Release
import_Win32_Debug

nothing

runtime_x64_Release
runtime_x64_Debug
runtime_Win32_Release
runtime_Win32_Debug

.exe files

debugging_x64_Release
debugging_x64_Debug
debugging_Win32_Release
debugging_Win32_Debug

.pdb files

Default Web Types

There is currently only one configuration set type intended for web applications. The naming pattern defines the Stage and Configuration axes, and omits the Platform axis; the Testing and Subcomponent axes can be added by a specific configuration set.

  • WEB_LIB: web application components, for use as part of a larger application, containing JavaScript, HTML, CSS and related files.

This configuration set type defines configuration name parts, "extends from" relationships, and contents as follows.

Name Extends Contains

build

nothing

nothing, because this configuration is private

import_common

nothing

Configuration-independent files which other modules need to build against this one.

import_Release
import_Debug

import_common

Configuration-specific files which other modules need to build against this one.

runtime_Release
runtime_Debug

nothing

Configuration-specific files which other modules need at runtime to use this one.

debugging_Release
debugging_Debug

nothing

Configuration-specific files which other modules need to debug against this one.

WEB_LIB

This type is for web application components, for use as part of a larger application, containing JavaScript, HTML, CSS and related files.

Name Contains

build

nothing, because this configuration is private

import_common

Build scripts or other files which other modules need to build against this one.

import_Release
import_Debug

Configuration-specific build scripts or files which other modules need to build against this one.

runtime_common

HTML and/or similar content which does not differ between Release (minified) and Debug (non-minified).

runtime_Release
runtime_Debug

JavaScript, CSS, XSLT, and/or similar content which differs between Release (minified) and Debug (non-minified).

debugging_common

Debugging support files which do not differ between Release (minified) and Debug (non-minified).

debugging_Release
debugging_Debug

Source maps and/or other debugging support files which differ between Release (minified) and Debug (non-minified).

Configuration Mapping Rules

These configuration set types can be passed to the configurationSet method of sourceDependencies (configurationSet) or packedDependencies (configurationSet) and the Holy Gradle will automatically create mappings between configurations, according to the following rules.

  • The runtime_* and debugging_* configurations in a dependency are always mapped from the corresponding configurations in this module. That is, runtime_Win32_Debug->runtime_Win32_Debug, runtime_Win32_Release->runtime_Win32_Release, and so on. This is true even for static libraries because they can have runtime dependencies on DLLs.

  • The import_common configuration does not need to be mapped between modules because other modules are only expected to use the import_* configurations (import_Win32_Debug and so on), and those configurations will include everything in the other module’s import_common, because each import_* configuration extends import_common.

  • The import_* configurations in a dependency may be

    • mapped from the corresponding public configurations in this module (import_x64_Release->import_x64_Release and so on) if

      • either the interfaces of that module should be exposed from this module;

      • or both this module and the dependency are static libraries, because in that case another module which links against this one will also have to link against the dependency;

    • mapped from the private build configurations in this module (build->import_x64_Release and so on) if the interfaces of that module should be encapsulated within this module;

    • not mapped at all, if the dependency is an EXE-style module, because they do not have build-time interfaces.

The rule for the import_* configurations can also be represented by the following table. The From/To columns are the configuration set type for the source and destination of the mapping. The export: column indicates what value for that option (default false) is passed to the configurationSet method of sourceDependencies or packedDependencies.

From To export: Map to import_*?

LIB

LIB

false

from same

true

from same

DLL

false

from build

true

from same

EXE

n/a

no

DLL

LIB

false

from build

true

from same

DLL

false

from build

true

from same

EXE

n/a

no

EXE

LIB

n/a

no

DLL

n/a

no

EXE

n/a

no

The following diagram shows the configurations for two modules a and b of type DLL, where a is exposing the headers (in import_common) of b; plus a simple packaging of Doxygen (using a single configuration) as an example build tool. The blue lines would be missing if a did not export the imports of b.

Figure 1: Configuration Mapping

User-defined Configuration Set Types

It is possible to define your own configuration set types, though the ways of doing this are currently not documented. Please look at the source for the interfaces ConfigurationSetType and ConfigurationSet, and the classes which implement them.

configurationSets

This DSL block is used in combination with the configurationSetTypes block to provide a convenient way to define several related configurations in your project, and their mapping to configurations in other modules, following common patterns. For background information about configurations and configuration mapping, see the Gradle Module Concepts overview and the Configurations section of the Workflow With Gradle and Artifactory page

You will commonly use this block in your build.gradle file although it is not required: you can define individual configurations yourself, but the configuration mapping in source and packed dependency definitions may be more verbose. You can also define both configuration sets and individual configurations.

This is a container for configuration set objects. The first level below configurationSets defines the names of the individual configuration sets. The name of the set is only used to refer to this set elsewhere in the build script; it does not automatically appear in the configuration names. For example:

configurationSets {
    main {
        type configurationSetTypes.DLL_64
    }
    test {
        prefix "test"
        type configurationSetTypes.EXE_64
    }
}

The above example is equivalent to a configurations block like the following.

configuration {
    // "main" configuration set
    build { visible = false }
    import_common
    import_x64_Release.extendsFrom import_common
    import_x64_Debug.extendsFrom import_common
    // ...
    runtime_x64_Release
    // ...
    debugging_x64_Release
    // ...

    // "test" configuration set
    test_build { visible = false }
    test_import_common
    test_import_x64_Release.extendsFrom import_common
    test_import_x64_Debug.extendsFrom import_common
    // ...
    test_runtime_x64_Release
    // ...
    test_debugging_x64_Release
    // ...
}

type

This method takes one parameter, the configuration set type for this configuration set. For the pre-defined types, the names of the configurations in the set will be those from the type, with the prefix added at the beginning.

→ You must call this method exactly once.

prefix

This method takes one parameter, a string prefix to be added to all names from the type. If this method is not called for a configuration set, the names from its type will be used directly. If you have more than one configuration set of the same or similar types in your build script, you will probably have to use a prefix on all but one of them to avoid configuration name overlaps.

→ Call this method at most once.

Configuration Set Methods

Configuration sets themselves have methods which may be useful in your build script.

type

This property returns the configuration set type for this set.

axes

This property returns a map with

  • keys which are the names of each axis used in the configuration set type; and

  • values which are the set of values for that axis defined by the type.

configurationNames

This property returns a map with

  • keys which are maps of name-value pairs for each axis used in the configuration set type; and

  • values which are the names of the corresponding configuration defined by this set.

getConfigurations

This method takes a Gradle Project object, creates the Configuration objects for the names defined by this set (if they do not already exist), and returns a map with

  • keys which are maps of name-value pairs for each axis used in the configuration set type; and

  • values which are the corresponding Configuration objects in the given Project.

dependenciesSettings

This object has settings which apply to all dependencies.

dependenciesSettings {
    defaultFailOnVersionConflict = false // defaults to true
}

defaultFailOnVersionConflict

If this flag is true, Intrepid will call configuration.resolutionStrategy.failOnVersionConflict() for each configuration which is created. This is in contrast to the default Gradle behaviour, which allows conflicts and resolves them according to configurable strategies. To turn off this default behaviour, set the property to false. This behaviour is implemented in this way because there is no Gradle mechanism to revert to allowing resolution of version conflicts after failOnVersionConflict() has been called.

This value can be set in each project in a multi-project build. If it’s not set in a sub-project, that project will use the value from the root project; failing that, it defaults to true.

sourceDependencies

This is a container for source-dependency objects. The first level below sourceDependencies defines the names of the individual source code dependencies e.g.

sourceDependencies {
    foo.hg "http://path/to/hg/repo"
    bar {
        svn "http://path/to/svn/repo"
    }
}

declares that there are two source-code dependencies and they should be retrieved to the directories <workspace>/foo and <workspace>/bar respectively.

Relative Paths and Subprojects

Source dependencies and packed dependencies are specified as relative paths rather than being immediate subdirectories of the workspace. See Relative paths.

Note that Gradle’s concept of subprojects applies to source dependencies, but not in the way you might expect. Gradle projects can have subprojects, which are subfolders of a source repository containing a build.gradle, selected using include 'path/to/subproject' in the root project’s settings.gradle. A project can discover and operate on its subprojects in its build.gradle, and the build.gradle for a project is (normally) evaluated before the build.gradle of its subprojects. If the settings.gradle includes 'foo' and 'foo/bar' then foo will be a subproject of the root project, and bar will be a subproject of foo. However, any source dependency in any Holy Gradle project is added as a direct subproject of the root project, ignoring where it appears in the folder hierarchy of the root project. This is because the Holy Gradle allows source dependency to exist on disk at a location outside the folder of the project which depends on it; also, it does not require the root project to know about all transitive source dependencies.

The Gradle concept of project/module dependencies does apply to source dependencies, as described under configuration.

Credentials

By default, intrepid can only fetch source from repositories which do not require authentication. If the project also has the my-credentials-plugin applied, it will use passwords cached in Windows Credential Manager to access Mercurial and Subversion repositories. If cloning, checking out, or exporting a repository for which no password is already cached, the plugin will initialise the cached username and password for that repository based on a value stored under "Intrepid - type" for some string "type".

Before 7.10.0 this type was always the default username and password managed by my-credentials-plugin, "Domain Credentials". Since 7.10.0 you can override the credential used as a basis for the source repository with the plugin-intrepid.html#_credentialBasis property.

using the value stored under "Intrepid - Domain Credentials" (the default username and password managed by my-credentials-plugin).

hg

This method takes one parameter - the URL of the Mercurial repository to clone.

You can optionally specify a particular Mercurial revision after an @ character e.g.

foo.hg "http://path/to/foo@384039b6ec8b44cec59dc48b264056882f60b7a7"

This will result in a clone that has been updated to the specific Mercurial revision.

After the source dependency has been retrieved you may make local modifications or update to a different revision and rerunning fetchAllDependencies will have no effect on your local copy.

→ Call this method at most once.

→ You must call one of hg, git, or svn.

Note
If the Mercurial repository has an https: URL, some setup may be required. See Mercurial HTTPS Access.

git

This method takes one parameter - the URL of the Git repository to clone.

You can optionally specify a particular Git revision after an @ character e.g.

foo.git "http://path/to/foo.git@384039b6ec8b44cec59dc48b264056882f60b7a7"

This will result in a clone that has been updated to the specific Git revision.

After the source dependency has been retrieved you may make local modifications or update to a different revision and rerunning fetchAllDependencies will have no effect on your local copy.

→ Call this method at most once.

→ You must call one of hg, git, or svn.

svn

This method takes one parameter - the URL of the Subversion repository to checkout from.

You can optionally specify a particular SVN revision after an @ character e.g.

bar.svn "http://path/to/bar@143"

This will result in a checkout of the repository, pinned to a specific version.

After the source dependency has been retrieved you may make local modifications or update to a different revision and rerunning fetchAllDependencies will have no effect on your local copy.

→ Call this method at most once.

→ You must call one of hg, git, or svn.

credentialBasis

This is an optional property that defines which "Intrepid - type" credential will be used for the source repository. Using another credential type as a basis means that the user only has to enter their username and password once, even if the same credentials are used for multiple source repositories. The intrepid plugin will read the username and password from the "Intrepid - type" credential in the Windows Credential Manager and cache it for the relevant source control software.

Usage:

sourceDependencies {
    foo {
        svn "http://shared-server.example-corp.com/path/to/foo"
        // Uses "Intrepid - Domain Credentials" by default
    }
    bar {
        hg "http://my-server.my-corp.com/path/to/bar"
        credentialBasis = "MyCorp" // Uses "Intrepid - MyCorp"
    }
    baz {
        git "http://other-server.other-corp.com/path/to/baz"
        credentialBasis = "OtherCorp" // Uses "Intrepid - OtherCorp"
    }
}

branch

This is an optional property that defines which named branch of the repository to use. This is only valid for Mercurial and Git repositories, and the default value is null which equates to retrieving the default branch. This is equivalent to hg clone --branch special — http://path/to/foo foo or git clone --branch special — http://path/to/foo.git foo.

Usage:

sourceDependencies {
    foo {
        hg "http://path/to/foo"
        branch = "special"
    }
}

configuration

This method defines the configuration mapping for the dependency - see the <dependency … conf="…"/> attribute in the example module descriptor above. Mappings are defined from configurations in this module to configurations in the dependency module. This creates a Gradle project dependency between the two projects; see the documentation for DependencyHandler. This dependency means that mappings will be included in the module descriptor when this module is published. The "to" configurations also determine which packaged artifacts from the dependency module will be fetched and unpacked.

More details on extending configurations, and mapping sets of related configurations between modules, can be found in the Configurations section of the link:workflows.html page.

You can pass any number of string parameters to this method, or call the method multiple times.

You can also not call this method, in which case the source will be checked out but its Gradle project configurations will not be linked to the configurations of this module and, when this module is published, it will have no dependency on any module which may be defined in the checked-out repository. This can be useful if, for example,

  • you want to join multiple source repositories into one published module;

  • your main source is in a Mercurial repo but you also have large binary files in a Subversion repo, such as Word documents.

Here are some examples of string parameters passed to the configuration method and the resulting mappings:

"my-config"
  • configuration mapping from "my-config" to "my-config"

"foo->bar"
  • configuration mapping from "foo" to "bar"

"foo->bar,far"
  • configuration mapping from "foo" to "bar"

  • configuration mapping from "foo" to "far"

"aa,bb->cc"
  • configuration mapping from "aa" to "cc"

  • configuration mapping from "bb" to "cc"

"aa,bb->cc,dd"
  • configuration mapping from "aa" to "cc"

  • configuration mapping from "aa" to "dd"

  • configuration mapping from "bb" to "cc"

  • configuration mapping from "bb" to "dd"

→ Call this method and/or configurationSet at least once, usually.

configurationSet

This method creates multiple configuration mappings from this module to a source dependency module, based on the names defined by configuration sets and configuration set types, and any specific mapping rules implemented by the types.

The method takes two main arguments:

  • the source, which can be a ConfigurationSet or a Gradle Configuration in this module; and

  • the target, which can be a ConfigurationSetType or a ConfigurationSet which matches the dependency module.

If the source is a ConfigurationSet, then the optional "export:" boolean argument can be passed, which affects the mapping of import_* configurations (see Configuration Mapping Rules).

The Holy Gradle does not check that the type for the target is correct. You may be able to get documentation for a dependency, or work out which mapping to use by looking at its package contents or the ivy.xml. From version 7.7.2 the description for each configuration in a configuration set contains a hint about how to use it.

→ Call this method and/or configuration at least once, usually.

Following are some examples of parameters passed to the configurationSet method and the resulting mappings. They assume the following configuration sets in both this module and the target.

configurationSets {
    main {
        type configurationSetTypes.DLL_64
    }
    extra {
        prefix "extra"
        type configurationSetTypes.DLL_64
    }
}
ConfigurationSet to ConfigurationSetType

This is the most common usage, where this module has one or more configuration sets, and the target configuration set in the dependency module does not have a prefix.

sourceDependencies {
    foo {
        hg "http://path/to/foo"
        configurationSet configurationSets.main, configurationSetTypes.DLL_64
    }
}

This generates configuration mappings as follow.

  • build->import_x64_Release

  • build->import_x64_Debug

  • runtime_x64_Release->runtime_x64_Release

  • runtime_x64_Debug->runtime_x64_Debug

  • debugging_x64_Release->debugging_x64_Release

  • debugging_x64_Debug->debugging_x64_Debug

If the export flag is set:

        configurationSet configurationSets.main, configurationSetTypes.DLL_64, export: true

then the import mappings change as follows:

  • import_x64_Release->import_x64_Release

  • import_x64_Debug->import_x64_Debug

ConfigurationSet to ConfigurationSet

This version is used when the target configuration set has a prefix.

sourceDependencies {
    foo {
        hg "http://path/to/foo"
        configurationSet configurationSets.main, configurationSetTypes.DLL_64.makeSet { prefix "extra" }
    }
}

This generates configuration mappings as follow.

  • build->extra_import_x64_Release

  • build->extra_import_x64_Debug

  • runtime_x64_Release->extra_runtime_x64_Release

  • runtime_x64_Debug->extra_runtime_x64_Debug

  • debugging_x64_Release->extra_debugging_x64_Release

  • debugging_x64_Debug->extra_debugging_x64_Debug

If the export flag is set:

        configurationSet configurationSets.main, configurationSetTypes.DLL_64.makeSet { prefix "extra" }, export: true

then the import mappings change as follows:

  • import_x64_Release->extra_import_x64_Release

  • import_x64_Debug->extra_import_x64_Debug

Configuration to ConfigurationSetType

This version can be used when you want to map to several configurations of a target module from one configuration of this module. A common use for this would be to add a dependency on some build helper tool, from this module’s build configuration.

sourceDependencies {
    foo {
        hg "http://path/to/foo"
        configurationSet configurations.build, configurationSetTypes.DLL_64_RELEASE
    }
}

This generates configuration mappings as follow.

  • build->import_x64_Release

  • build->import_x64_Debug

  • build->runtime_x64_Release

  • build->runtime_x64_Debug

  • build->debugging_x64_Release

  • build->debugging_x64_Debug

When the source is a Configuration rather than a ConfigurationSet, the export flag is not applicable.

Configuration to ConfigurationSetType

This version can be used when you want to map to several configurations of a target module from one configuration of this module, and those configurations have a prefix.

sourceDependencies {
    foo {
        hg "http://path/to/foo"
        configurationSet configurations.build, configurationSetTypes.DLL_64_RELEASE.makeSet { prefix "extra" }
    }
}

This generates configuration mappings as follow.

  • build->extra_import_x64_Release

  • build->extra_import_x64_Debug

  • build->extra_runtime_x64_Release

  • build->extra_runtime_x64_Debug

  • build->extra_debugging_x64_Release

  • build->extra_debugging_x64_Debug

Again, the the export flag is not applicable because the source is a Configuration rather than a ConfigurationSet.

Warning
For source dependencies, you might try to reference a configuration set object from the dependency module’s Gradle project as the target for a mapping, rather than creating one with ConfigurationSetType#makeSet. However, it is difficult to make this work in all cases; for example, if running gw fetchAllDependencies from a clean checkout, the source for the dependency project may not exist yet.

publishing

Warning
This block is deprecated as of version 7.7.0, and will be removed in a future version of the Holy Gradle.

This is like a struct which groups together a few different configuration options relating to how the source-code dependency will be referenced (in the Ivy module descriptor XML) when the consumer module is published. The consumer module can be published as depending on the published output of a source-code dependency.

If the publishing configuration is omitted then the source-code dependency will not be mentioned in the module descriptor.

This is an example of a fully-specified publishing element:

sourceDependencies {
    dependency_module {
        svn "http://path/to/svn/repo"
        publishing {
            group "com.company.team"
            configuration "import_x64_Debug->compileVc10Debug", "import_x64_Release->compileVc10Release"
            version "1.0.+"
        }
    }
}
Note
In order for dynamic version numbers to be resolved you will need to configure the repositories element.
group

This method configures the group name for the dependency - see the dependency->org attribute in the above module descriptor.

If this method is not called then the group will default to the group specified in publishPackages.

→ Call this method at most once.

configuration

This method is the same as the configuration method of a source dependency block.

version

This method defines the version number that will be used in the dependency elements in the module descriptor.

You can specify an exact version number (e.g. 1.2.3.4) or a dynamic version number (e.g. 1.2.+). The dynamic version number will be resolved to a concrete version number based on what artifacts exist in the target repository at the time that the module is published.

By default, if you do not specify a version, then the version used for publishing the module will also be used for the version number in the dependency element in the module descriptors for source dependencies.

→ Call this method at most once.

usePublishedVersion

This method was removed in version 7.7.0. The behaviour it was intended to provide — switching between binary and source versions of the same module — will be provided by a better mechanism in future.

sourceDependencyTasks

This container allows you to define commands to be invoked on all sourceDependencies.

For example:

sourceDependencyTasks {
    theTask {
        invoke("command", "could", "fail").failAtEnd()
        invoke("fails", "but", "dont", "care").ignoreFailures()
        invoke("failure", "is", "important").failImmediately()
        invoke("failure", "is", "important") // behaves as failImmediately
    }
}

The above script will define a task theTask to be invoked on the current project AND all of the source dependencies that it depends on. This means that you can define a set of useful commands in your root build script and make use of them in all subprojects.

Each invocation can be configured to behave appropriately with respect to the return codes.

  • failImmediately() - terminate as soon as possible when any invocation’s exit code is non-zero. This is the default behaviour.

  • failAtEnd() - after all projects have made the invocation, check for non-zero exit codes and summarise. If there were any failures then terminate here.

  • ignoreFailures() - ignore the exit codes.

Warning
If your source dependency command does something which modifies the Gradle scripts (for example, svn update or hg update) then the new versions of the Gradle scripts will not be applied until the next time you invoke Gradle. So for example gw hgUpdate buildRelease would do the Hg update but the build task would run according to the Gradle scripts prior to the update. So it would be better to run these two tasks separately: gw hgUpdate followed by gw buildRelease.

+ If such a task fails after only some source dependencies are updated, you may be unable to run Gradle if the build.gradle files across all the projects are no longer consistent. In that case you must update or otherwise modify each repository until the Gradle scripts reach a consistent state.

sourceControl

This DSL gives you read-only access to various properties of the source control repository for the current project e.g. protocol (hg or svn), url, revision, and whether there are any local modifications.

protocol

This read-only property returns a string "hg" or "svn" depending on whether this project directory is an hg clone or an svn checkout. If it is neither then "n/a" will be returned.

url

This read-only property returns the url for the source control repository/checkout in the project directory. Null is returned if the project directory is not under source control.

revision

This read-only property returns the revision for the source control repository/checkout in the project directory. Null is returned if the project directory is not under source control.

hasLocalChanges

This method returns a boolean indicating whether the source control repository/checkout in the project directory has any uncommitted modifications. False is returned if the project directory is not under source control.

packedDependencies

This is a container for packed dependency objects. The first level below packedDependencies defines the names of the individual dependencies e.g.

packedDependencies {
    "RenderingLib" {
        dependency "com.example-corp.rendering:RenderingLib:2012a2"
        configurationSet configurationSets.main, configurationSetTypes.LIB_64
    }
    "NUnit <version>" {
        dependency "org.nunit:NUnit:2.5.+"
        def testRuntimeConfs = configurationSets.test.configurationNamesMap.findAll { k, v ->
            k[stage] == 'runtime'
        }
        configuration "${testRuntimeConfs.join(',')}->bin"
        unpackToCache = false
    }
}

declares two dependencies. The value in quotes is used as the path where the dependency should appear in the workspace.

  • RenderingLib packages belonging to the configurations defined by the LIB_64 configuration type should be unpacked to the central cache, and a link should be created at <workspace>/RenderingLib. When this module is published, the RenderingLib dependency will be included in the module descriptor.

  • NUnit packages belonging to the configuration bin should be unpacked directly to the workspace in folder called <workspace>/NUnit 2.5.10 (assuming that 2.5.10 is the most recent version with the prefix "2.5").

Dependency Locations

Source dependencies and packed dependencies are now specified as relative paths rather than always being immediate subdirectories of the workspace. See Relative Paths and Subprojects.

Since 7.12.0, all packed dependencies which share a configuration must appear under the same parent folder, or an exception is thrown. Another simpler way to think of this is that every configuration must have exactly one folder where all its dependencies appear. Multiple configurations can share the same folder, and indeed usually will do when they are part of a configuration set.

Without this restriction, it would be possible for different versions of the same transitive dependency (from different packed dependencies) to target the same folder on disk. With this restriction, that can’t happen, because core Gradle ensures that there can be only one version of any dependency per configuration.

For example, the following declarations are okay, because the relative paths are the same wherever the configurations are the same.

packedDependencies {
    "dep/RenderingLib" {
        dependency "com.example-corp.rendering:RenderingLib:2012a2"
        configurationSet configurationSets.main, configurationSetTypes.LIB_64
    }
    "dep/OtherLib" {
        dependency "com.example-corp.teamA:OtherLib:2.5.2"
        configurationSet configurationSets.main, configurationSetTypes.LIB_64
    }
    "dep_test/TestLib" {
        dependency "com.example:TestLib:4.6"
        configurationSet "test", configurationSetTypes.LIB_64
    }
}

However, the following declarations will fail, because the "build" configuration is part of the main configuration set, so all three dependencies share the "build" configuration, but TestLib is declared to appear under a different parent.

packedDependencies {
    "dep/RenderingLib" {
        dependency "com.example-corp.rendering:RenderingLib:2012a2"
        configurationSet configurationSets.main, configurationSetTypes.LIB_64
    }
    "dep/OtherLib" {
        dependency "com.example-corp.teamA:OtherLib:2.5.2"
        configurationSet configurationSets.main, configurationSetTypes.LIB_64
    }
    "dep_test/TestLib" {
        dependency "com.example:TestLib:4.6"
        configurationSet "build", configurationSetTypes.LIB_64
    }
}
Transitive Dependency Locations

By default, when a packed dependency has further transitive dependencies, the unpacked directories or links for those will appear next to the packed dependency. If a transitive dependency is also specified as a direct dependency, and the path specified for it as a direct dependency is different from this default path, then the transitive dependency will also appear at the specified other path. For example, suppose you have the following script.

packedDependencies {
    "libA" {
        dependency "com.example-corp.common:libA:1.0"
        configurationSet configurationSets.main, configurationSetTypes.LIB_64
    }
    "subdir/libB" {
        dependency "com.example-corp.common:libB:1.0"
        configurationSet configurationSets.main, configurationSetTypes.LIB_64
    }
}

If libA (with path rootDir/libB ) depends on libB then the default path for libB would be rootDir/libB ; but instead path rootDir/subdir/libB will be used.

Similarly, a transitive dependency can appear in multiple locations if is a dependency of two or more packed dependencies which are configured to appear in different locations.

Before 7.12.0, each transitive dependency would have only one link or unpack location, even if it should have appeared at multiple locations. The Holy Gradle did not guarantee which location would be chosen.

Using <version>

You can specify <version> anywhere within the name of a packed dependency e.g. "my_component-<version> blah". Once the version number has been resolved, the <version> will be replaced with the actual version of the packed dependency that has been retrieved.

The name of the packed dependency affects:

  • the path to which the dependency will be unpacked within your workspace (if unpackToCache is false)

  • the name of the link in your workspace (if unpackToCache is true, which is the default)

For the example above, the workspace would look like this:

<workspace>
|   build.gradle
|   ...
+---my-rendering-lib   (link ---> <unpack cache>/com.company.rendering/RenderingLib/2012a2)
|   ...
+---NUnit 2.5.10
    ...

dependency

This method configures the coordinates of the dependency. The single parameter must follow the pattern "<groupId>:<artifactId>:<version>" where:

  • groupId is an identifier (such as the name of the organisation) to disambiguate the artifactId. The groupId can contain full-stops e.g. org.common.util.

  • artifactId is an identifier for the artifact.

  • version is a version number.

Warning
Floating version numbers (e.g. 1.2.+) are supported. (See how dependency resolution works.) However, use of floating version numbers is not recommended because they are ordered lexicographically rather than numerically: that means 1.10 comes before 1.2, for example.

→ Call this method exactly once.

configuration

This method works the same as configuration for sourceDependencies. You will normally call this method or configurationSet at least once for a packed dependency, otherwise no packed artifacts will be fetched and unzipped.

→ Call this method and/or configurationSet at least once, usually.

configurationSet

This method works the same as configurationSet for sourceDependencies. You will normally call this method or configuration at least once for a packed dependency, otherwise no packed artifacts will be fetched and unzipped.

→ Call this method and/or configuration at least once, usually.

Private dependencies

Sometimes you may want to use the Holy Gradle to fetch build tools — for example, Doxygen to document C++ code. This is different from when your module depends on another library module, because usually another developer will not need these build tools to use your module. In cases like these, you can map the dependency from a private configuration. This means the configuration is marked as visible="false" in the ivy.xml, which means some tools will ignore it in default cases. You can do this when you specify a configuration in your module:

configurations {
    build { visible = false } // private
    other // public
}

Also, with the Holy Gradle, any configurations with a name beginning with "private" will be marked private.

configurations {
    privateBuild // private
    other // public
}

unpackToCache

This property defines whether the dependency should be unpacked to the central cache for unpacked dependencies, or unpacked directly to the workspace.

→ Default value: true

noCreateLinkToCache()

Calling this method on a packed dependency causes the rebuildLinks and deleteLinks task to ignore it. This allows you to create your own folder or link in the appropriate location, to replace a packed dependency with, for example, a locally-built version.

It is expected that developers will normally only use this method temporarily in their local check-out, and not commit it to shared version-controlled source. If this method is called on a packed dependency, then attempting to publish the containing project will throw an exception, because Gradle cannot know the real version of the dependency.

Note that, before Holy Gradle 7.8.0, this method was called noCreateSymlinkToCache.

readonly

This property determines whether the unpacked dependency directory (and all subdirectories and files) should be set to read-only. This can help to prevent the unpacked dependency files being unintentionally modified.

Note: this will only be done if applyUpToDateChecks is false.

→ Default value: true

applyUpToDateChecks

This property determine whether fetchAllDependencies should perform full up-to-date checks to compare the dependency zip files with the target directory. This will take much longer to execute, but avoids any possibility that the unpacked dependencies have been modified.

→ Default value: false

packedDependenciesSettings

This DSL block contains settings which control how packed dependencies are unpacked.

Unpack cache

The default location for the unpacked dependency cache is <Gradle User Home dir>/unpackCache. You can change the location of your Gradle User Home directory. If you want more specific control then that can be achieved with the following piece of DSL.

packedDependenciesSettings {
    unpackedDependenciesCacheDir = "d:/dep_cache"
}

If this value is set in the root project, it will affect all sub-projects unless they override it.

Since this specifies a machine-specific location you should probably avoid including the path in a build script that will be shared with other developers. You may wish to use a user.gradle file or gradle.properties file to configure the path.

If you set this to point to a network share, the Holy Gradle will use a symlink. If your user account has local Administrator rights, then this requires the Gradle process to run with Administrator privileges on Windows 8 and above. Putting your cache on a share is not recommended, for performance reasons.

Published relative paths for transitive dependencies

Older versions of the Holy Gradle allowed published modules to specify the relative location where each transitive packed dependency should appear. This design caused various problems, as follows.

  • If a packed dependency is used in multiple places in the dependency graph, it would require extra effort to create a link at each location, and waste space if unpackToCache = false is used.

  • If relative paths points to child folders of dependencies, arbitrarily long paths may be created, causing problems with Windows' maximum path length of 260 characters.

  • If relative paths points to child folders of dependencies, and the same dependencies are used in separate projects, and at least one of those projects forces a dependency to resolve to a different version, there is no way to create a correct set of links (in the default case where unpackToCache = true).

In practice, all uses had a relative path of "../module_name", so this is now the default. Since 7.12.0 the previous behaviour has been removed. Attempting to use the following DSL will now cause an exception.

packedDependenciesSettings {
    useRelativePathFromIvyXml = true
}

sourceOverrides

Warning
This feature is "incubating": the syntax and behaviour may change in future releases.

This container allows you to override the handling of specific packed dependencies to instead point to existing source on a local disk. This is intended for testing changes to dependencies without having to first package and publish those dependencies. The replaced source does not have to use the Holy Gradle to build, or use Gradle at all, provided the correct XML files can be provided, as described below.

sourceOverrides {
    "framework" {
        dependency "com.company:framework:1.1"
        from "../source/framework-1.1"
    }
    "ext_lib" {
        dependency "com.company.rendering:RenderingLib:2012a2"
        from "C:/projects/renderingLib"
    }
}

packedDependencies {
    "framework" {
        dependency "com.company:framework:1.1"
        configuration "privateBuild->compileVc10Debug,compileVc10Release"
    }
}

This declares one dependency framework and instead of using the packed binaries, will create links to ../source/framework-1.1. In addition, any transitive dependencies on RenderingLib will be linked to C:/projects/renderingLib instead of the packed binaries.

Warning
Use of source overrides will make the plugin-intrepid.html#_fetchAllDependencies task slower, because it is also necessary to fetch and check the dependencies from the override source projects. You can temporarily skip this step by passing -PuseCachedSourceOverrideFiles to gw.bat. However, if you do that, the Holy Gradle cannot warn you about inconsistent dependency versions between the containing project and the override projects. This may result in unexpected errors at build time or runtime.

Limitations and restrictions

There are several limitations restrictions in using source overrides.

  • The source override mechanism allows for the override source to be built with a different version of the Holy Gradle, or built with some tool other than Gradle. That can be useful but has some constraints.

    • Source overrides in the root project apply to all sub-projects, if it is a multi-project build, but they do not apply to any projects within the override source locations. This is because the build tool for the override source may not provide a standard way to override direct and transitive dependencies. If you want the same overrides to apply within an override source project, you must add source overrides within that project if it is a Holy Gradle project, or use some other mechanism if it uses a different build tool.

    • Fetching dependencies for the root project will also attempt to fetch dependencies in override source projects, so that an up-to-date list of dependencies can be checked against the root project and other override projects. However, other tasks in the root project are not linked into the replacement projects, because there may be no standard way to do that. Therefore, if you make changes in the override projects, you must rebuild them independently before you build the root project. It would be possible to add custom tasks to your root project’s build.gradle to do that, but the details of how to do that are beyond this documentation.

    • The containing project will expect the published files of the source override project to be in the same location, relative to the link in the containing project’s workspace, as they would be if the override project had been published, with its files zipped, uploaded, downloaded, and unzipped. If this is not the case, then you should modify the override source project so that the location of its packaged files is the same before packaing as when unzipped. If you cannot do that, you will need to add some extra build steps, in the containing project or the override source project, to copy or link those files into the expected location.

  • A project with source overrides defined can not be published because it depends on versions of some other modules which are unpublished. Therefore it would be impossible to produce a valid ivy.xml file for the project itself.

  • In a multi-project build, the sourceOverrides block can only be used in the root project, and the overrides apply to all projects. (This restriction is in place because otherwise it is difficult or impossible to calculate the complete override structure.)

  • Source overrides apply to all configurations in a project. If a packed dependency is used by two or more unconnected configurations, it is not possible to override it to source in some of those configurations and leave it as a packed dependency in others.

  • Dependencies that use unpackToCache = false can not be overridden. This is because their files are copied into the project workspace directly, instead of being linked in, so it is not possible to change a link to point to the override source.

Transitive Dependencies

The direct and transitive dependencies of a source override may differ from those of the binary package it is replacing. In that case, the Holy Gradle will make sure that dependency versions are consistent between the root project and the override source — just as Gradle checks consistency for binary dependencies. This check also applies if there are multiple dependencies with source overrides, or if the override source project itself contains source overrides.

To do this check, the sourceOverride requires a valid Ivy file with the list of immediate dependencies at <sourceOverride>/build/publications/ivy/ivy.xml and an XML file at <sourceOverride>/all-dependencies.xml containing a full list of immediate and transitive dependencies for the project in the format created by the summariseAllDependencies task, as follows.

<?xml version="1.0" encoding="UTF-8"?>
<Configurations>
  <Configuration name="compileVc10Debug">
    <Dependency name="framework" group="com.company" version="1.0" configuration="compileVc10Debug"/>
    <Dependency name="external-lib" group="com.company" version="1.0" configuration="compileVc10Debug"/>
  </Configuration>
  <Configuration name="compileVc10Release">
    <Dependency name="framework" group="com.company" version="1.0" configuration="compileVc10Release"/>
    <Dependency name="external-lib" group="com.company" version="1.0" configuration="compileVc10Debug"/>
  </Configuration>
  <Configuration name="source"/>
</Configurations>
Note
The ivy.xml file is needed to "publish" a dummy module (with only one, empty ZIP artifact) to a private local "file://" repository, so that the Holy Gradle can force core Gradle to fetch that instead of the real module. The "all dependencies" file is needed to check the versions of all transitive dependencies of the override project against all dependencies of the containing project. It might seem that it should be possible to just add the direct dependencies of the override project to the containing project, then let Gradle find the transitive dependencies as normal. However, that might give a different result because the override project source might use different repositories to find modules, or have custom dependency resolution rules. Therefore the override project must generate these files.

To make sure these files are up-to-date, when trying to resolve dependencies the plugin will try several methods in turn until one succeeds, as follows.

# If the sourceOverride declares an plugin-intrepid.html#_ivyFileGenerator closure, call that to generate the two files and return the paths to them. # If the batch file <sourceOverride>/generateSourceOverrideDetails.bat exists, run that to generate the two files. # Run "<sourceOverride>/gw.bat -PrecordAbsolutePaths generateIvyModuleDescriptor summariseAllDependencies".

dependency

This method specifies the coordinates of the dependency to override. It should follow the same pattern as the packedDependency definition. Transitive dependencies of this and any source dependencies will also be overridden but transitive dependencies of a source override will not. This will be reported as a version conflict at dependency resolution time. If you need to override the transitive dependencies of a source override, then you must use sourceOverrides in that source override project as well.

from

This method defines a path, either absolute or relative to the root project, where the override source is located.

ivyFileGenerator

This method defines a closure to run to generate the dependency files for this source override. It should return a list of two File objects, one for the direct dependency Ivy file and the other for the transitive dependency XML file. The SourceOverrideHandler is passed into the closure giving access to (among other things) the project object.

This is useful in cases where the source does not use a compatible version of the Holy Gradle, or is not a Holy Gradle project, or not a Gradle project at all. It is also useful if want to override some dependencies which are built as part of a multi-project build; the following example will generate the dependency files for a sub-project of "framework".

sourceOverrides {
    "subproject" {
        dependency "com.company:subproject:1.1"
        from "C:/projects/framework/subproject"
        ivyFileGenerator { /*SourceOverrideHandler*/ handler ->
            project.exec {
                workingDir handler.from
                executable "gw.bat"
                args "-PrecordAbsolutePaths",
                    "subproject:summariseAllDependencies", "subproject:generateIvyModuleDescriptor"
            }
            return [
                new File("${handler.from}/subproject", "build/publications/ivy/ivy.xml"),
                new File("${handler.from}/subproject", "all-dependencies.xml")
            ]
        }
    }
}

Note that the SourceOverrideHandler is private to the Holy Gradle so you cannot actually reference that type in your build script. However, since Groovy is a dynamic language, you can still call methods from SourceOverrideHandler on that object.

The next example just returns static files; for example, these might be created by hand.

sourceOverrides {
    "framework" {
        dependency "com.company:framework:1.1"
        from "C:/projects/framework/"
        ivyFileGenerator { /*SourceOverrideHandler*/ handler ->
            return [
                new File(handler.from, "ivy.xml"),
                new File(handler.from, "all-dependencies.xml")
            ]
        }
    }
}

If you override a module which comes from a multi-project build, and that module depends on other modules in that multi-project build, you may want to override several modules from one build. To do that, you can use a pattern like the following.

project.ext.subProjectOverrideProcessedRoots = new HashSet<File>()

project.ext.subProjectOverride = { rootRelativePath ->
    return { /*SourceOverrideHandler*/ handler ->
        File overrideRootDir = new File(handler.from, rootRelativePath)
        // Note: HashSet's add method returns true if the item was NOT already in the set.
        if (subProjectOverrideProcessedRoots.add(overrideRootDir)) {
            project.exec {
                workingDir overrideRootDir
                executable "gw.bat"
                args "-PrecordAbsolutePaths", "generateIvyModuleDescriptor", "summariseAllDependencies"
            }
        }
        return [
            new File(handler.from, "build/publications/ivy/ivy.xml"),
            new File(handler.from, "all-dependencies.xml")
        ]
    }
}

// These definitions assume that the folder with the root "build.gradle" for "framework" is
// "C:/projects/framework" -- that is, ""C:/projects/framework/subprojectA/..".
sourceOverrides {
    "subprojectA" {
        dependency "com.company:subprojectA:1.1"
        from "C:/projects/framework/subprojectA"
        ivyFileGenerator subProjectOverride("..")
    }
    "subprojectB" {
        dependency "com.company:subprojectB:1.1"
        from "C:/projects/framework/subprojectB"
        ivyFileGenerator subProjectOverride("..")
    }
    "subprojectC" {
        dependency "com.company:subprojectC:1.1"
        from "C:/projects/framework/subprojectC"
        ivyFileGenerator subProjectOverride("..")
    }
}

→ Calling this method is optional.

This extension allows you to define directory links for packedDependencies or sourceDependencies. For example, this extract of build-script for a module named consumer_module

links {
    to "dependency_module", "dependency_module/common"
}

will result in links (by default, directory junctions) as follows:

<workspace>
|   build.gradle
|
+---consumer_module
|   |   build.gradle
|   |   common            (link --------|
|   |   dependency_module (link ---|    |
|   +---src                        |    |
|   |   ...                        |    |
|   +---lib                        |    |
|       ...                        |    |
|                                  |    |
+---dependency_module   <----------|    |
    |   build.gradle                    |
    +---src                             |
    |   ...                             |
    +---common  <-----------------------|

Note that, before Holy Gradle 7.8.0, this block was called links.

to

This method takes one or more parameters. Each parameter defines a location in the retrieved source-code dependency folder. The name of the resulting link will always correspond to the last component of the path argument.

→ Call this method any number of times with any number of arguments.

packageArtifacts

This is a container for packages. The first level below packageArtifacts defines the names of the individual packages e.g.

packageArtifacts {
    import_common.include "src/**/*.h"
    configurationSets.main.axes['Configuration'].each { conf ->
        "${conf}Exe" {
            configuration = "runtime_x64_$conf"
            include "bin/$conf/*.exe"
            exclude "**/helper.exe"
        }
        "${conf}Pdb" {
            configuration = "debugging_x64_$conf"
            include "bin/$conf/*.pdb"
        }
    }
    docs {
        from "docs/output"
        to "api"
        include "aaa/a.txt", "bbb/b.txt"
    }
}

defines 6 packages: import_common, docs, DebugExe, DebugPdb, ReleaseExe and ReleasePdb.

Each package: * is a zip-file which will be created at publish-time; * defines, implicitly or explicitly, which configuration it belongs to; * will be referenced in the module descriptor (ivy.xml).

configuration

This property defines the configuration that the package belongs to. For example look at the conf attribute below:

<publications>
    <artifact name="example-import_x64_Debug" type="zip" ext="zip" conf="import_x64_Debug"/>
    ...
</publications>

You do not have to explicitly set the configuration if the name of the package already matches the name of the configuration.

→ Default value: package name

include

This method defines file-path patterns to include in the package, subject to those files not being excluded by the exclude patterns.

You can pass any number of string parameters to this method, or call the method multiple times.

→ Call this method at least once.

exclude

This method defines file-path patterns to exclude from the package. This will take priority over any include patterns.

Some patterns will be excluded by default: .gradle, packages/ivy.xml, packages/*.zip

You can pass any number of string parameters to this method, or call the method multiple times.

→ Call this method any number of times.

from and to

These methods allow you to change the path to files that you include in a package. By default the packaging process behaves as if both from and to had been set to ".".

In the example above the files docs/output/aaa/a.txt and docs/output/bbb/b.txt will be included in the docs package, but their path will be api/aaa/a.txt and api/bbb/b.txt.

You can use multiple from/to combinations in the same package e.g.

packageArtifacts {
    docs {
        from ("component1/docs/output") {
            to "api"
            include "*.chm"
        }
        from ("component2/docs/output") {
            to "api"
            include "*.chm"
        }
    }
}

→ Call from any number of times. Call to at most once in each context.

includeTextFile

This method allows you to dynamically create a text file to be included in the package. e.g.

packageArtifacts {
    preBuiltArtifacts {
        includeTextFile("readme.txt") {
            add "the first line with a ${variable}"
            add "as many lines as you want"
            add """a triple-quoted string
which can span multiple lines
and is a standard feature of the Groovy language"""
        }
    }
}
add

This method adds a line of text to the text file.

→ Call this method any number of times.

includeBuildScript

This extension allows you to dynamically create a build script to be included in the package. There is support for taking the sourceDependencies already defined and converting them to pinned source dependencies or to packedDependencies. This facilitates the creation of source and binary meta-packages for releases.

packageArtifacts {
    pinnedSource {
        include "gradle/**", "gw.bat", "gradle.properties"
        includeBuildScript {
            add "// This text goes at the top."
            addPinnedSourceDependency "*"
            add "// This text goes at the bottom."
        }
    }
    preBuiltArtifacts {
        include "gradle/**", "gw.bat"
        includeBuildScript {
            addIvyRepository "http://path/to/repo"
            addPackedDependency "foo", "fromConf1->toConf1", "fromConf2->toConf2"
        }
    }
}
createDefaultSettingsFile

If this property is true, and includeBuildScript is called, but includeSettingsFile is not, then the plugin will add a default settings.gradle file to the package. If the property is false then, in that case, the package will have no settings file. That may lead to unexpected behaviour when the package is used, if it is unzipped in a location where some ancestor folder has a settings file. See Gradle documentation for the Settings file, Initialization and the -u / --no-search-upwards command line option.

This property is true by default.

add

This method allows you to include arbitrary text in the generated build script. If you call this before any call to addPinnedSourceDependency or addPackedDependency then the text will appear at the top of the build script. Otherwise it will appear at the bottom.

→ Call this method any number of times.

addPinnedSourceDependency

This method allows you to select source dependencies to be included in the generated build script, pinned to the current source-control revision.

This method takes any number of parameters which refer to source dependencies in the current project, or any sub-project in a multi-project workspace. Each parameter is the name of the source dependency to include.

→ Call this method any number of times.

Note: This method used to support a wildcard syntax but no longer does. To add all source dependencies from all projects, you can use the following code inside an includeBuildScript block.

gradle.projectsEvaluated {
    allprojects { sourceDependencies.each { addPinnedSourceDependency it }
}
addPackedDependency

This method allows you to specify dependencies to be included in the generated build script as packedDependencies i.e. to retrieve pre-built artifacts instead of the source code repository.

The first parameter of this method can represent several different things. It can be:

  • the name of the source dependency (without any path) in the current project, or any sub-project in a multi-project workspace.

  • the name of the packed dependency (without any path) in the current project, or any sub-project in a multi-project workspace.

  • a fully qualified dependency coordinate e.g. "org:my-module:1.0.2"

All subsequent parameters are configurations which indicate which parts of the packed dependency should be retrieved. These parameters follow the same format as is used in packedDependencies e.g. "fromConf→toConf"

→ Call this method any number of times.

addIvyRepository

This method allows you to specify an ivy repository url which will be included in the build script like this:

repositories.ivy {
    credentials {
        username my.username()
        password my.password()
    }
    url <the url>
}

This is useful if you call addPackedDependency because in order to retrieve the published artifacts you will need to specify the repository to retrieve them from.

You can also pass a second parameter to this method, which gets used as follows: my.username(<param>) and my.password(<param>).

→ Call this method any number of times.

generateSettingsFileForSubprojects

This property allows you to define whether a settings file should be generated by the build script, listing all the source dependencies added by addPinnedSourceDependency. If the settings file is generated, then the build.gradle files from those source dependencies will be included in the multi-project build of the generated build script. If not, they will just be present as source code but ignored by the Gradle build.

→ Default value: true

publishPackages

This structure contains a few elements for configuring the publish settings for the module. For example:

publishPackages {
    group "com.company.team"
    nextVersionNumberAutoIncrementFile "next_version.txt"
    repositories.ivy {
        credentials {
            username "user"
            password "password"
        }
        url "http://artifactory-server/artifactory/my-repo/"
    }
}

defines that when this module is published then:

  • the group name will be "com.company.team";

  • the version number will be read from the text file next_version.txt;

  • the repository to publish to and the necessary credentials are as stated.

The name of the module is by default taken from the folder which contains the build.gradle file. Since version 7.3.0, you can override this for the root project in settings.gradle like this:

rootProject.name = "my_module"

You may want the group value to identify your entire organisation, or you may want to add sub-levels for separate departments or teams. The latter makes it easier to tell who is responsible for a published module, and allows you to set up permissions in a repository server to allow only the relevant team to publish modules with that group value to shared repositories.

When published, the module descriptor would look something like this:

<?xml version="1.0" encoding="UTF-8"?>
<ivy-module version="2.0">
  <info organisation="com.company.team" module="example" revision="1.2.3.4" status="release" publication="20121115134228"/>
  <configurations>...</configurations>
  <publications>...</publications>
  <dependencies>...</dependencies>
</ivy-module>
Warning
The group and version methods in this block are not yet deprecated but are likely to be removed in a future version of the Holy Gradle. Instead, you should just set the group and version at the top level of the file, near the beginning, as shown in the example build script at the top of this page. That example shows how to set the version from an environment variable, like nextVersionNumberEnvironmentVariable; the other methods nextVersionNumber and nextVersionNumberAutoIncrementFile have not been used in practice.

group

Warning
This method is not yet deprecated but is likely to be removed in future—see above.

This method defines the group name for the published artifacts. It will determine part of the URL to the artifacts once they are published, and also determines what goes in the info->organisation attribute in the module descriptor above. The group is often based on the name of the organisation, and helps to disambiguate modules with the same name from different organisations.

Calling this method has the same effect as setting the group property on the Project.

→ Call this method exactly once.

nextVersionNumber

Warning
This method is not yet deprecated but is likely to be removed in future—see above.

This method defines the version number for the published artifacts. This will determine part of the URL to the artifacts once they are published, and also determines what goes in the info->revision attribute in the module descriptor above.

There are two variant of this method: one taking a single string parameter, and another taking a closure which should return a string value.

Calling this method has the same effect as setting the version property on the Project.

→ Call one of nextVersionNumber, nextVersionNumberAutoIncrementFile and nextVersionNumberEnvironmentVariable

nextVersionNumberAutoIncrementFile

Warning
This method is not yet deprecated but is likely to be removed in future—see above.

This method has the same effect as nextVersionNumber with a few important differences:

  • the single string parameter defines a relative path to a file containing the next version number

  • once the module has been successfully published the last component of the version number in the file will automatically be incremented

→ Call one of nextVersionNumber, nextVersionNumberAutoIncrementFile and nextVersionNumberEnvironmentVariable

nextVersionNumberEnvironmentVariable

Warning
This method is not yet deprecated but is likely to be removed in future—see above.

This method has the same effect as nextVersionNumber except that the single string parameter defines the name of an environment variable which contains the version number. This may be useful if, for example, your autobuild already has a notion of version number and is invoking the publish task.

→ Call one of nextVersionNumber, nextVersionNumberAutoIncrementFile and nextVersionNumberEnvironmentVariable

repositories

This method has the same type as the repositories method on the Project. However, only publishing to Ivy repositories is supported. This is because Ivy supports publishing multiple artifacts, which is not currently supported by the Maven publishing functionality provided by Gradle.

→ Minimal usage:

repositories.ivy {
    url "http://artifactory-server/artifactory/my-repo/"
    credentials {
        username "user"
        password "password"
    }
}

If your repository supports anonymous publishing (which is not recommended) then you could omit the credentials section. Note that it is recommended that you use the my-credentials-plugin rather than hard-coding credentials.

Tasks

See using the Gradle command line for general information on invoking tasks.

fetchAllDependencies

This task retrieves all packedDependencies and sourceDependencies and sets up the necessary links. Links are always deleted and rebuilt.

If a source-code dependency is retrieved then the settings-subprojects.txt file (used by the Holy Gradle’s generated settings.gradle) will be updated to include the new module. Afterwards, the plugin will attempt to automatically relaunch the task in order to allow the new module to be taken into account and for further dependencies to be resolved. The gradle process is terminated with a special exit code and as a result a slightly customised Gradle Wrapper re-launches the gradle process. This slightly hacky approach has a few implications.

  • Wake sure that any Gradle Wrapper you create comes from the custom-gradle-core plugin’s createWrapper task. This version has special code to re-run Gradle when a new source dependency is fetched, in case that source dependency has its own build.gradle.

  • It is advisable to run the fetchAllDependencies by itself, without any other tasks on the same invocation.

  • Usage of the daemon is automatically disabled by the customised Gradle Wrapper.

Transitive dependencies

This task also fetches transitive dependencies. For example, assume a module’s build script declares a packed dependency on foo:

packedDependencies {
    foo {
        dependency "com.example-corp.teamA:foo:1.2.3.4"
        configurationSet configurationSets.main, configurationSetTypes.DLL_64
    }
}

and the module descriptor for foo declares a transitive dependency on bar as follows:

<ivy-module version="2.0">
  <info organisation="com.example-corp.teamA" module="foo" revision="1.2.3.4" status="release" publication="20121115134228"/>
  <configurations>
    ...
    <conf name="runtime_x64_Debug" visibility="public" />
    <conf name="runtime_x64_Release" visibility="public" />
  </configurations>
  <publications>...</publications>
  <dependencies>
    <dependency org="com.company.team" name="bar" rev="9.8.7" conf="runtime_x64_Debug->runtime_x64_Debug;runtime_x64_Runtime->runtime_x64_Runtime"/>
  </dependencies>
</ivy-module>

and the module descriptor for bar declares artifacts as follows:

<ivy-module version="2.0">
  <info organisation="com.company.team" module="bar" revision="9.8.7" status="release" publication="20121115134228"/>
  <configurations>
    ...
    <conf name="runtime_x64_Debug" visibility="public" />
    <conf name="runtime_x64_Release" visibility="public" />
    <conf name="debugging_x64_Debug" visibility="public" />
    <conf name="debugging_x64_Release" visibility="public" />
  </configurations>
  <publications>
    ...
    <artifact name="bar-runtime_x64_Debug" type="zip" ext="zip" conf="runtime_x64_Debug"/>
    <artifact name="bar-runtime_x64_Release" type="zip" ext="zip" conf="runtime_x64_Release"/>
    <artifact name="bar-debugging_x64_Debug" type="zip" ext="zip" conf="debugging_x64_Debug"/>
    <artifact name="bar-debugging_x64_Release" type="zip" ext="zip" conf="debugging_x64_Release"/>
  </publications>
  <dependencies>...</dependencies>
</ivy-module>

In this example the top-level module is dependent on the configurations runtime_x64_Debug and runtime_x64_Runtime of foo, which are then dependent on the same named configurations of bar. Although bar defines four artifacts, only bar-runtime_x64_Debug and bar-runtime_x64_Release belong to the configurations of interest, so these will be fetched. Finally, since both of these artifacts have the file-extension "zip" (see the artifact->ext attribute in the module descriptor for bar) the intrepid plugin assumes that they should be unpacked to your unpacked cache, and creates a link from your workspace.

fetchFirstLevelSourceDependencies

This is like fetchAllDependencies except that it is not recursive and only fetches source dependencies. This task will:

  • retrieve all first-level (non-transitive) source-code module dependencies

  • create/modify the settings.gradle file to refer to the fetched source-code modules

Along with collectDependencies, this task can be useful in pre-fetching dependencies so that you can package them up and give them to a customer who doesn’t have access to your network.

collectDependencies

This task will navigate through all of the project’s dependencies. This includes packedDependencies and their transitive dependencies, Gradle plugins and all of their dependencies. A folder named local_artifacts will be created. Ivy and Maven style dependencies will be placed in their respective subfolders. Source dependencies will be summarised in a "build_info" folder but the source itself will not be included. Finally, the custom-gradle Gradle distribution used by the Holy Gradle's Gradle wrapper will be placed in its own subfolder.

The result is a directory which contains all packed dependencies. This is useful for pre-fetching dependencies so that you can package them up and give them to a customer who doesn’t have access to your network.

TO DO
Link to description of how local_artifacts is used.

This task is a subclass of the usual Gradle Copy task, so the destinationDir and other aspects can be customised as normal.

zipDependencies

This task collects the same files as collectDependencies but puts them in a ZIP file in the project root. By This task is a subclass of the usual Gradle Zip task, so the baseName and other aspects can be customised as normal.

This task performs a subset of the functionality of the fetchAllDependencies task - it simply rebuilds all the links for all dependencies.

Note that, before Holy Gradle 7.8.0, this task was called rebuildSymlinks.

This task deletes any links created by rebuildLinks or fetchAllDependencies. This does not delete all links under the project folder; only links which would be created by the other tasks are deleted.

Note that, before Holy Gradle 7.8.0, this task was called deleteSymlinks.

packageEverything

This task will create all zip-file packages that you have defined in the packageArtifacts section. You do not have to invoke this task prior to publishing because the publish task already depednds on this task. However you might wish to invoke this task in order to inspect the resulting zip files prior to their being published. The zips will be placed in a packages subdirectory of the module.

If you are testing the packaging, you may also wish to run the standard Gradle task, generateIvyModuleDescriptor. This will create the ivy.xml for your module in ${project.buildDir}/publications/ivy/ivy.xml.

publish

This task publishes the artifacts of your module:

  • creates all zip file packages

  • prepares an Ivy module descriptor

  • uploads the packages to the specified repository

  • increments the version number in the text file (if using nextVersionNumberAutoIncrementFile)

You may notice a task called publishIvyPublicationToIvyRepository or similar. This exists because the intrepid plugin uses the ivy-publish plugin.

summariseAllDependencies

Warning
This feature is "incubating": the syntax and behaviour may change in future releases.

This task resolves all dependencies and saves them as a flat list grouped by configuration to all-dependencies.xml in the project directory. For example.

<?xml version="1.0" encoding="UTF-8"?>
<Configurations>
    <Configuration name="compileVc10Debug">
        <Dependency name="framework" group="com.company" version="1.0" configuration="compileVc10Debug"/>
        <Dependency name="external-lib" group="com.company" version="1.0" configuration="compileVc10Debug" sourcePath="C:/projects/external-lib"/>
    </Configuration>
    <Configuration name="compileVc10Release">
        <Dependency name="framework" group="com.company" version="1.0" configuration="compileVc10Release"/>
        <Dependency name="external-lib" group="com.company" version="1.0" configuration="compileVc10Debug" sourcePath="C:/projects/external-lib"/>
    </Configuration>
    <Configuration name="source"/>
</Configurations>

Relative paths

Both sourceDependencies and packedDependencies support project-relative paths and workspace-relative paths. This means that you can nest projects inside each other and put projects completely outside the workspace root, on a relative path. For example, in the example below the root Gradle project is called Root and it is able to pull in source and/or packed dependencies to form the following directory hierarchy. The contents of the square brackets shows the dependencies from one module to others. If the dependency starts with a slash then it’s relative to the Root workspace, otherwise it’s relative to the module which is defining the dependency.

C:
+---Projects
    +---PJ42
        +---Root                    source  [MyApp, MyLib]
        |   +---Boost               packed
        |   +---MyLib               source  [../../Stuff/UtilityLib, SubLib, /Boost]
        |   |   +---SubLib          source  [/Boost]
        |   +---MyApp               source  [../../Stuff/Framework, ../MyLib]
        +---Stuff
            +---Framework           packed  [../UtilityLib, ../RenderingLib]
            +---UtilityLib          packed  [../RenderingLib]
            +---RenderingLib        packed

Transitive dependencies of packedDependencies will appear next to those dependencies. In the above example, none of the source projects directly depends on RenderingLib. However, it will still be unpacked and appear in the Stuff folder, next to UtilityLib, because UtilityLib depends on RenderingLib.

Warning
Previous versions of the Holy Gradle allowed published packed dependencies to specify that their transitive dependencies should appear at some other relative location. This feature is deprecated and will be removed in future. See Published relative paths for transitive dependencies.

Ivy files

Warning
This feature is deprecated and will be removed in future. See Published relative paths for transitive dependencies.

Whenever you publish a module which specifies source dependencies or packed dependencies the Ivy XML will have the relative path baked in. So, for example, if you published MyLib you could examine the XML and notice the relative path ../../Stuff/ specified for the UtilityLib dependency, and if you published SubLib you would see the relative path ../ for the Boost dependency.

Relative paths chain together as you would expect. For example Root depends on MyLib, which depends on SubLib and all the relative paths are empty. This means that the directories are nested. After MyLib and SubLib are published and subsequently fetched on someone’s machine, the directory structure is still nested, even when these modules are unpacked to the central cache. This approach does mean that it would be rather difficult to allow versions of transitive dependencies to be overridden. Therefore overriding transitive dependencies using resolutionStrategy.force isn’t currently supported — it may work, if your relative paths don’t result in nesting, but it’s not guaranteed.