ocamlfindで親子階層のパッケージを登録する

外が暑いというよりかは、部屋の中が暑いことにようやく気付きました。日当たりが良すぎるもの考えものですな。

さて、ニート生活も慣れてきまして、色々試したりなんだりとしていたところ、次のような問題に当たりました。

あるパッケージ A の内部でだけ利用しているけど、Aの内部には含めたくないライブラリ B もocamlfindに登録したい。
けど、それ単独では登録したくない。

いまいち何がやりたいのか自分でもわかりませんが、とりあえずこういうことをやりたくなりました。開発者必携のツールと評判のocamlfindは、METAというファイルの内容に従って、パッケージの情報を収集しています。
普通は一つのパッケージについてひとつのMETAだけなのですが、上記のような場合、普通にMETAを書いてしまうと、AのMETAとBのMETAが必要になります。しかしBはAの内部でしか利用していないため、あまり外部には公開したくありません。というか基本的には公開したくありません。ですが、Aを使うためにはBが必要なので、やっぱりBも必要です。
こういう時に利用できる?と思われるのが、METAに親子間の関係を記述する機能です。ocamlfindのドキュメントには書いてあります。

The outermost variable assignments and additions belong to the main package. The name of the main package is not defined within META; it is either the name of the directory containing META or the suffix of the META file (if the name of the META file is formed like META.name).

The keyword package starts the definition of a subpackage. There must not be two such definitions with the same name. Within the parantheses, the variable assignments and additions refer to the subpackage. It is allowed that a subpackage contains further subpackages.

The package name following package is the local name relative to the main package, i.e. the name of the main package is not mentioned. At all other places, however, the subpackage must be prefixed by the name of the containing package, separated by a '.'.

Subpackages are independent of the containing package, except that the subpackage points to the same installation directory as the containing package (i.e. the location of the installation directory is inherited from the containing package).

http://projects.camlcity.org/projects/dl/findlib-1.3.3/doc/ref-html/r681.html

つまりは相対的なパッケージを登録できる、ってことですね。

さて、こいつをどんな感じに書くのかというと、私はsexplibのMETAを参考(ほぼパクりとも言う)しましたが、実際には以下のようになりました。

version = "0.1"
description = "SDL - OCaml bindings"
requires = "num sdlcaml.extlib"
archive(byte) = "sdlcaml.cma"
archive(byte, plugin) = "sdlcaml.cma"
archive(native) = "sdlcaml.cmxa"
exists_if = "sdlcaml.cma"
package "extlib" (
  version = "0.1"
  description = "sdlcaml internal library"
  archive(byte) = "extlib.cma"
  archive(byte, plugin) = "extlib.cma"
  archive(native) = "extlib.cmxa"
  exists_if = "extlib.cma"
)

ここで、requiresにサブパッケージであるsdlcaml.extlibが書かれていますが、これでもちゃんと動きますし、OMakeでのOCAMLPACKSに、sdlcamlとだけ書いても動きます。それと、sdlcaml.extlibの内部についてもちゃんと利用できます。

上のocamlfindからの引用に書いてありますが、サブパッケージはメインパッケージのMETAがあるところと同じ位置に置けば問題ありません。私はこんなやつについて利用するために(あまり需要は無いはずですが)、自分のOMakefileに↓のような関数を登録して使用するようにしました。(実際はもうちょっと追加されてます。

#| return files which are generated by MyOCamlPackage.
#  You have to send same arguments when MyOCamlPackage called.
public.OCamlPackageGeneratedFiles(library_name, cmodules) =
   CSTUBLIBRARIES=
   if $(not $(equal $(cmodules), $(EMPTY)))
       CSTUBLIBRARIES= dll$(name).so lib$(name)$(EXT_LIB)
       export

   return ($(name).cmi $(name).cmo $(name).cmx $(name).cma $(name).cmxa $(name).o $(name).a $(CSTUBLIBRARIES))

public.OCamlInstallParentPackage(name, files) =
	targets[] = $(files)

	install:
		$(OCAMLFIND) install -add $(name) $(targets)
	uninstall:
		$(OCAMLFIND) remove $(name)

OCamlInstallParentPackageのnameを、親としたいパッケージの名前にすれば、相対的なパッケージとして登録されます。もちろん事前にMETAは記述しておく必要があります。とりあえずこんな感じで、OMakeを使ってビルド〜インストール〜アンインストールまでできるようになりました。非常に快適ですので、やっぱり皆さんOMakeを使いましょう。

ocamlfindについては、利用方法については結構書かれているのですが、ocamlfindに登録するパッケージを作成するときの情報がほとんど見つけられなかったので、調べるついでに記事にしてみました。有用なライブラリを書かれている方には当然のことかもしれませんが、私のようにOCaml初心者にとってはこの辺も敷居が高そうな感じに思いました。英語しか一次情報が無いというのも慣れてきましたが、やっぱり日本人には日本語の情報があったほうがいいですね。