ocamlbuildのメモ

参考URL: http://brion.inria.fr/gallium/index.php/Using_an_external_library

酷評されているocamlbuildを使ってみましたので忘れないうちにメモ。

その1

main.mlがあったとします。

$ ocamlbuild main.native

_buildという作業ディレクトリが作られて、なんやかんややった末に_build/main.nativeができて、カレントディレクトリにmain.nativeのシンボリックリンクができます。

簡単ですね!

  • 疑問点1: このmain.nativeというファイル名は、多くの場合このままでは嬉しくないと思われます。変える方法はあるのでしょうか?
  • 疑問点2: このままですと_buildディレクトリを消したときに実行ファイルも残りません。ユースケース次第ですがこれが嬉しくないこともあるはず。シンボリックリンクではなくて実体のコピーにする方法はないのでしょうか。
  • 疑問点3: _buildディレクトリにmain.mlのコピーができてますが、ハードリンクじゃなくて単なるコピーみたいです。main.nativeはシンボリックリンクなのに何故ソースコードはコピーする……。
  • 疑問点4: しれっとmain.cmoもできてますが、私はネイティブコード生成を指示したのであってバイトコードは別にいらんです。ビルド時間がかかってしょうがないのでこれやめさせることはできないものでしょうか……。

その2

実行ファイルを複数作りたいのでディレクトリを分けました。

exe1/
  main.ml
exe2/
  main.ml
source/
  shared.ml

exe1、exe2それぞれのディレクトリでビルドする算段。
ただ、このままではエラーになります。

$ ocamlbuild -I ../source main.native
Failure: Included or excluded directories must be implicit (not "../source").
Compilation unsuccessful after building 0 targets (0 cached) in 00:00:00.

フルパスにしても同じで、どうやら全てのソースファイルがカレントディレクトリ以下に必要な模様。
なのでリンク。

exe1/
  main.ml
  source -> ../source
exe2/
  main.ml
  source -> ../source
source/
  shared.ml
$ ocamlbuild -I source main.native

めでたしめでたし。

  • 疑問点5: ここでリンクの名前を_sourceみたいにすると無視されてしまいます。ビルドディレクトリも_buildですし、どうやらアンダースコアで始まる名前は無視対象のようです。無視を解除して_sourceという名前を使うにはどうしたらよいでしょうか。
  • 疑問点6: そもそもカレントディレクトリの外にあるソースを使うには、これが本当に正しい方法なのでしょうか?絶対もっといい方法ありますよね!

その3

外部ライブラリを使いたいと思います。
ocamlが初めから持っているBigarrayやUnixやなんかは-tag use_bigarrayで充分なのですが、野良ライブラリの場合は位置を教えてあげる必要があります。なのでmyocamlbuild.mlを書きます。

open Ocamlbuild_plugin;;

dispatch begin function
| After_rules ->
	 ocaml_lib ~extern:true ~dir:("../lib") "gmp";
	 ocaml_lib ~extern:true ~dir:("../lib") "mpfr";
	 ocaml_lib ~extern:true ~dir:("../lib") "unicode";
	 tag_file "main.ml" [
	 	"use_gmp";
		"use_mpfr";
		"use_unicode"];
	 tag_file "main.native" [
	 	"use_bigarray";
	 	"use_gmp";
	 	"use_mpfr";
	 	"use_unicode";
	 	"use_unix"]
| _ ->
	()
end;;

配置としては、main.mlと同じところにlibというディレクトリを作って、野良ライブラリ自身のmakefileでもってlibにライブラリを配置させます。ocamlbuildからは配置済みのライブラリを使おうという戦略です。私はOCamlのライブラリ付属のmakefileは全く信用していないのですが、ocamlmklibやなんかはもっと信用していません。(この例で使っているGMPラッパーなんかは私自身が作ったライブラリですので、そのmakefileは私が書いたものです。なので、私の環境限定で上手く動作することは確実なのでそれを使います。これを読んでいる人は、ライブラリのmakefile(私のものも含めて)も信用しないようにお願いします。必ず自分でビルドしましょう。)

ocaml_libのオプションですが、~extern:trueは、ocamlbuildにビルドさせないために必要です。このパラメータを省略しますと、親切に全部ビルドしようとしてくれやがりますがソースが見つからずにエラーになります。~dirが"../lib"になってますが、これは_buildからの相対パスなのでこうなります。
tag_fileは、_tagsというファイルを作りたくないのでここに書いてるだけです。ひとつのビルドツールのために複数ファイル書きたくないです。

後は、このlibディレクトリ自身が依存関係解析に巻き込まれないように、無視するようオプションをつければOKです。

$ ocamlbuild -X lib -I source main.native
  • 疑問点7: Gmpモジュールが使われていたら自動でuse_gmp扱いにする、みたいな挙動はできないのでしょうか?
  • 疑問点8: ライブラリ間の依存関係は書けないのでしょうか。この例では、実はBigarrayはUnicodeライブラリが使っているだけで、main.mlにBigarrayの文字は出てきません。
  • 疑問点9: -build-dirで_buildディレクトリの名前を変えられますが、そこで階層ごと変えられると_buildからの相対パスは破綻します。カレントディレクトリからの相対パスに直してくれるぐらいしてもいいと思いませんか……。(解決方法: Sys.getcwd () ^ "/lib")

ocamlbuildは標準で入っている素晴らしいツールです。少なくともocamldepを駆使したmakefileを書くよりは良いので、俺ビルドツールを作るよりも先に試してみましょう。俺ビルドツールが散乱してもいいことは何もありませんからね!*1

*1:ちなみにYTの俺ビルドツールはhttps://github.com/ytomino/ocamlmakeにあります。バカですね!