Top
Photo Drawings Software Writing Reading Memo Study Profile Bookmark 

SML with GNU

Autoconf,AutomakeなどGNUツールでSMLプログラムを扱う方法

autoconf macro package for SML-NJ

このページで説明している各種のチェックをおこなうautoconfマクロを作成しました。 こちらで説明しています。 configure.in に記述するチェックはこれらのマクロを使用することで大幅に 省略できます。

SMLの実行ファイル名の判定

Unix上ではAC_CHECK_PROGでsmlコマンドが存在することを確認でき る。 しかし、Windows版ではsmlはバッチファイル (sml.bat/sml-cm.bat)としてインストールされ、 AC_CHECK_PROGがこれを認識してくれない。 仕方ないので、つぎのようにCygwin環境とそれ以外の場合とで判定方法を別に する。
AC_CYGWIN
   :
if test x"$CYGWIN" = x"yes";then
  dnl AC_CHECK_PROG can not find sml-cm.bat on cygwin, so set manually.
  smlcm=sml-cm.bat
else
  AC_CHECK_PROG(smlcm, sml-cm, sml-cm, sml)
fi
AC_SUBST(smlcm)
厳密には、CM.make();という式が実行できるかどうかをここで テストすべき。

exportFnで生成されるファイル名を判定する

SMLofNJ.exportFn関数が生成するファイル名には、 hoge.sparc-solarisのようにマシンアーキテクチャとOSを指す文字列が含まれる。 これらの文字列はSMLNJのソースファイル中でべた書きでdefineされているも ので、unameなど何らかのコマンドから得られるものではない。
smlで記述したプログラムをMakeコマンドで管理しようとするときに、 これが困る。 Makefileにマシンアーキテクチャ名とOS名をべた書きする以外の、より良い 方法を探る。

Autoconf,Automakeを使う場合、 以下のようにしてsmlで記述したプログラムを扱うことができる。
まず、configure.inにつぎのような内容を記述する。

smlcmsuffix=` echo 'SMLofNJ.exportFn ("conftest",fn _=>OS.Process.success);' | ${smlcm} >/dev/null 2>&1 
for f in conftest.*-*;do
	echo $f | sed 's/^.*\.\(@<:@^.@:>@*\)$/\1/' ;rm $f;
done`
AC_SUBST(smlcmsuffix)
これは、exportFnによって生成されるヒープイメージファイル 名の拡張子をsmlcmsuffix変数に設定する。
AC_SUBST(var)というマクロは、Makefile.am中に現れる @var@ という文字列をシェル変数varの値に置き換えることを指示する。
三行目のsedコマンドは、シェル変数fに格納されている ファイル名から拡張子を取り出している。 bashであれば、これは${f##*.}と記述できる。 しかし、configureはshによって実行されるので、このようなbash特有の機能 は使えず、sedを使って実現することになる。 ファイル名中の'.'以降の文字列を得たいので、つぎのようにすればよい。
sed 's/^.*\.\([^.]*\)$/\1/'
しかし、autoconfが内部で使用しているマクロプロセッサm4が、 [,]を特別な意味を持つ文字として扱うので、 これをそのままconfigure.inに記述することはできない。 そこで、[,]の替りに @<:@,@:>@を使う。 これらのエスケープシーケンスは、 configure.inから生成されるconfigureファイル中で[,]に置き換えられる。

Makefile.amにはつぎのように記述する。 eslambdaはラッパースクリプト名で、 eslambda.@smlcmsuffix@exportFnにより生成されるヒー プイメージファイル名となる。

sml_sources = hoge.sml ...

bin_SCRIPTS = eslambda 
pkgdata_DATA = eslambda.@smlcmsuffix@

eslambda: 
	echo "@smlcm@ @SMLload=$(datadir)/$(PACKAGE)/eslambda.@smlcmsuffix@ \$$*" > eslambda

eslambda.@smlcmsuffix@: $(sml_sources)
	echo 'CM.make();SMLofNJ.exportFn ("eslambda",EsLambda.main);' | @smlcm@

Cygwin上でのSMLプログラムのラッパー生成

SMLNJでプログラムをコンパイルするとヒープイメージファイルが生成される。 このヒープイメージファイルをsmlへの引数として渡すと、プログラムが実行される。 いちいちヒープイメージファイルを指定してsmlを起動するのは面倒なので、 ラッパースクリプトを用意したい。 そこで、上記のMakefile.am中では、
eslambda: 
	echo "@smlcm@ @SMLload=$(datadir)/$(PACKAGE)/eslambda.@smlcmsuffix@ \$$*" > eslambda
として、ヒープイメージファイルをsmlに渡す一行スクリプトを生成している。
Unix系の場合はこれでうまくいく。Unix版のsmlコマンドはそれ自体が smlの実行ファイル本体を呼び出すラッパースクリプトなのだが、 渡された引数を見て適切にヒープイメージファイルをsmlの実行ファイルに渡してくれる。 しかし、Windows版の場合、sml.batはたったの二行から構成されるバッチファイルで、 デフォルトのヒープイメージファイルと、コマンドラインで指定された引数を smlの実行ファイルに渡しているだけである。 コマンドライン引数でヒープイメージファイルを指定した場合でも デフォルトのヒープイメージファイルを常に最初の引数として渡しているために、 Unix版の場合に比べ、smlの実行ファイルに渡される引数がひとつ増えてしまう。 このため、ヒープイメージのエントリーポイント、すなわちSMLofNJ.exportFnに渡した関数 への引数もひとつ増えてしまう。 具体的には、コマンドラインから渡されたヒープイメージファイル名が、 エントリーポイントへの引数に渡されるリストの先頭要素となる。

そこで、つぎのようにして、デフォルトのヒープイメージファイルを渡さず、 コマンドラインから指定するヒープイメージファイルのみをsmlの実行ファイルに渡す ラッパースクリプトが生成されるようにする。

eslambda@script_suffix@ :
        case @host@ in \
        *cygwin*) heapwinpath=`cygpath -w $(datadir)/$(PACKAGE)/eslambda.@smlcmsuffix@ | sed 's;\\\\;\\\\\\\\;g'` ;\
                  cat `which @smlcm@` | sed "s;@SMLload=[^ ]*;@SMLload=$$heapwinpath;g" > eslambda@script_suffix@  ;; \
        *) echo "@smlcm@ @SMLload=$(datadir)/$(PACKAGE)/eslambda.@smlcmsuffix@ \$$*" > eslambda@script_suffix@  ;; \
        esac
