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) で構造体っぽい定数定義をしたい を参照
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しましょう。
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に切り出したものが以下になります。
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)
}
}
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()
というメソッドが使えるようになりますので以下のように呼び出します。
publishingSettings()
bintraySettings()
以上です。