I am trying publish flavour based artifact on my Artifactory server. Gradle code looks like this
def GroupId = 'com.example.directory'
def ArtifactId = 'demo
def Version = '0.0.1-IB1''
productFlavors {
extended {
ArtifactId = "extended"
}
mini {
ArtifactId = "mini"
}
}
publishing {
publications {
aar(MavenPublication) {
android.libraryVariants.all { variant ->
groupId GroupId
version Version
artifactId ArtifactId
def filename = "${archivesBaseName}-${variant.baseName}.aar"
artifact("$buildDir/outputs/aar/${filename}")
}
pom.withXml {
def dependenciesNode = asNode().appendNode('dependencies')
def deps = configurations.implementation.allDependencies + configurations.api.allDependencies
deps.each {
if (it.group != null && (it.name != null || "unspecified" == it.name) && it.version != null && "unspecified" != it.version) {
def dependencyNode = dependenciesNode.appendNode('dependency')
dependencyNode.appendNode('groupId', it.group)
dependencyNode.appendNode('artifactId', it.name)
dependencyNode.appendNode('version', it.version)
}
}
}
}
}
}
artifactory {
contextUrl = 'http://artifactoryUrl/artifactory'
publish {
repository {
repoKey = GroupId
username = "${userName}"
password = "${pass}"
}
defaults {
publications('aar')
publishArtifacts = true
properties = ['qa.level': 'basic', 'dev.team': 'core']
publishPom = true
}
}
}
But when I run gradle command it generates the specific flavour + build type artifact. But while publishing it tries to publish all varient of the library and throws an error for other aar file names.
./gradlew clean && ./gradlew :demo:assembleextendedrelease && ./gradlew :demo:artifactoryPublish
Short version:
Module A and B are modules of my Android library.
While releasing module A which depends on module B. How do I set the version of module B in the release process?
I developed an Android library using Kotlin with multiple modules with some of them depending on each other. I am using JFrog Artifactory for hosting and its gradle plugin for publishing of the library modules.
To generate a valid POM, I'm generating it manually in the publish closure of the modules build.gradle. While receiving all dependencies of the module when integrating it in a different project, i fail to set the version of local dependencies.
Code I use to set local dependency:
implementation project(path: ':core')
implementation project(path: ':auth')
Code I use to generate the POM
task androidJavadocs(type: Javadoc) {
source = android.sourceSets.main.java.srcDirs
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
options {
failOnError false
encoding "UTF-8"
charSet "UTF-8"
addStringOption('Xdoclint:none', '-quiet')
}
android.libraryVariants.all { variant ->
if (variant.name == 'release') {
variant.javaCompileProvider.configure{
owner.classpath += variant.javaCompiler.classpath
}
}
}
exclude '**/R.html', '**/R.*.html', '**/index.html'
}
task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) {
archiveClassifier = 'javadoc'
from androidJavadocs.destinationDir
}
task sourcesJar(type: Jar) {
from android.sourceSets.main.java.srcDirs
archiveClassifier = 'sources'
}
publishing {
publications {
aar(MavenPublication) {
groupId POM_GROUP_ID
artifactId POM_ARTIFACT_ID
version POM_VERSION
description POM_DESCRIPTION
artifact("$buildDir/outputs/aar/${project.getName()}-release.aar")
artifact androidJavadocsJar
artifact sourcesJar
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
if (dep.group == "MyDevGroup") // I am skipping my local dependencies on other modules
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") }
}
}
}
}
When i sync project, android studio warn could not get unknown property 'bundleRelease' for object of type org.gradle.api.publish.maven.internal.publication.DefaultMavenPublication.
I add project.afterEvaluate{//block},but it doesn't work. What should i do to set the artifact
Android Gradle Plugin 3.3.x (at least -alpha releases at the time of writing this answer) has breaking change, task bundleRelease was renamed to bundleReleaseAar
So the solution is to use: bundleReleaseAar instead of bundleRelease.
Note: "release" in the task name is buildType/flavor combination, thus it might be different in your setup.
Generic answer: bundleRelease is a task, to find its new name you can run ./gradlew tasks --all
So the answer from Artem Zunnatullin is correct. Just one addition, the project.afterEvaluate{//block} is necessary to make it work. This information can be overlooked very easily.
Complete example:
project.afterEvaluate {
publishing {
publications {
mavenDebugAAR(MavenPublication) {
artifact bundleDebugAar
pom.withXml {
def dependenciesNode = asNode().appendNode('dependencies')
configurations.api.allDependencies.each { ModuleDependency dp ->
def dependencyNode = dependenciesNode.appendNode('dependency')
dependencyNode.appendNode('groupId', dp.group)
dependencyNode.appendNode('artifactId', dp.name)
dependencyNode.appendNode('version', dp.version)
if (dp.excludeRules.size() > 0) {
def exclusions = dependencyNode.appendNode('exclusions')
dp.excludeRules.each { ExcludeRule ex ->
def exclusion = exclusions.appendNode('exclusion')
exclusion.appendNode('groupId', ex.group)
exclusion.appendNode('artifactId', ex.module)
}
}
}
}
}
mavenReleaseAAR(MavenPublication) {
artifact bundleReleaseAar
pom.withXml {
def dependenciesNode = asNode().appendNode('dependencies')
configurations.api.allDependencies.each { ModuleDependency dp ->
def dependencyNode = dependenciesNode.appendNode('dependency')
dependencyNode.appendNode('groupId', dp.group)
dependencyNode.appendNode('artifactId', dp.name)
dependencyNode.appendNode('version', dp.version)
if (dp.excludeRules.size() > 0) {
def exclusions = dependencyNode.appendNode('exclusions')
dp.excludeRules.each { ExcludeRule ex ->
def exclusion = exclusions.appendNode('exclusion')
exclusion.appendNode('groupId', ex.group)
exclusion.appendNode('artifactId', ex.module)
}
}
}
}
}
}
repositories {
maven {
name 'nexusSnapshot'
credentials {
username '<User with deployment rights>'
password '<User password>'
}
url '<URL to nexus>'
}
maven {
name 'nexusRelease'
credentials {
username '<User with deployment rights>'
password '<User password>'
}
url '<URL to nexus>'
}
}
}
}
I'm trying to export an Android Library Module developed in Kotlin using gradle's maven-publish plugin. aar file is successfully generated and exported however no dependency is added to it - at all, not even Kotlin ones. Below is the plugin configuration based on this answer:
apply plugin: 'maven-publish'
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
ext {
releaseRepository = "s3://****/maven/releases"
snapshotsRepository = "s3://****/maven/snapshots"
artifactRepository = "$buildDir/outputs/aar/render-engine-release.aar"
_version = properties.getProperty('lib.version')
_artifact = properties.getProperty('lib.name')
_group = properties.getProperty('lib.group')
}
task sourceJar(type: Jar) {
from android.sourceSets.main.java.srcDirs
classifier "source"
}
publishing {
publications {
maven(MavenPublication) {
groupId _group
artifactId _artifact
version _version
artifact (sourceJar)
artifact artifactRepository
pom.withXml {
def dependenciesNode = asNode().appendNode('dependencies')
configurations.compile.allDependencies.each {
if(_group != null && (_artifact != null || "unspecified" == _artifact) && _version != null) {
def dependencyNode = dependenciesNode.appendNode('dependency')
dependencyNode.appendNode('groupId', _group)
dependencyNode.appendNode('artifactId', _artifact)
dependencyNode.appendNode('version', _version)
}
}
}
}
}
repositories {
maven {
if(_version.endsWith('-SNAPSHOT')) {
url snapshotsRepository
} else {
url releaseRepository
}
credentials(AwsCredentials) {
accessKey AWS_ACCESS_KEY
secretKey AWS_SECRET_KEY
}
}
}
}
Ok, so keeping reading the answers on the linked question I've implemented this that solved my problem. My final publish.gradle is like:
apply plugin: 'maven-publish'
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
ext {
releaseRepository = "s3://****/maven/releases"
snapshotsRepository = "s3://****/maven/snapshots"
artifactRepository = "$buildDir/outputs/aar/render-engine-release.aar"
_version = properties.getProperty('lib.version')
_artifact = properties.getProperty('lib.name')
_group = properties.getProperty('lib.group')
}
task sourceJar(type: Jar) {
from android.sourceSets.main.java.srcDirs
classifier "source"
}
publishing {
publications {
maven(MavenPublication) {
groupId _group
artifactId _artifact
version _version
artifact(sourceJar)
artifact artifactRepository
pom.withXml {
def dependenciesNode = asNode().appendNode('dependencies')
configurations.compile.getAllDependencies().each { Dependency dep ->
if (dep.group == null || dep.version == null || dep.name == null || dep.name == "unspecified")
return
def dependencyNode = dependenciesNode.appendNode('dependency')
dependencyNode.appendNode('groupId', dep.group)
dependencyNode.appendNode('artifactId', dep.name)
dependencyNode.appendNode('version', dep.version)
if (!dep.transitive) {
def exclusionNode = dependencyNode.appendNode('exclusions').appendNode('exclusion')
exclusionNode.appendNode('groupId', '*')
exclusionNode.appendNode('artifactId', '*')
} else if (!dep.properties.excludeRules.empty) {
def exclusionsNode = dependencyNode.appendNode('exclusions')
dep.properties.excludeRules.each { ExcludeRule rule ->
def exclusionNode = exclusionsNode.appendNode('exclusion')
exclusionNode.appendNode('groupId', rule.group ?: '*')
exclusionNode.appendNode('artifactId', rule.module ?: '*')
}
}
}
}
}
}
repositories {
maven {
if (_version.endsWith('-SNAPSHOT')) {
url snapshotsRepository
} else {
url releaseRepository
}
credentials(AwsCredentials) {
accessKey AWS_ACCESS_KEY
secretKey AWS_SECRET_KEY
}
}
}
}
Can somebody give me a hint on how to use the maven-publish Gradle plugin to publish a com.android.library project/module with AAR and source jar? I am able to do this with the old maven plugin - but I would like to use the new maven-publish plugin.
With Android Gradle Plugin 7.1 it is now very simple to do this without needing any complicated scripts. AGP now also handles creating source and javadocs jar.
You don't need any separate scripts, just write everything into your build.gradle file of your module:
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'
}
}
}
}
See also: https://developer.android.google.cn/studio/build/maven-publish-plugin
Old answer
Since release of Android Studio 3.6 the support for building AAR (or even APK and AAB) is implemented in Android Gradle plugin 3.6.0 (and newer).
We don't need to handle the XML dependencies and stuff ourselves anymore.
Here is my updated Gist for Android Studio 3.6.0: https://gist.github.com/Robyer/a6578e60127418b380ca133a1291f017
Code from 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'
}
}
}
}
Original answer:
Here is my improved solution, based on other answers.
Gist: https://gist.github.com/Robyer/a6578e60127418b380ca133a1291f017
Changes from other answers:
Changed classifier - it must be "sources" (not "source")
Handles dependencies
Supports also #aar and transitive: false. In that case we set exclusion in POM to ignore all transitive dependencies of this dependency.
Supports also custom exclude rules on dependencies, e.g.:
compile('com.example:something:1.0', {
exclude group: 'com.exclude.this', module: 'some-module'
})
Doesn't need to specify artifact path manually.
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") }
}
}
}
}
}
Here's a sample using the new maven-publish plugin.
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"
}
}
}
Publish with ./gradlew clean build publish
A little tweak to dskinners answer with correct dependency generation:
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"
}
}
}
And you can change version and groupId by defining:
version = '1.0.0'
group = 'foo.bar'
If you want to avoid boilerplate codes, because the maven-publish plugin do not write dependencies into pom.xml
Try this plugin: android-maven-publish
publishing {
publications {
mavenAar(MavenPublication) {
groupId 'com.example'
artifactId 'mylibrary'
version '1.0.0'
from components.android
}
}
repositories {
maven {
url "$buildDir/releases"
}
}
}
Update:
android-maven-publish plugin is deprecated, since maven-publish is officially supported by AGP.
This is how I included Dokka (view it online) and sources JARs for my Android Kotlin library using Kotlin DSL (build.gradle.kts):
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)
// ...
}
}
}
Using 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")
}
}
}
You can also use the android maven plugin. It creates the .aar, javadoc.jar, sources.jar and .pom and updates the maven-metadata.xml after uploading the files to the maven repository. I also put the script on 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")
}
}
}
}
Call it with
./gradlew uploadArchives