[連載] Erlangプロジェクトでもmixを使うべきか（２）

# はじめに

[前回の記事](http://qiita.com/massn-san/items/804cd48ff189fa4b8e90)で`rebar`でやったことを今度はElixirのビルドツール`mix`を使ってやってみましょう。

## mixにおける依存アプリケーション・バージョン衝突問題の回避
ElixirはErlangのアプリケーションをそのまま読み込めますので[前回の記事](http://qiita.com/massn-san/items/804cd48ff189fa4b8e90)で使用したErlangのサブアプリケーションを今度はElixirプロジェクトが必要としているという場面を想定しましよう。サブ・アプリケーションがErlangなのかElixirなのかは本質的に重要ではありません。

> https://github.com/massn/sample_main_elixir/tree/v0.0.1

に空のElixirプロジェクトを組んでみました。Elixirはビルド時にビルドツールの[mix](http://qiita.com/massn-san/items/7c7a02f09abf43d68275)やテストツールやら開発に必要なもろもろが既に組み込まれており、ほとんどの開発者がこのプロジェクトと同じようなフォーマットで開発を進めていくことと思います。
Erlang/rebarプロジェクトの`rebar.config`にあたるものが、Elixir/mixでは`mix.exs`にあたります。

```ex:mix.exs
defmodule SampleMainElixir.Mixfile do
  use Mix.Project

  def project do
    [app: :sample_main_elixir,
     version: "0.0.1",
     elixir: "~> 1.0",
     deps: deps]
  end

  defp deps do
    [{:sample_sub1, git: "https://github.com/massn/sample_sub1.git", tag: "v0.0.1"},
     {:sample_sub2, git: "https://github.com/massn/sample_sub2.git", tag: "v0.0.1"}]
  end
end
```

`deps`という関数が返すリストに依存アプリケーションが書かれています。書き方が少々Erlangと違いますがなんとなくわかると思います（糖衣構文なども含まれていますが、表現しているのはErlangと互換性のあるデータ構造です）。依存アプリケーションをダウンロードしてみましょう。

```bash
$ ./mix deps.get
```
このコマンドで`mix.lock`というファイルにダウンロードされるべき全てのアプリケーションのバージョンが書き込まれ、同時にダウンロードが行われます。RubyでGemパッケージを使うときの`Gemfile.lock`のようなものです。`mix.lock`ファイルを見てみましょう。

```ex:mix.lock
%{"cowboy": {:git, "https://github.com/ninenines/cowboy.git", "ee3ad5e51005b453230804036e092e1d2ceebe4f", [ref: "0.10.0"]},
  "cowlib": {:git, "git://github.com/extend/cowlib.git", "e2ffefe828b918486e2cd76e44c54ae9b62c616e", [ref: "0.6.2"]},
  "ranch": {:git, "git://github.com/extend/ranch.git", "3189ef2d47843945efc96c224963380462c33d40", [ref: "0.10.0"]},
  "sample_sub1": {:git, "https://github.com/massn/sample_sub1.git", "1700f7e6d4d708351a2c02274643646808e35911", [tag: "v0.0.1"]},
  "sample_sub2": {:git, "https://github.com/massn/sample_sub2.git", "0fe69055e0da389d4b28257e4db44495d1a3e641", [tag: "v0.0.1"]}}
```

すべての依存アプリケーションの使うべきバージョン、さらにはコミットIDまで列挙されてますね。`mix.lock`ファイルは`mix.exs`を元にしており、その時点でそこから推測できるアプリケーション情報を持っています。例えばあるアプリケーションのmasterブランチが`mix.exs`に指定されていると、そのときのmasterブランチのコミットIDが記載されるわけです。このロックファイルをgit管理することで開発者が使った依存アプリケーションの環境をそのまま再現することが可能になります。
ところで前回問題にした`cowboy`はどのバージョンが使われているのでしょう？`sample_sub2`が要求する`0.10.0`です。うーん、今度は`deps`が返すリストの後ろの方が優先順位が高いようです。。。わかりにくい。できればリストの順番なんか気にしたくない。
ここで、`mix deps.check`というコマンドを使ってみましょう。

```bash
$ ./mix deps.check
Unchecked dependencies for environment dev:
* cowboy (https://github.com/ninenines/cowboy.git)
  different specs were given for the cowboy app:

  > In deps/sample_sub2/rebar.config:
    {:cowboy, ~r/.*/, [git: "https://github.com/ninenines/cowboy.git", ref: "0.10.0"]}

  > In deps/sample_sub1/rebar.config:
    {:cowboy, ~r/.*/, [git: "https://github.com/ninenines/cowboy.git", ref: "0.8.0"]}

  Ensure they match or specify one of the above in your SampleMainElixir.Mixfile deps and set `override: true`
```

おお。

_「deps内のアプリケーションが同一アプリケーションの別バージョンを要求しているよ。そいつらのバージョンを合わせるか、`deps`に`override: true`をセットしてアプリケーションを要求してね」_

というありがたいアドバイスが。正しくその通りです。`mix.exs`を以下のように修正しましょう（ https://github.com/massn/sample_main_elixir/tree/v0.0.2 ）。

```ex:mix.exs
defmodule SampleMainElixir.Mixfile do
  use Mix.Project

  def project do
     [app: :sample_main_elixir,
      version: "0.0.1",
      elixir: "~> 1.0",
      deps: deps]
   end

  defp deps do
     [{:cowboy, git: "https://github.com/ninenines/cowboy.git", ref: "0.10.0", override: true},
      {:sample_sub1, git: "https://github.com/massn/sample_sub1.git", tag: "v0.0.1"},
      {:sample_sub2, git: "https://github.com/massn/sample_sub2.git", tag: "v0.0.1"}]
    end
end
```

バージョン競合が起きているアプリケーションについては、明示的にこのバージョンを使用しろと書くわけです。`override: true`を書かないと再び`mix deps.check`でエラーが出てしまうのでお忘れなく。これで`./mix deps.check`をするとエラーが出ず、コンパイルが始まります。

# まとめ
mixでの開発において依存アプリケーションのバージョン指定については以下のような指針を設けるのがよさそうです。

**全ての依存アプリケーションにおいてバージョン衝突があるかないかを`mix deps.check`コマンドで確認する**

**バージョン衝突が起きた場合は、バージョンをそろえるか、一番上の階層の（依存アプリケーションが`mix.exs`を持つこともある）`mix.exs`に使用するアプリケーションのバージョンを明示する（`override: true`を忘れずに）**

**生成された`mix.lock`ファイルをgit管理などして、全ての使用アプリケーションのバージョンを明示する**


これはrebarよりいい感じですね。

**つづく**