Previous Contents Next
Chapter 18 Interfacing C with Objective Caml

この章では C で記述したユーザ定義プリミティブに Caml のコードをリンクしたり、逆に Caml の関数からコールしたりする方法について解説します。

18.1 Overview and compilation information

18.1.1 Declaring primitives

ユーザプリミティブは実装ファイルや struct...end モジュール式中で external キーワードを使って宣言します。
        external name : type = C-function-name
これは変数名 nametype 型の関数として定義します。この関数を呼び出すと与えられた C 関数が実行されます。例えば、標準ライブラリのモジュール Pervasivesinput プリミティブはこのように宣言されています。
        external input : in_channel -> string -> int -> int -> int
                       = "input"
引数をとるプリミティブは常にカリー化されます。C の関数名は ML の関数名と同じである必要はありません。

このように定義された外部関数は、インターフェイスファイルや sig...end signature で通常の値として扱えます。
        val name : type
このようにすると、この関数の実装が C の関数であるということは隠されます。また、明示的に外部関数であることを示すこともできます。
        external name : type = C-function-name
このようにすると、このモジュールを使用する側は、対応する Caml の関数を通さず、直接 C の関数を呼び出すようになるので、若干効果的です。

プリミティブの arity (引数の数) は external 宣言で与えられた Caml の型をみて、関数適用の矢印の数を数えて自動的に決定します。例えば、先ほど示した input の場合は arity 4 で、C の関数 input は 4 引数の関数として呼び出されます。同様に、
    external input2 : in_channel * string * int * int -> int = "input2"
は arity 1 で、C の関数 input2 は 1 引数 (quadruple な Caml 値) の関数になります。

プリミティブの atity を決定するとき、型の略記は展開されません。つまり、
        type int_endo = int -> int
        external f : int_endo -> int_endo = "f"
        external g : (int -> int) -> (int -> int) = "f"
f は arity 1 になりますが、g は arity 2 になります。こうすることで、関数値を返すプリミティブを宣言できるようになります。上の f がその例です。返す値が関数型であるときは型の略記で名前をつけるということを覚えておいてください。(just remember to name the functional return type in a type abbreviation.)

18.1.2 Implementing primitives

arity n 5 であるユーザプリミティブは、value 型の引数を n 個とり、n 型の結果を返す C の関数として記述します。value 型とは Caml の値の実体を示す型で、Caml のデータ構造だけでなく、ベースとなる型 (integers, floating-point numbers, strings, ...) のオブジェクトをエンコードしたものです。value 型や、それに対応する変換関数やマクロは後で細説します。例えば、input プリミティブの実装となる C の関数の宣言は以下のようになります。
CAMLprim value input(value channel, value buffer, value offset, value length)
{
  ...
}
Caml のプログラムでプリミティブ関数に何かの引数を適用すると、その引数式の値を引数として C の関数を呼び出します。関数が返す値は、関数適用の結果として Caml のプログラムに戻ることになります。

arity が 5 以上のユーザプリミティブは 2 つの C の関数として記述するようにしてください。1 つ目の関数は、バイトコードコンパイラ ocamlc と連動するもので、Caml の値 (引数の値) の配列へのポインタと、引数の数を表す整数の、2 つの引数を受け取ります。2 つ目の関数は、ネイティブコードコンパイラ ocamlopt と連動するもので、引数を直接受け取ります。例えば、7 引数のプリミティブ Nat.add_nat の C の関数は以下の 2 つになります。
CAMLprim value add_nat_native(value nat1, value ofs1, value len1,
                              value nat2, value ofs2, value len2,
                              value carry_in)
{
  ...
}
CAMLprim value add_nat_bytecode(value * argv, int argn)
{
  return add_nat_native(argv[0], argv[1], argv[2], argv[3],
                        argv[4], argv[5], argv[6]);
}
プリミティブ宣言中では、以下のようにして C の関数名を 2 つ与えてください。
        external name : type =
                 bytecode-C-function-name native-code-C-function-name
例えば add_nat の場合は、次のように宣言します。
        external add_nat: nat -> int -> int -> nat -> int -> int -> int -> int
                        = "add_nat_bytecode" "add_nat_native"
ユーザプリミティブの実装は、実質的に 2 つの作業にわかれます。1 つ目は、与えられた引数を Caml の値から C の値にデコードしたり、逆に返値を Caml の値としてエンコードしたりする作業、2 つ目は、実際に引数から返値を計算する作業です。非常に単純なプリミティブ以外は、2 つの異なる C の関数を用意して、上記 2 つの作業をそれぞれ実装するようにしたほうがいいと思います。1 つ目の関数は C の値を引数にとり C の値を返すプリミティブの実体で、2 つ目の関数はスタブコードと呼ばれる 1 つ目の関数のラッパで、引数を Caml の値から C の値に変換して 1 つ目の関数を呼び出し、その返値を Caml の値に変換するものです。例えば input プリミティブのスタブコードは以下のようになります。
CAMLprim value input(value channel, value buffer, value offset, value length)
{
  return Val_long(getblock((struct channel *) channel,
                           &Byte(buffer, Long_val(offset)),
                           Long_val(length)));
}
(ここで、Val_longLong_val などは value 型の変換マクロです。後で解説します。CAMLprim はコンパイラ指示語のマクロで、関数をエクスポートさせて Caml からアクセスできるようにします。) 実際の作業は getblock 関数が行います。getblock 関数は以下のように記述します。
long getblock(struct channel * channel, char * p, long n)
{
  ...
}
Objective Caml の値を処理する C コードを書くには、以下のインクルードファイルを使用してください。
Include file Provides
caml/mlvalues.h value 型や変換マクロの定義
caml/alloc.h アロケーション関数 (構造化された Caml オブジェクトを生成するときに使う)
caml/memory.h メモリに関するさまざまな関数やマクロ (GC インターフェイスや、内部書き換え式の構造など)
caml/fail.h 例外を発生する関数群 (see section 18.4.5)
caml/callback.h C から Caml へのコールバック (セクション 18.7)
caml/custom.h カスタムブロックの処理群 (セクション 18.9)
caml/intext.h カスタムブロックの、ユーザ定義のシリアライゼーションやデシリアライゼーション関数を書くための処理群 (セクション 18.9)

これらのファイルは Objective Caml 標準ライブラリディレクトリの caml/ サブディレクトリの中にあります (通常は /usr/local/lib/ocaml) 。

18.1.3 Statically linking C code with Caml code

