参考
はじめに
本記事ではKotlin/JVM,JS,Nativeで使用出来るマルチプラットフォームライブラリの作り方、及びその公開方法(ローカル、セントラル両方)について解説します。
公開方法はいくつか方法がありますが、今回はビルドはGradle、リポジトリはMavenを使用します。
また、自分はKotlinマルチプラットフォームのテンプレートをこの記事を通して作ります。この記事を追って行けば自分自身のKotlinマルチプラットフォームのテンプレートが作成出来ます。また、めんどくさい人は、私のテンプレートを変更しつつ使ってみてください。
プロジェクトセットアップ
作成
IntelliJ Ideaをインストールしていない人はインストールしてください。
IntelliJ Ideaを開いたら左上のファイル→新規→プロジェクトとすると以下のポップアップが出てきます。
ジェネレータはKotlin マルチプラットフォーム、Project templateはLibraryを選択します。
また、アーティファクトコーディネートでグループ名を設定しておきます。
ローカルだけで良い場合はなんでもいいですが、セントラルのMavenリポジトリを作成するまでセントラルで有効かどうかはわかりません。現在、個人の場合はio.github.id
が安牌です。私はio.github.arashiyama11
をグループ名にしました。
現在はcom.github.id
は使用不可なので注意してください。
Git連携
Mavenリポジトリを作成するときに必要になるのでGit連携をしておきます。
VHS→GitHubでプロジェクトを作成 から行えます。
コード作成
commonMain
まず、commonMain直下のkotlinディレクトリを左クリックし、ディレクトリをクリックします。新規ディレクトリ名にはグループ名をそのままぶち込んでください。
例えばio.github.arashiyama11
と打ち込むとちゃんとcommonMain/kotlin/io/github/arashiyama11
というディレクトリが作成されます。
次にそのディレクトリに任意の名前のKotlinファイルを作成してください。
自分はTemp.ktを作成しました
現在はまだBetaですが便利なのでexpect,actualなるものを使っていきます。
expect,actualはプラットフォーム依存のクラス等を書く時に使います。
package io.github.arashiyama11
//プラットフォーム依存のクラス
expect open class Plat() {
//プラットフォーム名を返す
fun platform():String
}
//共通の関数
class Temp: Plat() {
fun hello()="Hello!"
}
expectはcommonMainにて、actualはその他jvmMain,jsMain,nativeMain等で使います。
commonMain内でexpect宣言されたクラス、関数、あるいはオブジェクトは実装を持ちませんが、存在する全ての~~Mainディレクトリ内でactualを用いて実装しなければなりません。
その他Main
その他Mainも同じようにディレクトリ、ファイルを作成します。
がIntelij Idea君がサボらせてくれるというのでお言葉に甘えましょう。
現在Platクラスはexpect修飾子の条件を満たしていないため赤線が引っ張られているはずです。そこにカーソルを合わせてAlt+Shift+Enterを押せば自動で適当にディレクトリを作成してくれます。一個づつしか作れないので必要なだけやりましょう。
確認すると各Main下にちゃんとコードが生成されていました。
package io.github.arashiyama11
actual open class Plat actual constructor() {
actual fun platform(): String {
TODO("Not yet implemented")
}
}
それを各プラットフォームごとに変更しましょう。
jsMainならちゃんとjsのconsole.log
等が使用出来ます。
package io.github.arashiyama11
actual open class Plat actual constructor() {
actual fun platform() = "js"
}
テストを書く
テストも同じようなものです。
commonTest
まずcommonTestから書きます。
commonTest/kotlinを左クリックして、ファイルを作成から、io.github.arashiyama11.Test
と入力して以下のようにします。
package io.github.arashiyama11
import kotlin.test.Test
import kotlin.test.assertEquals
class Test {
@Test
fun testHello(){
assertEquals("Hello!",Temp().hello())
}
}
Testアノテーションが付けられたメソッドがテスト対象です。
個別に実行するなら左側にある緑色の三角、全て実行するならターミナルで以下を実行しましょう、
./gradlew check
テストが失敗し、例外が投げられるとBUILD FAILED
となり、全て成功するとBUILD SUCCESSFUL
と表示されます。
その他Test
jsが一番上にあるのでjsを例に取ります。
jsTest/kotlin/io/github/arashiyama11/JSTest
を作成します。
package io.github.arashiyama11
import kotlin.test.Test
import kotlin.test.assertEquals
class JSTest {
@Test
fun testPlat(){
assertEquals("js",Temp().platform())
}
}
commonとやることは同じです。
ローカルのMavenリポジトリに公開
build.gradle.ktsのpluginsに一行追加するだけです。
plugins {
kotlin("multiplatform") version "1.7.21"
//下の一行を追加
`maven-publish`
}
group = "io.github.arashiyama11"
version = "1.0.0"
ターミナルで以下を実行します。
./gradlew publishToMavenLocal
使用方法は使用したいプロジェクトのbuild.gradle.kts
に以下の記述を足すだけです。
repositories {
mavenCentral()
//ここを追加
mavenLocal()
}
dependencies {
//ここを追加
implementation("io.github.arashiyama11:kotlin-multiplatform-library-template:1.0.0")
testImplementation(kotlin("test"))
}
implementationの引数は"${グループ名}:${プロジェクト名}:${バージョン}"
となっています。グループ名、バージョンはbuild.gradle.kts
、プロジェクト名はsettings.gradle.kts
のが使用されます。
セントラルのMavenリポジトリに公開
ネット上のMavenリポジトリに公開し、誰でも使えるようにしていきます。
リポジトリ作成
Sonatype Jiraを用いて作成します。
こちらにアクセスしてサインアップを行ってください。
進んで行くと以下のような画面になるはずです。
真ん中のCreate an issue
を選択し、プロジェクトはCommunity Support - Open
、課題タイプはNew Project
を選択します。
次の画面では課題の詳細を入力します。
私は次のように入力しました。
基本的にBotが対応するのであまり畏まる必要はありません。
- 要約:Create repository for io.github.arashiyama11:kotlin-multiplatform-library-template
- 説明:空欄
- Group Id:io.github.arashiyama11
- Project URL:https://github.com/arashiyama11/kotlin-multiplatform-library-template
- SCM url:https://github.com/arashiyama11/kotlin-multiplatform-library-template.git
- Username(s):arashiyama
- Already Synced to Central:No
課題を作成してから少し経つとBotからコメントが付きます。
私は以下のようなコメントが付きました。
グループ名が無効だったりするとここで直すように言われます。
To continue the registration process, please follow these steps:
1.Create a temporary, public repository called https://github.com/arashiyama11/OSSRH-87327 to verify github account ownership.
2.Edit this ticket and set Status to Open.
If you do not own this github account, you must define a new groupId.
言われた通りにOSSRH-87327
リポジトリを作成し上のほうにあるRespond
をクリックします。
10分ぐらい待つとお祝いがきました。(長いので最初の一部のみ)
Congratulations! Welcome to the Central Repository!
io.github.arashiyama11 has been prepared, now user(s) arashiyama can:
Publish snapshot and release artifacts to s01.oss.sonatype.org
ちゃんとリポジトリが作成できたようですね。
GPGキーの取得
Mavenセントラルにプロジェクトを公開するにはGPG署名する必要があります。まだGPGのツールをダウンロードしていない方はgnupg.orgからダウンロードしてください。
Mac,WinのものにはGUIツールがついていますが、後々ファイル出力する必要があるのでIntellij Idea上のコマンドラインで行います。
以下のコマンドで作成できます。
> gpg --full-gen-key
gpg (GnuPG) 2.3.8; Copyright (C) 2021 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
ご希望の鍵の種類を選択してください:
(1) RSA と RSA
(2) DSA と Elgamal
(3) DSA (署名のみ)
(4) RSA (署名のみ)
(9) ECC (署名と暗号化) *デフォルト
(10) ECC (署名のみ)
(14) カードに存在する鍵
あなたの選択は? 1
RSA 鍵は 1024 から 4096 ビットの長さで可能です。
鍵長は? (3072) 4096
要求された鍵長は4096ビット
鍵の有効期限を指定してください。
0 = 鍵は無期限
<n> = 鍵は n 日間で期限切れ
<n>w = 鍵は n 週間で期限切れ
<n>m = 鍵は n か月間で期限切れ
<n>y = 鍵は n 年間で期限切れ
鍵の有効期間は? (0)0
鍵は無期限です
これで正しいですか? (y/N) y
GnuPGはあなたの鍵を識別するためにユーザIDを構成する必要があります。
最初の質問にはRSAとRSAの1を選択
鍵長は4096
有効期限は任意です。
また、この後名前、メールアドレス、パスワード等が聞かれるので適宜入力してください。
完了すると最後に公開鍵が出力されて終了します。
GPG公開鍵のアップロード
例のため7A5D73CFEDDDBC915986998A36271B955BEF072A
という公開鍵が出来たとします。
公開鍵の最後の8文字、この場合は5BEF072A
をサーバーにアップロードします。
コマンドは以下の通りです。
gpg --keyserver keyserver.ubuntu.com --send-keys 5BEF072A
次に秘密鍵の出力をします。
gpg --export-secret-keys --output private-key 5BEF072A
これでprivate-keyフォルダに秘密鍵が出力されます
公開ロジックの作成
公開ロジックは長いので別ディレクトリに作成します。
convention-plugins/build.gradle.kts
を作成して以下の内容を書き込みます。
plugins {
`kotlin-dsl`
}
repositories {
gradlePluginPortal()
}
また、convention-plugins/src/main/kotlin/convention.publication.gradle.kts
を作成し、以下を適切に変更して書き込みます。
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.tasks.bundling.Jar
import org.gradle.kotlin.dsl.`maven-publish`
import org.gradle.kotlin.dsl.signing
import java.util.*
plugins {
`maven-publish`
signing
}
ext["signing.keyId"] = null
ext["signing.password"] = null
ext["signing.secretKeyRingFile"] = null
ext["ossrhUsername"] = null
ext["ossrhPassword"] = null
val secretPropsFile = project.rootProject.file("local.properties")
if (secretPropsFile.exists()) {
secretPropsFile.reader().use {
Properties().apply {
load(it)
}
}.onEach { (name, value) ->
ext[name.toString()] = value
}
} else {
ext["signing.keyId"] = System.getenv("SIGNING_KEY_ID")
ext["signing.password"] = System.getenv("SIGNING_PASSWORD")
ext["signing.secretKeyRingFile"] = System.getenv("SIGNING_SECRET_KEY_RING_FILE")
ext["ossrhUsername"] = System.getenv("OSSRH_USERNAME")
ext["ossrhPassword"] = System.getenv("OSSRH_PASSWORD")
}
val javadocJar by tasks.registering(Jar::class) {
archiveClassifier.set("javadoc")
}
fun getExtraString(name: String) = ext[name]?.toString()
publishing {
repositories {
maven {
name = "sonatype"
setUrl("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/")
credentials {
username = getExtraString("ossrhUsername")
password = getExtraString("ossrhPassword")
}
}
}
publications.withType<MavenPublication> {
artifact(javadocJar.get())
pom {
//下の3行変更
name.set("kotlin-multiplatform-library-template")
description.set("kotlin-multiplatform-library-template")
url.set("https://github.com/arashiyama11/kotlin-multiplatform-library-template")
licenses {
license {
name.set("MIT")
url.set("https://opensource.org/licenses/MIT")
}
}
developers {
developer {
//下2行変更
id.set("arashiyama11")
name.set("arashiyama")
}
}
scm {
//下1行変更
url.set("https://github.com/arashiyama11/kotlin-multiplatform-library-template")
}
}
}
}
signing {
sign(publishing.publications)
}
settings.gradle.kts
に一行書き足します。
rootProject.name = "kotlin-multiplatform-library-template"
//下一行を追加
includeBuild("convention-plugins")
build.gradle.kts
のmaven-publish
をid("convention.publication")
に変えます。
plugins {
kotlin("multiplatform") version "1.7.21"
id("convention.publication")
}
また、local.properties
ファイルを作成し、以下を書き込みます。
# The GPG key pair ID (last 8 digits of its fingerprint)
signing.keyId=...
# The passphrase of the key pair
signing.password=...
# Private key you exported earlier
signing.secretKeyRingFile=...
# Your credentials for the Jira account
ossrhUsername=...
ossrhPassword=...
signing.keyIdはキーサーバーにアップロードしたものと同じ、公開鍵の最後の8文字です。(digitsと書いてあるのはそれを16進数の数字と捉えているからだと思います。)今回の場合は5BEF072A
です。
signing.passwordはGPG鍵作成の時に聞かれたパスワードです。
signing.secretKeyRingFileは先程エクスポートしたファイルです。特に変えてなければprivate-key
です
ossrhUsername,ossrhPasswordはJiraアカウントのユーザー名とパスワードです。
公開
やっと公開作業です
./gradlew publishAllPublicationsToSonatypeRepository
を実行してください。
成功したら以下にアクセスしてください。
ログインしたら左側のStaging Repositories
を選択すると真ん中に自分が作成したリポジトリがあるはずです。
それを選択して上にあるCloseボタンを押して一旦クローズしてください。
Closeが完了するとReleaseボタンが押せるようになるので、押してリリースします。
最後に先程のJiraの課題に戻り、The first version has been released
と報告し
Central sync is activated for io.github.arashiyama11. After you successfully release, your component will be available to the public on Central https://repo1.maven.org/maven2/, typically within 30 minutes, though updates to https://search.maven.org can take up to four hours.
みたいなのが帰ってくればもう完了です。あとは反映されるまで待つだけです。
反映されれば、mavenLocal()
なしでもライブラリが使用出来るはずです。
repositories {
mavenCentral()
}
dependencies {
//ここを追加
implementation("io.github.arashiyama11:kotlin-multiplatform-library-template:1.0.0")
testImplementation(kotlin("test"))
}
終わりに
バージョンアップする時はbuild.gradle
のバージョンを上げてから公開作業をもう一度行ってください。
また、2回目以降のライブラリ作成で、グループ名を変えない場合はSonatype Jiraの課題制作やGPGの鍵出力は必要ありません。2回目以降で変える(変わる)のはsettings.gradle.kts
のrootProject.name
やconvention.publication.gradle.kts
のURL類ぐらいです。(コードは言うまでもなく)