Previous Contents Next
Chapter 8 Batch compilation (ocamlc)

この章では Objective Caml のバッチコンパイラ ocamlc の解説をします。ocamlc は Caml のソースファイルをバイトコードのオブジェクトファイルにコンパイルしたり、そのオブジェクトファイルをリンクして実行可能なバイトコードファイルを作成したりできます。その実行ファイルはバイトコードインタプリタ ocamlrun の上で実行することになります。

8.1 Overview of the compiler

ocamlc コマンドは、よくある C コンパイラに似たコマンドラインインターフェイスになっています。引数にはいくつか型があります。 リンク段階で出力されるファイルはコンパイル済みバイトコードを含み、Objective Caml バイトコードインタプリタ ocamlrun の上で実行できます。出力されたファイル名が caml.out だとすると、以下のコマンドで caml.out 中のコンパイル済みコードを実行できます。
        ocamlrun caml.out arg1 arg2 ... argn
文字列 arg1 から argn までが引数として実行ファイルに渡されます (詳細は 10 章を見てください) 。

多くのシステムでは出力されたファイルは以下のようにそのまま実行できます。
        ./caml.out arg1 arg2 ... argn
出力されたファイルは実行権限ビットがセットされていて、自力でバイトコードインタプリタを起動しようとします。

8.2 Options

ocamlc に認識されるコマンドラインオプションは以下の通りです。
-a
コマンドラインに与えられたオブジェクトファイル (.cmo) をとり、ライブラリ (.cma) を作成します。実行ファイルは生成されません。ライブラリの名前は -o オプションで指定できます。デフォルトでは library.cma です。

コマンドラインに -custom-cclib-ccopt オプションが渡されたら、これらのオプションは出力される .cma ライブラリに保存され、このライブラリをリンクするときに自動的に -custom-cclib-ccopt が追加されます (ただしリンク時に -noautolink が指定されない場合) 。

-c
コンパイルのみ行います。リンクを行いません。ソースコードファイルをコンパイル済みファイルに変換しますが、実行ファイルは生成されません。このオプションはモジュールの分割コンパイル時に使用できます。

-cc ccomp
ocamlc -custom で呼ばれる C リンカや .c ソースファイルをコンパイルする C コンパイラとして ccomp を使用します。

-cclib -llibname
カスタムランタイムモードでのリンク時、C リンカにオプションとして -llibname を渡し、指定された C ライブラリをプログラムにリンクします。

-ccopt option
カスタムランタイムモードでのリンク時、C コンパイラやリンカに与えられたオプションを渡します。例えば、-ccopt -Ldir なら C リンカは C ライブラリを探す際ディレクトリ dir を見るようになります。

-custom
カスタムランタイムモードでリンクします。デフォルトのリンクモードでは、リンカは共有ランタイムシステム ocamlrun で使用するバイトコードを生成します。カスタムランタイムモードでは、リンカが生成するファイルにはランタイムシステムとプログラムのバイトコードが両方含まれます。よって生成されるファイルは大きくなりますが、直接実行できるので ocamlrun のない環境でも動作します。さらにカスタムランタイムモードでは Caml コードにユーザが定義した C 関数を静的にリンクできます。詳細は 18 章を見てください。   Unix:
ocamlc -custom で生成された実行ファイルに対して strip コマンドは使用しないで下さい。実行ファイルのバイトコード部分が削除されてしまいます。


-dllib -llibname
ランタイムシステム ocamlrun がプログラムスタート時に動的ロードする C 共有ライブラリ dlllibname.so (Windows では dlllibname.dll) を指定します。

-dllpath dir
ランタイムシステムが共有 C ライブラリを検索するディレクトリ dir を追加します。リンク時は共有ライブラリを標準検索パス (-I オプションと関連する) へ検索しにいきます。-dllpath オプションで dir が単純に生成される実行ファイルへ埋め込まで、ocamlrun がそれを見つけ探しに行きます。詳細は 10.3 章を見てください。

-g
コンパイルとリンク時にデバッグ情報を追加します。ocamldebug でプログラムをデバッグする場合はこのオプションが必要です。詳細は 16 章を見てください。

-i
実装ファイル (.ml) をコンパイルする際、定義されている名前をすべて型と定義付きで表示します。コンパイラがどのように型推論をしているかチェックするときに使用できます。また出力はインターフェイスの文法に従っているので、インターフェイスファイル (.mli) を書くときの手助けになります。標準出力を .mli ファイルにリダイレクトして、非公開な名前の宣言を消していくだけです。

