Relationship between build and build{confName}, and publish and publish{confName}?

I’ve got what seems like it should be a fairly straightforward Maven project, except for the fact that the artifacts are Tarballs, not Jar files, and I’m trying to make an uber-tarball out of all of them:

apply plugin: 'maven'
apply plugin: 'maven-publish'
group = 'com.example'
version = '1.0'
repositories {
  mavenLocal()
  maven { url ... }
}
  configurations {
  runtime { transitive = true }
}
  dependencies {
  runtime 'foo.bar:baz:2.3' // a tarball with dependencies that also happen to be tarballs, too
}
  task myTarOfTars(type: Tar) {
  configurations.runtime.each { File f ->
    from tarTree(f)
  }
   compression Compression.GZIP
}
  publishing {
  publications {
    runtime(MavenPublication) {
      artifact(myTarOfTars)
    }
  }
}

I find it odd that neither ‘gradle build’ nor ‘gradle publish’ invoke myTarOfTars…not even ‘gradle buildRuntime’ triggers the task. If I do ‘gradle generatePomFileForRuntimePublication’, it doesn’t build the publication, although it does seem to be pulling information from the ‘myTarOfTars’ task, in that the pom file packaging is ‘tgz’, and if I remove the ‘compression’ setting from myTarOfTars, the pom file packaging changes to ‘tar’.

What is the relationship between build and buildRuntime? And between publish and publishRuntime? Am I supposed to manually do something to connect them, or is this something that I’ve subtly misconfigured?

The ‘build’ task won’t run the tar task very simply because it does not depend on it. The ‘publish’ task additionally doesn’t build your artifact because you have no repositories defined, therefor the ‘publish’ task doesn’t do anything. Your tar task would be run when running ‘publish’ if you declared a repository. Alternatively you could see the same behavior by running ‘publishToMavenLocal’.

The ‘build’ task will build all artifacts of a given configuration. This is completely distinct from a publication, which is what you have defined above. You can assign an artifact to a configuration using the ‘artifacts {…}’ configuration block.

artifacts {

runtime myTarOfTars

}

Running ‘buildRuntime’ would then run your tar task. Running ‘build’ would also do so, since ‘build’ will build artifacts of every configuration.

Excellent!

That’s about 95% of what I was missing. I had been misreading the (generally excellent) documentation; my mistaken impression was that the new-style publishing/publications declarations didn’t use the artifacts declaration. (see feedback, below)

Anyway, the last 5% that is still eluding me is not fully understanding the relationship between build dependencies and publishing dependencies. The pom.xml generated by my ‘runtime’ publication does not include the ‘runtime’ dependencies. (I thought that marking the dependencies as ‘transitive’ would be sufficient to make them appear in the pom.xml, but no such luck.)

I can force the pom to include the dependencies by hacking the xml:

publishing {
  publications {
    runtime(MavenPublication) {
      artifact(myTarOfTars) {
        pom.withXml {
          Node dependenciesNode = asNode().appendNode('dependencies');
          configurations.runtime.dependencies.each { Dependency dependency ->
            Node d = dependenciesNode.appendNode('dependency')
            d.appendNode('groupId', dependency.group)
            d.appendNode('artifactId', dependency.name)
            d.appendNode('version', dependency.version)
            d.appendNode('scope', 'runtime')
          }
        }
      }
    }
  }
}

But it seems like there must be a less clunky way to tell gradle to include the dependencies in the pom…any suggestions?


Feedback for the people working on documentation: I was confused because the ‘artifacts’ declaration doesn’t appear anywhere in any of the examples on the new-style publishing (Chapter 66: https://gradle.org/docs/current/userguide/publishing_maven.html ), only on the old-style page (Chapter 52: https://gradle.org/docs/current/userguide/artifact_management.html). Personally, it might have helped me understand that the ‘artifacts’ declaration was still relevant if it appeared somewhere on the new-style page.

If you are publishing a tar, why would your POM have dependencies in it? How is this artifact going to be used?

I was confused because the artifacts declaration doesn’t appear anywhere in any of the examples on the new-style publishing

That’s because the ‘artifacts’ block isn’t really applicable to the new publishing mechanism. There’s typically no need to specify your artifacts in both places. Like I said above, if you declare your publications correctly, calling ‘publish’ will run the tasks necessary to create the artifacts and publish them. The only reason you would want to define the artifact in an ‘artifacts’ block as well, is if you needed to depend on it from another project as a project dependency.

You’re right that my example is terrible…I was trying to simplify the code snippet by minimizing the number of configurations. My actual build script is for a non-Java environment. It contains multiple configurations and multiple subprojects and looks more like this:

project(':app') {
  configurations {
    compile
    runtime { transitive = true }
  }
  dependencies {
     compile project(':custom-library', configuration: 'headers')
     compile project(':golang')
     runtime project(':custom-library', configuration: 'runtime')
     runtime project(':erlang', configuration: 'runtime')
  }
  task buildApp {
    // uses a polyglot environment with C++, Go, Erlang, and a custom library to compile an app
  }
  publishing {
    publications {
      runtime {
        artifact(buildApp.tarballFile) {
          builtby: buildApp
          // WANTED:
include "runtime" dependencies in pom file under "runtime" scope
        }
      }
    }
    repositories { ... }
  }
}

At runtime, the app won’t run without the dynamically-linked “custom-library” and an erlang runtime. (The golang environment and the custom-library headers only need to be present during compilation.)

Since I’m publishing a ‘runtime’ artifact and the ‘runtime’ configuration is transitive, I expected the pom to include entries for ‘runtime’ dependencies. Does this mean I’ve misconfigured the dependencies/publications? Can I generate a pom file with non-Java dependencies without xml hacking?

(Note: before anyone suggests that since I’m compiling Go, I should use Makefiles or (some other build system), this is just a small slice of the languages in the greater project, which is why we thought that Gradle would be a good language-agnostic tool.)

Can I generate a pom file with non-Java dependencies without xml hacking?

Basically no. Right now only java and web components export dependencies to the publishing mechanism which generates the artifact descriptor. If you want to publish something else (ie. a tar file) with a POM that has dependencies in it, then you’ll have to manually modify the pom.xml.