0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Juliaの環境管理で毎回迷うこと:Project.toml、Manifest.toml、activate、instantiateの違い

0
Posted at

はじめに

Juliaを使っていると、ある程度早い段階で次のようなコマンドに出会います。

] activate .
] add DataFrames
] instantiate
] status

あるいは、GitHubからJuliaのプロジェクトをcloneしてきたときに、

julia --project=.

のような起動方法を見ることもあります。

最初はなんとなく動くのですが、少し経つと次のような疑問が出てきます。

  • Project.tomlManifest.toml は何が違うのか
  • activate は何をしているのか
  • instantiate はいつ必要なのか
  • add したパッケージはどこに記録されるのか
  • Manifest.toml はGit管理すべきなのか
  • --project=.--project=@. は何が違うのか

この記事では、Juliaの環境管理で毎回迷いやすいところだけを整理します。

対象読者は、Juliaの文法入門は一通り終わっていて、CSV.jlDataFrames.jlPlots.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.jsonpackage-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"

ここには、

  • CSV
  • DataFrames
  • Plots

のように、自分が直接 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.tomlManifest.toml が揃っていれば、パッケージのバージョンは完全に固定できます。

ただし、注意点もあります。

Manifest.toml が固定するのは、基本的にはパッケージの依存関係です。
実際の実行結果まで完全に同じになるかは、次の要素にも影響されます。

  • Julia本体のバージョン
  • OSやCPUなどのプラットフォーム差
  • JLLなどのバイナリアーティファクト
  • 外部コマンドや外部ライブラリ
  • 乱数や並列実行など、コード側の非決定性

したがって、正確には、

Manifest.toml があれば、パッケージの依存関係は同一状態に復元できる。
ただし、Julia本体のバージョンやプラットフォーム差まで含めた完全再現には別途注意が必要。

という理解がよいです。

Project.tomlManifest.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.tomlManifest.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を検討するとよいです。

adddev の違い

Juliaで自作パッケージを触り始めると、adddev の違いも気になります。

ざっくり言うと、

コマンド 用途
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.toml
  • Manifest.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.tomlManifest.toml を残しておくと、未来の自分が助かります。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?