はじめに
Juliaを使っていると、ある程度早い段階で次のようなコマンドに出会います。
] activate .
] add DataFrames
] instantiate
] status
あるいは、GitHubからJuliaのプロジェクトをcloneしてきたときに、
julia --project=.
のような起動方法を見ることもあります。
最初はなんとなく動くのですが、少し経つと次のような疑問が出てきます。
-
Project.tomlとManifest.tomlは何が違うのか -
activateは何をしているのか -
instantiateはいつ必要なのか -
addしたパッケージはどこに記録されるのか -
Manifest.tomlはGit管理すべきなのか -
--project=.と--project=@.は何が違うのか
この記事では、Juliaの環境管理で毎回迷いやすいところだけを整理します。
対象読者は、Juliaの文法入門は一通り終わっていて、CSV.jl、DataFrames.jl、Plots.jl などをプロジェクトごとに使い分けたい人です。
この記事の結論
先に結論を書くと、だいたい次の理解で十分です。
| 用語 | ざっくり言うと |
|---|---|
Project.toml |
そのプロジェクトが直接使うパッケージや互換性を書くファイル |
Manifest.toml |
直接・間接依存まで含めた、完全な環境のスナップショット |
activate |
このフォルダの環境を使います、とJuliaに伝える |
instantiate |
Project.toml / Manifest.toml を見て、必要なパッケージを揃える |
add |
現在activateされている環境にパッケージを追加する |
status |
現在の環境に入っているパッケージを見る |
特に大事なのはこれです。
] activate .
] instantiate
これは、Juliaプロジェクトを動かすときの基本セットです。
activate は環境の切り替え、instantiate は環境の復元、と覚えるとわかりやすいです。
まず、Pkgモードに入る
JuliaのREPLで ] を押すと、Pkgモードに入ります。
通常のJulia REPLではこうですが、
julia>
] を押すと、こうなります。
(@v1.10) pkg>
ここで、
activate .
add DataFrames
status
instantiate
などのコマンドを打てます。
Pkgモードから通常のREPLに戻るには、空の入力行でBackspaceを押します。
この記事では、Pkgモードのコマンドを次のように書きます。
] activate .
] add DataFrames
] status
環境とは何か
Juliaの「環境」は、簡単に言うと、
どのパッケージを、どのバージョンで使うかを決める単位
です。
Pythonでいう仮想環境、Node.jsでいう package.json と package-lock.json に近いものだと思うと理解しやすいです。
Juliaでは、プロジェクトごとに環境を持てます。
たとえば、
my_analysis/
analysis.jl
Project.toml
Manifest.toml
というフォルダがあるとします。
この my_analysis の中で、
] activate .
すると、Juliaはこのフォルダを現在の環境として扱います。
Project.toml とは何か
Project.toml は、そのプロジェクトが直接使うパッケージや互換性などを書くファイルです。
たとえば、次のような内容になります。
[deps]
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
ここには、
CSVDataFramesPlots
のように、自分が直接 using するパッケージが記録されます。
依存パッケージの追加・削除は、基本的に手で編集する必要はありません。
] add DataFrames
のように実行すると、Pkgが自動で Project.toml を更新してくれます。
ただし、パッケージ開発では [compat] を手で書くことが多いです。
[compat] は「このパッケージは、どのバージョンの依存パッケージと互換性があるか」を指定するための項目です。
たとえば、
[compat]
DataFrames = "1"
julia = "1.10"
のように書きます。
つまり、
-
add/rmはPkgに任せる -
[compat]は必要に応じて自分で書く
と考えるとよいです。
Manifest.toml とは何か
Manifest.toml は、もっと細かいファイルです。
Project.toml が「直接使うパッケージのリスト」だとすると、Manifest.toml は、
そのパッケージたちを動かすために必要な、すべての依存関係とバージョンの記録
です。
たとえば、自分は DataFrames しか追加していなくても、DataFrames 自体が他のパッケージに依存しています。
つまり実際には、
自分のコード
└─ DataFrames
├─ Tables
├─ PrettyTables
├─ PooledArrays
└─ ...
のような依存関係があります。
Manifest.toml は、このような間接依存まで含めて、どのバージョンを使ったかを正確に記録します。
そのため、Project.toml と Manifest.toml が揃っていれば、パッケージのバージョンは完全に固定できます。
ただし、注意点もあります。
Manifest.toml が固定するのは、基本的にはパッケージの依存関係です。
実際の実行結果まで完全に同じになるかは、次の要素にも影響されます。
- Julia本体のバージョン
- OSやCPUなどのプラットフォーム差
- JLLなどのバイナリアーティファクト
- 外部コマンドや外部ライブラリ
- 乱数や並列実行など、コード側の非決定性
したがって、正確には、
Manifest.tomlがあれば、パッケージの依存関係は同一状態に復元できる。
ただし、Julia本体のバージョンやプラットフォーム差まで含めた完全再現には別途注意が必要。
という理解がよいです。
Project.toml と Manifest.toml の違い
ざっくり比べるとこうです。
| ファイル | 役割 | 人間が読む? | 手で編集する? |
|---|---|---|---|
Project.toml |
直接依存・互換性・プロジェクト情報を書く | 読む | 場合によっては編集する |
Manifest.toml |
完全な依存関係を記録する | 基本読まない | 基本編集しない |
もう少し感覚的に言うと、
Project.toml
= このプロジェクトでは DataFrames と Plots を使います
Manifest.toml
= DataFrames v1.x.x、Plots v1.x.x、その依存のAはv...、Bはv...を使いました
という関係です。
Project.toml は意図を書いたファイル、Manifest.toml は実際に解決された結果を書いたファイル、と見るとわかりやすいです。
activate は何をしているのか
activate は、
これから操作する環境を切り替える
コマンドです。
たとえば、次のようなフォルダで作業しているとします。
my_analysis/
analysis.jl
ここで、
] activate .
を実行すると、現在のフォルダ . がJuliaの環境になります。
その後、
] add CSV DataFrames Plots
を実行すると、このフォルダに Project.toml と Manifest.toml が作られます。
my_analysis/
analysis.jl
Project.toml
Manifest.toml
つまり、activate . してから add すると、そのフォルダ専用の環境にパッケージが入ります。
activate しないとどうなるか
activate せずに add すると、多くの場合、デフォルト環境にパッケージが追加されます。
たとえば、REPLの表示がこうなっているとします。
(@v1.10) pkg>
この状態で、
] add Plots
すると、Juliaのデフォルト環境に Plots が追加されます。
これ自体は悪いことではありません。
ただし、いろいろなプロジェクトで同じデフォルト環境を使い続けると、
- どのプロジェクトでどのパッケージを使っていたかわからない
- 依存関係が増えすぎる
- 昔のコードを動かしたときに環境が変わっている
ということが起きやすくなります。
解析、実験、論文、アプリ開発などでJuliaを使うなら、プロジェクトごとに
] activate .
するのがおすすめです。
instantiate は何をしているのか
instantiate は、
Project.toml/Manifest.tomlに書かれた環境を、今のPC上に揃える
コマンドです。
特に、GitHubからJuliaプロジェクトをcloneしてきたときによく使います。
git clone <repository>
cd <repository>
julia
Juliaを起動したら、
] activate .
] instantiate
とします。
これで、必要なパッケージがインストールされます。
Manifest.toml がある場合は、そのManifestに記録されたパッケージ状態を復元します。
Manifest.toml がない場合は、Project.toml に書かれた依存関係と互換性制約から、現在取得できるバージョンを解決します。
activate だけでは足りない
ここは重要です。
] activate .
は、あくまで「この環境を使う」と切り替えるだけです。
パッケージの実体をインストールするわけではありません。
そのため、他人のプロジェクトをcloneした直後などは、
] activate .
] instantiate
まで実行するのが安全です。
activate は環境の切り替え、instantiate は環境の準備、と覚えるとわかりやすいです。
コマンドラインから使うなら --project
スクリプトを実行するときは、REPL内で毎回 activate . する代わりに、次のように起動できます。
julia --project=. analysis.jl
これは、
カレントディレクトリの
Project.tomlを使ってanalysis.jlを実行する
という意味です。
普段の解析スクリプトでは、これを使うとかなり便利です。
たとえば、プロジェクトルートで、
julia --project=. scripts/plot_result.jl
のように実行すれば、そのプロジェクトの環境でスクリプトが動きます。
--project=. と --project=@. の違い
ここも少し迷いやすいところです。
| 指定 | 意味 |
|---|---|
--project=. |
カレントディレクトリそのものを環境として使う |
--project=@. |
カレントディレクトリから親方向に Project.toml を探す |
たとえば、次のような構成を考えます。
my_project/
Project.toml
Manifest.toml
scripts/
analysis.jl
プロジェクトルート my_project/ にいるなら、
julia --project=. scripts/analysis.jl
で問題ありません。
しかし、scripts/ の中に移動してから、
cd scripts
julia --project=. analysis.jl
とすると、Juliaは scripts/ の中にある Project.toml を探します。
この場合、scripts/Project.toml がなければ、意図した環境になりません。
一方で、
cd scripts
julia --project=@. analysis.jl
とすると、Juliaは現在のディレクトリから親方向に Project.toml を探します。
つまり、scripts/ から始めて、親の my_project/Project.toml を見つけてくれます。
そのため、サブディレクトリからスクリプトを実行することが多いなら、
julia --project=@. scripts/analysis.jl
または
julia --project=@. analysis.jl
のように @. を使う方が堅牢です。
迷ったら、次のように考えるとよいです。
プロジェクトルートから実行するなら --project=.
どこから実行するか揺れるなら --project=@.
よく使う基本コマンド
最低限覚えるなら、これだけで十分です。
] activate .
現在のフォルダを環境として使う。
] add DataFrames
現在の環境にパッケージを追加する。
] status
現在の環境に入っているパッケージを見る。
] instantiate
Project.toml / Manifest.toml に従って環境を揃える。
] update
依存パッケージを更新する。
] rm DataFrames
現在の環境からパッケージを削除する。
典型例1:自分の解析フォルダを作る
たとえば、CSVを読んでグラフを書く解析フォルダを作るとします。
mkdir my_analysis
cd my_analysis
touch analysis.jl
julia
Juliaを起動したら、
] activate .
] add CSV DataFrames Plots
これで、
my_analysis/
analysis.jl
Project.toml
Manifest.toml
のようになります。
analysis.jl には、たとえば次のように書けます。
using CSV
using DataFrames
using Plots
df = CSV.read("data.csv", DataFrame)
plot(df.x, df.y)
savefig("result.png")
実行するときは、プロジェクトルートからなら、
julia --project=. analysis.jl
とします。
これで、この解析フォルダ専用の環境で実行できます。
典型例2:他人のJuliaプロジェクトを動かす
GitHubからJuliaプロジェクトをcloneしてきたとします。
git clone <repository>
cd <repository>
julia
まずは、
] activate .
] instantiate
を実行します。
その後、READMEに書かれているスクリプトを実行します。
julia --project=. main.jl
またはJulia REPLの中で、
include("main.jl")
としてもよいです。
ポイントは、先にそのプロジェクトの環境を有効化しておくことです。
典型例3:一時的にパッケージを試す
ちょっと新しいパッケージを試したいだけなら、現在の環境を汚したくないことがあります。
その場合は、一時環境を使えます。
] activate --temp
] add Example
この環境は一時的なものなので、気軽にパッケージを試せます。
バグ報告用の最小再現コードを作るときにも便利です。
Manifest.toml はGit管理すべきか
これはよく迷うところです。
個人的には、次の基準で考えるとよいと思います。
解析・実験・論文・アプリなら基本的に入れる
解析用コード、実験データ処理、論文用の図生成、アプリケーションなどでは、Manifest.toml もGit管理するのがおすすめです。
理由は、後から同じパッケージ環境を復元しやすいからです。
Project.toml
Manifest.toml
の両方があれば、未来の自分や共同作業者が、
] activate .
] instantiate
するだけで、同じパッケージ状態を復元できます。
特に、論文の図を作るコードや、実験データの処理コードでは、Manifest.toml を残しておく価値が高いです。
ライブラリ・登録パッケージなら方針が変わる
一方で、誰かに使ってもらうJuliaパッケージを作る場合は、少し事情が変わります。
ライブラリ開発では、利用者側の環境で依存関係が解決されます。
そのため、重要なのは多くの場合、
Project.toml
に書かれた依存関係と [compat] です。
特に登録パッケージでは、Manifest.toml をGit管理せず、許容したバージョン範囲で毎回依存解決してテストする運用がよく使われます。
つまり、
| 用途 | Manifest.tomlをGit管理する? |
|---|---|
| 解析スクリプト | する |
| 論文・研究用コード | する |
| アプリケーション | する |
| チュートリアル | することが多い |
| ライブラリ開発 | 方針による |
| 登録パッケージ | しないことが多い |
という感覚です。
ManifestはJuliaバージョンにも注意する
Manifest.toml は、Julia本体のバージョンとも関係します。
たとえば、ある人がJulia 1.10で作った Manifest.toml を、別の人がJulia 1.11で使うとします。
多くの場合はそのまま使えますが、依存関係や標準ライブラリの扱いによっては、同じように解決できない場合もあります。
そのため、共同作業では、
julia --version
でJulia本体のバージョンも揃えておくと安全です。
特に再現性を重視するなら、
Julia 1.10.4で動作確認
のようにREADMEに書いておくと親切です。
Julia 1.10.8以降のバージョン別Manifest
Julia 1.10.8以降では、JuliaのマイナーバージョンごとにManifestを分ける仕組みもあります。
たとえば、同じフォルダに次の2つがあるとします。
Manifest.toml
Manifest-v1.11.toml
このとき、Julia 1.11は Manifest-v1.11.toml を優先して使います。
一方で、他のバージョンのJuliaは通常の Manifest.toml を使います。
複数のJuliaバージョンをまたいで開発・検証したい場合には便利です。
ただし、これは自動で作られるものではありません。
必要に応じて既存のManifestをリネームしたり、バージョンごとに管理したりします。
初学者向けには、まずは普通の
Manifest.toml
を使えば十分です。
複数JuliaバージョンでCIを回すようになったら、Manifest-v1.11.toml のようなバージョン別Manifestを検討するとよいです。
add と dev の違い
Juliaで自作パッケージを触り始めると、add と dev の違いも気になります。
ざっくり言うと、
| コマンド | 用途 |
|---|---|
add |
通常の依存として追加する |
dev |
ローカルで開発中のパッケージを追加する |
たとえば、普通に DataFrames を使うなら、
] add DataFrames
で十分です。
一方で、手元にある自作パッケージを編集しながら使いたい場合は、
] dev /path/to/MyPackage
のようにします。
dev したパッケージは、ローカルのソースコードを参照します。
そのため、パッケージを編集しながら別のプロジェクトで試す、という開発フローで便利です。
resolve はいつ使うのか
通常はあまり使わなくても大丈夫です。
ただし、手で Project.toml を編集したり、ローカル開発中のパッケージ構成を変えたりしたときに、依存関係の整合性を取り直したい場合があります。
そのようなときに、
] resolve
を使います。
日常的には、
] add
] update
] instantiate
を覚えておけば十分です。
おすすめの運用パターン
Juliaで解析や研究コードを書くなら、まずは次のような構成がわかりやすいです。
my_project/
Project.toml
Manifest.toml
data/
scripts/
analysis.jl
plot.jl
figures/
最初に一回だけ、
] activate .
] add CSV DataFrames Plots
を実行します。
以降は、スクリプトを実行するときに、プロジェクトルートから
julia --project=. scripts/analysis.jl
とします。
または、どのディレクトリから実行するかが揺れるなら、
julia --project=@. scripts/analysis.jl
を使います。
新しいPCや別の環境で動かすときは、
] activate .
] instantiate
を実行します。
この流れを作っておくと、グローバル環境が汚れにくく、後から見ても何を使っていたかがわかりやすくなります。
src/ を置くとパッケージ化の話になる
解析フォルダでも、コードが増えてくると
src/
MyProject.jl
のような構成にしたくなることがあります。
これは悪くありませんが、src/MyProject.jl を置いて、
name = "MyProject"
uuid = "..."
のように Project.toml に書くと、そのプロジェクト自体を
using MyProject
で読み込めるパッケージとして扱う方向になります。
この記事の主題は環境管理なので、最初は
scripts/
analysis.jl
plot.jl
くらいから始めるのがわかりやすいと思います。
自作パッケージとして整理したくなったら、その時点で src/ と name / uuid を導入するとよいです。
よくある勘違い
勘違い1:activate すればパッケージも入る
入りません。
activate は環境を切り替えるだけです。
必要なパッケージを揃えるには、
] instantiate
を使います。
勘違い2:Project.toml だけあれば完全再現できる
パッケージ環境を正確に復元したいなら、Manifest.toml も重要です。
Project.toml は直接依存や互換性制約の情報を持ちますが、間接依存の正確なバージョンまでは固定しません。
勘違い3:Manifest.toml は手で直すもの
基本的には手で直さない方がよいです。
Manifest.toml はPkgが管理するファイルです。
勘違い4:全部デフォルト環境に入れればよい
小さい試行ならそれでもよいですが、複数プロジェクトを扱うと破綻しやすくなります。
解析、論文、アプリ、チュートリアルごとに activate . する方が安全です。
勘違い5:Manifestがあれば実行結果まで必ず完全一致する
Manifest.toml はパッケージの依存関係を正確に固定します。
ただし、実行結果にはJulia本体のバージョン、OS、CPU、外部ライブラリ、乱数、並列実行なども影響することがあります。
特に再現性を重視する場合は、
Project.tomlManifest.toml- Julia本体のバージョン
- 実行OS
- 実行コマンド
まで残しておくと安心です。
まとめ
Juliaの環境管理は、最初は少しわかりにくいですが、基本はかなりシンプルです。
覚えるべきことは次の通りです。
] activate .
今いるフォルダの環境を使う。
] add PackageName
その環境にパッケージを追加する。
] instantiate
Project.toml / Manifest.toml に従って環境を揃える。
julia --project=. script.jl
そのプロジェクトルートの環境でスクリプトを実行する。
julia --project=@. script.jl
現在位置から親方向に Project.toml を探して、その環境でスクリプトを実行する。
特に、他人のJuliaプロジェクトを動かすときは、
] activate .
] instantiate
をまず試すとよいです。
自分の解析コードでも、プロジェクトごとに Project.toml と Manifest.toml を残しておくと、未来の自分が助かります。