ひだまりソケットは壊れない

ソフトウェア開発に関する話を書きます。 最近は主に Android アプリ、Windows アプリ (UWP アプリ)、Java 関係です。

まじめなことを書くつもりでやっています。 適当なことは 「一角獣は夜に啼く」 に書いています。

Android アプリ用ライブラリの AAR パッケージを Maven Central で公開する方法

AAR パッケージを Maven Central に公開している人は結構いるけど、build.gradle をどういう感じに書けばいいのかわかりづらかったので書き残しておきます。

使用するリポジトリとビルドツール

Sonatype OSS Maven Repository の使用について

Maven Central Repository にパッケージを公開する方法の 1 つに、Sonatype OSS Maven Repository にパッケージを公開し、それを Maven Central Repository に同期するという方法があります。 これは Apache による Maven Central への公開方法のガイドにも書かれています。

多くの場合、Maven Central への公開には Sonatype OSS Maven Repository を使うのが簡単で良いでしょう。

Sonatype OSS Maven Repository へのアップロードと Maven Central Repository への同期

基本的な流れ

次のガイドを見るとある程度詳細に書かれていますので、この記事では詳しく説明しません。

1. 「Introduction」 から 6. 「Central Sync Requirement」 までは書かれている通りに作業したり、単に読んだりするだけで良いです。 実質的に必要な作業は、Sonatype JIRA アカウントを作って、JIRA チケットを作成する、ということと、GPG クライアントをインストールし、GPG キーを作成して公開鍵を配布するというぐらいのはずです (GPG 鍵周りの話は後述します)。 JIRA チケットを作ってから実際にリポジトリにアップロードできるようになるまでに 2 日ほどかかりますので、ソースコードの方の準備ができてなくても、JIRA チケットを作る作業は先に進めておくといいかもしれません。 ちなみに私が android-lib-ZXingCaptureActivity のリリースをしたときに作った JIRA チケットは次のものです。

難しいのは 7. の箇所ですね。 パッケージを作成して、Sonatype OSS Maven Repository にアップロードします。 Maven を使う場合や Ant を使う場合などの説明があり、Gradle についても 7d. 「Stage Releases with Gradle」 にて次のページが紹介されています。

JAR をリリースするのであればここを参考にすればよいでしょう。 AAR をリリースする場合の方法は後で説明します。

アップロードした artifact は、(リリースバージョンの場合) 最初は Staging Repository になります。 これをリリースするためには、8a. に書かれている手順に従って web 上で操作して、Staging Repository を close し、それから release します。 さらに、初回のリリースの場合は、9. に書かれているように JIRA チケットにコメントを行い、Maven Central への同期を有効化してもらう必要があります。 (同期は 2 時間程度ごとに行われるようです。)

GPG クライアントをインストールし、GPG キーを作成して公開鍵を配布する

するべきことは、Maven Central に公開する成果物に署名するための GPG キーを生成して、公開鍵を配布することです。 方法は次のページに書かれています。

まず、GnuPG をインストールします。 次に、次のコマンドで鍵ペアを生成します。

gpg --gen-key

そして、公開鍵を次のコマンドで配布します。 keyid (下の例だと C6EED57A) は適切に置きかえてください。

gpg --keyserver hkp://pool.sks-keyservers.net --send-keys C6EED57A

Gradle で AAR パッケージを生成して Maven リポジトリにアップロードする方法

AAR パッケージをアップロードするための build.gradle の設定を説明していきます。

Maven リポジトリへの公開方法としては maven-publish プラグイン もありますが、ここでは uploadConfigurationName タスクmaven プラグイン の組み合わせを使用します。

uploadConfigurationName タスクについて

uploadConfigurationName タスクは、Gradle の標準プラグインのひとつである base プラグインで提供されるものです。 Android Gradle プラグインを使用する場合は、自動的にこのプラグインが適用されるようです *1

uploadConfigurationName タスクは、その名のとおり、ConfigurationNameコンフィギュレーションに属する artifact をビルドし、ビルド結果をアップロードするタスクです。 例えば archives コンフィギュレーションに属する artifact をビルドしてアップロードするために、uploadArchives というタスクを使用できます。

archives コンフィギュレーションについて

依存ライブラリなどを管理するためのコンフィギュレーションの仕組みが Gradle にはあるわけですが、アップロードする artifact の管理にもコンフィギュレーションが使用されます。 Java プラグインによってプロジェクトで生成される JAR が含まれるデフォルトのコンフィギュレーションarchives です。 同様に、Android Gradle プラグインで生成される AAR パッケージも、デフォルトで archives コンフィギュレーションに含まれます。

自分でコンフィギュレーションを定義して、そこに AAR パッケージを含めるようにしてもいいのですが、特にそうする理由がなければ、この archives コンフィギュレーションをそのまま使えばいいでしょう。

Javadoc の JAR と sources の JAR を作成するタスクを定義し、それらの JAR を archives configuration に追加する

