Publishing of a modular library to Maven using Gradle
Asked Answered
M

1

16

Suppose I'm using Gradle for a modular library development. In my root project I have subprojects geometry, algorithms, visualizer, and I'd like to publish a jar artifact of each.

As for now in my root build.gradle I have the following part:

apply plugin: 'maven-publish'
publishing {
    publications {
        publishDemos(MavenPublication) {
            groupId 'ru.ifmo.ctddev.igushkin.cg'
            artifactId 'geometry'
            version globalVersion
            artifact project(':geometry').tasks.getByName('jar')
        }
        publishAlgorithms(MavenPublication) {
            groupId 'ru.ifmo.ctddev.igushkin.cg'
            artifactId 'algorithms'
            version globalVersion
            artifact project(':algorithms').tasks.getByName('jar')
        }
        publishVisualizer(MavenPublication) {
            groupId 'ru.ifmo.ctddev.igushkin.cg'
            artifactId 'visualizer'
            version globalVersion
            artifact project(':visualizer').tasks.getByName('jar')
        }
    }
}

My first question: is there a shorter way of describing the publications? For example, I'd like to state that for each subproject I need a publication with the artifactId set from its name.


Next, my subprojects depend on each other, both algorithms and visualizer depend on classes from geometry, but at this point the jars do not contain the dependencies, and, for example, the user will have to add dependencies to both geometry and algorithms if they want to use algorithms.

So, is there a way for to provide some sort of auto-dependency, so that adding algorithms would also add geometry? If yes, how do I do it? If no, what is the idiomatic way of providing modular libraries? Should I assemble jars with dependencies instead?

UPD: Am I right that instead of artifact ... I should just use from project(':...').components.java, because it will pick up both artifacts and dependencies? How do I pick dependencies separately if I use artifact ...?

Mantis answered 2/2, 2016 at 1:27 Comment(0)
T
27

You can do less verbose publication declaration by injecting the same publication config into each subproject. For example for a multi-build project with the structure:

ROOT
│   build.gradle
│   settings.gradle
├───subA
│       build.gradle
│
├───subB
│       build.gradle
│
└───subC
        build.gradle

In your root build.gradle, you can do:

apply plugin:'maven-publish'

subprojects{
    publishing {
        publications {
            "$project.name"(MavenPublication) {
                groupId project.group
                artifactId project.name
                version project.version
                from components.java
            }
        }
    }
}

Each subproject defines its own groupid and version like so:

group = 'org.test.sample.A'
version = '1.0'

The artifactId is picked up from the subproject name. Running gradle publish results in a repo of this structure:

org
└───test
    └───sample
        ├───A
        │   └───subA
        │       └───1.0
        │               subA-1.0.jar
        │               subA-1.0.pom
        ├───B
        │   └───subB
        │       └───1.0
        │               subB-1.0.jar
        │               subB-1.0.pom
        └───C
            └───subC
                └───1.0
                        subC-1.0.jar
                        subC-1.0.pom

Dependencies

This config also automatically takes care of dependencies. For instance if in subproject subA you had:

dependencies{
    compile project(':subB')
}

Since I am using from components.java instead of artifact, the plugin knows to look for dependencies and generates a pom for subA that includes:

  <dependencies>
    <dependency>
      <groupId>org.gradle.sample.B</groupId>
      <artifactId>subB</artifactId>
      <version>1.0</version>
      <scope>runtime</scope>
    </dependency>
  </dependencies>
Trimolecular answered 2/2, 2016 at 2:51 Comment(4)
Absolutely fantastic answer. Thank you :)Lim
This works, but produces tasks like :generatePomFileForFooPublication instead of :foo:generatePomFileForMavenPublication, which I want so that I can do all the publishing with one command. However, if I replace "$project.name" with something like maven, it complains that Maven publication 'maven' cannot include multiple components. Any way to make this work? Thanks!Predacious
@max The answer says ( I wrote the answer, but several years ago, I don't recall the specifics) that running gradle publish should do all the publishing in 1 shot, does that not work for you?Trimolecular
What happens when subprojects have different from clauses? Subproject A needs from components.java but subproject B needs from components.web, which is only available if plugin "war" is applied. How can I do?Mezzosoprano

© 2022 - 2024 — McMap. All rights reserved.