1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

jCenter (bintray) にアップロードするbuild.gradle.kts

Last updated at Posted at 2020-08-15

Into the Sunset on May 1st: Bintray, JCenter, GoCenter, and ChartCenter
jCenterは閉鎖する(リードオンリーで参照のみ可能)とのことですので、Maven Centralに移行しましょう。

Maven Centralへ移行する方法はこちらを参照
jCenterで公開していたKotlinライブラリをMaven Centralで公開する


Android StudioのKTSサポートも強化され、ビルドスクリプトを徐々にGroovy DSLからKotlin DSLに乗り換える人も増えてきていると思います。この記事は jCenter (bintray) にアップロードするbuild.gradle のKTS版です。

projectのbuild.gradleに記述していた定義は、buildSrcでobjectとして定義してます。
Gradle Kotlin DSL (build.gradle.kts) で構造体っぽい定数定義をしたい を参照

ProjectProperties.kt
package build

object ProjectProperties {
    const val groupId: String = "com.example"

    private const val versionMajor: Int = 1
    private const val versionMinor: Int = 0
    private const val versionPatch: Int = 0
    const val versionName: String = "$versionMajor.$versionMinor.$versionPatch"
    const val versionCode: Int = versionMajor * 10000 + versionMinor * 100 + versionPatch

    object Url {
        const val site: String = "https://github.com/example/example"
        const val github: String = "https://github.com/example/example"
        const val scm: String = "scm:git:https://github.com/example/example.git"
    }
}

また、ホームディレクトリの.gradle/gradle.propertiesにて以下のようにbintrayのuse名とアップロード用のkeyを定義しておきます。ここは一緒。

bintray_user=xxx
bintray_key=xxxxxxxxxxxxxxxxxxxxxxxxxxxxx

各モジュールではライブラリ名や、グループID、バージョンを設定しています。
archivesBaseName がKotlin DSLでは base.archivesBaseName にしないといけないのがなかなか気づけなかったです。

base.archivesBaseName = "library name"
group = ProjectProperties.groupId
version = ProjectProperties.versionName

publishingタスク

Groovy DSLでは以下のように書いていました。

apply plugin: "maven-publish"

publishing {
    publications {
        bintray(MavenPublication) {
            artifact("$buildDir/libs/${libraryName}-${version}.jar")
            groupId = pj.groupId
            artifactId = libraryName
            version = pj.versions.name

            artifact sourcesJar

            pom.withXml {
                def node = asNode()

                def licenses = node.appendNode("licenses")
                appendLicense(licenses, "The MIT License", "https://opensource.org/licenses/MIT", "repo")

                appendScm(node, pj.scmConnection, pj.githubUrl)

                def dependencies = node.appendNode("dependencies")
                configurations.api.dependencies.each {
                    appendDependency(dependencies, it.group, it.name, it.version, "compile")
                }
                configurations.implementation.dependencies.each {
                    appendDependency(dependencies, it.group, it.name, it.version, "runtime")
                }
            }
        }
    }
}

static def appendLicense(parentNode, name, url, distribution) {
    def node = parentNode.appendNode("license")
    node.appendNode("name", name)
    node.appendNode("url", url)
    node.appendNode("distribution", distribution)
}

static def appendScm(parentNode, connection, url) {
    def node = parentNode.appendNode("scm")
    node.appendNode("connection", connection)
    node.appendNode("url", url)
}

static def appendDependency(parentNode, groupId, artifactId, version, scope) {
    def node = parentNode.appendNode("dependency")
    node.appendNode("groupId", groupId)
    node.appendNode("artifactId", artifactId)
    node.appendNode("version", version)
    node.appendNode("scope", scope)
}

これをKTSにすると以下のようになります。

plugins {
    `maven-publish`
}

publishing {
    publications {
        create<MavenPublication>("bintray") {
            artifact("$buildDir/outputs/aar/${base.archivesBaseName}-release.aar")
            groupId = ProjectProperties.groupId
            artifactId = base.archivesBaseName
            version = ProjectProperties.versionName
            artifact(tasks["sourcesJar"])
            pom.withXml {
                val node = asNode()
                val licenses = node.appendNode("licenses")
                appendLicense(licenses, "The MIT License", "https://opensource.org/licenses/MIT", "repo")
                appendScm(node, ProjectProperties.Url.scm, ProjectProperties.Url.github)
                val dependencies = node.appendNode("dependencies")
                configurations.api.get().dependencies.forEach {
                    appendDependency(
                        dependencies,
                        groupId = it.group ?: "",
                        artifactId = it.name,
                        version = it.version ?: "",
                        scope = "compile"
                    )
                }
                configurations.implementation.get().dependencies.forEach {
                    appendDependency(
                        dependencies,
                        groupId = it.group ?: "",
                        artifactId = it.name,
                        version = it.version ?: "",
                        scope = "runtime"
                    )
                }
            }
        }
    }
}

