elixirの実行コマンドの実体を見る

## はじめに

この記事は[ElixirAdventCalendar2022](https://qiita.com/advent-calendar/2022/elixir)の24日目の記事です。

## elixirのコマンド

elixirをインストールすると、以下のコマンドが実行可能になります。

* elixir
* elixirc
* iex
* mix

これらのコマンドはelixirの[ソースコード](https://github.com/elixir-lang/elixir)の[bin](https://github.com/elixir-lang/elixir/tree/main/bin)ディレクトリ配下に配置されています。
elixirのインストール時に、これらの実行コマンドのファイル(shellスクリプト)のシンボリックリンクが、インストール先に作成され、elixirのコマンドが実行可能になります。

## elixirの実行コマンドの関係

`elixir`、`elixirc`、`iex`、`mix`はそれぞれ以下の様な相関関係があります。

|コマンド|処理|
|-----|----|
|elixirc| `+elixir`オプションを付けて`exec elixir`|
|iex|`--no-halt --erl "-noshell -user Elixir.IEx CLI" +iex`オプションを付けて`exec　elixir`|
|mix|`Mix.start()` `Mix.CLI.main()`を実行する[elixirスクリプト](https://github.com/elixir-lang/elixir/blob/main/bin/mix)|

つまり、`elixirc`も`iex`も`mix`も`elixir`コマンドを内部的に呼び出しています。

## elixirコマンド実行時に何が起こっているか

`elixir`コマンドの実体は`erl`コマンドです。
`elixir`は、コマンドに渡されるオプションや環境変数を、`erl`コマンドに渡すオプションに変換して、最終的に`erl`コマンドを実行します。

## 起動時のオプションの確認方法

`elixir`実行時に、`ELIXIR_CLI_DRY_RUN`の環境変数を指定することで、実行時の挙動を以下の様に`exec`から`echo`に変更して、起動時のオプションを確認することができます。


```elixir
if [ -n "$ELIXIR_CLI_DRY_RUN" ]; then
  echo "$@"
else
  exec "$@"
fi
```

※ 詳細は[elixir](https://github.com/elixir-lang/elixir/blob/main/bin/elixir)参照

## コマンド実行時のオプション表示例

### elixir --version

```bash
$ elixir --version
Erlang/OTP 25 [erts-13.1.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit:ns]

Elixir 1.13.3 (compiled with Erlang/OTP 22)
```

```bash
$ ELIXIR_CLI_DRY_RUN=1 elixir --version
erl 
-pa /Users/ohara_tsunenori/.asdf/installs/elixir/1.13.3/bin/../lib/eex/ebin 
    /Users/ohara_tsunenori/.asdf/installs/elixir/1.13.3/bin/../lib/elixir/ebin 
    /Users/ohara_tsunenori/.asdf/installs/elixir/1.13.3/bin/../lib/ex_unit/ebin 
    /Users/ohara_tsunenori/.asdf/installs/elixir/1.13.3/bin/../lib/iex/ebin 
    /Users/ohara_tsunenori/.asdf/installs/elixir/1.13.3/bin/../lib/logger/ebin 
    /Users/ohara_tsunenori/.asdf/installs/elixir/1.13.3/bin/../lib/mix/ebin 
-elixir ansi_enabled true 
-noshell 
-s elixir start_cli 
-extra --version
```

※ 見やすい様に改行を適宜入れています

### iex --version

```bash
$ iex --version                                                                                      ✘ 1
Erlang/OTP 25 [erts-13.1.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit:ns]

IEx 1.13.3 (compiled with Erlang/OTP 22)
~ ❯❯❯
```

```bash
$ ELIXIR_CLI_DRY_RUN=1 iex --version
erl 
-pa /Users/ohara_tsunenori/.asdf/installs/elixir/1.13.3/bin/../lib/eex/ebin
    /Users/ohara_tsunenori/.asdf/installs/elixir/1.13.3/bin/../lib/elixir/ebin
    /Users/ohara_tsunenori/.asdf/installs/elixir/1.13.3/bin/../lib/ex_unit/ebin
    /Users/ohara_tsunenori/.asdf/installs/elixir/1.13.3/bin/../lib/iex/ebin 
    /Users/ohara_tsunenori/.asdf/installs/elixir/1.13.3/bin/../lib/logger/ebin 
    /Users/ohara_tsunenori/.asdf/installs/elixir/1.13.3/bin/../lib/mix/ebin 
-elixir ansi_enabled true 
-noshell 
-user Elixir.IEx.CLI 
-extra --no-halt +iex --version
~ ❯❯❯
```

※ 見やすい様に改行を適宜入れています

### erlコマンドの主要オプション

|オプション|意味|
|-------|---|
|-pa|後ろに続くディレクトリ配下のモジュールを`erl`実行にロード|
|-noshell|erlangの対話シェルを起動せずに`erl`を実行|
|-s <Mod> <Func> <Arg>|<Mod>モジュールの<Func>関数を引数<Arg>で実行|

## まとめ

elixirのコンパイラはerlangで実装されていることもあり、言語のコア部分はerlangと密接に関連しています。
オプション表示例から、elixirの実体はerlang(のerlコマンド)という事が見て取れます。