Objective Caml のランタイムシステムは、バイトコードインタプリタ、メモリマネージャ、プリミティブ処理を実装となる C 関数の集合の 3 つの部分からなります。バイトコード命令には、関数のテーブル (プリミティブのテーブル) を指定されたオフセットでひいて得られる C の関数を呼び出すものがあります。

デフォルトモードで Caml のリンカが出力するのは、標準のプリミティブ集合からなる標準ランタイムシステム用のバイトコードです。この標準集合にないプリミティブを参照しようとしたら、「利用できない C プリミティブ」エラーとなります (ただし C ライブラリの動的ロードがサポートされていない場合。以下のセクション 18.1.4 を見てください) 。

「カスタムランタイム」モードでは、Caml のリンカはオブジェクトファイルをスキャンして、必要なプリミティブの集合を決定し、ネイティブコードリンカを呼んで適当なランタイムシステムをビルドします。このときネイティブコードリンカには、以下のものが渡されます。 これで必要なプリミティブを持ったランタイムシステムがビルドされます。Caml リンカは、このカスタムランタイムシステム用のバイトコードを生成します。バイトコードはカスタムランタイムシステムの終端に追加されるので、この出力ファイル (カスタムランタイム + バイトコード) を起動すれば、自動的にこのバイトコードが実行されます。

「カスタムランタイム」モードでのリンクは、ocamlc に以下の引数を指定して実行して行ってください。 ネイティブコードコンパイラ ocamlopt を使うときは、-custom フラグは必要ありません。ocamlopt のリンクは最終的に常にスタンドアロンな実行ファイルをビルドします。Caml/C の混ざった実行ファイルのビルドは、ocamlopt に以下の引数を指定して実行して行ってください。 OCaml 3.00 から、Caml ライブラリファイル .cma.cmxa の中に、C ライブラリの名前だけでなく -custom オプションも記録できるようになりました。例として、Caml オブジェクトファイル a.cmob.cmo があって、これらが libmylib.a の C コードを参照しているとき、これらから Caml ライブラリ mylib.cma を生成することを考えます。以下のようにライブラリが生成されたら、
        ocamlc -a -o mylib.cma -custom a.cmo b.cmo -cclib -lmylib
このライブラリのユーザは単純に mylib.cma をリンクするだけとなります。
        ocamlc -o myprog mylib.cma ...
システムは自動的に -custom-cclib -lmylib オプションを追加し、以下のようにしたときと同じように動作します。
        ocamlc -o myprog -custom a.cmo b.cmo ... -cclib -lmylib
もちろんライブラリをビルドするとき余分なオプションをつけないで
        ocamlc -a -o mylib.cma a.cmo b.cmo
ライブラリのユーザがリンク時に -custom-cclib -lmylib オプションをつける必要があるということにもできます。
        ocamlc -o myprog -custom mylib.cma ... -cclib -lmylib
けれど前者の方が、ライブラリのユーザには便利です。

18.1.4 Dynamically linking C code with Caml code

OCaml 3.03 から、-custom を使った C コードの静的リンク以外の方法も可能になりました。このモードでは Caml リンカは、カスタムランタイムシステム用ではなく、純粋なバイトコードの実行ファイルを生成します。この実行ファイルには C コードを持つ動的ロードライブラリの名前が記録されています。標準 Caml ランタイムシステム ocamlrun はバイトコードの実行に移る前に、このライブラリを動的にロードして、必要なプリミティブの参照を解決します。

今のところ、この機能は Linux と Windows (native Windows port) でサポート/動作確認されています。FreeBSD 、Tru64 、Solaris 、Irix ではサポートはされていますが、まだ十分にテストできていません。他の Unix や、Windows/Cygwin 、MacOS ではまだサポートされていません。

C コードを Caml コードに動的リンクするには、まず C コードを、Unix では共有ライブラリに、Windows では DLL にコンパイルする必要があります。1) まず、C ファイルをコンパイルします。この際、position-independent コードを生成するように、適切なフラグを C コンパイラに与えてください。2) 次に得られたオブジェクトファイルから、共有ライブラリを生成します。こうして得られた共有ライブラリや DLL ファイルを、ocamlrun がプログラム起動時に見つけられる位置 (セクション 10.3 を見てください) にインストールしてください。3) 最後に、ocamlc コマンドに以下のオプションをつけて実行してください。 -custom フラグは設定しないでください。設定するとセクション 18.1.3 で説明したような静的リンクとなってしまいます。Unix では、ocamlmklib ツールを使うと、2) と 3) のステップを自動でおこなえます。 (セクション 18.10 を見てください) 。

静的リンクの場合と同じように、Caml の .cmo ライブラリアーカイブの中に C のライブラリの名前を記録できます (この方法は推奨されています) 。今回も Caml オブジェクトファイル a.cmob.cmo があって、これらが dllmylib.so の C コードを参照していて、これらから Caml ライブラリ mylib.cma を生成することを考えます。以下のようにライブラリが生成されたら、
        ocamlc -a -o mylib.cma a.cmo b.cmo -dllib -lmylib
このライブラリのユーザは単純に mylib.cma をリンクするだけと鳴ります。
        ocamlc -o myprog mylib.cma ...
システムは自動的に -dllib -lmylib オプションを追加して、以下のようにしたときと同じように動作します。
        ocamlc -o myprog a.cmo b.cmo ... -dllib -lmylib
このメカニズムを使うと、ライブラリ mylib.cma のユーザは、このライブラリが (-custom を使った) 静的リンクを行って C コードを参照しているのか、動的リンクを行っているのか関知する必要がありません。

18.1.5 Choosing between static linking and dynamic linking

C コードを Caml コードにリンクする 2 種類の方法を説明し終えた上で、それぞれのプラス面とマイナス面について述べてみます。Caml と C ライブラリの混ざったプログラムを書く際、どちらの方法を選ぶかの参考にしてください。

動的リンクの主な利点は、バイトコード実行ファイルがプラットフォーム非依存になれるということです。つまり、バイトコード実行ファイルはマシンコードを含まないので、プラットフォーム A でコンパイルされたものは、必要な共有ライブラリが使用できるプラットフォーム BC ……でも実行可能だということです。それに対して、ocamlc -custom で生成された実行ファイルは、生成したプラットフォームに併せて調整されたカスタムランタイムシステムを含んでいるので、そのプラットフォームでしか動きません。さらに、動的リンクのほうは出力される実行ファイルのサイズが小さくなります。

動的リンクにはさらに利点があります。ライブラリの最終的なユーザは、C コンパイラ、C リンカ、C ランタイムライブラリをマシンにインストールしていなくてもよいのです。これは Unix や Cygwin においては大したことではありませんが、ocamlc -custom を行えるようにするためだけに Microsoft Visual C をインストールしたくはない多くの Windows ユーザにとってはうれしいことです。