private fun appendLicense(parentNode: Node, name: String, url: String, distribution: String) {
    parentNode.appendNode("license").apply {
        appendNode("name", name)
        appendNode("url", url)
        appendNode("distribution", distribution)
    }
}

private fun appendScm(parentNode: Node, connection: String, url: String) {
    parentNode.appendNode("scm").apply {
        appendNode("connection", connection)
        appendNode("url", url)
    }
}

private fun appendDependency(
    parentNode: Node,
    groupId: String,
    artifactId: String,
    version: String,
    scope: String
) {
    parentNode.appendNode("dependency").apply {
        appendNode("groupId", groupId)
        appendNode("artifactId", artifactId)
        appendNode("version", version)
        appendNode("scope", scope)
    }
}

bintrayタスク

Groovy DSLでは以下のように書いていました。

bintray {
    user = project.hasProperty("bintray_user") ? bintray_user : ""
    key = project.hasProperty("bintray_key") ? bintray_key : ""
    publications = ["bintray"]

    pkg {
        repo = "maven"
        name = pj.groupId + "." + libraryName
        licenses = ["MIT"]
        websiteUrl = pj.siteUrl
        vcsUrl = pj.githubUrl + ".git"
        issueTrackerUrl = pj.githubUrl + "/issues"
        publicDownloadNumbers = true
        version {
            name = project.version
        }
    }
}

bintrayUpload.dependsOn assemble

これをKTSにすると以下のようになります。
※dryRun = trueなので実際のアップロードは行われません。アップロードする場合はfalseに書き換えるか、削除してください。

bintray {
    user = project.findProperty("bintray_user") as? String ?: ""
    key = project.findProperty("bintray_key") as? String ?: ""
    setPublications("bintray")

    dryRun = true

    pkg(closureOf<BintrayExtension.PackageConfig> {
        repo = "maven"
        name = ProjectProperties.groupId + "." + base.archivesBaseName
        setLicenses("MIT")
        websiteUrl = ProjectProperties.Url.site
        vcsUrl = ProjectProperties.Url.github + ".git"
        issueTrackerUrl = ProjectProperties.Url.github + "/issues"
        publicDownloadNumbers = true
        version = VersionConfig().apply {
            name = ProjectProperties.versionName
        }
    })
}

tasks.named("bintrayUpload") {
    dependsOn("assemble")
}

モジュール共通の処理をまとめる

マルチモジュールなプロジェクトだと、これらの処理をまとめたくなります。
ライブラリ名は base.archivesBaseName を参照しているので、完全にコピペになってしまいますからね。

Groovy DSLでは共通の処理をcommon.gradleとかにまとめて書いて、各gradleファイルからは以下のように書くだけでいけました。

apply from: "${rootDir}/common.gradle"

Kotlin DSLではこの方法が使えません(commonの方がGroovy DSLなら問題無くできますが)

ではどうやるかというと、これらの処理をbuildSrcで定義するようにします。

buildSrc側にビルドスクリプトを各場合はいくつかの細工が必要です。
まず、プラグインに関する処理の場合、buildscriptのdependenciesに書いているclasspathをimportする必要があります。
拡張関数などを使う場合はkotlin-stdlibなどもimportしましょう。

buildSrc/build.gradle.kts
plugins {
    kotlin("jvm") version "1.3.72"
    `kotlin-dsl`
}

repositories {
    google()
    jcenter()
}

dependencies {
    implementation(kotlin("stdlib"))
    implementation("com.android.tools.build:gradle:4.0.1")
    implementation("com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.5")
}

KTSのスクリプトはProjectに対する拡張関数として実装されているので、同様に拡張関数をbuildSrcとして実装することになります。
ただし、KTSで使用できるアクセッサの一部はライブラリではなく自動生成されているものがあるようで、それらは自前で実装する必要があります。

デフォルトで使用できるアクセッサは org.gradle.kotlin.dsl のパッケージになっているので、このパッケージをワイルドカードでimportしておいてコーディングすると良いでしょう。

import org.gradle.kotlin.dsl.*

シンボルを解決できないと言われたら、go to implementationでKTSから飛んで実装をコピペで持ってくれば良いです。
上記のpublishingとbintrayの設定をbuildSrcに切り出したものが以下になります。

Publishing.kt
package build.internal

