使用AAR和源码JAR将Android库发布到Maven

104

请问有人能够给我一些提示,如何使用maven-publish Gradle插件发布一个包含AAR和源码jar的com.android.library项目/模块?我已经可以使用旧版maven插件来完成此操作,但我想尝试使用新版maven-publish插件。


请参阅此相似帖子 - Mahozad
7个回答

138
使用Android Gradle Plugin 7.1,现在可以非常简单地完成这个操作,无需任何复杂的脚本。AGP现在还可以处理源代码和javadoc jar的创建。
您不需要任何单独的脚本,只需将所有内容写入您模块的build.gradle文件中即可。
plugins {
    ...
    id 'maven-publish'
}
android {
    ...
    publishing {
        singleVariant("release") {
            // if you don't want sources/javadoc, remove these lines
            withSourcesJar()
            withJavadocJar()
        }
    }
}
afterEvaluate {
    publishing {
        publications {
            release(MavenPublication) {
                from components.release
                groupId 'com.example'
                artifactId 'mylibrary'
                version '1.0.0'
            }
        }
    }
}

查看官方文档:https://developer.android.com/build/publish-library

旧答案

自从Android Studio 3.6发布以来,对于构建AAR(甚至APK和AAB)的支持已经在Android Gradle插件3.6.0(及更高版本)中实现。

我们不再需要自己处理XML依赖和其他事务。

这是我针对Android Studio 3.6.0更新的Gist:https://gist.github.com/Robyer/a6578e60127418b380ca133a1291f017

Gist中的代码:

apply plugin: 'maven-publish'

task androidJavadocs(type: Javadoc) {
    source = android.sourceSets.main.java.srcDirs
    classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
    android.libraryVariants.all { variant ->
        if (variant.name == 'release') {
            owner.classpath += variant.javaCompileProvider.get().classpath
        }
    }
    exclude '**/R.html', '**/R.*.html', '**/index.html'
}

task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) {
    archiveClassifier.set('javadoc')
    from androidJavadocs.destinationDir
}

task androidSourcesJar(type: Jar) {
    archiveClassifier.set('sources')
    from android.sourceSets.main.java.srcDirs
}

// Because the components are created only during the afterEvaluate phase, you must
// configure your publications using the afterEvaluate() lifecycle method.
afterEvaluate {
    publishing {
        publications {
            // Creates a Maven publication called "release".
            release(MavenPublication) {
                // Applies the component for the release build variant.
                from components.release

                // Adds javadocs and sources as separate jars.
                artifact androidJavadocsJar
                artifact androidSourcesJar

                // You can customize attributes of the publication here or in module's build.gradle file (if you save this as script and include it build.gradle file, then you can just replicate this whole block there only with changed fields).
                //groupId = 'com.example'
                //artifactId = 'custom-artifact'
                version = android.defaultConfig.versionName // or just '1.0'
            }
        }
    }
}

原始答案:

这是我根据其他答案改进的解决方案。

要点: https://gist.github.com/Robyer/a6578e60127418b380ca133a1291f017

与其他答案的不同之处:

  • 更改分类器 - 必须是"sources"(而不是"source"
  • 处理依赖关系
    • 还支持@aartransitive: false。在这种情况下,我们在POM中设置排除以忽略此依赖项的所有传递依赖。

    • 还支持对依赖项进行自定义排除规则,例如:

        compile('com.example:something:1.0', {
            exclude group: 'com.exclude.this', module: 'some-module'
        })
      
  • 不需要手动指定构件路径。

apply plugin: 'maven-publish'

task androidJavadocs(type: Javadoc) {
    source = android.sourceSets.main.java.srcDirs
    classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
    android.libraryVariants.all { variant ->
        if (variant.name == 'release') {
            owner.classpath += variant.javaCompile.classpath
        }
    }
    exclude '**/R.html', '**/R.*.html', '**/index.html'
}

task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) {
    classifier = 'javadoc'
    from androidJavadocs.destinationDir
}

task androidSourcesJar(type: Jar) {
    classifier = 'sources'
    from android.sourceSets.main.java.srcDirs
}