動的リンクには 2 つ欠点があります。まず、実行ファイルがスタンドアロンでなくなり、実行するマシンには ocamlrun だけでなく、共有ライブラリもインストールされている必要があります。スタンドアロンな実行ファイルを頒布したい場合は、ocamlc -custom -ccopt -staticocamlopt -ccopt -static を使って静的リンクしたもののほうがいいでしょう。また、動的リンクは「DLL 地獄」問題を引き起こします。共有ライブラリのバージョンが正しいか、起動時に確かめるよう、注意する必要があります。

動的リンクの 2 つ目の欠点は、ライブラリの構築が複雑になることです。position-independent なコードをコンパイルし、共有ライブラリをビルドするための C コンパイラやリンカのフラグは Unix システムごとにさまざまです。また、動的リンクはすべての Unix システムでサポートされているわけではありません。その場合のために、ライブラリの Makefile に静的リンクをおこなうように書く必要があります。ocamlmklib はこのようなシステム依存の部分をある程度隠すためにあります (セクション 18.10 を見てください) 。

まとめますと、native Windows port では、ポータビリティの問題がないことや、エンドユーザにとっての利便性を考えると、動的リンクが強く推奨されます。Unix では、安定したよく使われているライブラリに対しては、プラットフォーム非依存なバイトコードが得られる動的リンクがいいと思います。しかし新しいライブラリや滅多に使われていないライブラリに対しては、静的リンクのほうがずっと簡単にセットアップできます。

18.1.6 Building standalone custom runtime systems

ocamlc -custom のように、Caml コードを C ライブラリをリンクするたびにカスタムランタイムシステムをビルドするのは、たまに面倒なことになります。(質の悪いリンカや遅いリモートファイルシステムのせいなどで) ランタイムシステムをビルドするのがすごく遅いシステムであるとか、プラットフォーム非依存なバイトコードファイルではないので、目的のプラットフォームごとに ocamlc -custom でリンクしなくてはならなくなるとかいった問題です。

ocamlc -custom に代わる方法として、必要な C ライブラリを含んだカスタムランタイムシステムを別途ビルドしておいて、その上で動く「純粋な」(ランタイムシステムを含んでいない) バイトコード実行ファイルを生成する方法があります。これは ocamlc-make_runtime-use_runtime のフラグを用いることで可能です。例えば、「Unix」と「Threads」ライブラリの C の部分を含んだカスタムランタイムシステムを作るには、以下のようにします。
        ocamlc -make-runtime -o /home/me/ocamlunixrun unix.cma threads.cma
このランタイムシステムの上で動くバイトコード実行ファイルを生成するには、以下のようにします。
        ocamlc -use-runtime /home/me/ocamlunixrun -o myprog \
                unix.cma threads.cma your .cmo and .cma files
バイトコード実行ファイル myprog は通常右のように起動します。 myprog args or /home/me/ocamlunixrun myprog args.

バイトコードライブラリ unix.cmathreads.cma は、ランタイムシステムをビルドするときと、バイトコード実行ファイルをビルドするときの 2 回指定する必要があることに注意してください。前者は ocamlc に必要な C プリミティブを伝えるために、後者は unix.cmathreads.cma のバイトコードをを実際にリンクするために必要です。

18.2 The value type

すべての Caml オブジェクトは C の value 型として表されます。この型は、この型の値を処理するマクロと一緒に、インクルードファイル caml/mlvalues.h のなかで定義されています。value 型のオブジェクトは、以下のいずれかになります。
18.2.1 Integer values

整数値は 31 ビット符号付き整数です (64 ビットアーキテクチャでは 63 ビットです) 。これは unbox です (メモリ割り当てされていません) 。

18.2.2 Blocks

ヒープのブロックはガベージコレクトされるので、構造は厳しく制限されています。それぞれのブロックにはヘッダがあり、ヘッダにはブロックのサイズ (ワード単位) と、ブロックのタグがあります。タグはブロックの中身がどのように構造化されているかを表します。No_scan_tag より小さいタグは、そのブロックが well-form な値を含む構造化ブロックであることを示し、ガベージコレクタはこの well-form な値を再帰的に辿ります。No_scan_tag と同じかそれより大きいタグは、そのブロックが未加工ブロックであることを示し、ガベージコレクタはこの中身をスキャンしません。大小比較や構造化された入出力のようにアドホックな多相プリミティブによって、構造化ブロックと未加工ブロックはタグによってさらに以下のように分類されます。
Tag Contents of the block
0 to No_scan_tag-1 構造化ブロック (Caml オブジェクトの配列) 。それぞれのフィールドは value です。
Closure_tag 関数値を表す closure 。最初のワードはコード部分へのポインタで、残りのワードは環境を含む value です。
String_tag 文字列。
Double_tag double 精度の浮動小数点小数値。
Double_array_tag double 精度の浮動小数点小数値の配列やレコード。
Abstract_tag 抽象データ型を表すブロック。
Custom_tag ユーザ定義の終了処理、比較、ハッシュ、シリアライゼーション、デシリアライゼーション関数を持つ抽象データ型を表すブロック。

18.2.3 Pointers outside the heap

ヒープの外のアドレスを指すワードアラインされたポインタは、value 型と安全に、相互にキャストできます。このようなポインタには、malloc が返したポインタや、(最低でも 1 ワードのサイズを持つ) C の変数に & 演算子を用いて得られるポインタがあります。

警告: malloc が返したポインタを value 型にキャストして Caml に返したら、free を使ってポインタを明示的に解放するのは、Caml の世界からはまだこのポインタへアクセスできるかもしれないため、危険な場合があります。さらに悪いときには、あるメモリ空間を free で解放したあと、そこが Caml のヒープの部分として再割り当てが行われるかもしれません。元々 Caml のヒープの外を指していたポインタは、今では Caml のヒープの中を指していて、ガベージコレクタが混乱してしまいます。この問題を避けるために、ポインタは Abstract_tagCustom_tag のタグで示されるような Caml のブロックでラップしたほうがいいでしょう。

18.3 Representation of Caml data types

This section describes how Caml data types are encoded in the value type.

18.3.1 Atomic types

Caml type Encoding
int Unboxed integer values.
char Unboxed integer values (ASCII code).
float Blocks with tag Double_tag.
string Blocks with tag String_tag.
int32 Blocks with tag Custom_tag.
int64 Blocks with tag Custom_tag.
nativeint Blocks with tag Custom_tag.

18.3.2 Tuples and records

