#はじめに
あるアプリケーションが他のアプリケーション群に依存し、またそれらのアプリケーション群が各々さらに別のアプリケーション群に依存するようなとき、一般に「依存アプリケーションのバージョン管理」や「依存パッケージのバージョン管理」と呼ばれるような一連の問題が起こります。
main
├──app1
| ├─app2(ver.1)
| └─app3(ver.1)
└──app2(ver.2)
└─app3(ver.2)
上図ではmainのアプリケーションの子にあたるapp1とapp2がそれぞれ別バージョンのapp3を要求していますし、app2は子と孫の位置で別バージョンを要求しています。
こういった場合、同じアプリケーションの別バージョンを取り込むのは非効率なのでどれかのアプリケーションが依存するバージョンを取り込んで共有して使うことになります。しかし、依存アプリケーションが特定のバージョンでないと正常動作しないアプリケーションはままありますね。そうなると、どこかの依存アプリケーションが異常動作を起こし、その異常はアプリケーション全体にまで広がります。
この連載では、Erlangの標準的なビルドツールrebarの依存アプリケーション管理方法と、Elixirのビルドツールであるmixの依存アプリケーション管理方法を比較し、Erlag/Elixir系においてあるべきパッケージ管理方法を考察します。
#rebarでのバージョン衝突解決
メイン/サブ・アプリケーションの依存パッケージ衝突回避
まずメインのアプリケーションがあります。こんな感じの。
このメイン・アプリケーションが依存するアプリケーションはrebar.config
に書かれています。
{deps, [
{sample_sub1, ".*", {git, "https://github.com/massn/sample_sub1.git", "v0.0.1"}},
{sample_sub2, ".*", {git, "https://github.com/massn/sample_sub2.git", "v0.0.1"}},
{cowboy, ".*", {git, "https://github.com/ninenines/cowboy.git", "0.9.0"}}
]}.
早速、依存アプリケーションをダウンロードしてみましょう。
$ ./rebar get-deps
結果、deps
ディレクトリは以下のようになります。
deps
├──cowboy(0.0.9)
├──cowlib(0.4.0)
├──ranch(0.9.0)
├──sample_sub1(v0.0.1)
└──sample_sub2(v0.0.1)
ここではメイン・アプリケーションの要求通りcowboyというアプリケーションの0.0.9が取り込まれていますが、実はsample_sub1、sample_sub2もcowboyを要求しています。
sample_sub1のrebar.configは
{deps, [
{cowboy, ".*", {git, "https://github.com/ninenines/cowboy.git", "0.8.0"}}
]}.
sample_sub2のrebar.configは
{deps, [
{cowboy, ".*", {git, "https://github.com/ninenines/cowboy.git", "0.10.0"}}
]}.
となっています。
つまりサブ・アプリケーションは別バージョンを指し示していても、メイン・アプリケーションのバージョン指定があった場合は後者が優先されるということです。つまり、
メイン・アプリケーションとサブ・アプリケーションの依存パッケージが別バージョンの場合は、メイン・アプリケーションのものが優先される
同一階層内の依存パッケージ衝突回避
では、メイン・アプリケーションが依存せず、複数のサブ・アプリケーションが依存するアプリケーションが異なるバージョンを指定していた場合、どのバージョンが選ばれるのでしょう?
のrebar.config
は
{deps, [
{sample_sub1, ".*", {git, "https://github.com/massn/sample_sub1.git", "v0.0.1"}},
{sample_sub2, ".*", {git, "https://github.com/massn/sample_sub2.git", "v0.0.1"}}
]}.
となっておりcowboyに依存していません。
しかし、sample_sub1
とsample_sub2
という同一階層内のアプリケーションがcowboyに依存しているため、このアプリケーション全体としてはcowboyをダウンロードせざるを得ません。早速、
$ ./rebar get-deps
すると、depsにはcowboyの__0.9__がダウンロードされています。つまり、sample_sub1が依存するバージョンが優先されたわけですね。ということは、、、。
rebar.config
を以下のように書き換えてみましょう(https://github.com/massn/sample_main/tree/v0.0.3)。
{deps, [
{sample_sub2, ".*", {git, "https://github.com/massn/sample_sub2.git ", "v0.0.1"}},
{sample_sub1, ".*", {git, "https://github.com/massn/sample_sub1.git ", "v0.0.1"}}
]}.
このようにサブ・アプリケーションの順番を換えてみて./rebar get-deps
してみると、今度は__0.10__がダウンロードされています。つまり、
同一階層内のアプリケーションが同じアプリケーションに依存している場合は、rebar.config
のdeps
オプションに先に書いた方が優先される
まとめ
まとめますと、rebarには2つの依存アプリケーション管理原則があるようです。再掲します。
-
同一階層内のアプリケーションが同じアプリケーションに依存している場合は、
rebar.config
のdeps
オプションに先に書いた方が優先される -
メイン・アプリケーションとサブ・アプリケーションの依存パッケージが別バージョンの場合は、メイン・アプリケーションのものが優先される
1の原則を意識するとアプリケーションが依存するすべてのアプリケーションを知らなくてはなりません(子アプリケーションの子アプリケーション。そのまた子アプリケーション。そのまた・・・。)。2の原則を意識するにはすべての依存アプリケーションのバージョンを知らなくてはならない。
これ、なかなかしんどいですよね?
つづく。