-I directory
コンパイル済みインターフェイスファイル (.cmi) 、コンパイル済みオブジェクトコードファイル (.cmo) 、ライブラリ (.cma) 、-cclib -lxxx で指定される C ライブラリを検索する際検索しに行くディレクトリのリストに与えられたディレクトリを追加します。デフォルトでは、まずカレントディレクトリが、それから標準ライブラリディレクトリが検索されます。-I で追加されたディレクトリは、カレントディレクトリより後に、標準ライブラリディレクトリより前に、コマンドラインに与えられた順に検索されます。

+ で始まるディレクトリ名は、標準ライブラリディレクトリからの相対パスとみなされます。例えば -I +labltk は標準ライブラリのサブディレクトリ labltk を検索パスに追加します。

-impl filename
ファイル filename を実装ファイルとしてコンパイルします。拡張子が .ml でなくても構いません。

-intf filename
ファイル filename をインターフェイスファイルとしてコンパイルします。拡張子が .mli でなくても構いません。

-linkall
ライブラリに含まれるモジュールをすべて強制的にリンクします。このフラグが与えられなかったら、参照されないモジュールはリンクされません。ライブラリを作成するときに (-a flag) これを与えると、そのライブラリを使用するプログラムをリンクする際、ライブラリ内のモジュールはすべて強制的にリンクされます。

-make-runtime
カスタムランタイムシステムを作成します。このランタイムシステムはコマンドラインで与えられた C オブジェクトやライブラリと合体しています。ocamlc -use-runtime runtime-name オプションで生成されたバイトコードがこのカスタムランタイムシステムを使用するようになります。詳しくはセクション 18.1.6 を見てください。

-noassert
assertion のチェックをやめます。assertion はコンパイルされません。 すでにコンパイルされたファイルをリンクする場合には、このフラグは何の意味も持ちません。

-noautolink
.cma ファイルをリンクするとき、ライブラリが潜在的に持つ -custom-cclib-ccopt といったオプション (ライブラリを作成するときに指定されたもの) を無視します。ライブラリが間違った C ライブラリや C オプションの指定をしているときに使用できます。この場合、リンクの際コマンドラインで -noautolink を指定して、正しい C ライブラリやオプションを指定し直してください。

-nolabels
型の中でオプションではないラベルを無視します。適用にラベルは使用できません。パラメータの順序が絶対になります。

-o exec-file
リンカが出力するファイルの名前を指定します。デフォルトの出力名は Unix の伝統に則り a.out です。-a が指定されている場合は生成されるライブラリ名の指定になります。-output-obj が指定されている場合は生成されるファイル名の指定になります。

-output-obj
リンカは実行ファイルではなく C オブジェクトファイルを生成します。C のプログラムから呼べるように、Caml のコードを C のライブラリとしてラップするのに使います。18 章のセクション 18.7.5 を見てください。出力されるオブジェクトファイルの名前はデフォルトで camlprog.o です。-o オプションで変更可能です。

-pack
コマンドラインで与えられたオブジェクトファイルをまとめて、オブジェクトファイル (.cmo) とそのコンパイル済みインターフェイス (.cmi) を生成します。元のオブジェクトは出力された .cmo ファイルのサブモジュールとなります。出力される .cmo ファイルの名前は -o オプションで必ず与えてください。
        ocamlc -pack -o p.cmo a.cmo b.cmo c.cmo
例として、上記のコマンドは p.cmop.cmi を生成します。それらは 3 つのサブモジュール ABC を持ち、それぞれオブジェクトファイル a.cmob.cmoc.cmo の内容に対応します。他のプログラムからは、P.AP.BP.C などとしてこれらの内容を参照することができます。

-pp command
コンパイラは与えられたコマンド command をプリプロセッサとしてすべてのソースファイルに適用します。command の出力は一時ファイルにリダイレクトされてからコンパイルされます。コンパイルでエラーが発生しなかったら一時ファイルは削除されます。ソースファイルのベース名と拡張子 .ppi (インターフェイス .mli の場合) もしくは .ppo (実装 .ml の場合) から構成される名前が一時ファイルの名前になります。

-principal
型チェックの際 information path をチェックします。すべての型が principal way で導出されているか確認します。ラベル付き引数や多相メソッドを使用しているときは、このフラグをつけてコンパイルの通らないコードは、将来コンパイラのバージョンアップで型チェックの内部アルゴリズムが変更された場合正しく型推論できなくなるかもしれません。 -principal モードでコンパイルの通るプログラムは必ずデフォルトモード (equivalent type) でもコンパイルが通ります (ただしバイナリの signature は異なります) 。このオプションを付けると型チェックが遅くなるかもしれません。ソースコードを公開する前に一度これでコンパイルしてみるといいです。

-rectypes
型チェックの際、任意の再帰的な型を許します。デフォルトでは、オブジェクト型を経由する再帰による再帰的な型しかサポートされません。

-thread
24 章で解説される threads ライブラリと組み合わせてマルチスレッドプログラムをコンパイル、リンクします。このオプションを付けると、特別なスレッドセーフな標準ライブラリを選択するようになります。