project.afterEvaluate {
    publishing {
        publications {
            maven(MavenPublication) {
                //groupId 'cz.example'
                //artifactId 'custom-artifact'
                //version = android.defaultConfig.versionName
    
                artifact bundleReleaseAar
                artifact androidJavadocsJar
                artifact androidSourcesJar
    
                pom.withXml {
                    final dependenciesNode = asNode().appendNode('dependencies')
    
                    ext.addDependency = { Dependency dep, String scope ->
                        if (dep.group == null || dep.version == null || dep.name == null || dep.name == "unspecified")
                            return // ignore invalid dependencies
    
                        final dependencyNode = dependenciesNode.appendNode('dependency')
                        dependencyNode.appendNode('groupId', dep.group)
                        dependencyNode.appendNode('artifactId', dep.name)
                        dependencyNode.appendNode('version', dep.version)
                        dependencyNode.appendNode('scope', scope)
    
                        if (!dep.transitive) {
                            // If this dependency is transitive, we should force exclude all its dependencies them from the POM
                            final exclusionNode = dependencyNode.appendNode('exclusions').appendNode('exclusion')
                            exclusionNode.appendNode('groupId', '*')
                            exclusionNode.appendNode('artifactId', '*')
                        } else if (!dep.properties.excludeRules.empty) {
                            // Otherwise add specified exclude rules
                            final exclusionNode = dependencyNode.appendNode('exclusions').appendNode('exclusion')
                            dep.properties.excludeRules.each { ExcludeRule rule ->
                                exclusionNode.appendNode('groupId', rule.group ?: '*')
                                exclusionNode.appendNode('artifactId', rule.module ?: '*')
                            }
                        }
                    }
    
                    // List all "compile" dependencies (for old Gradle)
                    configurations.compile.getDependencies().each { dep -> addDependency(dep, "compile") }
                    // List all "api" dependencies (for new Gradle) as "compile" dependencies
                    configurations.api.getDependencies().each { dep -> addDependency(dep, "compile") }
                    // List all "implementation" dependencies (for new Gradle) as "runtime" dependencies
                    configurations.implementation.getDependencies().each { dep -> addDependency(dep, "runtime") }
                }
            }
        }
    }
}

1
def dependencyNode = dependenciesNode.appendNode('dependency') throws an exception Could not get unknown property 'dependenciesNode' for object of type org.gradle.api.publish.maven.internal.publication.DefaultMavenPublication. - jcayzac
1
更改分类器 - 必须是“sources”(而不是“source”)对我很有帮助!! - fantouch
3
在新的Gradle中,使用bundleReleaseAar会再次抛出错误,但似乎我们只需将整个publishing块包装在project.afterEvaluate { publishing { ... } }中。然后它就能正常工作了。(我刚刚更新了答案) - Robyer
1
@androiddeveloper androidJavadocs 命令用于创建 Java 文档,androidJavadocsJar 命令则根据文档创建一个 *.jar 文件以便发布。这些命令可能无法在 Kotlin 代码中正常工作,但是有人在我的 Gist 上进行了分支并添加了相关支持。详情请见:https://gist.github.com/Robyer/a6578e60127418b380ca133a1291f017#gistcomment-2965601 - Robyer
1
我更新了答案,适用于新的Android Gradle插件7.1,这极大地简化了此任务。 - Robyer
显示剩余33条评论

109

这是一个使用新的maven-publish插件的示例。

apply plugin: 'maven-publish'

task sourceJar(type: Jar) {
    from android.sourceSets.main.java.srcDirs
    classifier "sources"
}

publishing {
    publications {
        bar(MavenPublication) {
            groupId 'com.foo'
            artifactId 'bar'
            version '0.1'
            artifact(sourceJar)
            artifact("$buildDir/outputs/aar/bar-release.aar")
        }
    }
    repositories {
        maven {
            url "$buildDir/repo"
        }
    }
}

使用./gradlew clean build publish进行发布


41
似乎在生成的pom文件中没有记录依赖项。 - misaka-10032
4
请使用此插件,它可以将依赖项生成到pom文件中:https://github.com/wupdigital/android-maven-publish - bvarga
2
在我将 classifier "source" 替换为 classifier "sources" 后,这个问题得到了解决。 - hqzxzwb
无效的发布“library”:具有相同扩展名和分类器('aar','null')的多个构件。 - mudit_sen
这是一个具有多模块、android.gradle、kotlin-dsl、maven发布和源代码jar的演示项目:https://github.com/danbrough/jitpackdemo - Dan Brough
显示剩余5条评论

34
稍微调整一下dskinner的答案,并生成正确的依赖关系:

在正确生成依赖关系的基础上,对dskinner的答案进行微调:

apply plugin: 'maven-publish'

task sourceJar(type: Jar) {
    from android.sourceSets.main.java.srcDirs
    classifier "source"
}

publishing {
    publications {
        bar(MavenPublication) {
            groupId 'com.foo'
            artifactId 'bar'
            version '0.1'
            artifact(sourceJar)
            artifact("$buildDir/outputs/aar/bar-release.aar")
            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 {
                    if(it.group != null && (it.name != null || "unspecified".equals(it.name)) && it.version != null)
                    {
                        def dependencyNode = dependenciesNode.appendNode('dependency')
                        dependencyNode.appendNode('groupId', it.group)
                        dependencyNode.appendNode('artifactId', it.name)
                        dependencyNode.appendNode('version', it.version)
                    }
                }
            }
        }
    }
    repositories {
        maven {
            url "$buildDir/repo"
        }
    }
}

您可以通过定义以下内容来更改versiongroupId

version = '1.0.0'
group = 'foo.bar'

6
可以使用artifact bundleRelease来代替硬编码aar文件名。另外,条件(it.name != null || "unspecified".equals(it.name))看起来不正确,是想表达(it.name != null && "unspecified" != it.name)吗? - Henry
1
如何在项目模块中包含依赖项? - Anton Holovin

24

如果您希望避免样板代码,因为maven-publish插件不会将依赖项写入pom.xml文件中

请尝试此插件:android-maven-publish

