57

I have a Gradle project I'm using the maven-publisher plugin to install my android library to maven local and a maven repo.

That works, but the generated pom.xml does not include any dependency information. Is there a workaround to include that information, or am I forced to go back to the maven plugin and do all the manual configuration that requires?


Researching I realized that I'm not telling the publication where the dependencies are, I'm only specifying the output/artifact, so I need a way to link this MavenPublication to the dependencies, but I have not yet found how to do that in the documentation.

------------------------------------------------------------
Gradle 1.10
------------------------------------------------------------

Build time:   2013-12-17 09:28:15 UTC
Build number: none
Revision:     36ced393628875ff15575fa03d16c1349ffe8bb6

Groovy:       1.8.6
Ant:          Apache Ant(TM) version 1.9.2 compiled on July 8 2013
Ivy:          2.2.0
JVM:          1.7.0_60 (Oracle Corporation 24.60-b09)
OS:           Mac OS X 10.9.2 x86_64

Relevant build.gradle sections

//...
apply plugin: 'android-library'
apply plugin: 'robolectric'
apply plugin: 'maven-publish'

//...
repositories {
     mavenLocal()
     maven  {
         name "myNexus"
         url myNexusUrl
     }
     mavenCentral()
}

//...
android.libraryVariants
publishing {
    publications {
        sdk(MavenPublication) {
            artifactId 'my-android-sdk'
            artifact "${project.buildDir}/outputs/aar/${project.name}-${project.version}.aar"
        }
    }
    repositories {
        maven  {
            name "myNexus"
            url myNexusUrl
            credentials {
                username myNexusUsername
                password myNexusPassword
            }
        }
    }
}

Generated pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.example.android</groupId>
  <artifactId>my-android-sdk</artifactId>
  <version>gradle-SNAPSHOT</version>
  <packaging>aar</packaging>
</project>
C. Ross
  • 31,137
  • 42
  • 147
  • 238

7 Answers7

87

I was able to work around this by having the script add the dependencies to the pom directly using pom.withXml.

//The publication doesn't know about our dependencies, so we have to manually add them to the pom
pom.withXml {
    def dependenciesNode = asNode().appendNode('dependencies')

    //Iterate over the compile dependencies (we don't want the test ones), adding a <dependency> node for each
    configurations.compile.allDependencies.each {
        def dependencyNode = dependenciesNode.appendNode('dependency')
        dependencyNode.appendNode('groupId', it.group)
        dependencyNode.appendNode('artifactId', it.name)
        dependencyNode.appendNode('version', it.version)
    }
}

This works for my project, it may have unforeseen consequences in others.

