8
2

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.

ScalaとGraalVMでクリスマスっぽいAAをプリントするだけのCLIツールを作る

Last updated at Posted at 2020-12-24

概要

ScalaとGraalVMを使ってクリスマスっぽいAAを出すだけの超シンプルなCLIツールを作ってみようというお話です。

CLIツールなどで起動時間が気になる場合はGraalVMでnative image作ればよさそうと考えて、GRAALVM, PICOCLIとJAVAでときめくネイティブコマンドラインアプリを作ろうを参考にScala+Picocli+GraalVMで何か作ってみようと思ったのですが、今日いざ書こうとしたら既に先駆者がいらっしゃいました。

もう既にピンズドな記事があるのでこの記事の意義は割と皆無ですが、他にネタもないのでこのままやっていこうと思います。

準備

GraalVMのセットアップ

使用するGraalVMのバージョンは19.3.4です。
GraalVMの最新バージョンは20.3.0ですが、このバージョンではnative imageを作成できませんでした。1
そこで、GraalVMのバージョンは去年リリースされたものを採用しています。2

GraalVMはこちらを参考に以下のようにセットアップしました。

$ curl -LO https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-19.3.4/graalvm-ce-java11-darwin-amd64-19.3.4.tar.gz
$ tar -xvf graalvm-ce-java11-darwin-amd64-19.3.4.tar.gz
$ sudo mv graalvm-ce-java11-19.3.4/ /Library/Java/JavaVirtualMachines/
$ export PATH=/Library/Java/JavaVirtualMachines/graalvm-ce-java11-19.3.4/Contents/Home/bin:$PATH

またnative imageの作成にはnative-imageが必要なので、GraalVMのセットアップ後にインストールしてください。

$ gu install native-image

プロジェクトのセットアップ

元となるプロジェクトはsbtを使用して作成しました。

$ sbt new scala/scala-seed.g8 --name=merry-christmas

Scalaのバージョンは2.12.7としています。
こちらも最新ではありませんが、GraalVMの最新バージョンを使用した場合と同様のエラーが出るため、2.12系を採用しています。

native imageの作成はsbt-native-packagerを使用して作成します。
以下のようにしてScalaプロジェクトにプラグインを追加してください。

project/plugin.sbt
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.8.0")

次にGraalVMNativeImagePluginを有効にします。

build.sbt
lazy val root = (project in file("."))
  .enablePlugins(GraalVMNativeImagePlugin) // GraalVMNativeImagePluginを有効化
  .settings(
    name := "merry-christmas",
  )

オプションパーサーはPicocliを使うとパクリになってしまうので、ほんのりオリジナリティを出します。
PicocliではAnnotationProcessorを使っていてビルドの設定がちょっぴり複雑になるため、ここではScalaで書かれたscoptを採用しました。

build.sbt
lazy val root = (project in file("."))
  .enablePlugins(GraalVMNativeImagePlugin)
  .settings(
    name := "merry-christmas",
    libraryDependencies += "com.github.scopt" %% "scopt" % "4.0.0", // scopt v4.0.0を追加
  )

本体の実装

scoptはREADMEを読みながら雰囲気で書いています。
作成するツールは-nまたは--nameで名前を受け取って、クリスマスツリーとともにMerry Christmas, <NAME>!と印字するだけです。
とても簡素な内容なので、特に参考になるものでもないと思います。

package example

import scopt.OParser

case class Options(name: String = "")

object MerryChristmas {
  val builder = OParser.builder[Options]
  val parser = {
    import builder._
    OParser.sequence(
      programName("merry-christmas"),
      head("merry-christmas", "0.1.0"),
      opt[String]('n', "name")
        .required()
        .action((n, o) => o.copy(name = n))
        .text("Your name")
    )
  }

  def main(args: Array[String]): Unit = {
    OParser.parse(parser, args, Options()) match {
      case Some(options) =>
        print(options)
      case _ =>
    }
  }
  // print関数は省略...
}

native imageの作成・実行

native imageはsbt graalvm-native-image:packageBinを実行するとtarget/graalvm-native-image/に作成されます。

今回作成されたnative imageは約10.2MBと、誰もが同じ感想を持つと思いますが結構なサイズです。
この問題は早くからGithub上にissueが上がっていますが、コメントにもあるようにプログラム規模が大きくなっても5MBのオーバーヘッドは変わらないので小さいimageでのみの問題だそうです。(本当?)

では実行してみます。

$ ./target/graalvm-native-image/merry-christmas --name tsatow

          ⭐
         彡ミ
        彡*◎。
       彡彡‡*。
     +彡★ミ♪ミ。
     ‡彡※◎ミ▲+ミ
   +彡彡▲彡★ミミ+  Merry Christmas, tsatow!
   彡゚◎彡♪ミ☆*ミ☆
 。彡★*彡彡◆ミ+ミ◎。
 彡彡彡☆彡彡ミ★ミミ
         ┃┃
       ■■■■■■
        ■■■■
        ■■■■

ツリーがちょっと歪んでますが、とにもかくにも表示されました。
自分の作ったツールが自分にMerry Christmasと言ってくれると、なんだか心が満たされる感じがしますね。

気になる実行時間ですが、以下の環境で動作させたところ大体0.010ms程度でした。

$ sysctl machdep.cpu.brand_string
machdep.cpu.brand_string: Intel(R) Core(TM) i7-4770HQ CPU @ 2.20GHz
$ java -version
openjdk version "11.0.9" 2020-10-20
OpenJDK Runtime Environment GraalVM CE 19.3.4 (build 11.0.9+10-jvmci-19.3-b18)
OpenJDK 64-Bit Server VM GraalVM CE 19.3.4 (build 11.0.9+10-jvmci-19.3-b18, mixed mode, sharing)

sbt-assemblyでfat jarを作成して計測すると平均0.550ms程度なので、小さなプログラムであれば3native-imageの恩恵を受けられそうです。

まとめ

起動時間が気になるようなCLIツールも、native-imageを使えばScalaで作ることができそうです。
しばらく色々作って試して遊んでみます。

それでは皆様、良いクリスマスを。

  1. issueが上がっています。

  2. 時間の都合上、どのバージョンから失敗するようになるのかは細かく調査していません。ざっくりissueが上がった時点の一つ前のバージョンを採用しています。

  3. 処理の実行自体が問題になるほどの規模ではないので、ここでは起動時間の差が大きく影響していると考えています。(ちゃんと調べろ)

8
2
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
8
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?