Gradle's Support for Maven POM Profiles

Maven profiles provide the ability to customize build-time metadata under certain conditions, for example if a specific system property is set. A typical use case is applying certain configuration for different runtime environments such as Linux versus Windows. For example, the project being built may require different dependencies on these different platforms.

Implementing build-time profiles in Gradle projects

If you think about it, Maven profiles are logically nothing other than limited if statements. Gradle does not need a special construct for that. Why is this? Gradle utilizes a programming language, not XML, to define the build model. This gives you the full range of expression available in a programming language to define your criteria for certain parts of the configuration. Gradle’s model is richer and more flexible, allowing for a more fine-grained expression of the kinds of things that Maven profiles are typically used for. One very simple and well used pattern is to split up the conditional logic into different script plugins to make the conditional build logic as modular and maintainable as possible. The following code snippet demonstrates such an implementation:

if (project.hasProperty('env') && project.getProperty('env') == 'prod') {
    apply from: 'gradle/production.gradle'
} else {
    apply from: 'gradle/development.gradle'
}

The above approach allows ad-hoc expression of different configuration under whatever circumstances you need. This is analogous to the Maven profile approach, but cleaner and more flexible.

An alternative approach to conditional configuration for expressing variance is to make variance a first class citizen. The Android and C/C++ Gradle plugins take this approach. This is indicative of what the Gradle team believes to be a much better approach for most scenarios: making the variance in the build definition a first-class citizen as opposed to something that is expressed conditionally. This variance-as-first-class-citizen approach is being added to the JVM-based plugins to cater for variance such as JDK compatibility, and deeply embedded into Gradle’s dependency management engine.

Consuming Maven dependencies that rely on profiles

We have discussed the role of Maven profiles in configuring a build and alternative approaches in Gradle. As Gradle supports depending on POM-based artifacts we must also consider how Maven profiles affect dependency resolution.

Some published Maven artifacts, especially in the public Maven Central repository, depend on the information declared in their POM profiles for the dependency resolution process. Such a practice is considered to be an anti-pattern to be avoided as it influences the reproducibility of a build. Nevertheless, it is used in practice and Gradle 1.12 introduces support for some profile usage that affects dependency resolution.

Supported profile criteria in Gradle

There are two activation criteria Gradle considers:

  1. Profiles that are active by default (available with Gradle 1.12 and higher)
  2. Profiles that are active on the absence of a system property (available with Gradle 2.0 and higher)

Let’s have a look at examples that demonstrate both use cases.

Profiles that are active by default

Profiles can be considered active by default. This behavior is controlled by the activation element activeByDefault. Let’s consider the following profile section of a published POM file:

<project>
    ...
    <profiles>
        <profile>
            <id>profile-1</id>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
            <dependencies>
                <dependency>
                    <groupId>org.apache.commons</groupId>
                    <artifactId>commons-lang3</artifactId>
                    <version>3.3.2</version>
                </dependency>
            </dependencies>
        </profile>
    </profiles>
</project>

We can see that the element activeByDefault is set to true. Upon resolution of the corresponding artifact from Gradle, the profile named profile-1 will become active. This profile declares the dependency org.apache.commons:commons-lang3:3.3.2 as a transitive dependency. In turn, Gradle will take care of resolving this dependency as well.

Profiles that are active on the absence of a system property

An alternative to activeByDefault is to trigger the activation of a profile by the absence of a specific system property. Let’s have a look at another POM file that uses this type of profile activation:

<project>
    ...
    <profiles>
        <profile>
            <id>profile-2</id>
            <property>
                <name>!env.type</name>
            </property>
        </profile>
    </profiles>
</project>

This POM file will activate the profile named profile-2 whenever the system property env.type is not defined.

Alternative approaches to profiles for dependency resolution

We have seen how Maven profiles are used to express variance in build configuration and how that affects Gradle dependency resolution when consuming a Maven artifact that uses profiles as part of its runtime definition. We have also seen how the next generation of Gradle plugins (Android, C/C++) take a different approach to variance in making it first class, and that this concept is also coming to Gradle’s support for general JVM languages. This also has deep implications for dependency resolution, in a similar manner to how Maven profiles affect dependency resolution.

One significant benefit to making variance a first-class citizen in the build model, is that this information can be leveraged to provide variant-aware dependency resolution. For example, when building a binary for the x86 architecture with debug symbols, the x86 variants of the declared dependencies should be used and preferably a variant that includes debug symbols if available. This will be enforced automatically for the whole transitive dependency graph. There are countless cases of variance in the building of software and dependency management.

Hans Dockter, the founder of Gradle and CEO of Gradle Inc., recently published the 2014 Gradle Roadmap where you’ll see Variant Aware Dependency Management as a key item currently being worked on. Expect to see new Gradle features roll out over the coming year in this area.