C. Ross
  • 31,137
  • 42
  • 147
  • 238
  • 6
    +1 fantastic. Added this inside my `*(MavenPublication)` block and all my dependencies are now in my aar's pom. Many thx – Dori May 11 '15 at 15:11
  • I just tried this and it caused an exception for duplicate 'dependencies' tags when parsing the generated tree. It seems the dependencies were being generated all along but still aren't being added to the published file for some reason. It sounds like I (and possibly others) could be suffering from the same problem but with a different root cause. – Alex Jansen Jun 16 '15 at 06:16
  • 8
    I needed to add a check for `it.group`, `it.name` not being null or unspecified so that it skips the default dependency on lib directory in android studio. Without it gradle generates an invalid POM file and crashes. – Adwiv Jun 25 '15 at 08:52
  • Do you have dependencies on another android modules? For me it seems they are not added to – marekdef Sep 07 '15 at 08:48
  • Better to check if (it instanceof ExternalModuleDependency) before adding it to the pom file (similar to what @adwiv suggested but seems cleaner to me) – Nati Dykstein Jun 22 '16 at 16:11
  • 3
    Suppose that the aar has other aar dependencies. Then, how to publish them? I tried the same way. The app is built, but getting crash (NoClassDefFoundError). After I included those inner dependencies in the app, it is running without any crash. Kindly let me know if there is any other proper way to do it. – gopalanrc Aug 29 '16 at 10:51
  • @c. Ross Can you provide one file that uses this snippet, when I am trying to use this getting a build error asNode() not recognised :( – Nayan Srivastava Nov 04 '16 at 07:00
  • 8
    You can replace "compile" with "implementation" if you’ve switched to the 4.1 gradle version/3.0.1 cradle plugin. – drdaanger Feb 12 '18 at 17:33
  • 3
    "compile" doesn't work for me today. What does work is "releaseCompileClasspath". E.g. to iterate over the dependencies use "configurations.releaseCompileClasspath.allDependencies.each" – Sofi Software LLC Aug 07 '18 at 18:37
  • @gopalanrc according to this [blog post](https://jeroenmols.com/blog/2015/08/13/artifactory2/) "Note that while you can similarly add other repositories in this way to the pom.xml, Gradle itself won’t look for repositories in that file. Therefore if you use libraries from 3rd party repositories, you still need to add those repositories to the build.gradle of your app." – Panos Gr Aug 19 '19 at 09:24
  • 1
    Nice solution! How would it work for dependencies like the following? `implementation project ":myModule"` In my case, the version is marked as unspecified in the resulting pom.xml. Do you know of any mechanism for getting the version number in these situations? – narko Sep 24 '19 at 15:04
  • Sorry @narko I haven't touched Gradle in two years, and I don't recall that type. – C. Ross Sep 30 '19 at 00:33
  • Still relevant today. The plugin should get an update to handle cases like this more naturally. – Steve Benett Nov 19 '19 at 12:24
  • You need to add tag too. – JonatasTeixeira Jun 10 '20 at 21:34
30

I'm upgraded C.Ross solution. This example will generate pom.xml with dependecies from compile configuration and also with special build type dependecies, for example if you use different dependencies for release or debug version (debugCompile and releaseCompile). And also it adding exlusions

publishing {
    publications {
        // Create different publications for every build types (debug and release)
        android.buildTypes.all { variant ->
            // Dynamically creating publications name
            "${variant.name}Aar"(MavenPublication) {

                def manifest = new XmlSlurper().parse(project.android.sourceSets.main.manifest.srcFile);
                def libVersion = manifest['@android:versionName'].text()
                def artifactName = project.getName()

                // Artifact properties
                groupId GROUP_ID
                version = libVersion
                artifactId variant.name == 'debug' ? artifactName + '-dev' : artifactName

                // Tell maven to prepare the generated "*.aar" file for publishing
                artifact("$buildDir/outputs/aar/${project.getName()}-${variant.name}.aar")

                pom.withXml {
                    //Creating additional node for dependencies
                    def dependenciesNode = asNode().appendNode('dependencies')

                    //Defining configuration names from which dependencies will be taken (debugCompile or releaseCompile and compile)
                    def configurationNames = ["${variant.name}Compile", 'compile']

                    configurationNames.each { configurationName ->
                        configurations[configurationName].allDependencies.each {
                            if (it.group != null && it.name != null) {
                                def dependencyNode = dependenciesNode.appendNode('dependency')
                                dependencyNode.appendNode('groupId', it.group)
                                dependencyNode.appendNode('artifactId', it.name)
                                dependencyNode.appendNode('version', it.version)

                                //If there are any exclusions in dependency
                                if (it.excludeRules.size() > 0) {
                                    def exclusionsNode = dependencyNode.appendNode('exclusions')
                                    it.excludeRules.each { rule ->
                                        def exclusionNode = exclusionsNode.appendNode('exclusion')
                                        exclusionNode.appendNode('groupId', rule.group)
                                        exclusionNode.appendNode('artifactId', rule.module)
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
cooperok
  • 4,249
  • 3
  • 30
  • 48
  • Thanks for the snippet! I suspect I'll have to manage the variants myself (will update this thread if I have something working) – Kikiwa Apr 08 '16 at 10:47
  • Comparing it to the previous pom that was created for the `uploadArchives` task it appears to be missing the `scope` attribute. Is there a way to easily include that to your code above? – amadib May 03 '16 at 22:57
  • **scope** in pom.xml should be the same as configurationName. There is line `def configurationNames = ["${variant.name}Compile", 'compile']` debugCompile and releaseCompile shoulb be skipped, but configuration name compile is that you need in scope attribute. To make pom with dependensies of all scopes you need to add all of them (runtime, etc.) in that array. And in loop just check if it's not debugCompile or releaseCompile append node scope. `exclusionNode.appendNode('scope', configurationName)` – cooperok May 04 '16 at 07:06
9

With gradle 3 implemention was introduced. Replace compile with implementation. Use this instead.

pom.withXml {
    def dependenciesNode = asNode().appendNode('dependencies')
    configurations.implementation.allDependencies.each {
        def dependencyNode = dependenciesNode.appendNode('dependency')
        dependencyNode.appendNode('groupId', it.group)
        dependencyNode.appendNode('artifactId', it.name)
        dependencyNode.appendNode('version', it.version)
    }
}
Pedro Romão
  • 2,285
  • 28
  • 22
5

Kotlin DSL version of the accepted answer:

    create<MavenPublication>("maven") {
        groupId = "com.example"
        artifactId = "sdk"
        version = Versions.sdkVersionName
        artifact("$buildDir/outputs/aar/Example-release.aar")
        pom.withXml {
            val dependenciesNode = asNode().appendNode("dependencies")
            val configurationNames = arrayOf("implementation", "api")
            configurationNames.forEach { configurationName ->
                configurations[configurationName].allDependencies.forEach {
                    if (it.group != null) {
                        val dependencyNode = dependenciesNode.appendNode("dependency")
                        dependencyNode.appendNode("groupId", it.group)
                        dependencyNode.appendNode("artifactId", it.name)
                        dependencyNode.appendNode("version", it.version)
                    }
                }
            }
        }
    }
Meanman
  • 1,474
  • 20
  • 17
3

I guess it has something to do with the from components.java directive, as seen in the guide. I had a similar setup and it made the difference to add the line into the publication block:

publications {
    mavenJar(MavenPublication) {
        artifactId 'rest-security'
        artifact jar
        from components.java
    }
}
Peter and the Wolf
  • 1,321
  • 1
  • 9
  • 14
  • 2
    this works when using `.jar` output but not for `aar` afaik. Apparently its broken for aar at present (gradle plugin 1.2.2) – Dori May 11 '15 at 14:59
  • @Dori ran into same problem.. no dependencies for `aar` and a need to remove md5/sha files because of overwritten `pom` – kagali-san Jun 04 '15 at 09:32
  • Directive still missing in version 1.5.0 of the Android Plugin for Gradle. If you add it to your Android project's `build.gradle` file, you'll see this error message when trying to parse the file: `"Could not find property 'java' on SoftwareComponentInternal set."` – Adil Hussain Mar 08 '16 at 13:57
  • version `2.1.0` also doesn't work for `aar`s. Is there a work around? – amadib May 03 '16 at 23:23
  • This saved my life! – Marcello DeSales Oct 31 '19 at 22:38
  • If multiple jars are included, then you must declare the `classifier` on each of them to avoid the error https://github.com/gradle/gradle/issues/1624#issuecomment-330821182. I declared the classifier for the main jar as `default` and I got both the sources and the main jar published! – Marcello DeSales Nov 02 '19 at 18:16
0

I was using the maven-publish plugin for publishing my aar dependency and actually I could not use the maven task in my case. So I used the mavenJava task provided by the maven-publish plugin and used that as follows.

apply plugin 'maven-publish'

publications {
    mavenAar(MavenPublication) {
        from components.android
    }

    mavenJava(MavenPublication) {
        pom.withXml {
            def dependenciesNode = asNode().appendNode('dependencies')
            // Iterate over the api dependencies (we don't want the test ones), adding a <dependency> node for each
            configurations.api.allDependencies.each {
                def dependencyNode = dependenciesNode.appendNode('dependency')
                dependencyNode.appendNode('groupId', it.group)
                dependencyNode.appendNode('artifactId', it.name)
                dependencyNode.appendNode('version', it.version)
            }
        }
    }
}

I hope that it helps someone who is looking for help on how to publish the aar along with pom file using the maven-publish plugin.

Reaz Murshed
  • 23,691
  • 13
  • 78
  • 98
0

now that compile is deprecated we have to use implementation.

pom.withXml {
def dependenciesNode = asNode().appendNode('dependencies')
    configurations.implementation.allDependencies.each {
    def dependencyNode = dependenciesNode.appendNode('dependency')
    dependencyNode.appendNode('groupId', it.group)
    dependencyNode.appendNode('artifactId', it.name)
    dependencyNode.appendNode('version', it.version)
}
vikoo
  • 2,451
  • 20
  • 27