Publishing your Android library - the local way
Tags: android, maven, mavenlocal, library
Published: 2021-07-22 15:43:37

If you ever built an Android library, meant to be used in one or more projects, you have for sure faced the need to publish it in order to test its integration into your target app. You probably publish your libraries in some repository, such as JitPack, Nexus, JFrog, or other. But, what if you need to continue introducing changes or working on fixes in your library? Maybe publishing it to a remote repository each time is not the most time-saving option, especially if your library is a bit overweight. Or maybe your Internet access was cut off; how are you supposed to continue working like this? Well, there is actually a convenient, effective way to quickly get your library published and available to be integrated into your other projects: the Maven local repository.

## Maven local
Maven local is actually a repository, but it is located in your own computer. As simple as that. So you can use this directory to store your project dependencies.

Depending on your computer's operating system, the default location for Maven local repository may vary.

* Mac: `/Users/[username]/.m2`
* Linux: `/home/[username]/.m2`
* Windows: `C:\Users\[username]\.m2`

![.m2 folder contents][image_0]

## Publishing your library
First, we are going to navigate to our library's module, and there we are going to create a Gradle file, which will contain all the necessary code to publish the library locally. We are going to name it `publishLocal.gradle`. Its content will be the following:

```gradle
apply plugin: 'maven-publish' 

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

project.afterEvaluate {
    publishToMavenLocal { 
        def groupId = LIBRARY_GROUP 
        def artifactId = LIBRARY_ARTIFACT_ID 
        def versionName = LIBRARY_VERSION_NAME + "-local" 
        def debugSuffix = "-debug" 
        def releaseSuffix = "-release" 
        publishing { 
            publications { 
                LibraryRelease(MavenPublication) { 
                    from components.release 
                    artifact(sourceJar) 
                    setGroupId groupId 
                    setArtifactId artifactId 
                    version versionName + releaseSuffix 
                } 
                LibraryDebug(MavenPublication) { 
                    from components.debug 
                    artifact(sourceJar) 
                    setGroupId groupId 
                    setArtifactId artifactId 
                    version versionName + debugSuffix 
                } 
            } 
            publications.all { 
                pom.withXml { 
                    asNode().dependencies.'*'
                    .findAll() { 
                        it.scope.text() == 'runtime' &&
                        project.configurations.implementation.allDependencies.find {
                            dep -> dep.name == it.artifactId.text() 
                        }
                    }.each { it.scope*.value = 'compile'} 
                } 
            } 
        } 
        
        doLast { 
            def prettyPrint = { 
                1.upto(100, { print "=" }) 
                println()
            } 
            println() 
            prettyPrint()
            println "PUBLICATION FINISHED" 
            println "Artifact RELEASE: " + groupId + ":" + artifactId +  ":" + versionName + releaseSuffix 
            println "Artifact DEBUG: " + groupId + ":" + artifactId +  ":" + versionName + debugSuffix
            prettyPrint() 
        } 
    }
}
```

Now we can include this script in our module-level `build.gradle` file. And that's it!

```gradle
... 
apply from: 'publishLocal.gradle'
```

### The code in depth
If you take a look at our first line of code, you can see that we are including the [Maven Publish](https://docs.gradle.org/current/userguide/publishing_maven.html) plugin, which lets us publish artifacts to an Apache Maven repository.

Below that line, we configure a task called `sourceJar`, which will generate the `.jar` file with our library's source files.

Now we are getting to the meaty part. We configure `publishToMavenLocal` task, indicating the publications it should perform. In this case, there are two: `LibraryRelease` and `LibraryDebug`; each of them uses a different build variant.

> LIBRARY_GROUP, LIBRARY_ARTIFACT_ID, and LIBRARY_VERSION_NAME are constants defined in gradle.properties file.

Let's see each `MavenPublication` in detail:

```gradle
LibraryRelease(MavenPublication) { 
    from components.release 
    artifact(sourceJar) 
    setGroupId groupId 
    setArtifactId artifactId 
    version versionName + releaseSuffix 
}
```

We are taking `LibraryRelease` as an example. First, we apply the component with the release build variant. After that, we indicate that besides the `.aar` file that is going to be generated, we also want to compile a `.jar` file with the source files (this step is optional). Finally, we set a group ID, an artifact ID, and a version name for our library (in this case we are using different suffixes for debug and release variants).

It is now time to generate a `.pom` file for each of the publications. The `.pom` file contains all the necessary configuration details that Maven uses to compile our project. Here we can find, among other things, the dependencies our library includes in detail.

```gradle
publications.all { 
    pom.withXml { 
        asNode().dependencies.'*'
        .findAll() { 
            it.scope.text() == 'runtime' &&
            project.configurations.implementation.allDependencies.find {
                dep -> dep.name == it.artifactId.text() 
            }
        }.each { it.scope*.value = 'compile'} 
    }
}
```

> In my particular case, I had to write a special rule to correct the Maven scope of some of the dependencies I use in my library. This may not be necessary for you. More information on Maven scopes [here](https://reflectoring.io/maven-scopes-gradle-configurations/) and [here](https://andresalmiray.com/maven-scopes-vs-gradle-configurations/).

We end our script by printing a log with information about the published artifacts.

### Publishing to Maven local
If you head to Gradle's task tree, inside the publishing section you will see we have these tasks available, which we can run depending on our needs:

* `publish[MODULE_NAME]DebugPublicationToMavenLocal` compiles the project in debug mode.
* `publish[MODULE_NAME]ReleasePublicationToMavenLocal` compiles the project in release mode.
* `publishToMavenLocal` generates both debug and release versions.

![Gradle task tree][image_1]

In the following screenshot we can observe the output of a successful execution of `publishToMavenLocal`:

![Published library log][image_2]

> Gradle tasks can also be run from the command line, for example:
> `./gradlew publishToMavenLocal`
> `./gradlew publishLibraryDebugPublicationToMavenLocal`

Awesome! We already have our library, with its two variants and their artifacts, published in Maven local. But now, how can we integrate it into another project?

## Integrating your library in a project
To import our library into another library or into an Android app, first we need to open our project-level `build.gradle` file, and instruct it to use `mavenLocal()` as repository:

```gradle
buildscript { 
    repositories { 
        mavenLocal() 
        ... 
    } 
    ... 
} 

allprojects { 
    repositories { 
        mavenLocal() 
        ... 
    } 
}
```

Once this is done, we must include our library as a dependence in our app-level (or module-level) `build.gradle` file:

```gradle
dependencies { 
    implementation "com.me:my-awesome-lib:1.0.0" 
    ... 
}
```

> Our library's identifier, in this case `com.me:my-awesome-lib:1.0.0`, has the format `groupId:artifactId:versionName`, as we instructed to Gradle earlier. This information can also be seen in the .pom file generated with the publication.

So we arrived at the final step! Now it's time to compile our project and check that our library is imported correctly.

## Conclusion
Today we tried an alternative to publishing an Android library, which hopefully will help you speed up your development process. We configured a Gradle file to publish debug and release versions of an Android library to Maven local repository, and then we learnt how to integrate it into our main project. I hope this article serves as one more tool for your daily work as an Android library developer.

[image_0]: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r8jtaur3fkwp7x6xtvd2.png
[image_1]: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/72djerhkfu34y3lb9j8m.png
[image_2]: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sxmxsklx4q66jtkgbyme.png