ページ

2017-10-20

Kotlinでコマンドラインツールを作成する(Gradle/fat jar)

Kotlinを始めるにはコマンドラインツールを作ってみてはと言いつつ、そういう話あんまりなくない?と思ったので書くことにした。 Gradleでfat jarを生成し、javaがあれば実行できるようにするところまで。

sdkmanでGradleをインストール

http://sdkman.io/install.html からsdkmanをインストールして、gradleをインストールする。
$ sdk install gradle

gradle initで雛形の作成

適当なディレクトリを作成し、gradle initでbuild.gradle他ファイルを生成する。

$ mkdir kotlin-cmd-example
$ cd kotlin-cmd-example
$ gradle init

build.gradleの編集

Using Gradle - Kotlin Programming Languageを参考にして、build.gradleを記述する。

kotlin-gradle-pluginを適用して、kotlin-stdlib他必要なライブラリを追加すればOK。

ここではlog出力のためのlogbackのほか、fat jarを生成するための johnrengelman/shadow を使っている。 shadowの設定については Shadow Plugin User Guide & Examples を。

plugins {
    id 'org.jetbrains.kotlin.jvm' version '1.1.51'
    id 'com.github.johnrengelman.shadow' version '2.0.1'
}

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.jetbrains.kotlin:kotlin-stdlib-jre8'

    compile 'org.slf4j:slf4j-api:1.7.25'
    compile 'org.slf4j:jcl-over-slf4j:1.7.25'
    compile 'org.slf4j:jul-to-slf4j:1.7.25'
    compile 'ch.qos.logback:logback-classic:1.2.3'
}

apply plugin: 'application'
mainClassName = "com.example.kotlincmdexample.KotlinCmdExampleKt"

注意点としては、特に設定しないとJava側からはクラス名にはKtがついているのでつける。

src/main/kotlin に Kotlin ソースコードを作成

デフォルトではソースの場所はsrc/main/kotlinなので、ここにソースコードを配置していく。 IntelliJ IDEAであれば、build.gradleを開けば開始できる。

src/main/kotlin/com/example/kotlincmdexample/KotlinCmdExample.kt

package com.example.kotlincmdexample

import org.slf4j.LoggerFactory

fun main(args: Array<String>) {
    val log = LoggerFactory.getLogger("com.example.kotlincmdexample.KotlinCmdExample")

    if (args.isEmpty()) {
        println("Usage: kotlin-cmd-example name")
        return
    }

    log.info("Hello, {}", args[0])
}

あえてclassを作ってないのでgetLoggerが冗長な感じがするが、中途半端なのでloggerの名前を適当にしてもいいし、クラスを作って渡すかしたほうがいいかも。

./gradlew runでテスト実行

./gradlew runすれば実行できる。IDEAであれば右クリックしてRunあるいはDebugでもOK。

$ ./gradlew run 

> Task :run
Usage: kotlin-cmd-example name


BUILD SUCCESSFUL in 0s
2 actionable tasks: 1 executed, 1 up-to-date

./gradlew buildでビルド(jar生成)

./gradlew buildするとbuild/distributionsにkotlin-cmd-example-shadow.tarと.zipが入っているので、これを展開すると起動スクリプトがあるので実行できる。

build/libsに-all.jarがあるので、java -jarで直接起動することも可能。

$ java -jar build/libs/kotlin-cmd-example-all.jar 
Usage: kotlin-cmd-example name

$ java -jar build/libs/kotlin-cmd-example-all.jar MyFirstKotlinProject
19:49:00.796 [main] INFO com.example.kotlincmdexample.KotlinCmdExample - Hello, MyFirstKotlinProject

今回のプロジェクト

GitHubのsunnyone/kotlin-cmd-exampleに置いておくので、参考になれば。

2017-06-27

Kotlin で引数の名前つきでの呼び出しを強制する裏ワザ

小ネタ。

Kotlinでは引数を名前つきで呼び出せる。なしでもOK。
fun add(x: Int, y: Int) = x + y

fun f() {
    add(1, 2)
    add(x=1, y=2)
    add(1, y=2)
}

名前付きで呼んでほしいときはどうするか?名前つきにしてほしいところの前にvararg Voidをおく。

fun add(vararg useNamed: Void, x: Int, y: Int) = x + y

fun f() {
    // add(1, 2) // コンパイルエラー!
    add(x=1, y=2)
    // add(1, y=2) // コンパイルエラー!
}

途中からでもOK。
fun add(x: Int, vararg useNamed: Void, y: Int) = x + y

fun f() {
    // add(1, 2) // コンパイルエラー!
    add(x=1, y=2)
    add(1, y=2)
}

…いやいやだめだろこれ。もっといい方法があれば教えて下さい。
参考:How can I force calls to some constructors/functions to use named arguments?

2017-01-29

RTX810 からmydnsにIPアドレスを通知する(Luaをスケジュール実行する)

変わるIPを知ってるのはルータなので、ルータに通知してもらおうということでRTX810にmydnsの更新をさせることにする。なんのことはない、shiwork氏のLuaスクリプトをRTXで動かそうというだけの話。

ただし、いわゆるフラッシュ領域にtftpでスクリプトをもってきて実行するというネットワーク機器にありがちそうなやり方をしようとすると、いくつかのドキュメントにまたがって追わないといけないので、あとで追えるようにまとめておく。

スクリプトの準備

gistからスクリプトを持ってきて、ユーザID/パスワードを書き換えておく。

tftpによるLuaスクリプトの転送

RTXのいわゆるフラッシュ領域はRTFSというらしい。ここにディレクトリを作ってスクリプトを転送する。
(→RTFS

/scripts/ディレクトリの作成

ルートにカスタムなスクリプトをべたに置くと気持ち悪いので、/scriptsというディレクトリを作って置くことにする。
# make directory /scripts
# show file list /
2017/01/29 21:12:50 <DIR>           scripts

スクリプト転送元端末からのtftpアクセスを許可する

# tftp host 192.0.2.10

tftpで送り込む

c:\> tftp 192.0.2.254 PUT mydns.lua /scripts/mydns.lua/(administratorパスワード) 
adminパスを入れる必要があるのに気づかずにはまった。
(→RTシリーズのインストールとリビジョンアップに関するFAQ tftpの機能と注意事項

確認。
# show file list /scripts
2017/01/29 21:12:55            2769 mydns.lua

tftpの無効化

# tftp host none

Luaスクリプトの実行

単発実行

単発の実行はlua パスでOK。show status luaでsuccessなことを確認する。
# lua /scripts/mydns.lua
# show status lua
Lua Library Version:            Lua 5.1.5
Lua Script Function Version:    1.07

[running]
There is no running script.

[history]
(1)
Running Trigger:     executed by 'lua' command
Command Line:        lua /scripts/mydns.lua
Script File:         /scripts/mydns.lua
Running Counts:      1
Error Counts:        0
First Starting Date: 2017/01/29 21:14:52
Last Starting Date:  2017/01/29 21:14:52
Last Ending Date:    2017/01/29 21:14:53
Last Result:         success

スケジュール実行

scheduleコマンドで実行をスケジュールする。
schedule at 2 */* *:0,10,20,30,40,50 * lua /scripts/mydns.lua
これでおしまい。
(→Lua スクリプト機能