-
gem
とbundle
ってどっちを使えばいいんですか? -
pipenv
!? Pythonには何種類のパッケージングツールがあるんですか!! -
Gemfile.lock
ってコミットしないんですよね!?偉い人がそう言ってました!!
落ち着いてください。個別ツールの話をする前に、まずこれを覚えておいてください。
ライブラリ か アプリケーション かで、依存ライブラリの管理の仕方・バージョンの指定方法が違う
これを理解すれば、自然とツールの使い方も解るはずです。
「プロジェクト」には2種類ある。依存ライブラリにも2種類ある
RubyやPython(や他の言語)のプロジェクトは、大きく2種類に分けられます。
-
ライブラリ
- 他のライブラリやアプリケーションに組み込まれて使うもの
-
アプリケーション
- 直接使うことができるもの。他のプロジェクトに組み込まれることは基本的に無い
また、プロジェクトが依存しているライブラリも2種類に分けられます。
-
直接依存
- プロジェクトで利用しているライブラリのこと
-
間接依存
- 直接依存のライブラリが利用している別のライブラリのこと
※なお「直接依存」「間接依存」は一般的な用語ではありません(通じなくはないと思いますが)。「間接依存」より「推移的依存」の方が一般的かもしれません。
ライブラリのバージョン指定戦略
ライブラリでは、できるだけ緩くバージョンを指定します。
「このライブラリは pandas 0.22.0 をインストールした環境でしか動作しません。0.21.0 でも 0.22.1 でもダメです」なんて厳しい制約があるライブラリは使ってもらえないですからね。
なので、ライブラリでは「直接依存」についてのみバージョンを指定し、間接依存については指定しません。 直接依存についても、バージョンは特定の1バージョンではなく、範囲で指定します(pandas >= 0.22.0, < 1のように)。バージョンの下限はテストして動作が確認された最低バージョンを指定します。
なお、最大バージョンは微妙な問題があります。というのは、互換性がないバージョンアップをするライブラリもあるからです(有名な例だと Angular 1.x → 2.x とか)。"numpy ">= 1.14.3", "==1.*" のように、メジャーバージョンレベルで指定するのが穏当でしょう。
アプリケーションのバージョン指定戦略
アプリケーションでは、どの環境でも同じバージョンが使われるようにしたいはずです。さもないと・・・
「開発環境では動くのに、本番環境では動かない」
「俺のマシンでは動くのに、あいつのマシンでは動かない」
ということになります。
そのため、直接依存も間接依存も全て指定します。また、バージョンも厳密に(== x.y.z
の形で)指定します。
とはいえ、全部のバージョンを書き下して管理するのは大変なので、RubyやPythonでは2ファイルに分けて管理しています。
- 直接依存を指定するファイル(手動で管理する)
- Gemfile
- Pipfile
- 直接依存・間接依存の詳細なバージョンを指定するファイル(自動更新できる
- Gemfile.lock
- Pipfile.lock
「プロジェクトには2種類ある」と言ったな、あれは嘘だ
なお、困ったことにライブラリともアプリケーションとも言い切れないプロジェクトがあります。
例えば、curl の代替コマンドのHTTPie は、使い方の観点では明らかにアプリケーションです。しかし、Pythonのライブラリ配布の仕組みを使用して配布している(PyPIで公開され、pip install httpie
でインストールできる1)ので、依存ライブラリは、setup.py
に指定しています。
困ったもんです。
おそらく、HTTPieの場合は、そもそも配布方法が間違いなのです。pip install httpie
でインストールするとシステムのPythonを変更することになるので2、他のアプリケーションに影響を与える恐れがあります。
a. システムのPythonとは別にPythonインタープリタをビルドし、そこにインストールする
b. 依存ライブラリも全て含んだ、単一のバイナリとして配布する(Go等では可能。Pythonでもツールを使えばできるかも)
本来、このような方法をとるべきなのでしょう。