Tuples are represented by pointers to blocks, with tag 0.

Records are also represented by zero-tagged blocks. The ordering of labels in the record type declaration determines the layout of the record fields: the value associated to the label declared first is stored in field 0 of the block, the value associated to the label declared next goes in field 1, and so on.

As an optimization, records whose fields all have static type float are represented as arrays of floating-point numbers, with tag Double_array_tag. (See the section below on arrays.)

18.3.3 Arrays

Arrays of integers and pointers are represented like tuples, that is, as pointers to blocks tagged 0. They are accessed with the Field macro for reading and the modify function for writing.

Arrays of floating-point numbers (type float array) have a special, unboxed, more efficient representation. These arrays are represented by pointers to blocks with tag Double_array_tag. They should be accessed with the Double_field and Store_double_field macros.

18.3.4 Concrete types

Constructed terms are represented either by unboxed integers (for constant constructors) or by blocks whose tag encode the constructor (for non-constant constructors). The constant constructors and the non-constant constructors for a given concrete type are numbered separately, starting from 0, in the order in which they appear in the concrete type declaration. Constant constructors are represented by unboxed integers equal to the constructor number. Non-constant constructors declared with a n-tuple as argument are represented by a block of size n, tagged with the constructor number; the n fields contain the components of its tuple argument. Other non-constant constructors are represented by a block of size 1, tagged with the constructor number; the field 0 contains the value of the constructor argument. Example:
Constructed term Representation
() Val_int(0)
false Val_int(0)
true Val_int(1)
[] Val_int(0)
h::t Block with size = 2 and tag = 0; first field contains h, second field t

As a convenience, caml/mlvalues.h defines the macros Val_unit, Val_false and Val_true to refer to (), false and true.

18.3.5 Objects

Objects are represented as zero-tagged blocks. The first field of the block refers to the object class and associated method suite, in a format that cannot easily be exploited from C. The remaining fields of the object contain the values of the instance variables of the object. Instance variables are stored in the order in which they appear in the class definition (taking inherited classes into account).

18.3.6 Variants

Like constructed terms, values of variant types are represented either as integers (for variants without arguments), or as blocks (for variants with an argument). Unlike constructed terms, variant constructors are not numbered starting from 0, but identified by a hash value (a Caml integer), as computed by the C function hash_variant (declared in <caml/mlvalues.h>): the hash value for a variant constructor named, say, VConstr is hash_variant("VConstr").

The variant value `VConstr is represented by hash_variant("VConstr"). The variant value `VConstr(v) is represented by a block of size 2 and tag 0, with field number 0 containing hash_variant("VConstr") and field number 1 containing v.