publishing {
    publications {
        mavenAar(MavenPublication) {
            groupId 'com.example'
            artifactId 'mylibrary'
            version '1.0.0'
            from components.android
        }
    }

    repositories {
        maven {
            url "$buildDir/releases"
        }
    }
}

更新:

android-maven-publish插件已被弃用,因为maven-publish已由AGP官方支持。


谢谢你提供的链接bvarga。那个插件是否会在生成的pom文件中添加依赖排除呢? - Adil Hussain
bvarga,我刚试了一下,是的它可以!感谢你让我注意到这个插件。它非常好用! - Adil Hussain
2
这绝对是解决问题的最佳方案。我的看法是应该被接受的。 - F43nd1r
1
@HermanZun 请查看更新后的答案。您可以在任何地方指定存储库。 - Big McLargeHuge
3
android-maven-publish插件已被弃用,因为AGP官方支持maven-publish。请使用AGP 3.6.0或更新版本。 - bvarga
显示剩余3条评论

4

以下是我如何在使用Kotlin DSL(build.gradle.kts)时,将Dokka(在线查看online)和源JAR包包含到我的Android Kotlin库中的方法:

plugins {
    // ...
    id("org.jetbrains.dokka") version "1.4.32"
    id("maven-publish")
}

lateinit var sourcesArtifact: PublishArtifact
lateinit var javadocArtifact: PublishArtifact

tasks {
    val sourcesJar by creating(Jar::class) {
        archiveClassifier.set("sources")
        from(android.sourceSets["main"].java.srcDirs)
    }
    val dokkaHtml by getting(org.jetbrains.dokka.gradle.DokkaTask::class)
    val javadocJar by creating(Jar::class) {
        dependsOn(dokkaHtml)
        archiveClassifier.set("javadoc")
        from(dokkaHtml.outputDirectory)
    }
    artifacts {
        sourcesArtifact = archives(sourcesJar)
        javadocArtifact = archives(javadocJar)
    }
}

publishing {
    // ...
    publications {
        create<MavenPublication>("MyPublication") {
            from(components["release"])
            artifact(sourcesArtifact)
            artifact(javadocArtifact)
            // ...
        }
    }
}

2

使用 Kotlin build.gradle.kts

publishing.publications {
    register<MavenPublication>("aar") {
        groupId = "com.foo"
        artifactId = "bar"
        version = "0.1"

        artifact("$buildDir/outputs/aar/bar-release.aar")

        pom.withXml {
            val dependencies = asNode().appendNode("dependencies")

            val addNode = { groupId: String, artifactId: String, version: String ->
                val dependency = dependencies.appendNode("dependency")
                dependency.appendNode("groupId", groupId)
                dependency.appendNode("artifactId", artifactId)
                dependency.appendNode("version", version)
            }

            addNode("com.example", "dependency-name", "1.0")
        }
    }
}

谢谢你提供清晰的示例。不过,你实际上是如何发布它的呢? - undefined
1
@Glaucus 这超出了原始问题的范围,但我们正在使用jfrog,因此在构建文件中也有artifactory { ... }。这可能会有所帮助:https://jfrog.com/help/r/jfrog-integrations-documentation/configure-the-gradle-artifactory-plugin-version-4 - undefined

-2

您还可以使用Android Maven插件。它会创建.aar、javadoc.jar、sources.jar和.pom文件,并在将文件上传到Maven存储库后更新maven-metadata.xml。我还将脚本放在了GitHub上。

apply plugin: 'com.android.library'
apply plugin: 'maven'

//Your android configuration
android {
    //...
}

//maven repository info
group = 'com.example'
version = '1.0.0'

ext {
    //Specify your maven repository url here
    repositoryUrl = 'ftp://your.maven.repository.com/maven2'
    //Or you can use 'file:\\\\C:\\Temp' or 'maven-temp' for a local maven repository
}

//Upload android library to maven with javadoc and android sources
configurations {
    deployerJars
}

//If you want to deploy to an ftp server
dependencies {
    deployerJars "org.apache.maven.wagon:wagon-ftp:2.2"
}

// custom tasks for creating source/javadoc jars
task javadoc(type: Javadoc) {
    source = android.sourceSets.main.java.srcDirs
    classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
    destinationDir = file("../javadoc/")
    failOnError false
}

task javadocJar(type: Jar, dependsOn: javadoc) {
    classifier = 'javadoc'
    from javadoc.destinationDir
}

//Creating sources with comments
task androidSourcesJar(type: Jar) {
    classifier = 'sources'
    from android.sourceSets.main.java.srcDirs
}

//Put the androidSources and javadoc to the artifacts
artifacts {
    archives androidSourcesJar
    archives javadocJar
}

uploadArchives {
    repositories {
        mavenDeployer {
            configuration = configurations.deployerJars
            repository(url: repositoryUrl) {
                //if your repository needs authentication
                authentication(userName: "username", password: "password")
            }
        }
    }
}

使用以下代码调用:

./gradlew uploadArchives

7
这个问题的关键是要使用maven-publish插件代替旧的方式,即使用maven插件。 - Henry

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接