OCamlのソースからWindowsネイティブな実行ファイルを生成する(Native MS版バイナリで)

これは備忘録。

Objective Camlで書いたソースコードは、

などの使い方ができるはずなんだけど、Windows上でocamloptを使えるようにするには色々と準備が必要らしい。

Cygwin portやMinGW版のバイナリの場合、Cygwinが入ってないとocamloptを使うのは難しいらしい。でも残念ながらCygwinを使っていないので分からない。

Native MS版のバイナリを使用しているので、この環境でネイティブな実行ファイルを生成できないか試してみた。

用意するもの

必要なもの 今回使用したもの
OCamlのNative MS版のバイナリ ocaml-3.11.0-win-msvc.exe
Visual C++Windows SDK Visual C++ 2008 Express Edition SP1
FlexDLLのflexlink.exe flexdll-bin-0.17.zip

Visual C++ 2008 Express EditionにはWindows SDKも付いてくるので*1、これだけでOK。

準備

1. Visual C++Windows SDKをインストール

詳細は省略。ちなみにVC++ 2008 Expressはデフォルトの場所にインストールしてある。

2. 必要ならVC++関連のパスを環境変数PATHに追加する

バージョン等にもよるんだろうけど、VC++ 2005や2008では環境変数PATHに各種バイナリのある場所へのパスが追加されない。

VC++ 2008では、少なくとも以下のディレクトリへのパスが通っている必要がある模様。

C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE
C:\Program Files\Microsoft Visual Studio 9.0\VC\bin
C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin

これは環境変数PATHに追記しても構わないし、ビルド時に一時的に追加するようにしても構わない。

3. FlexDLLのバイナリを用意して、パスを通しておく

FlexDLLに含まれているflexlink.exeが必要。ビルド済みのバイナリが手に入るので、それを使う。

いざ実行――の前に

この状態でocamloptを実行しても、リンク時に必要なライブラリのある場所が分からなくて失敗してしまう。なので、ocamlopt実行時に少なくとも以下の2ヶ所をオプションで引き渡す必要がある。

C:\Program Files\Microsoft Visual Studio 9.0\VC\lib
C:\Program Files\Microsoft SDKs\Windows\v6.0A\Lib

例えばこんな感じ。

ocamlopt.exe -ccopt """-LC:\Program Files\Microsoft Visual Studio 9.0\VC\lib"" ""-LC:\Program Files\Microsoft SDKs\Windows\v6.0A\Lib""" -o hello.exe hello.ml

もしかしたら環境変数か何かで設定できるかもしれないけど、調べてないので不明。

ケーススタディ

試しに定番のhello, worldをビルドしてみる。

(* hello.ml *)
Pervasives.print_string "hello, world\n" ;;

環境変数を弄くったり、一々オプションを指定したりするのが面倒なので、こんなバッチファイルを用意。

:: @file   occ.bat
:: @brief  Objective Camlでネイティブな実行ファイルを生成する簡易バッチファイル
:: @note   ocaml-3.11.0-win-msvc + Microsoft Visual C++ 2008 Express Edition SP1 + flexdll-bin-0.17

@echo off
setlocal

	set PATH=%PATH%;C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE
	set PATH=%PATH%;C:\Program Files\Microsoft Visual Studio 9.0\VC\bin
	set PATH=%PATH%;C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin
	set PATH=%PATH%;C:\soft\flexdll

	ocamlopt.exe -verbose -ccopt """-LC:\Program Files\Microsoft Visual Studio 9.0\VC\lib"" ""-LC:\Program Files\Microsoft SDKs\Windows\v6.0A\Lib"" -v" %*

endlocal
@echo on

で、こんな感じで実行。

occ.bat -o hello.exe hello.ml

ビルドに成功すると、

  • hello.cmi
  • hello.cmx
  • hello.exe
  • hello.obj

が生成される。

問題点、課題

生成した実行ファイルが他人のPCで動作しない。

推測混じりになるけど、どうも原因はmsvcrt.libをリンクしている為らしい。これ、VC++でいうところの/MD(マルチスレッド DLL)でビルドした場合と同じのはず*2なので、実行するPCに必要なDLLが無いと動いてくれない。

解決方法は、

  1. クライアント側に「Visual C++ 2008 SP1 再頒布可能パッケージ」を入れてもらう。
  2. msvcrt.libじゃなくてlibcmt.libをリンクする。多分/MT(マルチスレッド)でビルドするのと同じはず。

が考えられるのだけど、1はテスト環境を用意できないので未確認で、2は色々と試してみたけどできなかった*3

*1:但し、SDKとしての全機能が揃っているかどうかは不明。

*2:cl.exeのヘルプを信じるなら……。

*3:まさかocamloptを自前でビルドする必要があるとかいうオチだったりして……。