Build jitpack library with multi flavor in Android - android

I am using Gradle 4.1 with Gradle-Android plugin 3.0.1 on Android Studio 3.2
I have 2 flavors 'production' and 'staging' and I am unable to build my project as a library with different build variants.
app build.gradle:
apply plugin: 'com.android.library'
apply plugin: 'com.github.dcendents.android-maven'
android {
...
productFlavors {
production {
}
staging {
}
}
defaultPublishConfig "productionRelease"
publishNonDefault true
}
if( android.productFlavors.size() > 0 ) {
android.libraryVariants.all { variant ->
if( android.publishNonDefault && variant.name == android.defaultPublishConfig ) {
def bundleTask = tasks["bundle${name.capitalize()}"]
artifacts {
archives(bundleTask.archivePath) {
classifier name.replace('-' + variant.name, '')
builtBy bundleTask
name name.replace('-' + variant.name, '')
}
}
}
...
Then I run: ./gradlew clean install, errors I got is:
Execution failed for task ‘:app:install’.
Could not publish configuration ‘archives’
A POM cannot have multiple artifacts with the same type and classifier. Already have MavenArtifact app:aar:aar:null, trying to add MavenArtifact app:aar:aar:null.
And to get this code to compile, I need to swap android.publishNonDefault with true, otherwise I will get an error of: Cannot get the value of write-only property 'publishNonDefault'
Any suggestions or hint would be really helpful, the aim is to build the library module on jitpack, where we can import it in project with build variants. thanks!

After digging into this for 2 days, and emailing Jitpack support, the issue is because the lib has updated and publishNonDefault is deprecated. you just need to change your app build.gradle to:
apply plugin: 'com.github.dcendents.android-maven'
dependencies {...}
group = 'com.github.your-group'
if (android.productFlavors.size() > 0) {
android.libraryVariants.all { variant ->
if (variant.name.toLowerCase().contains("debug")) {
return
}
def bundleTask = tasks["bundle${variant.name.capitalize()}"]
artifacts {
archives(bundleTask.archivePath) {
classifier variant.flavorName
builtBy bundleTask
name = project.name
}
}
}
}

problem was to create multiple flavors using jitpack
so what I do is, create a variable which stores the flavor name, after that loop through the available variant, pass the flavorBuild to artifact so when u push the code to GitHub and create artifact via jitpack then jitpack create the required implementation based on your build flavor and then u can use it. You need to just change build flavor
publishing {
publications {
def flavorBuild ='production'
android.libraryVariants.all { variant ->
"maven${variant.name.capitalize()}Aar"(MavenPublication) {
from components.findByName("android${variant.name.capitalize()}")
groupId = 'com.examplesdk'
artifactId = 'examplesdk'
version "1.0.0-${variant.name}"
artifact "$buildDir/outputs/aar/examplesdk-${flavorBuild}-release.aar"
pom.withXml {
def dependenciesNode = asNode().appendNode('dependencies')
configurations.api.allDependencies.each { dependency ->
def dependencyNode = dependenciesNode.appendNode('dependency')
dependencyNode.appendNode('groupId', dependency.group)
dependencyNode.appendNode('artifactId', dependency.name)
dependencyNode.appendNode('version', dependency.version)
}
}
}
}
}
repositories{
maven {
url "$buildDir/repo"
}
}
}

Related

publishing aar using maven plugin and kotlin dsl

I am having problems when trying to use maven-publish plugin from AS.
I tried that example with a project and it works without problem. But as soon as I move to kotlin dsl, I have this issue:
SoftwareComponentInternal with name 'release' not found.
This is my first time dealing with kotlin dsl. First, I do not know if you can have both, kotlin dsl and groovy, but I tried that the first time by just adding kotlin dsl to the root and the app:build.gradle. I have this error so I decided to migrate also the library to kotlin dsl: mylib:build.gradle. I ended having this code:
plugins {
id(BuildPlugins.androidLibrary)
id(BuildPlugins.kotlinAndroid)
id(BuildPlugins.kotlinAndroidExtensions)
id(BuildPlugins.mavenPublish)
}
afterEvaluate {
publishing {
publications {
// Creates a Maven publication called "release".
create<MavenPublication>("release") {
// Applies the component for the release build variant.
from(components["release"])
// You can then customize attributes of the publication as shown below.
groupId = "com.mylib"
artifactId = "alpha"
version = "0.1"
}
}
}
}
Any idea about this and how to solve it?
Please make sure that you're using AGP 3.6.0+ since this configuration was added starting 3.6.0. Documentation:
https://developer.android.com/studio/build/maven-publish-plugin
So far I could come with this solution when using kotlin dsl:
Keep in mind that in this case, the solution posted is in groovy. But you can do it in kotlin dsl without problem.
Why is this in groovy? Well, the problem I had started when I included kotlin dsl in the app module, even though I had groovy gradle files in the submodules (which I want to publish as libraries too).
In this example, I am publishing a debug and a release version.
afterEvaluate {
publishing {
publications {
def groupIdPublication = 'com.mypackage'
def artifactIdPublication = "util"
// Creates a Maven publication called "release".
release(MavenPublication) {
// Applies the component for the release build variant.
//from components.release - this is not working
// You can then customize attributes of the publication as shown below.
groupId = groupIdPublication
artifactId = artifactIdPublication
version = '0.1'
artifact("$buildDir/outputs/aar/${project.name}-release.aar") // this is the solution I came up with
pom.withXml {
def dependenciesNode = asNode().appendNode('dependencies')
applyDependenciesToPOM(dependenciesNode, configurations.api.allDependencies)
applyDependenciesToPOM(dependenciesNode, configurations.implementation.allDependencies)
}
}
// Creates a Maven publication called “debug”.
debug(MavenPublication) {
// Applies the component for the debug build variant.
//from components.debug - this is not working
groupId = groupIdPublication
artifactId = artifactIdPublication
version = 'debug-0.1'
artifact("$buildDir/outputs/aar/${project.name}-debug.aar") // this is the solution I came up with
pom.withXml {
def dependenciesNode = asNode().appendNode('dependencies')
applyDependenciesToPOM(dependenciesNode, configurations.api.allDependencies)
applyDependenciesToPOM(dependenciesNode, configurations.implementation.allDependencies)
}
}
}
}
}
static def applyDependenciesToPOM(Object dependenciesNode, DependencySet allDependencies) {
allDependencies.each {
if (it.group != null && (it.name != null && !it.name.equals("unspecified")) &&
(it.version != null && !it.version.equals("unspecified"))) {
def dependencyNode = dependenciesNode.appendNode('dependency')
dependencyNode.appendNode('groupId', it.group)
dependencyNode.appendNode('artifactId', it.name)
dependencyNode.appendNode('version', it.version)
}
}
}

'Received status code 400 from server: Bad Request' While publishing aar file to Github packages

I am trying to push my library to GitHub Packages.
I have configured the personal access token and have the github.properties file in the root folder.
The aar file is assembled, using gradle assemble.
When I am running the gradle publish command.
I am getting this error Received status code 400 from server: Bad Request
* What went wrong:
Execution failed for task ':library_to_publish:publishProductionPublicationToGitHubPackagesRepository'.
> Failed to publish publication 'Production' to repository 'GitHubPackages'
> Could not write to resource 'https://maven.pkg.github.com/<user>/GitHubPackagesDemo/com/avp/test/lib/githubpackages/library_to_publish/1.0.6/library_to_publish-1.0.6.aar'.
> Could not PUT 'https://maven.pkg.github.com/<user>/GitHubPackagesDemo/com/avp/test/lib/githubpackages/library_to_publish/1.0.6/library_to_publish-1.0.6.aar'. Received status code 400 from server: Bad Request
Here is my configuration in build.gradle file
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'maven-publish'
def githubProperties = new Properties()
githubProperties.load(new FileInputStream(rootProject.file("github.properties")))
def getVersionName = { ->
return "1.0.6"
}
def getArtificatId = { ->
return "library_to_publish"
}
publishing {
publications {
Production(MavenPublication) {
groupId 'com.avp.test.lib.githubpackages' // Replace with group ID
artifactId getArtificatId()
version getVersionName()
artifact("$buildDir/outputs/aar/${getArtificatId()}-release.aar")
pom.withXml {
def dependenciesNode = asNode().appendNode('dependencies')
// Iterate over the implementation dependencies (we don't want the test ones),
// adding a <dependency> node for each
configurations.implementation.allDependencies.each {
// Ensure dependencies such as fileTree are not included in the pom.
if (it.name != 'unspecified') {
def dependencyNode = dependenciesNode.appendNode('dependency')
dependencyNode.appendNode('groupId', it.group)
dependencyNode.appendNode('artifactId', it.name)
dependencyNode.appendNode('version', it.version)
}
}
}
}
}
repositories {
maven {
name = "GitHubPackages"
url = uri("https://maven.pkg.github.com/<user>/GitHubPackagesDemo")
credentials {
username = githubProperties['gpr.usr'] ?: System.getenv("GPR_USER")
password = githubProperties['gpr.key'] ?: System.getenv("GPR_API_KEY")
}
}
}
}
Does anyone have any idea why is this happening?
I managed to fix the problem, by changing the version name.
Github won't allow pushing the same version to overwrite the existing one. you have to update the version name every time you publish.

Gradle: how to get all the module dependencies of a project

I am trying to write a Gradle Plugin that runs Checkstyle over all the source code of a specific Android app, including all the variants and all the library modules that the app is using and generate a single report without duplicate errors. Currently I am able to run it through all the variants successfully and generate a report without duplications, but I have no idea how to get the list of modules that the app is using.
So, basically, if I have a project with this structure:
MyProject
+-MyAppFoo
+-MyAppBar
+-FeatureA
+-FeatureB
+-FeatureC
+-Core
And the app MyAppFoo has dependencies on the modules FeatureA and FeatureC and FeatureA depends on Core I would like to be able to access the project instance of FeatureA, Core and FeatureC to get their sources and classpaths.
My current code looks like this and I am applying it directly to only one app module of the project (eg. MyAppFoo):
apply plugin: 'checkstyle'
afterEvaluate {
def variants
if (project.plugins.hasPlugin('com.android.application')) {
variants = android.applicationVariants
} else if (project.plugins.hasPlugin('com.android.library')) {
variants = android.libraryVariants
} else {
return
}
def dependsOn = []
def classpath
def source
variants.all { variant ->
dependsOn << variant.javaCompiler
if (!source) {
source = variant.javaCompiler.source.filter { p ->
return p.getPath().contains("${project.projectDir}/src/main/")
}
}
source += variant.javaCompiler.source.filter { p ->
return !p.getPath().contains("${project.projectDir}/src/main/")
}
if (!classpath) {
classpath = project.fileTree(variant.javaCompiler.destinationDir)
} else {
classpath += project.fileTree(variant.javaCompiler.destinationDir)
}
}
def checkstyle = project.tasks.create "checkstyle", Checkstyle
checkstyle.group = "Verification"
checkstyle.dependsOn dependsOn
checkstyle.source source
checkstyle.classpath = classpath
checkstyle.exclude('**/BuildConfig.java')
checkstyle.exclude('**/R.java')
checkstyle.exclude('**/BR.java')
checkstyle.showViolations true
project.tasks.getByName("check").dependsOn checkstyle
}
What I would like to have is a list of projects containing only the modules that my MyAppFoo is using, this whay when I run gradle :MyAppFoo:checkstyle I want to run it on the modules MyAppFoo, FeatureA, Core and FeatureC.

Publishing Android Library (aar) to Bintray with chosen flavors

I've juste added some flavors (or productFlavors if you want) to my project.
The fact is that when I publish the library to bintray, all flavors are uploaded (which is great), but I'm unable to use them. The plugin used is the official one here.
The uploaded aar:
androidsdk-0.0.4-fullRelease.aar
androidsdk-0.0.4-fullDebug.aar
androidsdk-0.0.4-lightRelease.aar
androidsdk-0.0.4-lightDebug.aar
As you noted, the fullRelease is named as the classifier, see doc chapter 23.4.1.3.
I am searching for a solution to choose which flavors that I want to upload.
I've already looked at bintray examples (here and here) and this, with also other examples but I'm still stuck.
Here is my current script:
apply plugin: 'com.android.library'
apply plugin: 'com.github.dcendents.android-maven'
apply plugin: 'com.jfrog.bintray'
buildscript {
repositories {
jcenter()
}
}
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
defaultConfig {
minSdkVersion 9
targetSdkVersion 23
versionCode 64
versionName "0.0.4"
}
publishNonDefault true
productFlavors {
full {
}
light {
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:23.1.1'
compile 'com.android.support:recyclerview-v7:23.1.1'
fullCompile 'com.squareup.picasso:picasso:2.5.0'
}
version = android.defaultConfig.versionName
uploadArchives {
repositories.mavenDeployer {
pom.project {
packaging 'aar'
}
}
}
////////////////////////////////
// Bintray Upload configuration
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
bintray {
user = properties.getProperty("bintray.user")
key = properties.getProperty("bintray.apikey")
configurations = ['archives']
pkg {
repo = "MyRepo" // repo name
userOrg = 'hugo'
name = "AndroidSDK" // Package name
websiteUrl = siteUrl
vcsUrl = gitUrl
publish = true
}
}
To import the library I'm currently using this:
compile ('com.example.lib:sdk:0.0.8:fullRelease#aar') {
transitive = true;
}
I faced the same challenge, and here's the best I could make yet:
Using mavenPublications and the gradle maven-publish plugin along the bintray plugin, you can publish any variant to mavenLocal and bintray.
Here's the publish.gradle file I apply at the end of all my project's library modules I want to publish:
def pomConfig = {
licenses {
license {
name 'The Apache Software License, Version 2.0'
url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
}
}
developers {
developer {
id 'louiscad'
name 'Louis CAD'
email 'louis.cognault#gmail.com'
}
}
scm {
connection 'https://github.com/LouisCAD/Splitties.git'
developerConnection 'https://github.com/LouisCAD/Splitties.git'
url siteUrl
}
}
def publicationNames = []
publishing.publications {
android.libraryVariants.all { variant ->
if (variant.buildType.name == "debug") return // Prevents publishing debug library
def flavored = !variant.flavorName.isEmpty()
/**
* Translates "_" in flavor names to "-" for artifactIds, because "-" in flavor name is an
* illegal character, but is well used in artifactId names.
*/
def variantArtifactId = flavored ? variant.flavorName.replace('_', '-') : project.name
/**
* If the javadoc destinationDir wasn't changed per flavor, the libraryVariants would
* overwrite the javaDoc as all variants would write in the same directory
* before the last javadoc jar would have been built, which would cause the last javadoc
* jar to include classes from other flavors that it doesn't include.
*
* Yes, tricky.
*
* Note that "${buildDir}/docs/javadoc" is the default javadoc destinationDir.
*/
def javaDocDestDir = file("${buildDir}/docs/javadoc ${flavored ? variantArtifactId : ""}")
/**
* Includes
*/
def sourceDirs = variant.sourceSets.collect {
it.javaDirectories // Also includes kotlin sources if any.
}
def javadoc = task("${variant.name}Javadoc", type: Javadoc) {
description "Generates Javadoc for ${variant.name}."
source = variant.javaCompile.source // Yes, javaCompile is deprecated,
// but I didn't find any working alternative. Please, tweet #Louis_CAD if you find one.
destinationDir = javaDocDestDir
classpath += files(android.getBootClasspath().join(File.pathSeparator))
classpath += files(configurations.compile)
options.links("http://docs.oracle.com/javase/7/docs/api/");
options.links("http://d.android.com/reference/");
exclude '**/BuildConfig.java'
exclude '**/R.java'
failOnError false
}
def javadocJar = task("${variant.name}JavadocJar", type: Jar, dependsOn: javadoc) {
description "Puts Javadoc for ${variant.name} in a jar."
classifier = 'javadoc'
from javadoc.destinationDir
}
def sourcesJar = task("${variant.name}SourcesJar", type: Jar) {
description "Puts sources for ${variant.name} in a jar."
from sourceDirs
classifier = 'sources'
}
def publicationName = "splitties${variant.name.capitalize()}Library"
publicationNames.add(publicationName)
"$publicationName"(MavenPublication) {
artifactId variantArtifactId
group groupId
version libraryVersion
artifact variant.outputs[0].packageLibrary // This is the aar library
artifact sourcesJar
artifact javadocJar
pom {
packaging 'aar'
withXml {
def root = asNode()
root.appendNode("name", 'Splitties')
root.appendNode("url", siteUrl)
root.children().last() + pomConfig
def depsNode = root["dependencies"][0] ?: root.appendNode("dependencies")
def addDep = {
if (it.group == null) return // Avoid empty dependency nodes
def dependencyNode = depsNode.appendNode('dependency')
dependencyNode.appendNode('groupId', it.group)
dependencyNode.appendNode('artifactId', it.name)
dependencyNode.appendNode('version', it.version)
if (it.hasProperty('optional') && it.optional) {
dependencyNode.appendNode('optional', 'true')
}
}
// Add deps that everyone has
configurations.compile.allDependencies.each addDep
// Add flavor specific deps
if (flavored) {
configurations["${variant.flavorName}Compile"].allDependencies.each addDep
}
// NOTE: This library doesn't use builtTypes specific dependencies, so no need to add them.
}
}
}
}
}
group = groupId
version = libraryVersion
afterEvaluate {
bintray {
user = bintray_user
key = bintray_api_key
publications = publicationNames
override = true
pkg {
repo = 'splitties'
name = project.name
desc = libraryDesc
websiteUrl = siteUrl
issueTrackerUrl = 'https://github.com/LouisCAD/Splitties/issues'
vcsUrl = gitUrl
licenses = ['Apache-2.0']
labels = ['aar', 'android']
publicDownloadNumbers = true
githubRepo = 'LouisCAD/Splitties'
}
}
}
In order for this to work, I need to have the bintray_user and bintray_api_key properties defined. I personally just have them in my ~/.gradle/gradle.properties file like this:
bintray_user=my_bintray_user_name
bintray_api_key=my_private_bintray_api_key
I also need to define the following ext properties I used in the publish.gradle file in my root project's build.gradle file:
allprojects {
...
ext {
...
// Libraries
groupId = "xyz.louiscad.splitties"
libraryVersion = "1.2.1"
siteUrl = 'https://github.com/LouisCAD/Splitties'
gitUrl = 'https://github.com/LouisCAD/Splitties.git'
}
}
And now, I can finally use it in my android library module, where I have multiple productFlavors. Here's a snippet from a publishable library module's build.gradle file:
plugins {
id "com.jfrog.bintray" version "1.7.3" // Enables publishing to bintray
id "com.github.dcendents.android-maven" version "1.5" // Allows aar in mavenPublications
}
apply plugin: 'com.android.library'
apply plugin: 'maven-publish' // Used for mavenPublications
android {
...
defaultPublishConfig "myLibraryDebug" // Allows using this library in another
// module in this project without publishing to mavenLocal or Bintray.
// Useful for debug purposes, or for your library's sample app.
defaultConfig {
...
versionName libraryVersion
...
}
...
productFlavors {
myLibrary
myLibrary_logged // Here, the "_" will be replaced "-" in artifactId when publishing.
myOtherLibraryFlavor
}
...
}
dependencies {
...
// Timber, a log utility.
myLibrary_loggedCompile "com.jakewharton.timber:timber:${timberVersion}"; // Just an example
}
...
ext {
libraryDesc = "Delegates for kotlin on android that check UI thread"
}
apply from: '../publish.gradle' // Makes this library publishable
When you have all of this setup properly, with the name of your library instead of mine's (which you can use as an example), you can try publishing a version of your flavored library by trying to first publishing to mavenLocal.
To do so, run this command:
myLibrary $ ../gradlew publishToMavenLocal
You can then try adding mavenLocal in your app's repositories (example here) and try adding your library as a dependency (artifactId should be the flavor name, with "_" replaced with "-") and building it.
You can also check with your file explorer (use cmd+shift+G on Mac in Finder to access hidden folder) the directory ~/.m2 and look for your library.
When it's time to publish to bintray/jcenter, you just have to run this command:
myLibrary $ ../gradlew bintrayUpload
Important:
Before you publish your library to mavenLocal, Bintray or another maven repository, you'll usually want to try your library against a sample app which uses the library. This sample app, which should be another module in the same project just need to have the project dependency, which should look like this: compile project(':myLibrary'). However, since your library has multiple productFlavors, you'll want to test all of them. Unfortunately, it's currently impossible to specify which configuration you want to use from your sample app's build.gradle file (unless, you use publishNonDefault true in your library's build.gradle file, which breaks maven and bintray publications), but you can specify the default configuration (i.e. buildVariant) in your library's module as such: defaultPublishConfig "myLibraryDebug" in the android closure. You can see the available build variants for your library in the "Build Variants" tool Windows in Android Studio.
Feel free to explore my library "Splitties" here if you need an example. The flavored module is named concurrency, but I use my script for unflavored library modules too, and I tested it throughly on all the library modules in my project.
You can reach me out if you need help setting it up for you.
The setup:
buildTypes {
debug {
}
release {
}
}
publishNonDefault true
The fix:
defaultPublishConfig 'release'
// Fix for defaultPublishConfig not working as expected
// ref: https://github.com/dcendents/android-maven-gradle-plugin/issues/11
libraryVariants.all { variant ->
if( publishNonDefault && variant.name == defaultPublishConfig ) {
def bundleTask = tasks["bundle${variant.name.capitalize()}"]
artifacts {
archives(bundleTask.archivePath) {
classifier null //necessary to get rid of the suffix in the artifact
builtBy bundleTask
name name.replace('-' + variant.name, '')//necessary to get rid of the suffix from the folder name
}
}
}
}
This fix will still publish all the artifacts, but it will publish a default artifact without the flavour suffix, which is enough to make it all work.
The fix to upload only the default artifact would be this (if the bintray plugin knew what POM filters are):
install {
repositories.mavenInstaller {
/*
POM filters can be used to block artifacts from certain build variants.
However, Bintray does not respect POM filters, therefore this only works for maven deploy plugin.
Also, bintray crashes with named filters, since it always expects a /build/pom/pom-default.xml,
which does not happen with named filters.
*/
filter { artifact, file ->
// this how the default classifier is identified in case the defaultPublishConfig fix is applied
artifact.attributes.classifier == null
}
}
}
I didn't try it so I will delete the answer if it doesn't resolve the issue.
You should post a different artifact for each flavor (or build variant if you prefer).
In this way you will have in jcenter x artifacts, each of them with a pom file.
Something like:
groupId
|--library-full
|----.pom
|----.aar
|--library-light
|----.pom
|----.aar
In your top level file you can define
allprojects {
repositories {
jcenter()
}
project.ext {
groupId="xxx"
libraryName = ""
......
}
}
Then in your library module:
productFlavors {
full {
project.ext.set("libraryName", "library-full");
}
light {
project.ext.set("libraryName", "library-light");
}
}
bintray {
//...
pkg {
//...Do the same for other variables
name = project.ext.libraryName
}
}
Finally make sure to publish only the release build type (why also the debug version?)
If someone is still stuck with this problem here's what worked for me -
Let's say you want to publish the release build for your flavour1 add this to your build.gradle
android {
...
defaultPublishConfig "flavour1Release"
}
Remove publishNonDefault true if it is present in your gradle file.
Add this inside the bintray block like this
bintray {
...
archivesBaseName = 'YOUR_ARTIFACT_ID'
...
}
Then just run the bintrayUpload task as you would.
The defaultPublishConfig will have to be changed everytime you need to publish a new flavour.
It sounds like you don't want the classifier in the filename. It looks like the classifier is the same as the generated library file name. Have you tried giving them the same filename but outputting them to separate directories?
E.g. in the android scope:
libraryVariants.all { variant ->
variant.outputs.each { output ->
def outputFile = output.outputFile
if (outputFile != null && outputFile.name.endsWith('.aar')) {
def fileName = "same_name-${version}.aar"
output.outputFile = new File(outputFile.parent+"/${archivesBaseName}", fileName)
}
}
}

Publish an Android library to Maven with AAR and sources JAR

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

Categories

Resources