import build.ProjectProperties
import groovy.util.Node
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.NamedDomainObjectProvider
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.internal.HasConvention
import org.gradle.api.plugins.BasePluginConvention
import org.gradle.api.plugins.ExtensionAware
import org.gradle.api.publish.PublishingExtension
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.kotlin.dsl.create
import org.gradle.kotlin.dsl.get
import org.gradle.kotlin.dsl.getPluginByName
import org.gradle.kotlin.dsl.named

private fun Project.publishing(configure: PublishingExtension.() -> Unit): Unit =
    (this as ExtensionAware).extensions.configure("publishing", configure)

private val Project.base: BasePluginConvention
    get() = ((this as? Project)?.convention
        ?: (this as HasConvention).convention).getPluginByName("base")

private val NamedDomainObjectContainer<Configuration>.api: NamedDomainObjectProvider<Configuration>
    get() = named<Configuration>("api")

private val NamedDomainObjectContainer<Configuration>.implementation: NamedDomainObjectProvider<Configuration>
    get() = named<Configuration>("implementation")

fun Project.publishingSettings() {
    publishing {
        publications {
            create<MavenPublication>("bintray") {
                artifact("$buildDir/outputs/aar/${base.archivesBaseName}-release.aar")
                groupId = ProjectProperties.groupId
                artifactId = base.archivesBaseName
                version = ProjectProperties.versionName
                artifact(tasks["sourcesJar"])
                pom.withXml {
                    val node = asNode()
                    val licenses = node.appendNode("licenses")
                    appendLicense(
                        licenses,
                        "The MIT License",
                        "https://opensource.org/licenses/MIT",
                        "repo"
                    )
                    appendScm(node, ProjectProperties.Url.scm, ProjectProperties.Url.github)
                    val dependencies = node.appendNode("dependencies")
                    configurations.api.get().dependencies.forEach {
                        appendDependency(
                            dependencies,
                            groupId = it.group ?: "",
                            artifactId = it.name,
                            version = it.version ?: "",
                            scope = "compile"
                        )
                    }
                    configurations.implementation.get().dependencies.forEach {
                        appendDependency(
                            dependencies,
                            groupId = it.group ?: "",
                            artifactId = it.name,
                            version = it.version ?: "",
                            scope = "runtime"
                        )
                    }
                }
            }
        }
    }
}

private fun appendLicense(parentNode: Node, name: String, url: String, distribution: String) {
    parentNode.appendNode("license").apply {
        appendNode("name", name)
        appendNode("url", url)
        appendNode("distribution", distribution)
    }
}

private fun appendScm(parentNode: Node, connection: String, url: String) {
    parentNode.appendNode("scm").apply {
        appendNode("connection", connection)
        appendNode("url", url)
    }
}

private fun appendDependency(
    parentNode: Node,
    groupId: String,
    artifactId: String,
    version: String,
    scope: String
) {
    parentNode.appendNode("dependency").apply {
        appendNode("groupId", groupId)
        appendNode("artifactId", artifactId)
        appendNode("version", version)
        appendNode("scope", scope)
    }
}
Bintray.kt
package build.internal

import build.ProjectProperties
import com.jfrog.bintray.gradle.BintrayExtension
import org.gradle.api.Project
import org.gradle.api.internal.HasConvention
import org.gradle.api.plugins.BasePluginConvention
import org.gradle.api.plugins.ExtensionAware
import org.gradle.kotlin.dsl.closureOf
import org.gradle.kotlin.dsl.getPluginByName

private fun Project.bintray(configure: BintrayExtension.() -> Unit): Unit =
    (this as ExtensionAware).extensions.configure("bintray", configure)

private val Project.base: BasePluginConvention
    get() = ((this as? Project)?.convention ?: (this as HasConvention).convention).getPluginByName("base")

fun Project.bintraySettings() {
    bintray {
        user = project.findProperty("bintray_user") as? String ?: ""
        key = project.findProperty("bintray_key") as? String ?: ""
        setPublications("bintray")

        dryRun = true

        pkg(closureOf<BintrayExtension.PackageConfig> {
            repo = "maven"
            name = ProjectProperties.groupId + "." + base.archivesBaseName
            setLicenses("MIT")
            websiteUrl = ProjectProperties.Url.site
            vcsUrl = ProjectProperties.Url.github + ".git"
            issueTrackerUrl = ProjectProperties.Url.github + "/issues"
            publicDownloadNumbers = true
            version = VersionConfig().apply {
                name = ProjectProperties.versionName
            }
        })
    }

    tasks.named("bintrayUpload") {
        dependsOn("assemble")
    }
}

※dryRun = trueなので実際のアップロードは行われません。アップロードする場合はfalseに書き換えるか、削除してください。

これで、buildGradleからはpublishingSettings()bintraySettings()というメソッドが使えるようになりますので以下のように呼び出します。

build.gradle.kts
publishingSettings()
bintraySettings()

以上です。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?