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") }
}
}
}
}
Related
i have a android project with kotlin, it includes a library module, i would like to upload this library to jfrog artifactory. i have uploaded an aar file to artifactory successfully. now i would like to upload library-sources.jar to artifactory. But when i execute gradle task artifactoryPublish, it will generate two pom-default.xml files, and publish one of them randomly.
Project build.gradle
buildscript {
ext.kotlin_version = "1.4.30"
repositories {
maven {
url "http://localhost:8081/artifactory/my_virtual_repo/"
}
}
dependencies {
classpath "com.android.tools.build:gradle:4.1.2"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
classpath "org.jfrog.buildinfo:build-info-extractor-gradle:4.15.2"
}
}
allprojects {
repositories {
maven {
url "http://localhost:8081/artifactory/my_virtual_repo/"
}
}
}
library build.gradle
......
apply plugin: "com.jfrog.artifactory"
apply plugin: "maven-publish"
def MAVEN_LOCAL_PATH = "http://localhost:8081/artifactory"
def GROUP_ID = "com.xxx.artifactlib"
def ARTIFACT_ID = "artifactlib"
def VERSION_NAME = "1.3"
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
}
task sourcesJar(type: Jar) {
group = 'jar'
from android.sourceSets.main.java.srcDirs
classifier = 'sources'
}
artifacts {
archives sourcesJar
}
publishing {
publications {
mavenJava(MavenPublication) {
groupId = GROUP_ID
artifactId = ARTIFACT_ID
version = VERSION_NAME
artifact tasks.sourcesJar
pom.withXml {
def dependencies = asNode().appendNode("dependencies")
configurations.api.allDependencies.each {
def dependency = dependencies.appendNode("dependency")
print(it.group)
dependency.appendNode("groupId", it.group)
dependency.appendNode("artifactId", it.name)
dependency.appendNode("version", it.version)
}
}
}
}
publications {
aar_pub(MavenPublication) {
groupId = GROUP_ID
artifactId = ARTIFACT_ID
version = VERSION_NAME
artifact("$buildDir/outputs/aar/${project.getName()}-release.aar")
pom.withXml {
def dependencies = asNode().appendNode("dependencies")
configurations.api.allDependencies.each {
def dependency = dependencies.appendNode("dependency")
dependency.appendNode("groupId", it.group)
dependency.appendNode("artifactId", it.name)
dependency.appendNode("version", it.version)
}
}
}
}
}
artifactoryPublish {
contextUrl = MAVEN_LOCAL_PATH
publications("mavenJava", "aar_pub")
clientConfig.publisher.repoKey = "my_local_repo"
clientConfig.publisher.username = "xxx"
clientConfig.publisher.password = "xxx"
}
......
pom-default.xml of aar_pub
<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xxx.artifactlib</groupId>
<artifactId>artifactlib</artifactId>
<version>1.3</version>
<packaging>aar</packaging>
<dependencies/>
</project>
pom-default.xml of mavenJava
<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xxx.artifactlib</groupId>
<artifactId>artifactlib</artifactId>
<version>1.3</version>
<packaging>pom</packaging>
<dependencies/>
</project>
how to upload a given pom file(like pom-default of aar_pub)? thank you.
A single Publication contains all artifacts for a single release.
You defined two identical Publications (well, both have the aar and pom, only one of them has sources JAR) and are trying to upload both of them to the same coordinates at the same time.
Don't define one Publication for each of aar, jar, pom, whatever.
Merge all articacts into a single Publication.
In your case:
publishing {
publications {
mavenJava(MavenPublication) {
groupId = GROUP_ID
artifactId = ARTIFACT_ID
version = VERSION_NAME
artifact tasks.sourcesJar
artifact("$buildDir/outputs/aar/${project.getName()}-release.aar")
pom.withXml {
def dependencies = asNode().appendNode("dependencies")
configurations.api.allDependencies.each {
def dependency = dependencies.appendNode("dependency")
print(it.group)
dependency.appendNode("groupId", it.group)
dependency.appendNode("artifactId", it.name)
dependency.appendNode("version", it.version)
}
}
}
}
}
artifactoryPublish {
publications("mavenJava")
}
Potential issues
There's no formal relationship between the publication and whatever task that produces the AAR. You'll have to manually execute assemble before publish. (And remember to do that every time.)
Consider migrating to Android Gradle Plugin 3.6.0 or newer which supports maven-publish plugin natively. See documentation. This has the following benefits:
You don't have to manually write dependencies to POM.
Whenever AAR would change the publication would pick it up automatically. (You don't publish nothing after a clean build. You don't have to manually execute assemble before publish.)
You still have to handle sources JAR until support for withSourcesJar() is added to AGP. See issue.
In your case it would look something like this:
// Because the components are created only during the afterEvaluate phase, you must
// configure your publications using the afterEvaluate() lifecycle method.
afterEvaluate {
publishing {
publications {
mavenJava(MavenPublication) {
// Applies the component for the release build variant.
from components.release
artifact tasks.sourcesJar
groupId = GROUP_ID
artifactId = ARTIFACT_ID
version = VERSION_NAME
}
}
}
artifactoryPublish {
// ...
}
}
I have a library with 4 modules (all of them are aar's).
Before updating to newest gradle for each module I needed to run artifactoryPublish task. Right now I need to clean & rebuild a project before triggering each publish task.
> Task :module-1:generatePomFileForAarPublication
> Task :module-1:artifactoryPublish
> Task :extractModuleInfo
No publisher config found for project: android-integration-sdk-light
> Task :module-1:extractModuleInfo
> Task :module-2:extractModuleInfo
> Task :module-3:extractModuleInfo
> Task :module-4:extractModuleInfo
[pool-4-thread-1] Deploying artifact: https://xxx.yyy
[pool-4-thread-1] Deploying artifact: https://xxx.yyy
> Task :artifactoryDeploy
BUILD SUCCESSFUL in 6s
First task run
> Task :module-1:generatePomFileForAarPublication
> Task :module-1:artifactoryPublish
> Task :extractModuleInfo UP-TO-DATE
> Task :module-1:extractModuleInfo UP-TO-DATE
> Task :module-2:extractModuleInfo UP-TO-DATE
> Task :module-3:extractModuleInfo UP-TO-DATE
> Task :module-4:extractModuleInfo UP-TO-DATE
> Task :artifactoryDeploy
BUILD SUCCESSFUL in 2s
Second task run
Config for artifactory:
apply plugin: 'com.jfrog.artifactory'
apply plugin: 'maven-publish'
apply from: "../artifactory/config.gradle"
publishing {
publications {
aar(MavenPublication) {
groupId project.groupId
version project.versionName
artifactId project.name
artifact("$buildDir/outputs/aar/${project.getName()}-release.${project.fileExtension}")
pom.withXml {
def node = asNode()
// ugly hack to set the packaging property in the POM as 'aar'
((NodeList) node.get('packaging')).get(0).value = project.fileExtension
def dependenciesNode = node.appendNode('dependencies')
def cl = { Dependency dep ->
if (dep.group == null || dep.name == null || dep.name == "unspecified") {
return // ignore invalid dependencies
}
def dependencyVersion
//Only a temporary hack - need more investigation
def dependencyGroup
if (dep.group == "mobile-library") {
dependencyGroup = groupId
} else {
dependencyGroup = dep.group
}
//"fix" for compile projects
if (dep.version == "unspecified" || dep.version == null) {
dependencyVersion = project.versionName
} else {
dependencyVersion = dep.version
}
def dependencyNode = dependenciesNode.appendNode('dependency')
dependencyNode.appendNode('groupId', dependencyGroup)
dependencyNode.appendNode('artifactId', dep.name)
dependencyNode.appendNode('version', dependencyVersion)
if (!dep.transitive) {
// If this dependency is transitive, we should force exclude all its dependencies them from the POM
def exclusionNode = dependencyNode.appendNode('exclusions').appendNode('exclusion')
exclusionNode.appendNode('groupId', '*')
exclusionNode.appendNode('artifactId', '*')
} else if (!dep.properties.excludeRules.empty) {
// Otherwise add specified exclude rules
def exclusionsNode = dependencyNode.appendNode('exclusions')
dep.properties.excludeRules.each { ExcludeRule rule ->
def exclusion = exclusionsNode.appendNode('exclusion')
exclusion.appendNode('groupId', rule.group ?: '*')
exclusion.appendNode('artifactId', rule.module ?: '*')
}
}
}
// List all dependencies and write to POM
configurations.api.getAllDependencies().each cl
configurations.implementation.getAllDependencies().each cl
}
}
}
}
artifactory {
contextUrl = repositoryPath
publish {
repository {
repoKey = repositoryName
username = repositoryUserName
password = repositoryPassword
maven = true
}
defaults {
publications('aar')
publishArtifacts = true
// Publish generated POM files to Artifactory (true by default)
}
}
}
Use of the file in sub-modules, each module has it's own name:
project.ext {
name = "module-1"
fileExtension = "aar"
}
apply plugin: 'com.android.library'
apply from: "../artifactory/release.gradle"
Should I force clean task & rebuild task before each publish?
Or should I try to have one entry-point for each module in main gradle file?
This issue should have been resolved in Gradle Artifactory plugin 4.18.0.
You can find more information about this issue here:
https://github.com/jfrog/build-info/issues/383
Please upgrade your Gradle Artifactory plugin.
I need to upload my libraries(multi-module project) into sonatype repository through Bintray. So that I need to generate the Javadoc individually for each module.
First I faced the problems in these similar questions
Android Studio Javadoc: Cannot find symbol
Gradle Javadoc fails with “error: package … does not exist”
From the solution in the first question, I can resolve the classes that I have used from third party libraries(retrofit2, gson) and #NonNull annotations errors.
But in my case one of my modules(Lib2) depends on another module(Lib1).
When generating the Javadoc for Lib2 module, its warning package does not exist & cannot find symbol for the classes of another module(Lib1).
And both modules reports package does not exist error for android support & design libraries.
And Every import <packagename>.R; reports cannot find symbol
My Javadoc generating task
task androidJavadocs(type: Javadoc) {
source = android.sourceSets.main.java.source
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
ext.androidJar ="${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar"
classpath += files(ext.androidJar)
project.android.libraryVariants.all { variant ->
classpath += files(variant.javaCompile.classpath.files)
}
}
dependencies in Lib1 build.gradle
compile rootProject.appcompat
compile rootProject.design
compile rootProject.retrofit2
compile rootProject.support
compile rootProject.gsonConverter
compile rootProject.httpLogInterceptor
compile rootProject.universalImageLoader
dependencies in Lib2 build.gradle
compile project(':core')
compile rootProject.appcompat
compile rootProject.design
compile rootProject.retrofit2
compile rootProject.support
compile rootProject.cardview
I have tried
task androidJavadocs(type: Javadoc) {
source = android.sourceSets.main.java.srcDirs
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
}
afterEvaluate {
androidJavadocs.classpath += project.android.libraryVariants.toList().first().javaCompile.classpath
}
My entire gradle-mvn-push.gradle
apply plugin: 'maven'
apply plugin: 'com.jfrog.bintray'
// Declaring variables
afterEvaluate { project ->
bintray {
user = bin_user
key = bin_key
configurations = ['archives']
publish = true
dryRun = false
pkg {
repo = REPO
name = POM_ARTIFACT_ID
userOrg = USER_ORG
desc = POM_DESCRIPTION
websiteUrl = POM_URL
vcsUrl = GIT_URL
licenses = ['Apache-2.0']
version {
name = VERSION_NAME
vcsTag = VERSION_NAME
gpg {
sign = true
try {
passphrase = PASS_PHRASE
} catch (Exception ignored) {
println 'Failed to find credentials. GPG sign disabled'
}
}
}
}
}
if (project.getPlugins().hasPlugin('com.android.application') ||
project.getPlugins().hasPlugin('com.android.library')) {
task install(type: Upload, dependsOn: assemble) {
repositories.mavenInstaller {
configuration = configurations.archives
pom.groupId = GROUP
pom.artifactId = POM_ARTIFACT_ID
pom.version = VERSION_NAME
pom.project {
name POM_NAME
packaging POM_PACKAGING
description POM_DESCRIPTION
url POM_URL
scm {
url POM_SCM_URL
connection POM_SCM_CONNECTION
developerConnection POM_SCM_DEV_CONNECTION
}
licenses {
license {
name POM_LICENCE_NAME
url POM_LICENCE_URL
distribution POM_LICENCE_DIST
}
}
developers {
developer {
id pom_developer_id
name pom_developer_name
}
}
}
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)
}
}
}
}
task androidJavadocs(type: Javadoc) {
source = android.sourceSets.main.java.source
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
ext.androidJar ="${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar"
classpath += files(ext.androidJar)
project.android.libraryVariants.all { variant ->
classpath += files(variant.javaCompile.classpath.files)
}
}
task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) {
classifier = 'javadoc'
from androidJavadocs.destinationDir
}
task androidSourcesJar(type: Jar) {
classifier = 'sources'
from android.sourceSets.main.java.source
}
} else {
install {
repositories.mavenInstaller {
pom.groupId = GROUP
pom.artifactId = POM_ARTIFACT_ID
pom.version = VERSION_NAME
pom.project {
name POM_NAME
packaging POM_PACKAGING
description POM_DESCRIPTION
url POM_URL
scm {
url POM_SCM_URL
connection POM_SCM_CONNECTION
developerConnection POM_SCM_DEV_CONNECTION
}
licenses {
license {
name POM_LICENCE_NAME
url POM_LICENCE_URL
distribution POM_LICENCE_DIST
}
}
developers {
developer {
id POM_DEVELOPER_ID
name POM_DEVELOPER_NAME
}
}
}
}
}
task sourcesJar(type: Jar, dependsOn:classes) {
classifier = 'sources'
from sourceSets.main.allSource
}
task javadocJar(type: Jar, dependsOn:javadoc) {
classifier = 'javadoc'
from javadoc.destinationDir
}
}
if (JavaVersion.current().isJava8Compatible()) {
allprojects {
tasks.withType(Javadoc) {
options.addStringOption('Xdoclint:none', '-quiet')
}
}
}
artifacts {
if (project.getPlugins().hasPlugin('com.android.application') ||
project.getPlugins().hasPlugin('com.android.library')) {
archives androidSourcesJar
archives androidJavadocsJar
} else {
archives sourcesJar
archives javadocJar
}
}
}
I got solved the issue using below snippet
task androidJavadocs(type: Javadoc, dependsOn: 'assembleRelease') {
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
android.libraryVariants.all { variant ->
if (variant.name == 'release') {
owner.classpath += variant.javaCompile.classpath
}
}
source = android.sourceSets.main.java.srcDirs
exclude '**/R.html', '**/R.*.html', '**/index.html'
}
But still import <packagename>.R; reports cannot find symbol.
References:
Adventures with Javadocs, part 2
(Introducing Android Framework Classes)
Adventures with Javadocs, part 3 (Introducing Classes From Third Party Dependencies)
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
I'd like to split up a module (library) in two modules (lib-core) and (lib-extras).
Currently, I'm publishing with artifact id library, and I want to keep this functionality as well. I also want to publish the two separate modules.
For example:
lib-core
class A
lib-extras
class B
library
*(empty, depends on lib-core and lib-extras)*
What I want to achieve is when publishing these modules, I get an archive lib-core containing class A, and archive lib-extras containing class B, and an archive library, containing both classes A and B.
Is this possible using gradle?
This is my uploadArchives task, which is applied in all three modules:
apply plugin: 'maven'
apply plugin: 'signing'
def sonatypeRepositoryUrl
if (isRelease == 'true') {
sonatypeRepositoryUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
} else {
sonatypeRepositoryUrl = "https://oss.sonatype.org/content/repositories/snapshots/"
version = VERSION_NAME + '-SNAPSHOT'
}
afterEvaluate { project ->
uploadArchives {
repositories {
mavenDeployer {
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
pom.artifactId = POM_ARTIFACT_ID
repository(url: sonatypeRepositoryUrl) {
authentication(userName: nexusUsername, password: nexusPassword)
}
pom.project {
name POM_NAME
packaging POM_PACKAGING
description POM_DESCRIPTION
url POM_URL
scm {
url POM_SCM_URL
connection POM_SCM_CONNECTION
developerConnection POM_SCM_DEV_CONNECTION
}
licenses {
license {
name POM_LICENCE_NAME
url POM_LICENCE_URL
distribution POM_LICENCE_DIST
}
}
developers {
developer {
id POM_DEVELOPER_ID
name POM_DEVELOPER_NAME
}
}
}
}
}
}
signing {
required { isRelease == 'true' && gradle.taskGraph.hasTask("uploadArchives") }
sign configurations.archives
}
task androidJavadocs(type: Javadoc) {
source = android.sourceSets.main.java
}
task androidJavadocsJar(type: Jar) {
classifier = 'javadoc'
from androidJavadocs.destinationDir
}
task androidSourcesJar(type: Jar) {
classifier = 'sources'
from android.sourceSets.main.java.sourceFiles
}
artifacts {
archives androidSourcesJar
archives androidJavadocsJar
}
}