本日は
Julia アドベントカレンダーネタです.
本日は MyTemplate.jl というPkgTemplates.jl をベースに自分好みのテンプレートを作成する方法を紹介します.
背景
MyWorkflow.jl というリポジトリで Julia + Docker + Jupyter Notebook の組み合わせをする心地いいワークフローを開発していました.MyWorkflow.jl 自身は 別の Qiita でも書いています.
個人の自作パッケージを作る際や Julia に限らず Python で(個人的な)開発するときにもこのリポジトリで用いていたファイルをよく愛用していました.
とりあえず make
コマンドを叩けば環境が構築でき,docker-compose up lab
で Jupyter 環境を立ち上げることができるのはとても便利です(と思っています)
さて,問題だったのは新しい Julia のプロジェクトを行う際に, MyWorkflow.jl から Dockerfile
や Makefile
, docker-compose.yml
, .devcontainer/devcontainer.json
をいちいちコピペするという作業が必要でした.そしてイメージ名を変更したりとか...置換作業が必要でした.
なければテンプレートを作ればいいじゃない!
そもそも MyWorkflow.jl 自身が PkgTemplates.jl から作られたのでした.
PkgTemplates.jl は Julia プロジェクトを開発する時のテンプレートを作ってくれる Julia のパッケージです.
さて本題(MyTemplate.jl の使い方)
例えば YourPkg.jl
を作りたいとします. ひとまず下記のようにするとテンプレートができます.
$ git config --global github.user "あなたのGitHubアカウント名を入力" # これは初回のみ
$ git clone https://github.com/terasakisatoshi/MyTemplate.jl.git # コードをクローン
$ cd MyTemplate.jl
$ julia --project=@. -e 'using Pkg; Pkg.instantiate()' # `generate.jl` を使うための依存関係を追加
$ julia --project=@. generate.jl YourPkg --with-jupyter # YourPkg という名前のパッケージ名を作る
このようにすると YourPkg.jl
というディレクトリができます. tree
コマンドでディレクトリ構造を眺めてみましょう
$ cd YourPkg.jl
$ tree
.
├── Dockerfile
├── LICENSE
├── Makefile
├── Manifest.toml
├── Project.toml
├── README.md
├── docker-compose.yml
├── docs
│ ├── Manifest.toml
│ ├── Project.toml
│ ├── make.jl
│ └── src
│ └── index.md
├── jupytext.toml
├── playground
│ ├── notebook
│ └── pluto
├── src
│ └── YourPkg.jl
└── test
└── runtests.jl
jupytext.toml
や Dockerfile
, docker-compose.yml
, Makefile
, playground/
などが含まれています.これがオレオレテンプレートで,それ以外は PkgTemplates.jl がよしなに作成したものです.
テンプレートから作成できたら
作業する場所を YourPkg.jl
に移動して make
コマンドを叩きます
$ cd YourPkg.jl
$ make
...
ちょっと時間がかかる
make
コマンドで Jupyter 環境をコンテナ内で立ち上げるためのDockerイメージをビルドします(M1 Mac 環境だと JupyterLab 関連の extension のビルドが失敗するかもしれないです. 実機持ってないので GitHub Sponsor とかで経済的支援していただけると対応します)
イメージがビルドできたら docker-compose up lab
とすると docker-compose.yml
で定義したサービスを立ち上げることができます.
$ docker-compose up lab
...
... (色々ログが出力される)
...
| To access the server, open this file in a browser:
| file:///root/.local/share/jupyter/runtime/jpserver-1-open.html
| Or copy and paste one of these URLs:
| http://320812028b46:8888/lab?token=xxxxxxxx
| or http://127.0.0.1:8888/lab?token=xxxxxxxx
こういうログが出てきたらブラウザを開いて http://127.0.0.1:8888/lab?token=xxxxxxxx
にアクセスするとカスタマイズされた JupyterLab 環境が得られます.
Jupyter が不要な場合
とりあえず VSCode と組み合わせるだけに興味があって Jupyter 環境が不要な場合は上記で使用した generate.jl
のオプションでつけていた with-jupyter
を使わずに generate.jl
を実行してください:
$ https://github.com/terasakisatoshi/MyTemplate.jl.git # コードをクローン
$ cd MyTemplate.jl
$ julia --project=@. -e 'using Pkg; Pkg.instantiate()' # `generate.jl` を使うための依存関係を追加
$ julia --project=@. generate.jl YourPkg # YourPkg という名前のパッケージ名を作る
$ cd YourPkg
$ docker-compose run --rm julia
_
_ _ _(_)_ | Documentation: https://docs.julialang.org
(_) | (_) (_) |
_ _ _| |_ __ _ | Type "?" for help, "]?" for Pkg help.
| | | | | | |/ _` | |
| | |_| | | | (_| | | Version 1.6.4 (2021-11-19)
_/ |\__'_|_|_|\__'_| | Official https://julialang.org/ release
|__/ |
julia> # あとはお好きにどうぞ
こんな感じで楽にできるので使ってみてください.
VSCode との連携
VSCode の Remote Containers の機能を利用するだけです. いろんな設定は .devcontainer/devcontainer.json
で設定されているのでよしなにDockerによるコンテナ環境の中で作業できるようになります.
をインストールすると使えるようになります. そのあとは作成した YourPkg.jl
ディレクトリで code .
で開いたディレクトリをプロジェクトとしてみなしながら VSCode を立ち上げます. 画面左下に ><
というマークがあるのでそれをクリックして ReOpen in Container
をクリックするとコンテナ内部で作業することができます. 具体的な操作方法は Julia と関係ないので VSCode Docker などでググって調べてください.例えば
外部のブログ VS Code + Remote Containers で快適な Dev ライフを がとてもわかりやすいです.
自作テンプレートを追加する方法
例えば自作パッケージを作るときに自分が使う定型ファイル oreorefile.txt
も入れ込んでおきたい!とします.そのテンプレートはパッケージ名に依存しているとしましょう. なんでもいいので例えば下記のようなテンプレートを作っておきます:
Hi, I'm {{name}}. {{{PKG}}} is my julia package.
{{name}}
や {{{PKG}}}
の部分が動的に変わってほしい部分です. 例えば David さんが YourPkg
を作るのであれば Hi, I'm David. YourPkg is my julia package
というファイルができて欲しいとしましょう.
FilePlugin
のサブタイプを作る
ここでは Julia コードを持ってきて説明します. PkgTemplates.jl
ではファイルのテンプレートをハンドリングするために FilePlugin
という抽象型を定義しています. この型のサブタイプとして oreorefile.txt
を操作するための構造体(ここでは OreOreFile
という自作型)と対応する構造体に対して幾つかのメソッドを定義することになります. あまりJuliaに慣れてない,でもオブジェクト指向なら知っているよ!って読者なら,FilePlugin
というのは基底クラスで OreOreFile
は FilePlugin
を継承するというノリで読んでください. 幾つかのメソッドを定義するのは FilePlugin
を継承するクラスが持っているべきメソッドをオーバライドするものと解釈してください.
メソッドは source
, destination
, view
というメソッド(関数の具体的な手続き)を OreOreFile
構造体に対して実装することになります.
-
source
はテンプレートファイルのパスを返す -
destination
はテンプレートから作られたファイルを配置するパス(作成されるパッケージのディレクトリからの相対パス)を返す -
view
{{name}}
や{{{PKG}}}
となっているところに何を代入するかを指定するために必要な対応表(Juliaの辞書オブジェクトして使う)
PKG
はPkgTemplates.jl の中ではパッケージ名を表す特別なキーワードになっています. ですので {{{PKG}}}
と3重カッコが必要ですが,自作の変数であればカッコは2つで良いみたいです.
基本的に下記をそのままコピペすれば良いでしょう:
Base.@kwdef struct OreOreFile <: FilePlugin
file::String = "templates/oreorefile.txt"
destination::String = "oreorefile.txt"
name::String = "David"
end
source(p::OreOreFile) = p.file
destination(p::OreOreFile) = p.destination
view(of::OreOreFile, ::Template, pkg::AbstractString) = Dict("PKG" => pkg, "name"=>of.name)
OreOreFile
構造体のインスタンスを plugins
変数に渡す
あとは PkgTemplates.jl のお作法に則ればOKです. PkgTemplates.Template
のインスタンスに渡す plugins
引数に先ほど定義した OreOreFile
構造体のインスタンスを含めればOKです. 下記のように generate.jl
というスクリプト(名前はJuliaのスクリプトであればなんでも良い)を作ります.
using PkgTemplates
t = Template(;
dir = pwd(),
julia = v"1",
plugins = [
License(; name = "MIT"),
Git(; manifest = false, ssh = true),
OreOreFile(;name="あなたの名前を入れてね♪"),
]
)
t("YourPkg")
julia generate.jl
とすると無事にテンプレートから必要な情報が穴埋めされた oreorefile.txt
が完成します.
まとめ
こんな感じで Julia の型システムを使うと外部パッケージの機能と同等の機能を実現するオレオレ機能を実装し組み込むことができるようになります.
テンプレート作成だけでなく,Julia の型システムの一部も学ぶことができました.