-unsafe
配列や文字列アクセスのバウンダリチェックをオフにします (v.(i)s.[i]) 。-unsafe でコンパイルされたプログラムはいくらか速くなりますが安全ではありません。プログラムが配列や文字列のバウンド外をアクセスした場合何が起こるかわかりません。

-use-runtime runtime-name
あらかじめ ocamlc -make-runtime runtime-name で生成されているカスタムランタイムシステム runtime-name を使用して実行するバイトコード実行ファイルを生成します。 詳しくはセクション 18.1.6 を見てください。

-v
コンパイラのバージョンと標準ライブラリディレクトリを表示して終了します。

-verbose
外部コマンドを実行する前に、そのコマンドを表示します。特に -custom モードで C コンパイラやリンカの起動を表示します。C ライブラリプログラムをデバッグするのに使用できます。

-version
コンパイラのバージョンを短い表記 (3.06 みたいに) 表示して終了します。

-w warning-list
警告を有効/無効にします。引数 warning-list は文字列で、各文字は以下の意味を持ちます。
A/a
すべての警告を有効/無効にする
C/c
コメントかどうか疑わしいものに対する警告を有効/無効にする
D/d
推奨されていない (deprecated) 機能に対する警告を有効/無効にする
F/f
関数の部分適用に対する警告を有効/無効にする (つまり f x; expr のようなコードで、適用 f x が関数型を持つようなとき).
L/l
適用でラベルが省略されていることに対する警告を有効/無効にする
M/m
メソッドのオーバーライドに対する警告を有効/無効にする
P/p
部分マッチ (パターンマッチでどれにもマッチしない場合があるもの) に対する警告を有効/無効にする
S/s
文が unit 型以外を持つことに対する警告を有効/無効にする (例えば expr1; expr2 のようなコードで、expr1unit 以外の型を持つようなとき)
U/u
使用されない (冗長な) マッチケースに対するに対する警告を有効/無効にする
V/v
隠れたインスタンス変数に対する警告を有効/無効にする
X/x
その他の警告すべてを有効/無効にする
デフォルトでは -w Al になります (ラベル以外はすべて有効) 。

-warn-error warning-list
引数 warning-list で示される警告をエラーとして扱います。コンパイラはそのような警告が発生したらすぐにエラーとして停止して処理をやめます。warning-list の意味は -w オプションと同じで、大文字だと該当する警告をエラーとし、小文字だとエラーとしません。デフォルトでは -warn-error a になります (どの警告もエラーとしては扱わない) 。

-where
標準ライブラリの位置を表示して終了します。
8.3 Modules and the file system

このセクションではコンパイルユニットに対応するモジュールの名前と、それらのコンパイル済みインターフェイスや実装を持つファイルの名前との関係を明確にしておこうと思います。