Windows版のsmlの実行ファイルへの引数では、C:\path\to\fileのようなDOS形式で 指定しなければならない。 しかし、configureやmakeはcygwin環境で実行されることを前提にしており、 パスは/usr/local/...のようにUnixと同様の形式で扱われている。 そこで、Unix形式のパス名からDOS形式のパス名への変換が必要となる。 このため、つぎのようにcygwin付属のcygpathコマンドを使ってパス形式を変換する。
cygpath -w $(datadir)/$(PACKAGE)/eslambda.@smlcmsuffix@
これにより、DOS形式のパス名が得られる。

しかし、これをそのままbatファイルに書き込んではいけない。 cygpathによって得られるパス名には('\')が含まれている。 batファイル中の'\'はエスケープ文字として扱われるので、smlコマンドへ渡される時点で '\'が引数の文字列から取り除かれてしまう。 したがって、batファイル中には、'\'を'\\'に変換したパス名を書き込まなければならない。 ここでsedコマンドを使う。

sed 's;\;\\;g'
が、これはエラーとなる。 sedは'\'をエスケープ文字として扱う。 また、Makefile中のコマンドはshにより実行されるが、 shも'\'をエスケープ文字として扱う。 したがって、Makefile.amにはつぎのように二重に'\'をエスケープしたsedコマンドを記述する。
sed 's;\\\\;\\\\\\\\;g'
こうして、ヒープイメージファイルのDOS形式のパス名が得られる。

つぎに、このパス名をsmlの実行ファイルに渡すスクリプトを記述する。 しかし、smlの実行ファイルのパス名はsml.bat内にべた書きされており、 whichコマンドなどでは探すことができない。 幸い、sml.batの中身はたったの二行なので、この中身を適当に置換して ラッパースクリプトを生成する。

cat `which @smlcm@` | sed "s;@SMLload=[^ ]*;@SMLload=$$heapwinpath;g" > eslambda@script_suffix@
これは、sml.bat中でsmlの実行ファイルに渡されているデフォルトの ヒープイメージファイル名を、目的のヒープイメージファイル名で置き換えている。