Lambdaカクテル

京都在住Webエンジニアの日記です

Invite link for Scalaわいわいランド

Scala CLIのDockerイメージを使うと本当に簡単にシングルバイナリを生成できて便利

Scala CLIのおもしろい使い方を見付けたのでメモ。これまでと比べてはるかにScalaのちょっとしたスクリプトのシングルバイナリを生成できるようになるので、本当にGolangみたいに簡単にちょっとしたツールを作れるようになった。

scala-cli package

Scala CLIにはpackageというサブコマンドがあり、これを利用するとScala ScriptをそのままUber JAR(直接実行できるJARファイル)にビルドできるのでなかなか便利だ。

しかもなんと--nativeオプションを使うことでバックエンドがScala Nativeに切り替わり、ネイティブバイナリを生成できるようになる。

//> using scala 3.3.1
//> using dep "com.lihaoyi::os-lib::0.9.3"
// ↑こんな感じでライブラリも使える(コロン2つにする点に注意)

println("Hello, World!")
% scala-cli --power package --native -o helloworld helloworld.scala.sc
...
% ./helloworld
Hello, World!

Scala CLIなんぞやという話はこちら。

blog.3qe.us

ここからいくつかの要素を組み合わせる。

Scala Nativeはシングルバイナリを生成できる

シングルバイナリとはここでは「他の動的ライブラリに依存していない実行可能バイナリ」くらいの意味。

Scala NativeはLLVMを利用していて、Scala CLIからいくらかコンパイラオプションやリンカオプションをいじれるようになっている。

Scala CLIのpackageのオプションで--native-linking='-static'というオプションを追加すると、出力されるバイナリは静的リンクされてシングルバイナリにできる。これはユーティリティを配布したいときに非常に便利だ。

% scala-cli --power package --native --native-linking='-static' -o helloworld helloworld.scala.sc
% ldd helloworld
        動的実行ファイルではありません

とはいえ、Scala Nativeは実行環境にLLVMまわりのツールキットを必要とするので、ちょっと面倒だ。

Dockerイメージを利用すると便利

VirtusLab(Scala CLIの開発元)が出している公式のDockerイメージを使うとより面白いことができる。コンテナにはLLVMまわりのツールが最初から搭載されているので、本当に簡単にシングルバイナリのビルドができる。

% docker run --rm -it -v .:/app --workdir /app  virtuslab/scala-cli --power package --native --native-linking='-static' -o helloworld helloworld.scala.sc
% ldd helloworld
        動的実行ファイルではありません

用途

ここでは手許でバイナリをビルドしたが、Dockerfileでマルチステージビルドの前段としてvirtuslab/scala-cliを使えば、ペライチのScala Scriptからバイナリを生成したイメージをdistrolessベースで作成したりできる。ツールを配布したいときに便利だ。

クロスコンパイルはまだできなそう

clangにオプションを渡せるのでクロスコンパイルもイケるかと思ったけれど、うまくいかなかった。

% docker run --rm -it -v .:/app --workdir /app  virtuslab/scala-cli --power package --native --native-compile='--target=aarch64-linux-gnu' --native-linking='-static' helloworld.scala.sc

Starting compilation server
Compiling project (Scala 3.3.1, Scala Native 0.4.17)
Compiled project (Scala 3.3.1, Scala Native 0.4.17)
[info] Linking (1156 ms)
[info] Checking intermediate code (quick) (69 ms)
[info] Discovered 692 classes and 3825 methods
[info] Optimizing (debug mode) (955 ms)
[info] Generating intermediate code (623 ms)
[info] Produced 12 files
[error] In file included from /root/.cache/scalacli/virtual-projects/e1/project-51f8174f/.scala-build/app_1db1351409/native/native-code-javalib_native0.4_3-0.4.17-0/scala-native/netinet/in6.c:2:
[error] In file included from /usr/include/netinet/in.h:21:
[error] In file included from /usr/include/features.h:392:
[error] /usr/include/features-time64.h:20:10: fatal error: 'bits/wordsize.h' file not found
[error] #include <bits/wordsize.h>
[error]          ^~~~~~~~~~~~~~~~~
[error] 1 error generated.

まとめ

Scala CLIのDockerイメージを使ってシングルバイナリを作るテクについて紹介しました。ちょっとしたツール界といえば今はGoかRustの独擅場みたいになっていますが、Scalaで便利ツールを作るハードルはかなり低くなっています。

過去の記事

Scala CLIまわりだと過去にこういう記事も書いておりました。

blog.3qe.us

blog.3qe.us

★記事をRTしてもらえると喜びます
Webアプリケーション開発関連の記事を投稿しています.読者になってみませんか?