コンパイラはソースファイル (.ml.mli.ml.mli といった拡張子を取り除き、もしあればディレクトリ名も取り除き、最後に先頭の文字を大文字としたもの (モジュール名の先頭は大文字といいう仕様を満たすため) です。 例えば、mylib/misc.ml というファイルをコンパイルしたら、Misc というモジュールの実装が生成されます。 他のコンパイルユニットから mylib/misc.ml 中で定義されているコンポーネントを参照したいときは、Misc.name という名前で参照できます。open Misc したあとなら、そのまま name という名前も使用できます。

コンパイラが生成する .cmi.cmo ファイルのベース名はソースファイルと同じになります。なのでコンパイル済みファイルのベース名も、それらのファイル (.cmi.cmo) が記述、提供するモジュール名と同じになります (先頭文字の大文字化はありますが) 。

コンパイラがモジュール識別子 Mod を初めて見つけたら、検索パスから mod.cmi (先頭文字は小文字です) を探し、そのファイルからコンパイル済みインターフェイスを読み込みます。なので、.cmi ファイルの名前は変更しないでください。.cmi ファイルの名前は、それが提供するコンパイルユニットの名前と同じでなければなりません。他のディレクトリへ移動するのは構いませんが、ベース名を変えず、適当に -I オプションを指定してください。名前が変更された .cmi ファイルに対しては、コンパイラがエラーを出します。

一方コンパイル済みバイトコード (.cmo) は一度作成すれば自由に名前変更できます。というのは、リンカはモジュールの名前から .cmo ファイルを検索しないからで、裏を返せばユーザが手動で .cmo ファイルのリストを指定しないといけないということです。

8.4 Common errors

このセクションではよく出るエラーメッセージを解説します。
Cannot find file filename
ファイルがカレントディレクトリからも検索パスのディレクトリからも見つかりませんでした。filename はコンパイル済みインターフェイスファイル (.cmi) かコンパイル済みバイトコードファイル (.cmo) です。ファイル名が mod.cmi だった場合、mod モジュールの識別子を参照しているファイルをコンパイルしようとしたけど、まだ mod モジュールのインターフェイスファイルがコンパイルされていなかったのでしょう。対策として、まず mod.mlimod.ml をコンパイルして、インターフェイスファイル mod.cmi を作成してください。

ファイル名が mod.cmo だった場合、存在しないバイトコードオブジェクトファイルをリンクしようとしたのでしょう。対策として、まず mod.ml をコンパイルしてください。

プログラムがいくつかのディレクトリに分かれている場合、検索ディレクトリを指定していなくて発生する可能性もあります。コマンドラインに -I オプションを正しく追加してください。

Corrupted compiled interface filename
コンパイル済みインターフェイスファイル (.cmi) の構造が壊れていたら発生します。この .cmi ファイルが出力されたときに何かが起きたのでしょう (ディスクがいっぱいになって途中までしか書き出せなかったとか) 。.cmi ファイルが作成された後、何らかの変更を加えた場合も発生する可能性があります。壊れた .cmi ファイルを削除して、再度作成してください。

This expression has type t1, but is used with type t2
これは一番よく見る型エラーです。型 t1 はエラーの発生した式を推論した型で (この式の位置はエラーメッセージに表示されます) 、型 t2 は式の文脈からその位置にあるべきだと思われる型です (つまりプログラムの他の位置でこの式の値が使われているところをみて、この式が持つと推論された型) 。2 つの型 t1t2 とに互換性がなかったら、上記のエラーが発生します。

たまに、2 つの型 t1t2 とにどうして互換性がないのかよくわからないことがあります。例えば ``expression of type foo cannot be used with type foo'' など、2 つの型 foo は互換性があるように思えます。しかしそうではないのです。2 つの型コンストラクタが同じ名前でも、表す型が異なる場合があるのです。これは型コンストラクタを再定義したときに発生します。例えば以下のような例があります。
        type foo = A | B
        let f = function A -> 0 | B -> 1
        type foo = C | D
        f C
この場合エラーメッセージは ``expression C of type foo cannot be used with type foo'' となります。

The type of this expression, t, contains type variables that cannot be generalized
Type variables ('a, 'b, ...) in a type t can be in either of two states: generalized (which means that the type t is valid for all possible instantiations of the variables) and not generalized (which means that the type t is valid only for one instantiation of the variables). In a let binding let name = expr, the type-checker normally generalizes as many type variables as possible in the type of expr. However, this leads to unsoundness (a well-typed program can crash) in conjunction with polymorphic mutable data structures. To avoid this, generalization is performed at let bindings only if the bound expression expr belongs to the class of ``syntactic values'', which includes constants, identifiers, functions, tuples of syntactic values, etc. In all other cases (for instance, expr is a function application), a polymorphic mutable could have been created and generalization is therefore turned off.

Non-generalized type variables in a type cause no difficulties inside a given structure or compilation unit (the contents of a .ml file, or an interactive session), but they cannot be allowed inside signatures nor in compiled interfaces (.cmi file), because they could be used inconsistently later. Therefore, the compiler flags an error when a structure or compilation unit defines a value name whose type contains non-generalized type variables. There are two ways to fix this error:

Reference to undefined global mod
このエラーは不完全または不正確な順番でファイルをリンクしようとしたとき、または mod という名前のコンパイルユニットの実装 (普通このファイルは mod.cmo という名前か、ライブラリ中にそういう名前で入っているかのどちらか) をコマンドラインに指定し忘れていたときに発生します。.ml.cmo ファイルをコマンドラインに追加してください。mod という名前のモジュールを含む実装をちゃんとコマンドラインに指定している場合は、コマンドライン上の位置が後ろすぎるのでしょう。mod の実装は mod を参照するバイトコードオブジェクトファイルのどれよりも前にないといけません。コマンドライン上の .ml.cmo ファイルの順番を適当に変えてください。

このエラーはモジュール間で相互再帰する関数を作ると当然発生します。つまり関数 Mod1.f が関数 Mod2.g を呼び、かつ関数 Mod2.g が関数 Mod1.f を呼ぶときです。この場合、コマンドラインの順番をどのように変えてもプログラムはリンク時に拒否されます。対策として以下のようにしてください。

The external function f is not available
このエラーは C で書かれた外部関数を呼ぶコードをリンクするとき発生します。18 章で説明するように、f という C 関数を使用する実装は C ライブラリとリンクしなければいけません。C ライブラリが共有ライブラリ (DLL) でない場合は、カスタムランタイムモードでリンクしなければなりません。コマンドラインに使用する C ライブラリを追加して、必要なら -custom オプションも追加してください。

Previous Contents Next