PackageCompiler で GR バックエンドの Plots.jl を含めた Julia で書いたコードをコンパイルする
Deprecation Warning
- この記事はすでに古くPackageCompilerが PackageCompilerX.jl の内容に 置き換わるといった大幅な改変が行われました.(2020/2/12現在)
本日は
Julia の Docker Hub で公開している Docker のイメージ を拡張して PackageCompiler.jl による Julia のスクリプトを実行形式にしてみましょう。
え、そんなことできちゃうの?
できちゃうんです。そう、JuliaとPackageCompilerならね。
PackageCompiler の example は UnicodePlots.jl によるグラフ描画を可能にする例ですがコレを Plots.jl にしたらどうなるのかについて書いてみようと思います。
使い方
コンパイルしたいスクリプトを描く
Base.@ccallable function julia_main(ARGS::Vector{String})::Cint
を持つ Julia のモジュールを作成してやれば良いみたいです。例えば下記のようにしておきます。
module Hello
# 依存するパッケージをここに書いておく
using Plots
# 動かしたい処理を記述関数名は main とする必要はない
function main()
xs = -π:0.01:π
p=plot(xs, sin.(xs));
savefig(p, "sin_curve.png")
end
Base.@ccallable function julia_main(ARGS::Vector{String})::Cint
# ここに書いても良い
main()
return 0
end
end
コレで sin
関数の描画結果を sin_curve.png
という名前で保存することができます。
ビルドさせる処理を記述する
ここでは gen_executable.jl
という名前で下記の処理を書いたスクリプトを hello.jl
と同じ場所に保存しておきます。
using PackageCompiler
build_executable("hello.jl","hello")
そしてターミタル上で下記を実行します。
$ julia gen_executable.jl
もちろん Julia の REPL で動かしても問題ないです。
残念
このままだとビルドができません。下記のようなメッセージを含む長いエラーメッセージが出ます。
fatal: error thrown and no exception handler available.
ErrorException("error compiling draw_legend: error compiling legend_size: could not load library "libGR.so"
dlopen(libGR.so.dylib, 1): image not found")
libGR
がないと怒られてしまっています。 コレは Plots.jl
が依存しているライブラリなのでPackageCompilerがおこなうビルド作業をする前にこのライブラリの場所をおしえないといけません。
一般的な処方箋
Mac/Linuxの場合は export LD_LIBRARY_PATH=/path/to/lib
, Windows の場合は環境変数の Path に追加すればOKです。 Plots.jl に限らず C のライブラリに依存する場合に役にたつでしょう。
Julia のスクリプトで実現
Julia のスクリプトで閉じさせたい場合は ENV
という環境変数などの情報を記述している Base.EnvDict
型の変数に情報を記述すればOKです。 ENV
自体は PyCall.jl のビルドでよく使うので馴染みがある方も多いと思います。
余談ですが, PyCallが呼び出すPythonを既存のPython環境のそれを使いたい場合は
using Pkg
ENV["PYTHON"]=Sys.which("python")
pkg("build PyCall")
で実現できます。
さて、libGR 周りの解決ですが ~/.julia/packages
以下を find . | grep libgr
みたいなのを実行すると見つかると思います。そのディレクトリを指定させればOKです。Juliaではわざわざそんなことをしなくても
julia> using GR
julia> pathof(GR)
で大元の場所を特定させることができます。
Ubuntu/Mac と Windows では住んでいる場所が微妙に違います。
# Ubuntu/Macの場合
julia> joinpath(dirname(dirname(pathof(GR))),"deps","gr","lib")
# Windows
julia> joinpath(dirname(dirname(pathof(GR))),"deps","gr","bin")
こういった環境の差異を吸収するために下記のスクリプトを用意しました。
# gen_executable.jl
using PackageCompiler
using Pkg
if in("GR",keys(Pkg.installed()))
@eval using GR
else
Pkg.add("GR")
@eval using GR
end
if Sys.iswindows()
s=joinpath(dirname(dirname(pathof(GR))),"deps","gr","bin")
s*=';'
if !endswith(ENV["PATH"],';')
ENV["PATH"] *= ';'
end
ENV["PATH"] *= s
else
s=joinpath(dirname(dirname(pathof(GR))),"deps","gr","lib")
s*=':'
if haskey(ENV, "LD_LIBRARY_PATH")
ENV["LD_LIBRARY_PATH"] *= s
if !endswith(ENV["LD_LIBRARY_PATH"],':')
ENV["LD_LIBRARY_PATH"] *= ':'
end
else
ENV["LD_LIBRARY_PATH"] = s
end
end
build_executable("hello.jl","hello")
- 環境によっては ENV["PATH"] の末尾が ';' で終わっていない可能性があるのでそういう処理も書いてあります。
このスクリプトを実行して All Done と出ると完了です。bindir/hello
を実行させて動けばOKです。お疲れ様でした。
こちらの Gist も参考にどうぞ。
トラブルシューティング
- あ、実行できない・・・。
何かエラーが出る場合は Julia としてのコードを動かした場合に何かおかしいことが起きているかもしれません。下記のように動かして何か異常なことがあれば Julia としてのコードに異常があるのでそれを修正する必要があります。
julia> include("hello.jl")
julia> using .Hello
julia> Hello.main()
# 何かおかしいことがあればそれを直す
- 何かセグフォが起きるんだけど・・・
using PackageCompiler; compile_package("Plots.jl",force=true)
で作った Julia の環境ではビルドじにセグフォする悲しい現象が起きました。なので(壊してもいい)一度クリーンな環境で試すのをお勧めします。
やっぱり無理なんだけれど
デスヨネー、 Dockerのイメージをここに公開しているので コレでなら行くはずです。
ラズパイでビルドできないんだけれど
Arm 32 bit 環境への対応はもう少し整備が必要そうですね。
追記:ここに書いたIssueで解決策を書きました。PackageCompilerのソースをいじる必要があります。
Author And Source
この問題について(PackageCompiler で GR バックエンドの Plots.jl を含めた Julia で書いたコードをコンパイルする), 我々は、より多くの情報をここで見つけました https://qiita.com/SatoshiTerasaki/items/ad0f04872c30393e1313著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .