目次
TL;DR
- 野良パッケージはどんどん作ろう!
- 新規の METADATA デビューは今は待ったほうが良いかなぁ。。。
環境
Julia Version 1.0.0 Commit 5d4eaca0c9 (2018-08-08 20:58 UTC) Platform Info: OS: Linux (x86_64-pc-linux-gnu) CPU: Intel(R) Core(TM) i5-4460T CPU @ 1.90GHz WORD_SIZE: 64 LIBM: libopenlibm LLVM: libLLVM-6.0.0 (ORCJIT, haswell) Environment: JULIA_SHELL = /bin/bash JULIA_EDITOR = nvim
自作のパッケージ
オリジナルパッケージの雛形を作る
2019/1/29 追記: 以下の方法でも雛形作れますが、現在は PkgTemplates.jl を使えばライセンスファイルや TravisCI の設定ファイルを一度に作ることができるので最終的に GitHub に公開するならばこちらを使ったほうが後々の手間が省けます。
(v1.0) pkg> generate MyPackage # 自作のパッケージの雛形を作成 Generating project MyPackage: MyPackage/Project.toml MyPackage/src/MyPackage.jl shell> cd MyPackage/ /home/arch/tmp/MyPackage (v1.0) pkg> activate . (MyPackage) pkg>
自動的に出来るファイルの中身
src/MyPackage.jl
module MyPackage greet() = print("Hello World!") end # module
この module
の間にずらずらプログラムを書いていくと晴れて自分のパッケージが完成します。
詳しくは公式ドキュメント https://docs.julialang.org/en/stable/manual/modules/ を読んでください。
試しに作ったパッケージを読み込んでみます。
julia> using MyPackage [ Info: Precompiling MyPackage [f41f8282-a16f-11e8-2120-1f4619698387] julia> MyPackage.greet() Hello World!
関数を追加する
src/MyPackage.jl
module MyPackage greet() = print("Hello World!") hello(io::IO, name) = print(io, "Hello ", name) hello(name) = hello(stdout, name) end # module
Julia を立ち上げ直して、もう一度パッケージを読みこみ直す。
(v1.0) pkg> activate . (MyPackage) pkg> julia> using MyPackage [ Info: Recompiling stale cache file /home/arch/.julia/compiled/v1.0/MyPackage/o5PoH.ji for MyPackage [f41f8282-a16f-11e8-2120-1f4619698387] julia> MyPackage.greet() Hello World! julia> MyPackage.hello("Mike") Hello Mike
テストを書く
テストケースは test/runtests.jl
に書いていきます。@test
後が false
になるとテストを通すのに失敗します。
using Test, MyPackage @test sprint(MyPackage.hello, "Mike") == "Hello Mike"
Project.toml
を編集。テストだけに使うパッケージは [extras]
と [targets]
に書きます。
name = "MyPackage" uuid = "f41f8282-a16f-11e8-2120-1f4619698387" authors = ["goropikari <goropikari56@gmail.com>"] version = "0.1.0" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] test = ["Test"]
現在のディレクトリ構成はこんな感じ
shell> tree . ├── Project.toml ├── src │ └── MyPackage.jl └── test └── runtests.jl 2 directories, 3 files
テストする。 tests passed と出れば OK。
(MyPackage) pkg> test Testing MyPackage Resolving package versions... Testing MyPackage tests passed
自分のパッケージの中で他のパッケージを使う場合
自分のパッケージの中で Random
モジュールを使う例を通して v0.6 までとの違いを紹介します。
例えば、以下のようにプログラムを書いたとします。
MyPackage.jl
module MyPackage using Random greet() = print("Hello World!") hello(io::IO, name) = print(io, "Hello ", name) hello(name) = hello(stdout, name) randstr() = randstring() end # module
Julia v0.6 までは、今回の Random module のように Julia に標準実装された module に関しては REQUIRE *1 に書かずとも自分のパッケージ内で using
してもその module を普通に利用することができました。
しかし、Julia v1.0 からは例え標準モジュールであろうとも using
または import
で読み込む module は Project.toml に書かなければならなくなりました。なかなか面倒な仕様になったのですが、幸いこれらを自分で Project.toml に書く必要はありません。add
でこれらの module を追加すると自動的に Project.toml が更新されます。
(MyPackage) pkg> add Random Updating registry at `~/.julia/registries/General` Updating git-repo `https://github.com/JuliaRegistries/General.git` Resolving package versions... Updating `Project.toml` [9a3f8284] + Random Updating `Manifest.toml` [no changes]
Project.toml
が自動的に更新される。
name = "MyPackage" uuid = "f41f8282-a16f-11e8-2120-1f4619698387" authors = ["goropikari <goropikari56@gmail.com>"] version = "0.1.0" [deps] Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] test = ["Test"]
ついでに Manifest.toml
というのが出来きますが、このファイルにはテストしたときの全てのパッケージ情報が書かれていています。Project.toml
と Manifest.toml
という2つの軽量なテキストファイルを人に渡すだけで、自分がテスト通したときの環境がそっくりそのまま他の人のマシン上でも再現できるので、再現性が重要になってくる分野では便利でしょとのこと*2。
関数を追加したのでテストケースも追加します。
using Test, MyPackage, Random Random.seed!(0) @test sprint(MyPackage.hello, "Mike") == "Hello Mike" @test MyPackage.randstr() == "0IPrGg0J"
もう一度テスト
(MyPackage) pkg> test Testing MyPackage Resolving package versions... Testing MyPackage tests passed
Julia 以外のライブラリに依存する場合
既存のC*3のライブラリを使いたい、Julia だと処理が遅い部分を C で書いたコードを使いたいといった場合、deps/build.jl
を作って build
の仕方を書いていきます。
一番手っ取り早い方法だと Makefile
作って build.jl
内に run(`make`)
とか書けばいいのですけど、C のコンパイラ入れていない人もいるでしょうし、そもそも make
も使えるのか等々あるのでそんなに話は単純ではありません。
この辺のことについて私は詳しくないので以下を参考にしてください。 おそらく、公式ドキュメントには載っていません。
UUID
今までスルーしてきましたが Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
などの謎の文字列は各パッケージのUUIDです。
Generalを見れば調べられますが*4、探すの面倒なので上記の ]add Package
使って入れていくのが吉。ただ、add
で入れると全部 [deps]
のほうに入ってしまうのでテストだけにしか使わないものは適宜 [extras]
の方に自分で移し [targets]
も編集します。
GitHub に公開する
作ったパッケージを野良パッケージ*5として GitHub に公開してみましょう!
まずは GitHub に新しくリポジトリを作ります。Julia のパッケージの慣例としてパッケージ名の最後に .jl
をつけるので今回は MyPackage.jl
とします。(慣例で .jl
と名前をつけますが、これをつけておくと確か野良パッケージでも Julia Observer に見つけてもらえます。)
作ったら以下を実行。もちろん GitHub のアカウントはご自身のものに直してください。
$ cd /path/to/MyPackage $ git init $ git add -A $ git commit -m 'first commit' $ git remote add origin https://github.com/goropikari/MyPackage.jl $ git push -u origin master
これで野良パッケージの公開終了。今後は以下のようにして誰でもインストールできるようになります。
(v1.0) pkg> add https://github.com/goropikari/MyPackage.jl
適宜ライセンスやREADMEも加えましょう。
MyPackage に PATH を通す
作ったパッケージはディレクトリを activate
していると using
などで読み込めますが、そうでない一般の時は PATH が通っていないので読み込むことが出来ません。
julia> using MyPackage ERROR: ArgumentError: Package MyPackage not found in current path: - Run `Pkg.add("MyPackage")` to install the MyPackage package. Stacktrace: [1] require(::Module, ::Symbol) at ./loading.jl:817
バリバリ開発しているときは activate
すればよいですが、一段落付いて普通のパッケージとして使いたい時は下記のようにして PATH を通します。
julia> push!(LOAD_PATH, "/path/to/MyPackage/src")
こうすると他のパッケージ同様、どこのディレクトリにいても読み込むことが出来ます。
julia> using MyPackage
julia> MyPackage.greet()
Hello World!
ただこのままだと 毎度、毎度 PATH を通さなければならないので ~/.julia/config/startup.jl
に追記しておきましょう。
push!(LOAD_PATH, "/path/to/MyPackage/src")
METADATA.jl に登録する
作ったパッケージを Julia の公式パッケージとして登録*6したい場合は Travis CI を通すことが必須です。
Python の PyPI ではコードレビューが無いらしい*7ですが Julia の場合はあるので、print("Hello World")
しかないようなパッケージを登録しようとすると却下されます。
README またはドキュメントもちゃんと書きましょう。当たり前ですが英語で*8。 ドキュメントが書かれていないパッケージはいかに優れていたものだったとしても使う気になりません。
Julia v0.6 まではパッケージの命名に関する決まりごと*9等もドキュメントに書いてありましたが、現時点 (2018/8/17) の Julia v1.0 のドキュメントではパッケージの登録方法の項目が見当たらないので、どういうルールになったのかはよくわかりません。概ね一緒だと思いますが。
登録をするときは AttoBot を使うことが推奨されていますが、現状これを使う場合は v0.6 の作法の REQUIRE
というファイルを作らなければなりません。Pkg3
用に attobot3 というのもあるようですが、従来の attobot と同様にして使えるのかは不明。
パッケージの雛形を作ってくれる PkgDev.jl*10 がまだ Julia v1.0 に対応しておらず、また、Julia v1.0 が急にリリースされたせいで以前から Julia のパッケージを作っていた古参 Julian も困惑している感じが否めないので新規の METADATA デビュー *11 は当面控えたほうがよさそうといった印象です。
それでも「いますぐ私の素晴らしいパッケージを世に広めたいんだ!」という方は Julia Discourse や Julia の Slack であたりで質問してください。私は従来の方式でしかパッケージ公開をしたことないのでアドバイスできるようなノウハウがありません。
続編書きました。 goropikari.hatenablog.com
参考
- https://docs.julialang.org/en/stable/stdlib/Pkg/
- Packages · The Julia Language
- AttoBot for Pkg3 projects - Usage - JuliaLang
LICENSE
Copyright (c) 2018: goropikari
License: MIT https://opensource.org/licenses/MIT
Copyright (c) 2009-2018: Jeff Bezanson, Stefan Karpinski, Viral B. Shah, and other contributors:
*1:現在のProject.tomlに相当
*2:https://youtu.be/GBi__3nF-rM
*4:標準モジュールについてはありませんが
*5:METADATA.jl に登録されていないパッケージ
*6: add PackageName でインストールできるようになる
*8:今Julia使っている人の英語力なら特に問題ないはず
*9:https://docs.julialang.org/en/v0.6.4/manual/packages/#Creating-a-new-Package-1