SML/NJで実行ファイルを作る

タイトルはやや詐欺なのだけど。
SML/NJで実行ファイル、というか、型検査とコンパイル*1が終わった状態のファイルが欲しいことがある。
ソフトウェアをバイナリ配布するとか。あるいはベンチマークを取りたい(=パースや型検査の時間を入れたくない)場合が考えられる。


stack-over-flowにもあるんだけど日本語ですぐ出てこないので書いておく。

exportFnを使う

replから SMLofNJ.expportFn を使う。

val SMLofNJ.expportFn : string * (string * string list -> OS.Process.status) -> unit
- fun main (name, args) = (print (String.concatWith "," (name::args@["\n"])); OS.Process.success);
val main = fn : string * string list -> OS.Process.status
- SMLofNJ.exportFn("test",main);
$ ls
test.x86-linux

C言語のmain関数相当のシグネチャの関数を受け取って実行環境のサフィックスのついたヒープイメージを書き出す。

ml-build

上の方法ではいちいちreplを立ち上げる必要があって回りくどい。そのためこの機能を呼び出すラッパースクリプトが ml-build として用意されている。
.cmファイル、実行を開始するmain関数、書き出すファイル名(の一部)を指定する。

$ ml-build source.cm Main.main test
$ ls
source.cm test.x86-linux

以下のように通常の makefile に自然に記述することも出来るようになる。

all: test.x86-linux

%.x86-linux: source.cm
    ml-build $< Main.main test

ただしトップレベルに書いた関数は .cm から公開出来ないため、ml-build による方法では .cm からmain(相当)の関数を提供する structure を公開している必要がある。

実行

以上で得られたヒープイメージを smlコマンド に渡して実行する。サフィックスは付けなくても動く。

$ sml @SMLload=test foo bar bazz
/path/to/bin/sml,foo,bar,bazz,

*1:NJはネイティブコード吐いてるらしい