前回のエントリー「kotlinx datetime と Spring Boot , 2つの期間の重なりを判定」の追伸で、 Spring Boot ではなく、kotlinc で実行できないか試した。 結果、 import kotlinx.datetime.* は kotlinc で実行できなくて、 import java.time.* は kotlinc で実行できた。
でも、今頃気づいたのだが、kotlinc コマンドが JavaVM 用のものだったから、当然そのようになっただけで、 kotlin native 用の kotlinc-native コマンドを使えば、実行できるのではないのか?
そこで、 https://github.com/JetBrains/kotlin/releases/tag/v1.8.10 から kotlin-compiler-1.8.10.zip ではなく、kotlin-native-linux-x86_64-1.8.10.tar.gz を ダウンロードして使ってみた。 でも、 結局 kotlinc-native コマンドを使っても動かなかった。 バージョンが上がればそのうち動くようになるのかも知れない。
それで思い出したのだが、以前のエントリーで kotlin native は既に試していた。 それに沿って Gradle プロジェクトとしてこのコードをビルドしたところ、 そちらは難なく実行できたので、それを備忘録として書き残します。
kotlinc-native でうまく実行できなかった datetime.main.kts (コードが悪いわけではない!):
// datetime.main.kts
@file:Repository("https://repo1.maven.org/maven2/")
@file:DependsOn("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0")
import kotlinx.datetime.*
data class Period(val start: String, val stop: String)
fun isOverlapped(a: Period, b: Period): Boolean {
val toLocalDateTime: (String)->LocalDateTime = {
val v = LocalDate.parse(it)
val h = 0
val m = 0
LocalDateTime(v.year, v.month, v.dayOfMonth, h, m)
}
// (t0 < t1) かを検査:
val isBefore: (LocalDateTime, LocalDateTime)-> Boolean = { t0,t1->
(t0.compareTo(t1) < 0)
}
val isNotOverlapped: (LocalDateTime, LocalDateTime, LocalDateTime, LocalDateTime)->Boolean = { aStart, aStop, bStart, bStop->
//
// 重なりがないケースを考える:
//
// case 0)
// aStop が bStart より古い場合
// ---------------------------
// Period a: <--->
// Period b: <--->
// ---------------------------
val notOverlapped0 = isBefore(aStop, bStart)
// case 1)
// bStop が aStart より古い場合
// ---------------------------
// Period a: <--->
// Period b: <--->
// ---------------------------
val notOverlapped1 = isBefore(bStop, aStart)
// case 0 or 1 が true の場合
(notOverlapped0 || notOverlapped1)
}
return !isNotOverlapped(
toLocalDateTime(a.start),
toLocalDateTime(a.stop),
toLocalDateTime(b.start),
toLocalDateTime(b.stop))
}
val test1: ()->Unit = {
val a = Period("2022-01-01", "2022-12-31")
val b = Period("2021-12-01", "2021-12-31")
val result = isOverlapped(a,b)
println("- test1 assert false : ${result}")
}
val test2: ()->Unit = {
val a = Period("2022-01-01", "2022-12-31")
val b = Period("2022-01-10", "2022-01-31")
val result = isOverlapped(a,b)
println("- test1 assert true : ${result}")
}
test1()
test2()
では実行してみます。
まず kotlinc-native コマンドのバージョン確認:
$ kotlinc-native -version
info: kotlinc-native 1.8.10 (JRE 11.0.17+8-post-Ubuntu-1ubuntu220.04)
Kotlin/Native: 1.8.10
実行:
/kotlinc-native -script datetime.main.kts
error: compilation failed: Front-end Internal error: Failed to analyze declaration Datetime_main
File being compiled: (6,26) in /path/to/datetime.main.kts
The root cause org.jetbrains.kotlin.resolve.lazy.NoDescriptorForDeclarationException was thrown at: org.jetbrains.kotlin.resolve.lazy.BasicAbsentDescriptorHandler.diagnoseDescriptorNotFound(AbsentDescriptorHandler.kt:18)
* Source files: datetime.main.kts
* Compiler version info: Konan: 1.8.10 / Kotlin: 1.8.10
* Output kind: PROGRAM
exception: org.jetbrains.kotlin.util.KotlinFrontEndException: Front-end Internal error: Failed to analyze declaration Datetime_main
File being compiled: (6,26) in /path/to/datetime.main.kts
The root cause org.jetbrains.kotlin.resolve.lazy.NoDescriptorForDeclarationException was thrown at: org.jetbrains.kotlin.resolve.lazy.BasicAbsentDescriptorHandler.diagnoseDescriptorNotFound(AbsentDescriptorHandler.kt:18)
Front-end Internal error というエラーが出ます。長いので省略しています。 原因はわかりません。
それでは、datetime.main.kts を Gradle プロジェクトにして native 実行できるファイルを生成します。 基本的に、このエントリー「Kotlin Native で テキストファイルを読み込む Hello, World!」の通りです。
$ mkdir mydatetime
$ cd mydatetime/
$ touch build.gradle
$ mkdir -p src/nativeMain/kotlin
$ touch src/nativeMain/kotlin/main.kt
build.gradle
plugins {
id 'org.jetbrains.kotlin.multiplatform' version '1.8.10'
}
repositories {
mavenCentral()
}
kotlin {
linuxX64('native'){
binaries {
executable()
}
}
sourceSets {
commonMain {
dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-datetime:0.4.0"
}
}
}
}
src/nativeMain/kotlin/main.kt
import kotlinx.datetime.*
data class Period(val start: String, val stop: String)
fun isOverlapped(a: Period, b: Period): Boolean {
val toLocalDateTime: (String)->LocalDateTime = {
val v = LocalDate.parse(it)
val h = 0
val m = 0
LocalDateTime(v.year, v.month, v.dayOfMonth, h, m)
}
// (t0 < t1) かを検査:
val isBefore: (LocalDateTime, LocalDateTime)-> Boolean = { t0,t1->
(t0.compareTo(t1) < 0)
}
val isNotOverlapped: (LocalDateTime, LocalDateTime, LocalDateTime, LocalDateTime)->Boolean = { aStart, aStop, bStart, bStop->
//
// 重なりがないケースを考える:
//
// case 0)
// aStop が bStart より古い場合
// ---------------------------
// Period a: <--->
// Period b: <--->
// ---------------------------
val notOverlapped0 = isBefore(aStop, bStart)
// case 1)
// bStop が aStart より古い場合
// ---------------------------
// Period a: <--->
// Period b: <--->
// ---------------------------
val notOverlapped1 = isBefore(bStop, aStart)
// case 0 or 1 が true の場合
(notOverlapped0 || notOverlapped1)
}
return !isNotOverlapped(
toLocalDateTime(a.start),
toLocalDateTime(a.stop),
toLocalDateTime(b.start),
toLocalDateTime(b.stop))
}
fun main() {
val test1: ()->Unit = {
val a = Period("2022-01-01", "2022-12-31")
val b = Period("2021-12-01", "2021-12-31")
val result = isOverlapped(a,b)
println("- test1 assert false : ${result}")
}
val test2: ()->Unit = {
val a = Period("2022-01-01", "2022-12-31")
val b = Period("2022-01-10", "2022-01-31")
val result = isOverlapped(a,b)
println("- test1 assert true : ${result}")
}
test1()
test2()
}
以前のコードと基本的には同じです。 ただし、トップレベルに val test1 のように変数を宣言できないようなので、 そこのみ fun main() 関数でくくりました。
あとはビルドして native 実行ファイルをつくるだけです。
Spring Boot方式 よりは負担が少ない!
まず環境の確認:
$ gradle -version
------------------------------------------------------------
Gradle 7.4
------------------------------------------------------------
Build time: 2022-02-08 09:58:38 UTC
Revision: f0d9291c04b90b59445041eaa75b2ee744162586
Kotlin: 1.5.31
Groovy: 3.0.9
Ant: Apache Ant(TM) version 1.10.11 compiled on July 10 2021
JVM: 11.0.17 (Ubuntu 11.0.17+8-post-Ubuntu-1ubuntu220.04)
OS: Linux 5.4.0-137-generic amd64
続いてデバッグ実行:
$ gradle runDebugExecutableNative
> Task :runDebugExecutableNative
- test1 assert false : false
- test1 assert true : true
作動しています。
続いて native ファイルを生成:
$ gradle nativeBinaries
build/bin/native/releaseExecutable/mydatetime.kexe が生成されるので、これを実行します。
$ build/bin/native/releaseExecutable/mydatetime.kexe
- test1 assert false : false
- test1 assert true : true
こちらも問題なく作動しました。
このような実験コードは kotlinc-native コマンドで開発できると楽なのですが、それはまだ?できないようです。 (ここで試した範囲では、という意味ですが) しかし、Gralde プロジェクトにすれば、実行できました。 これなら、Spring Boot を使うよりはだいぶ楽ですね。