build SML/NJ 110.76 on Cygwin 1.7

いつの間にかcygwin上で SML/NJ がビルド出来なくなっていた。(いつからだろう?
具体的には base/runtime/mach-dep/cygwin-fault.c で参照されているヘッダ が消えているのでエラーとなっています。
現時点での最新版を含む SML/NJ 110.7{6,5,4} と cygwin 1.7 で同様のエラーになることを確認しています。

build & install

SML/NJのビルド方法は少し変わっていて、用意されているスクリプトを実行するとソースのダウンロードからビルドまでがいきなり完了します(^^;;


こんな感じ。

$ curl -O http://smlnj.cs.uchicago.edu/dist/working/110.76/config.tgz
$ tar -xf config.tgz
$ export SMLNJ_CYGWIN_RUNTIME=1  # cygwinでビルドする場合必要
$ vim config/target # ビルドしたい周辺ソフトの指定を必要に応じて編集
$ ./config/install.sh
$ bin/sml
Standard ML of New Jersey v110.76 [built: Sun Aug 18 08:46:02 2013]
-

うまく行った場合はカレントディレクトリから bin/sml の位置に実行ファイル(のラッパー)が出来ています。
Cygwin上で実行する時は常に SMLNJ_CYGWIN_RUNTIME=1 が環境変数に定義されている必要があるのでWindows環境変数に設定すると手間が無くてよいです。

compilation error

今回の環境ではコンパイルに失敗します(よね?)。

..(略)..

In file included from ../mach-dep/unix-fault.c:10:0:
../mach-dep/cygwin-fault.c:21:43: fatal error: exceptions.h: No such file or directory
 #include <exceptions.h> /* Cygwin stuff */
                                           ^
compilation terminated.
makefile:242: recipe for target 'unix-fault.o' failed
make[1]: *** [unix-fault.o] Error 1
make[1]: Leaving directory '/cygdrive/c/usr/sml110.76/base/runtime/objs'
mk.x86-cygwin:22: recipe for target 'all' failed
make: *** [all] Error 2
./config/install.sh: !!! Run-time system build failed for some reason.

patch

で、このエラーを修正するため exceptions.h をでっち上げるパッチ(?)を書きました。cygwin-fault.h に改名してますが。
(以下で言及している base/ は config/install.sh が自動でタウンロードしてくれるので、エラーが起きると分かっていても1度実行しておくとパッチを当てるのが楽です)


以下のようになりました。 Gistにも貼ってあります> https://gist.github.com/eldesh/9317060

$ diff -U 3 base/runtime/mach-dep/cygwin-fault.c.old base/runtime/mach-dep/cygwin-fault.c
--- base/runtime/mach-dep/cygwin-fault.c.old    2014-03-03 23:25:41.882968800 +0900
+++ base/runtime/mach-dep/cygwin-fault.c        2014-03-03 23:25:55.698398500 +0900
@@ -17,8 +17,7 @@
 #include "ml-state.h"
 #include "ml-globals.h"

-#include <windows.h>
-#include <exceptions.h> /* Cygwin stuff */
+#include "cygwin-fault.h" /* Cygwin stuff */

 #define SELF_VPROC      (VProc[0])
/* base/runtime/include/cygwin-fault.h */
#if !defined CYGWIN_FAULT_INCLUDED
#define      CYGWIN_FAULT_INCLUDED

#include <windows.h>

typedef int (exn_handler_fn)(EXCEPTION_RECORD *, void *, CONTEXT *, void *);
typedef struct exception_list {
	struct exception_list * prev;
	exn_handler_fn * handler;
} exception_list;

#endif    /* CYGWIN_FAULT_INCLUDED */

このようにして再度 config/install.sh を実行するとビルドに成功します。

修正内容

今回定義した exception_list は structured exception handling という、windowsが持っているエラー通知の仕組みにアクセスするための型です。
各スレッドの特定のレジスタに常に載っているので、そのアドレスの型を定義しただけでコンパイルは出来ました。

妥当性

どうやら cygwin-fault.c によると0割りとオーバーフローに関係しそうなので、
関連するコードをコメントアウトするとそれっぽい例外を起こして死ぬコードを動かして、問題無く動くようになることを確認してはあります。ごく簡単に。
(コードの中に例外に関するテストスイートがあったら動かしてみるかも)


あとバイナリ屋さんに伺ったところ、CygwinかNJが途中で変なハンドリングしてなければ大丈夫そうです。

@kikairoya さんありがとうございました。