自作のJuliaパッケージで、Cライブラリとの依存性を記述する

  • 1
    いいね
  • 0
    コメント

結論

BinDeps.jl を使うのがよいです。

まえがき

Juliaでパッケージを作る際には、パッケージの依存関係を REQUIRE という特殊なファイルに記述します。例えば、自作のパッケージが Distributions.jl に依存する場合は、

REQUIRE
Distributions

と記述します。すると、Pkg.add した際に、自動的に依存パッケージもインストールされます。

では、自作のパッケージがC言語等のJuliaのパッケージではないライブラリに依存する場合にはどうすればいいのでしょうか?パッケージをインストールする前にインストールしておいてもらう、というのはユーザに優しくありませんし、できればパッケージインストール時に自動で解決してくれるような仕組みを取り入れたいです。

さて、肝心の方法ですが、僕が確認した限りJuliaのドキュメントには見当たりません。というわけで、その方法について僕が知っていることを書きます。

Juliaのパッケージの作り方については、日本語でわかりやすい解説がありますので、あわせてどうぞ。

Juliaのパッケージをつくろう!

deps/build.jl という特殊なファイル

C等のライブラリの依存関係は、deps/build.jl に依存関係を記述します。これは、公式ドキュメントにも(いちおう)、上で挙げた解説にも書かれています。

で、どうやって書くか?が問題なわけですが、そこで BinDeps.jl の出番です。必須ではありませんが(例えばシェルで書くことも可能)、多くのパッケージでBinDeps.jlが使われています。

BinDeps.jlで依存関係を書く

BinDeps.jlを使えば、apt-get や yum や brew を呼んだり、ソースを落としてきてコンパイルさせる、といったことが簡単にできます。

詳細な使い方は BinDeps.jlの README を見てもらうとして、自分が書いた例を示して、(本当に)簡単に解説します。

SPTKという音声信号処理ツールキット(の俺俺版)のJuliaラッパー(SPTK.jl)を作ったときのコードです。簡単にコメントを書きました。

deps/build.jl
using BinDeps

# BinDepsを使うための初期化処理
@BinDeps.setup

# sptkという依存関係を定義する
deps = [
        sptk = library_dependency("libSPTK")
        ]

const version = "3.7.0"

# sptkにソースを紐付け
provides(Sources,
         URI("https://github.com/r9y9/SPTK/archive/v$(version).tar.gz"),
         sptk,
         unpacked_dir="SPTK-$(version)")

# インストール先とかソースのあるディレクトリとか         
prefix = joinpath(BinDeps.depsdir(sptk), "usr")
srcdir = joinpath(BinDeps.depsdir(sptk), "src", "SPTK-$(version)")

# sptkに対してビルドルールを紐付け
provides(SimpleBuild,
          (@build_steps begin
              GetSources(sptk)
              @build_steps begin
                  ChangeDirectory(srcdir)
                  `./waf configure --prefix=$prefix`
                  `./waf build`
                  `./waf install`
              end
           end), sptk, os = :Unix)

# インストール
@BinDeps.install [:libSPTK => :libSPTK]

依存関係を定義して、ソースなりビルドルールなりを関連付けて、ほいっとインストール、という感じです(適当

本当は、ソースではなくバイナリを落としてくる方が良いのですが(ユーザはコンパイラを持っていない可能性がある的な意味で)、この例ではご愛嬌

brewやaptを使うケースは、Githubで検索すればたくさん見つかるので、探してみてください…(力尽きました)

BinDeps.jl を使うパッケージを作るときの注意

deps/deps.jl という特殊なファイル

パッケージのインストール時(正確にはPkg.buildが呼ばれる時)に deps/build.jl が実行され、deps/deps.jl という特殊なファイルが作られます。

このファイルには、BinDeps.jl でマネジメントしているライブラリの参照先が書かれています。

deps/deps.jl

macro checked_lib(libname, path)
        (dlopen_e(path) == C_NULL) && error("Unable to load \n\n$libname ($path)\n\nPlease re-run Pkg.build(package), and restart Julia.")
        quote const $(esc(libname)) = $path end
    end
@checked_lib libSPTK "/Users/ryuichi/.julia/v0.3/SPTK/deps/usr/lib/libSPTK.dylib"

もし、すでにシステムにライブラリがインストール済であれば、次のようにインストール済のライブラリを使うように設定されます。

deps/deps.jl(システムにライブラリがインストール済の場合)
macro checked_lib(libname, path)
        (dlopen_e(path) == C_NULL) && error("Unable to load \n\n$libname ($path)\n\nPlease re-run Pkg.build(package), and restart Julia.")
        quote const $(esc(libname)) = $path end
    end
@checked_lib libSPTK "/usr/local/lib/libSPTK.so"

こんな感じです。

最後に、自作のパッケージ内で deps/deps.jl をincludeする必要があります。

SPTK.jl(一部抜粋)
deps = joinpath(Pkg.dir("SPTK"), "deps", "deps.jl")
if isfile(deps)
    include(deps)
else
    error("SPTK not properly installed. Please run Pkg.build(\"SPTK\")")
end

あとは、好きなように ccall すればOK。

includeする際に、deps.jl がない場合(Pkg.clone(xxx)した場合とか)は、エラーを吐くようにしています。これは、有名ドコロのJuliaのパッケージを習っています。慣習のようです。

おわり

以上、後半テキトーになったけど、メモとして残しておきます。

JuliaOpt のパッケージでは、バイナリの依存関係マネジメントにBinDeps.jlを使用することを薦めているので、そのあたりのbuild.jlを読むと、BinDeps.jlの使い方の参考になるかもしれません。

おわり

メモ

  • 関連して、pythonライブラリの依存関係をマネジメントするにはどうすればいいのか?と悩んでたんですが、まだ答えは見つかっていません
  • 正直途中で力尽きましたごめんなさい