Maven Central に公開する場合は、Javadoc を固めた JAR とソースコードを固めた JAR もアップロードする必要があります。 そのため、この 2 つの JAR を生成するタスクを定義し、それらの JAR を archives コンフィギュレーションに含めるようにする必要があります。

release variant で Javadoc JAR と sources JAR を作ればいいと思いますので、私は次のようにタスクを定義しました。

android.libraryVariants.all { variant ->
    if (variant.name == 'release') {
        def javadocTaskName = "javadoc${variant.name.capitalize()}";
        task(javadocTaskName, type: Javadoc) {
            description "Generates Javadoc for ${variant.name}."
            source = variant.javaCompile.source
            destinationDir = reporting.file("${variant.name}-docs")
            ext.androidJar = "${android.plugin.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar"
            classpath = files(variant.javaCompile.classpath.files) + files(ext.androidJar)
        }
        task("javadocsJar${variant.name.capitalize()}", type: Jar) {
            classifier = "javadoc"
            from tasks[javadocTaskName].destinationDir
        }
        task("sourcesJar${variant.name.capitalize()}", type: Jar) {
            classifier = "sources"
            from variant.javaCompile.source
        }

        /* artifacts management.
         * See: http://www.gradle.org/docs/current/userguide/artifact_management.html */
        artifacts {
            archives tasks["javadocsJar${variant.name.capitalize()}"]
            archives tasks["sourcesJar${variant.name.capitalize()}"]
        }
    }
}

もうちょっときれいに書ければ嬉しいのですが、なかなか難しそうです。

これ以降は AAR でも JAR でも大差ないのですが、一通り説明しておきます。

Maven の POM 情報を記述する

Maven プラグインを使って、uploadArchives の設定として POM の情報を記述します。

ここで記述されたプロジェクト名などを元に POM が生成されます。 以下のような感じになります。

apply plugin: 'maven'

version = "1.0-SNAPSHOT" // Maven のバージョン規約で、開発中のものには “-SNAPSHOT” を付ける
ext.isReleaseVersion = !version.endsWith("SNAPSHOT")
group = "org.example.android.sugoi.project"

uploadArchives {
    repositories.mavenDeployer {
        // 後からここに署名のための記述やリポジトリの URL の記述などを行う

        pom.artifactId = 'sugoi-library'
        pom.project {
            name 'Sugoi library'
            packaging 'aar'
            description 'This is Sugoi library'
            url 'http://sugoi.android.example.org/'

            scm {
                url 'scm:git@github.com:yourname/sugoi-library.git'
                connection 'scm:git@github.com:yourname/sugoi-library.git'
                developerConnection 'scm:git@github.com:yourname/sugoi-library.git'
            }

            licenses {
                license {
                    name 'The Apache License, Version 2.0'
                    url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
                    distribution 'repo'
                }
            }

            developers {
                developer {
                    id 'yourname'
                    name 'Your Name'
                    email 'yourname@example.org'
                }
            }
        }
    }
}
署名やアップロード先の記述

あとは、signing プラグインを使って署名するようにする設定と、アップロード先の Maven Repository の設定を書けば完了です。

次のような記述になります。

apply plugin: 'maven'
apply plugin: 'signing'

signing {
    required { isReleaseVersion && gradle.taskGraph.hasTask("uploadArchives") }
    sign configurations.archives
}

uploadArchives {
    repositories.mavenDeployer {
        beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }

        def sonatypeRepo = isReleaseVersion ?
                "https://oss.sonatype.org/service/local/staging/deploy/maven2/" :
                "https://oss.sonatype.org/content/repositories/snapshots/"
        repository(url: sonatypeRepo) {
            authentication(userName: sonatypeUsername, password: sonatypePassword)
        }

        // ...
}

signing プラグインで署名するためには、次のように Gradle プロパティを設定しておく必要があります。 ここで指定する keyId などは、「GPG クライアントをインストールし、GPG キーを作成して公開鍵を配布する」 の箇所で生成した鍵のものです。

signing.keyId=24875D73
signing.password=secret
signing.secretKeyRingFile=/Users/me/.gnupg/secring.gpg

また、sonatypeRepo にアクセスするためのユーザー名とパスワードを、次のように Gradle プロパティで設定する必要があります。

sonatypeUsername=your_username
sonatypePassword=your_password

Gradle プロパティは、プロジェクトや Gradle のホームディレクトリ (デフォルトでは USER_HOME/.gradle) に gradle.properties というファイルを作ってそこに記述することができますし、Gradle コマンドの -P オプションで指定することもできます *2

最終的な build.gralde のサンプル

私が android-lib-ZXingCaptureActivity 2.3.0-1.2 をリリースするときに使った build.gralde をサンプルとして置いておきます。

uploadArchive タスクを実行することで Sonatype のリポジトリにアップロードされます。

参考文献

*1:どういう継承関係で適用されるのかちゃんと調べてません。

*2:パスワードについては -P オプションで指定するようにした方がいいでしょう。