Unlike constructed values, variant values taking several arguments are not flattened. That is, `VConstr(v, v') is represented by a block of size 2, whose field number 1 contains the representation of the pair (v, v'), but not as a block of size 3 containing v and v' in fields 1 and 2.

18.4 Operations on values

18.4.1 Kind tests
18.4.2 Operations on integers
18.4.3 Accessing blocks
The expressions Field(v, n), Byte(v, n) and Byte_u(v, n) are valid l-values. Hence, they can be assigned to, resulting in an in-place modification of value v. Assigning directly to Field(v, n) must be done with care to avoid confusing the garbage collector (see below).

18.4.4 Allocating blocks

Simple interface
Low-level interface

The following functions are slightly more efficient than alloc, but also much more difficult to use.

From the standpoint of the allocation functions, blocks are divided according to their size as zero-sized blocks, small blocks (with size less than or equal to Max_young_wosize), and large blocks (with size greater than Max_young_wosize). The constant Max_young_wosize is declared in the include file mlvalues.h. It is guaranteed to be at least 64 (words), so that any block with constant size less than or equal to 64 can be assumed to be small. For blocks whose size is computed at run-time, the size must be compared against Max_young_wosize to determine the correct allocation procedure.
18.4.5 Raising exceptions

Two functions are provided to raise two standard exceptions: Raising arbitrary exceptions from C is more delicate: the exception identifier is dynamically allocated by the Caml program, and therefore must be communicated to the C function using the registration facility described below in section 18.7.3. Once the exception identifier is recovered in C, the following functions actually raise the exception:
18.5 Living in harmony with the garbage collector

Unused blocks in the heap are automatically reclaimed by the garbage collector. This requires some cooperation from C code that manipulates heap-allocated blocks.

18.5.1 Simple interface

All the macros described in this section are declared in the memory.h header file.
Rule 1   A function that has parameters or local variables of type value must begin with a call to one of the CAMLparam macros and return with CAMLreturn or CAMLreturn0.

There are six CAMLparam macros: CAMLparam0 to CAMLparam5, which take zero to five arguments respectively. If your function has fewer than 5 parameters of type value, use the corresponding macros with these parameters as arguments. If your function has more than 5 parameters of type value, use CAMLparam5 with five of these parameters, and use one or more calls to the CAMLxparam macros for the remaining parameters (CAMLxparam1 to CAMLxparam5).

The macros CAMLreturn and CAMLreturn0 are used to replace the C keyword return. Every occurence of return x must be replaced by CAMLreturn (x), every occurence of return without argument must be replaced by CAMLreturn0. If your C function is a procedure (i.e. if it returns void), you must insert CAMLreturn0 at the end (to replace C's implicit return).

Note:
some C compilers give bogus warnings about unused variables caml__dummy_xxx at each use of CAMLparam and CAMLlocal. You should ignore them.




Example:
void foo (value v1, value v2, value v3)
{
  CAMLparam3 (v1, v2, v3);
  ...
  CAMLreturn0;
}
Note:
if your function is a primitive with more than 5 arguments for use with the byte-code runtime, its arguments are not values and must not be declared (they have types value * and int).
Rule 2   Local variables of type value must be declared with one of the CAMLlocal macros. Arrays of values are declared with CAMLlocalN.

The macros CAMLlocal1 to CAMLlocal5 declare and initialize one to five local variables of type value. The variable names are given as arguments to the macros. CAMLlocalN(x, n) declares and initializes a local variable of type value [n]. You can use several calls to these macros if you have more than 5 local variables. You can also use them in nested C blocks within the function.

Example:
value bar (value v1, value v2, value v3)
{
  CAMLparam3 (v1, v2, v3);
  CAMLlocal1 (result);
  result = alloc (3, 0);
  ...
  CAMLreturn (result);
}

Rule 3   Assignments to the fields of structured blocks must be done with the Store_field macro (for normal blocks) or Store_double_field macro (for arrays and records of floating-point numbers). Other assignments must not use Store_field nor Store_double_field.

Store_field (b, n, v) stores the value v in the field number n of value b, which must be a block (i.e. Is_block(b) must be true).

Example:
value bar (value v1, value v2, value v3)
{
  CAMLparam3 (v1, v2, v3);
  CAMLlocal1 (result);
  result = alloc (3, 0);
  Store_field (result, 0, v1);
  Store_field (result, 1, v2);
  Store_field (result, 2, v3);
  CAMLreturn (result);
}
Warning:
The first argument of Store_field and Store_double_field must be a variable declared by CAMLparam* or a parameter declared by CAMLlocal* to ensure that a garbage collection triggered by the evaluation of the other arguments will not invalidate the first argument after it is computed.
Rule 4   Global variables containing values must be registered with the garbage collector using the register_global_root function.

Registration of a global variable v is achieved by calling register_global_root(&v) just before a valid value is stored in v for the first time.

A registered global variable v can be un-registered by calling remove_global_root(&v).

Note: The CAML macros use identifiers (local variables, type identifiers, structure tags) that start with caml__. Do not use any identifier starting with caml__ in your programs.

18.5.2 Low-level interface

We now give the GC rules corresponding to the low-level allocation functions alloc_small and alloc_shr. You can ignore those rules if you stick to the simplified allocation function alloc.
Rule 5   After a structured block (a block with tag less than No_scan_tag) is allocated with the low-level functions, all fields of this block must be filled with well-formed values before the next allocation operation. If the block has been allocated with alloc_small, filling is performed by direct assignment to the fields of the block:

        Field(v, n) = vn;
If the block has been allocated with alloc_shr, filling is performed through the initialize function:

        initialize(&Field(v, n), vn);

The next allocation can trigger a garbage collection. The garbage collector assumes that all structured blocks contain well-formed values. Newly created blocks contain random data, which generally do not represent well-formed values.

If you really need to allocate before the fields can receive their final value, first initialize with a constant value (e.g. Val_unit), then allocate, then modify the fields with the correct value (see rule 6).
Rule 6   Direct assignment to a field of a block, as in

        Field(v, n) = w;
is safe only if v is a block newly allocated by alloc_small; that is, if no allocation took place between the allocation of v and the assignment to the field. In all other cases, never assign directly. If the block has just been allocated by alloc_shr, use initialize to assign a value to a field for the first time:

        initialize(&Field(v, n), w);
Otherwise, you are updating a field that previously contained a well-formed value; then, call the modify function:

        modify(&Field(v, n), w);

To illustrate the rules above, here is a C function that builds and returns a list containing the two integers given as parameters. First, we write it using the simplified allocation functions:
value alloc_list_int(int i1, int i2)
{
  CAMLparam0 ();
  CAMLlocal2 (result, r);

  r = alloc(2, 0);                        /* Allocate a cons cell */
  Store_field(r, 0, Val_int(i2));         /* car = the integer i2 */
  Store_field(r, 1, Val_int(0));          /* cdr = the empty list [] */
  result = alloc(2, 0);                   /* Allocate the other cons cell */
  Store_field(result, 0, Val_int(i1));    /* car = the integer i1 */
  Store_field(result, 1, r);              /* cdr = the first cons cell */
  CAMLreturn (result);
}
Here, the registering of result is not strictly needed, because no allocation takes place after it gets its value, but it's easier and safer to simply register all the local variables that have type value.

Here is the same function written using the low-level allocation functions. We notice that the cons cells are small blocks and can be allocated with alloc_small, and filled by direct assignments on their fields.
value alloc_list_int(int i1, int i2)
{
  CAMLparam0 ();
  CAMLlocal2 (result, r);

  r = alloc_small(2, 0);                  /* Allocate a cons cell */
  Field(r, 0) = Val_int(i2);              /* car = the integer i2 */
  Field(r, 1) = Val_int(0);               /* cdr = the empty list [] */
  result = alloc_small(2, 0);             /* Allocate the other cons cell */
  Field(result, 0) = Val_int(i1);         /* car = the integer i1 */
  Field(result, 1) = r;                   /* cdr = the first cons cell */
  CAMLreturn (result);
}
In the two examples above, the list is built bottom-up. Here is an alternate way, that proceeds top-down. It is less efficient, but illustrates the use of modify.
value alloc_list_int(int i1, int i2)
{
  CAMLparam0 ();
  CAMLlocal2 (tail, r);

  r = alloc_small(2, 0);                  /* Allocate a cons cell */
  Field(r, 0) = Val_int(i1);              /* car = the integer i1 */
  Field(r, 1) = Val_int(0);               /* A dummy value
  tail = alloc_small(2, 0);               /* Allocate the other cons cell */
  Field(tail, 0) = Val_int(i2);           /* car = the integer i2 */
  Field(tail, 1) = Val_int(0);            /* cdr = the empty list [] */
  modify(&Field(r, 1), tail);             /* cdr of the result = tail */
  return r;
}
It would be incorrect to perform Field(r, 1) = tail directly, because the allocation of tail has taken place since r was allocated. tail is not registered as a root because there is no allocation between the assignment where it takes its value and the modify statement that uses the value.

18.6 A complete example

This section outlines how the functions from the Unix curses library can be made available to Objective Caml programs. First of all, here is the interface curses.mli that declares the curses primitives and data types:
type window                   (* The type "window" remains abstract *)
external initscr: unit -> window = "curses_initscr"
external endwin: unit -> unit = "curses_endwin"
external refresh: unit -> unit = "curses_refresh"
external wrefresh : window -> unit = "curses_wrefresh"
external newwin: int -> int -> int -> int -> window = "curses_newwin"
external mvwin: window -> int -> int -> unit = "curses_mvwin"
external addch: char -> unit = "curses_addch"
external mvwaddch: window -> int -> int -> char -> unit = "curses_mvwaddch"
external addstr: string -> unit = "curses_addstr"
external mvwaddstr: window -> int -> int -> string -> unit = "curses_mvwaddstr"
(* lots more omitted *)
To compile this interface:
        ocamlc -c curses.mli
To implement these functions, we just have to provide the stub code; the core functions are already implemented in the curses library. The stub code file, curses.o, looks like:
#include <curses.h>
#include <mlvalues.h>

value curses_initscr(value unit)
{
  CAMLparam1 (unit);
  CAMLreturn ((value) initscr());  /* OK to coerce directly from WINDOW * to
                              value since that's a block created by malloc() */
}

value curses_wrefresh(value win)
{
  CAMLparam1 (win);
  wrefresh((WINDOW *) win);
  CAMLreturn (Val_unit);
}

value curses_newwin(value nlines, value ncols, value x0, value y0)
{
  CAMLparam4 (nlines, ncols, x0, y0);
  CAMLreturn ((value) newwin(Int_val(nlines), Int_val(ncols),
                             Int_val(x0), Int_val(y0)));
}

value curses_addch(value c)
{
  CAMLparam1 (c);
  addch(Int_val(c));            /* Characters are encoded like integers */
  CAMLreturn (Val_unit);
}

value curses_addstr(value s)
{
  CAMLparam1 (s);
  addstr(String_val(s));
  CAMLreturn (Val_unit);
}

/* This goes on for pages. */
The file curses.c can be compiled with:
        cc -c -I/usr/local/lib/ocaml curses.c
or, even simpler,
        ocamlc -c curses.c
(When passed a .c file, the ocamlc command simply calls the C compiler on that file, with the right -I option.)

Now, here is a sample Caml program test.ml that uses the curses module:
open Curses
let main_window = initscr () in
let small_window = newwin 10 5 20 10 in
  mvwaddstr main_window 10 2 "Hello";
  mvwaddstr small_window 4 3 "world";
  refresh();
  for i = 1 to 100000 do () done;
  endwin()
To compile this program, run:
        ocamlc -c test.ml
Finally, to link everything together:
        ocamlc -custom -o test test.cmo curses.o -cclib -lcurses
(On some machines, you may need to put -cclib -ltermcap or -cclib -lcurses -cclib -ltermcap instead of -cclib -lcurses.)

18.7 Advanced topic: callbacks from C to Caml

So far, we have described how to call C functions from Caml. In this section, we show how C functions can call Caml functions, either as callbacks (Caml calls C which calls Caml), or because the main program is written in C.

18.7.1 Applying Caml closures from C

C functions can apply Caml functional values (closures) to Caml values. The following functions are provided to perform the applications: If the function f does not return, but raises an exception that escapes the scope of the application, then this exception is propagated to the next enclosing Caml code, skipping over the C code. That is, if a Caml function f calls a C function g that calls back a Caml function h that raises a stray exception, then the execution of g is interrupted and the exception is propagated back into f.

If the C code wishes to catch exceptions escaping the Caml function, it can use the functions callback_exn, callback2_exn, callback3_exn, callbackN_exn. These functions take the same arguments as their non-_exn counterparts, but catch escaping exceptions and return them to the C code. The return value v of the callback*_exn functions must be tested with the macro Is_exception_result(v). If the macro returns ``false'', no exception occured, and v is the value returned by the Caml function. If Is_exception_result(v) returns ``true'', an exception escaped, and its value (the exception descriptor) can be recovered using Extract_exception(v).

18.7.2 Registering Caml closures for use in C functions

The main difficulty with the callback functions described above is obtaining a closure to the Caml function to be called. For this purpose, Objective Caml provides a simple registration mechanism, by which Caml code can register Caml functions under some global name, and then C code can retrieve the corresponding closure by this global name.

On the Caml side, registration is performed by evaluating Callback.register n v. Here, n is the global name (an arbitrary string) and v the Caml value. For instance:
    let f x = print_string "f is applied to "; print_int n; print_newline()
    let _ = Callback.register "test function" f
On the C side, a pointer to the value registered under name n is obtained by calling caml_named_value(n). The returned pointer must then be dereferenced to recover the actual Caml value. If no value is registered under the name n, the null pointer is returned. For example, here is a C wrapper that calls the Caml function f above:
    void call_caml_f(int arg)
    {
        callback(*caml_named_value("test function"), Val_int(arg));
    }
The pointer returned by caml_named_value is constant and can safely be cached in a C variable to avoid repeated name lookups. On the other hand, the value pointed to can change during garbage collection and must always be recomputed at the point of use. Here is a more efficient variant of call_caml_f above that calls caml_named_value only once:
    void call_caml_f(int arg)
    {
        static value * closure_f = NULL;
        if (closure_f == NULL) {
            /* First time around, look up by name */
            closure_f = caml_named_value("test function");
        }
        callback(*closure_f, Val_int(arg));
    }
18.7.3 Registering Caml exceptions for use in C functions

The registration mechanism described above can also be used to communicate exception identifiers from Caml to C. The Caml code registers the exception by evaluating Callback.register_exception n exn, where n is an arbitrary name and exn is an exception value of the exception to register. For example:
    exception Error of string
    let _ = Callback.register_exception "test exception" (Error "any string")
The C code can then recover the exception identifier using caml_named_value and pass it as first argument to the functions raise_constant, raise_with_arg, and raise_with_string (described in section 18.4.5) to actually raise the exception. For example, here is a C function that raises the Error exception with the given argument:
    void raise_error(char * msg)
    {
        raise_with_string(*caml_named_value("test exception"), msg);
    }
18.7.4 Main program in C

In normal operation, a mixed Caml/C program starts by executing the Caml initialization code, which then may proceed to call C functions. We say that the main program is the Caml code. In some applications, it is desirable that the C code plays the role of the main program, calling Caml functions when needed. This can be achieved as follows:
18.7.5 Embedding the Caml code in the C code

The bytecode compiler in custom runtime mode (ocamlc -custom) normally appends the bytecode to the executable file containing the custom runtime. This has two consequences. First, the final linking step must be performed by ocamlc. Second, the Caml runtime library must be able to find the name of the executable file from the command-line arguments. When using caml_main(argv) as in section 18.7.4, this means that argv[0] or argv[1] must contain the executable file name.

An alternative is to embed the bytecode in the C code. The -output-obj option to ocamlc is provided for this purpose. It causes the ocamlc compiler to output a C object file (.o file) containing the bytecode for the Caml part of the program, as well as a caml_startup function. The C object file produced by ocamlc -output-obj can then be linked with C code using the standard C compiler, or stored in a C library.

The caml_startup function must be called from the main C program in order to initialize the Caml runtime and execute the Caml initialization code. Just like caml_main, it takes one argv parameter containing the command-line parameters. Unlike caml_main, this argv parameter is used only to initialize Sys.argv, but not for finding the name of the executable file.

The native-code compiler ocamlopt also supports the -output-obj option, causing it to output a C object file containing the native code for all Caml modules on the command-line, as well as the Caml startup code. Initialization is performed by calling caml_startup as in the case of the bytecode compiler.

For the final linking phase, in addition to the object file produced by -output-obj, you will have to provide the Objective Caml runtime library (libcamlrun.a for bytecode, libasmrun.a for native-code), as well as all C libraries that are required by the Caml libraries used. For instance, assume the Caml part of your program uses the Unix library. With ocamlc, you should do:
        ocamlc -output-obj -o camlcode.o unix.cma other .cmo and .cma files
        cc -o myprog C objects and libraries \
           camlcode.o -L/usr/local/lib/ocaml -lunix -lcamlrun
With ocamlopt, you should do:
        ocamlopt -output-obj -o camlcode.o unix.cmxa other .cmx and .cmxa files
        cc -o myprog C objects and libraries \
           camlcode.o -L/usr/local/lib/ocaml -lunix -lasmrun
Warning:
On some ports, special options are required on the final linking phase that links together the object file produced by the -output-obj option and the remainder of the program. Those options are shown in the configuration file config/Makefile generated during compilation of Objective Caml, as the variables BYTECCLINKOPTS (for object files produced by ocamlc -output-obj) and NATIVECCLINKOPTS (for object files produced by ocamlopt -output-obj). Currently, the only ports that require special attention are:
18.8 Advanced example with callbacks

This section illustrates the callback facilities described in section 18.7. We are going to package some Caml functions in such a way that they can be linked with C code and called from C just like any C functions. The Caml functions are defined in the following mod.ml Caml source:
(* File mod.ml -- some ``useful'' Caml functions *)

let rec fib n = if n < 2 then 1 else fib(n-1) + fib(n-2)

let format_result n = Printf.sprintf "Result is: %d\n" n

(* Export those two functions to C *)

let _ = Callback.register "fib" fib
let _ = Callback.register "format_result" format_result
Here is the C stub code for calling these functions from C:
/* File modwrap.c -- wrappers around the Caml functions */

#include <stdio.h>
#include <string.h>
#include <caml/mlvalues.h>
#include <caml/callback.h>

int fib(int n)
{
  static value * fib_closure = NULL;
  if (fib_closure == NULL) fib_closure = caml_named_value("fib");
  return Int_val(callback(*fib_closure, Val_int(n)));
}

char * format_result(int n)
{
  static value * format_result_closure = NULL;
  if (format_result_closure == NULL)
    format_result_closure = caml_named_value("format_result");
  return strdup(String_val(callback(*format_result_closure, Val_int(n))));
  /* We copy the C string returned by String_val to the C heap
     so that it remains valid after garbage collection. */
}
We now compile the Caml code to a C object file and put it in a C library along with the stub code in modwrap.c and the Caml runtime system:
        ocamlc -custom -output-obj -o modcaml.o mod.ml
        ocamlc -c modwrap.c
        cp /usr/local/lib/ocaml/libcamlrun.a mod.a
        ar r mod.a modcaml.o modwrap.o
(One can also use ocamlopt -output-obj instead of ocamlc -custom -output-obj. In this case, replace libcamlrun.a (the bytecode runtime library) by libasmrun.a (the native-code runtime library).)

Now, we can use the two fonctions fib and format_result in any C program, just like regular C functions. Just remember to call caml_startup once before.
/* File main.c -- a sample client for the Caml functions */

#include <stdio.h>

int main(int argc, char ** argv)
{
  int result;

  /* Initialize Caml code */
  caml_startup(argv);
  /* Do some computation */
  result = fib(10);
  printf("fib(10) = %s\n", format_result(result));
  return 0;
}
To build the whole program, just invoke the C compiler as follows:
        cc -o prog main.c mod.a -lcurses
(On some machines, you may need to put -ltermcap or -lcurses -ltermcap instead of -lcurses.)

18.9 Advanced topic: custom blocks

Blocks with tag Custom_tag contain both arbitrary user data and a pointer to a C struct, with type struct custom_operations, that associates user-provided finalization, comparison, hashing, serialization and deserialization functions to this block.

18.9.1 The struct custom_operations

The struct custom_operations is defined in <caml/custom.h> and contains the following fields:
18.9.2 Allocating custom blocks

Custom blocks must be allocated via the alloc_custom function. alloc_custom(ops, size, used, max) returns a fresh custom block, with room for size bytes of user data, and whose associated operations are given by ops (a pointer to a struct custom_operations, usually statically allocated as a C global variable).

The two parameters used and max are used to control the speed of garbage collection when the finalized object contains pointers to out-of-heap resources. Generally speaking, the Caml incremental major collector adjusts its speed relative to the allocation rate of the program. The faster the program allocates, the harder the GC works in order to reclaim quickly unreachable blocks and avoid having large amount of ``floating garbage'' (unreferenced objects that the GC has not yet collected).

Normally, the allocation rate is measured by counting the in-heap size of allocated blocks. However, it often happens that finalized objects contain pointers to out-of-heap memory blocks and other resources (such as file descriptors, X Windows bitmaps, etc.). For those blocks, the in-heap size of blocks is not a good measure of the quantity of resources allocated by the program.

The two arguments used and max give the GC an idea of how much out-of-heap resources are consumed by the finalized block being allocated: you give the amount of resources allocated to this object as parameter used, and the maximum amount that you want to see in floating garbage as parameter max. The units are arbitrary: the GC cares only about the ratio used / max.

For instance, if you are allocating a finalized block holding an X Windows bitmap of w by h pixels, and you'd rather not have more than 1 mega-pixels of unreclaimed bitmaps, specify used = w * h and max = 1000000.

Another way to describe the effect of the used and max parameters is in terms of full GC cycles. If you allocate many custom blocks with used / max = 1 / N, the GC will then do one full cycle (examining every object in the heap and calling finalization functions on those that are unreachable) every N allocations. For instance, if used = 1 and max = 1000, the GC will do one full cycle at least every 1000 allocations of custom blocks.

If your finalized blocks contain no pointers to out-of-heap resources, or if the previous discussion made little sense to you, just take used = 0 and max = 1. But if you later find that the finalization functions are not called ``often enough'', consider increasing the used / max ratio.

18.9.3 Accessing custom blocks

The data part of a custom block v can be accessed via the pointer Data_custom_val(v). This pointer has type void * and should be cast to the actual type of the data stored in the custom block.

The contents of custom blocks are not scanned by the garbage collector, and must therefore not contain any pointer inside the Caml heap. In other terms, never store a Caml value in a custom block, and do not use Field, Store_field nor modify to access the data part of a custom block. Conversely, any C data structure (not containing heap pointers) can be stored in a custom block.

18.9.4 Writing custom serialization and deserialization functions

The following functions, defined in <caml/intext.h>, are provided to write and read back the contents of custom blocks in a portable way. Those functions handle endianness conversions when e.g. data is written on a little-endian machine and read back on a big-endian machine.
Function Action
serialize_int_1 Write a 1-byte integer
serialize_int_2 Write a 2-byte integer
serialize_int_4 Write a 4-byte integer
serialize_int_8 Write a 8-byte integer
serialize_float_4 Write a 4-byte float
serialize_float_8 Write a 8-byte float
serialize_block_1 Write an array of 1-byte quantities
serialize_block_2 Write an array of 2-byte quantities
serialize_block_4 Write an array of 4-byte quantities
serialize_block_8 Write an array of 8-byte quantities
deserialize_uint_1 Read an unsigned 1-byte integer
deserialize_sint_1 Read a signed 1-byte integer
deserialize_uint_2 Read an unsigned 2-byte integer
deserialize_sint_2 Read a signed 2-byte integer
deserialize_uint_4 Read an unsigned 4-byte integer
deserialize_sint_4 Read a signed 4-byte integer
deserialize_uint_8 Read an unsigned 8-byte integer
deserialize_sint_8 Read a signed 8-byte integer
deserialize_float_4 Read a 4-byte float
deserialize_float_8 Read an 8-byte float
deserialize_block_1 Read an array of 1-byte quantities
deserialize_block_2 Read an array of 2-byte quantities
deserialize_block_4 Read an array of 4-byte quantities
deserialize_block_8 Read an array of 8-byte quantities
deserialize_error Signal an error during deserialization; input_value or Marshal.from_... raise a Failure exception after cleaning up their internal data structures

Serialization functions are attached to the custom blocks to which they apply. Obviously, deserialization functions cannot be attached this way, since the custom block does not exist yet when deserialization begins! Thus, the struct custom_operations that contain deserialization functions must be registered with the deserializer in advance, using the register_custom_operations function declared in <caml/custom.h>. Deserialization proceeds by reading the identifier off the input stream, allocating a custom block of the size specified in the input stream, searching the registered struct custom_operation blocks for one with the same identifier, and calling its deserialize function to fill the data part of the custom block.

18.9.5 Choosing identifiers

Identifiers in struct custom_operations must be chosen carefully, since they must identify uniquely the data structure for serialization and deserialization operations. In particular, consider including a version number in the identifier; this way, the format of the data can be changed later, yet backward-compatible deserialisation functions can be provided.

Identifiers starting with _ (an underscore character) are reserved for the Objective Caml runtime system; do not use them for your custom data. We recommend to use a URL (http://mymachine.mydomain.com/mylibrary/version-number) or a Java-style package name (com.mydomain.mymachine.mylibrary.version-number) as identifiers, to minimize the risk of identifier collision.

18.9.6 Finalized blocks

Custom blocks generalize the finalized blocks that were present in Objective Caml prior to version 3.00. For backward compatibility, the format of custom blocks is compatible with that of finalized blocks, and the alloc_final function is still available to allocate a custom block with a given finalization function, but default comparison, hashing and serialization functions. alloc_final(n, f, used, max) returns a fresh custom block of size n words, with finalization function f. The first word is reserved for storing the custom operations; the other n-1 words are available for your data. The two parameters used and max are used to control the speed of garbage collection, as described for alloc_custom.

18.10 Building mixed C/Caml libraries: ocamlmklib

The ocamlmklib command facilitates the construction of libraries containing both Caml code and C code, and usable both in static linking and dynamic linking modes.

  Windows:
This command is available only under Cygwin, but not for the native Win32 port.
  MacOS:
This command is not available.
The ocamlmklib command takes three kinds of arguments: It generates the following outputs: In addition, the following options are recognized:
-cclib, -ccopt, -I, -linkall
These options are passed as is to ocamlc or ocamlopt. See the documentation of these commands.
-pthread, -rpath, -R, -Wl,-rpath, -Wl,-R
These options are passed as is to the C compiler. Refer to the documentation of the C compiler.
-custom
Force the construction of a statically linked library only, even if dynamic linking is supported.
-failsafe
Fall back to building a statically linked library if a problem occurs while building the shared library (e.g. some of the support libraries are not available as shared libraries).
-Ldir
Add dir to the search path for support libraries (-llib).
-ocamlc cmd
Use cmd instead of ocamlc to call the bytecode compiler.
-ocamlopt cmd
Use cmd instead of ocamlopt to call the native-code compiler.
-o output
Set the name of the generated Caml library. ocamlmklib will generate output.cma and/or output.cmxa. If not specified, defaults to a.
-oc outputc
Set the name of the generated C library. ocamlmklib will generate liboutputc.so (if shared libraries are supported) and liboutputc.a. If not specified, defaults to the output name given with -o.
Example
Consider a Caml interface to the standard libz C library for reading and writing compressed files. Assume this library resides in /usr/local/zlib. This interface is composed of a Caml part zip.cmo/zip.cmx and a C part zipstubs.o containing the stub code around the libz entry points. The following command builds the Caml libraries zip.cma and zip.cmxa, as well as the companion C libraries dllzip.so and libzip.a:
ocamlmklib -o zip zip.cmo zip.cmx zipstubs.o -lz -L/usr/local/zlib
If shared libraries are supported, this performs the following commands:
ocamlc -a -o zip.cma zip.cmo -dllib -lzip \
        -cclib -lzip -cclib -lz -ccopt -L/usr/local/zlib
ocamlopt -a -o zip.cmxa zip.cmx -cclib -lzip \
        -cclib -lzip -cclib -lz -ccopt -L/usr/local/zlib
gcc -shared -o dllzip.so zipstubs.o -lz -L/usr/local/zlib
ar rc libzip.a zipstubs.o
If shared libraries are not supported, the following commands are performed instead:
ocamlc -a -custom -o zip.cma zip.cmo -cclib -lzip \
        -cclib -lz -ccopt -L/usr/local/zlib
ocamlopt -a -o zip.cmxa zip.cmx -lzip \
        -cclib -lz -ccopt -L/usr/local/zlib
ar rc libzip.a zipstubs.o
Instead of building simultaneously the bytecode library, the native-code library and the C libraries, ocamlmklib can be called three times to build each separately. Thus,
ocamlmklib -o zip zip.cmo -lz -L/usr/local/zlib
builds the bytecode library zip.cma, and
ocamlmklib -o zip zip.cmx -lz -L/usr/local/zlib
builds the native-code library zip.cmxa, and
ocamlmklib -o zip zipstubs.o -lz -L/usr/local/zlib
builds the C libraries dllzip.so and libzip.a. Notice that the support libraries (-lz) and the corresponding options (-L/usr/local/zlib) must be given on all three invocations of ocamlmklib, because they are needed at different times depending on whether shared libraries are supported.


Previous Contents Next