まえがき
去る2019/09/29に、無謀にも「fukuoka.ex#30:tokyo.exとコラボ、Elixir本体コード読込会(リモートのみ)」に参加した。主催はtokyo.exさんで、会場は福岡の自宅から1,102km離れた東京-目黒。よって、リモートによる"もくもく"参戦であった。
fukuoka.ex#30:tokyo.exとコラボ、Elixir本体コード読込会(リモートのみ)
さて、レクチャーは開始早々 makeのフロー解説に始まり、密かに期待していたElixir本体コードの解説はほぼなく、そのまま"もくもく"に突入した。正直なところ消化不良の感があったが、"もくもく"では俺々標準モジュールを追加してみたりと、とても楽しいひと時であった。
めでたし、めでたし……ではない、消化不良を解消せねば。さあ復習しておこう!!
ということで、超個人的復習日記なのである(^_-)
やっぱり makeから(汗)
GitHubからcloneしたElixirソースコードをビルドし 俺様Elixirを作る手順は、そのトップ・ディレクトリでおもむろに makeを実行するだけで良い♬ un*x系でお馴染みの ./configureもいらなければ、CMakeにお願いすることもない。拍子抜けするほど簡単である。
> cd elixir
> make
Recompile: src/elixir_utils
Recompile: src/elixir_tokenizer
Recompile: src/elixir_sup
Recompile: src/elixir_rewrite
Recompile: src/elixir_quote
Recompile: src/elixir_parser
...
そのうえ、ビルドの手順が記述されたMakefileは、僅か321行でたった一つだ。極めてシンプル♡
Makefileの中を覗いてみると、なにやらマクロの定義がごちゃごちゃして複雑そうに見える。しかしよーく見ると、
"#==> ほにゃららtasks"
の表札付きでタスク毎にキレイにブロック分けされているではないか。ハヤブサさん@hayabusa333曰く「読めば解る」とのアドバイスに、しごく納得なのである。こんなに親切なMakefileは、おじさん見たことがないぞ(^_-)[*1]。
[*1]なぜ makeなのだろう? もしかして Erlangの生みの親Joe Armstorng氏がシンプルなMakefileを愛用していたからだろうか? [[プログラミングErlang,Joe Armstrong著/榊原一矢訳,オーム社,p98]]
...
062 endef
063
064 #==> Compilation tasks
065
066 APP := lib/elixir/ebin/elixir.app
067 PARSER := lib/elixir/src/elixir_parser.erl
068 KERNEL := lib/elixir/ebin/Elixir.Kernel.beam
069 UNICODE := lib/elixir/ebin/Elixir.String.Unicode.beam
070
071 default: compile
072
073 compile: erlang $(APP) elixir
...
それでは、もくもくとMakefileを解読しようか……ちょっと待ったぁぁぁ!
小生が知りたいことは、Elixir本体をビルドする時に実行されるコマンド列なのだ。ならば、makeに直接聞けば良いではないか。Makefileを直接攻めるのではなく、makeの出口を観察するのが初対面のMakefileを調べる王道である(もくもく本番では失念していた 汗)
おもむろに makeを実行して、生成されるコマンド列を調べよう。おっと、このMakefileでは「Q := @」の定義でコマンドエコーを停止しているので、makeの引数に「Q=」を与えて解除することを忘れずに。
> make Q=
001 erlc -o lib/elixir/src/elixir_parser.erl +'{verbose,true}' +'{report,true}' lib/elixir/src/elixir_parser.yrl
002 if [ ! -f lib/elixir/ebin/elixir.app ]; then erl -noshell -eval '{V,_} = string:to_integer(erlang:system_info(otp_release)), io:fwrite("~s", [is_integer(V) and (V >= 20)])' -s erlang halt | grep -q '^true'; if [ $? != 0 ]; then echo "At least Erlang/OTP 20.0 is required to build Elixir"; exit 1; fi; fi
003 cd lib/elixir && mkdir -p ebin && erl -make
004 Recompile: src/elixir_utils
005 Recompile: src/elixir_tokenizer
006 Recompile: src/elixir_sup
007 Recompile: src/elixir_rewrite
008 Recompile: src/elixir_quote
009 Recompile: src/elixir_parser
010 Recompile: src/elixir_overridable
011 Recompile: src/elixir_module
012 Recompile: src/elixir_map
013 Recompile: src/elixir_locals
014 Recompile: src/elixir_lexical
015 Recompile: src/elixir_interpolation
016 Recompile: src/elixir_import
017 Recompile: src/elixir_fn
018 Recompile: src/elixir_expand
019 Recompile: src/elixir_errors
020 Recompile: src/elixir_erl_var
021 Recompile: src/elixir_erl_try
022 Recompile: src/elixir_erl_pass
023 Recompile: src/elixir_erl_for
024 Recompile: src/elixir_erl_compiler
025 Recompile: src/elixir_erl_clauses
026 Recompile: src/elixir_erl
027 Recompile: src/elixir_env
028 Recompile: src/elixir_dispatch
029 Recompile: src/elixir_def
030 Recompile: src/elixir_config
031 Recompile: src/elixir_compiler
032 Recompile: src/elixir_code_server
033 Recompile: src/elixir_clauses
034 Recompile: src/elixir_bootstrap
035 Recompile: src/elixir_bitstring
036 Recompile: src/elixir_aliases
037 Recompile: src/elixir
038 /home/shoz/Elixir/elixir/lib/elixir/generate_app.escript lib/elixir/src/elixir.app.src lib/elixir/ebin/elixir.app 1.9.1-shoz
039 Generated elixir.app
040 if [ ! -f lib/elixir/ebin/Elixir.Kernel.beam ]; then \
041 echo "==> bootstrap (compile)"; \
042 erl -I lib/elixir/include -noshell -pa lib/elixir/ebin -s elixir_compiler bootstrap -s erlang halt; \
043 fi
044 ==> bootstrap (compile)
045 Compiled lib/elixir/lib/kernel.ex
046 Compiled lib/elixir/lib/macro/env.ex
047 Compiled lib/elixir/lib/keyword.ex
048 Compiled lib/elixir/lib/module.ex
049 Compiled lib/elixir/lib/list.ex
050 Compiled lib/elixir/lib/macro.ex
051 Compiled lib/elixir/lib/code.ex
052 Compiled lib/elixir/lib/code/identifier.ex
053 Compiled lib/elixir/lib/module/locals_tracker.ex
054 Compiled lib/elixir/lib/kernel/typespec.ex
055 Compiled lib/elixir/lib/kernel/utils.ex
056 Compiled lib/elixir/lib/exception.ex
057 Compiled lib/elixir/lib/protocol.ex
058 Compiled lib/elixir/lib/stream/reducers.ex
059 Compiled lib/elixir/lib/enum.ex
060 Compiled lib/elixir/lib/inspect/algebra.ex
061 Compiled lib/elixir/lib/inspect.ex
062 Compiled lib/elixir/lib/regex.ex
063 Compiled lib/elixir/lib/string.ex
064 Compiled lib/elixir/lib/string/chars.ex
065 Compiled lib/elixir/lib/io.ex
066 Compiled lib/elixir/lib/path.ex
067 Compiled lib/elixir/lib/file.ex
068 Compiled lib/elixir/lib/system.ex
069 Compiled lib/elixir/lib/kernel/cli.ex
070 Compiled lib/elixir/lib/kernel/error_handler.ex
071 Compiled lib/elixir/lib/kernel/parallel_compiler.ex
072 Compiled lib/elixir/lib/kernel/lexical_tracker.ex
073 ==> elixir (compile)
074 cd lib/elixir && ../../bin/elixirc --verbose "lib/kernel.ex" -o ebin;
075 erl -pa /home/shoz/Elixir/elixir/bin/../lib/elixir/ebin -noshell -s elixir start_cli -extra +elixirc --verbose lib/kernel.ex -o ebin
076 cd lib/elixir && ../../bin/elixirc --verbose "lib/**/*.ex" -o ebin;
077 erl -pa /home/shoz/Elixir/elixir/bin/../lib/elixir/ebin -noshell -s elixir start_cli -extra +elixirc --verbose lib/**/*.ex -o ebin
078 make unicode
079 make[1]: ディレクトリ '/home/shoz/Elixir/elixir' に入ります
080 ==> unicode (compile)
081 bin/elixirc --verbose lib/elixir/unicode/unicode.ex -o lib/elixir/ebin;
082 erl -pa /home/shoz/Elixir/elixir/bin/../lib/elixir/ebin -noshell -s elixir start_cli -extra +elixirc --verbose lib/elixir/unicode/unicode.ex -o lib/elixir/ebin
083 Compiling /home/shoz/Elixir/elixir/lib/elixir/unicode/unicode.ex (its taking more than 15s)
084 bin/elixirc --verbose lib/elixir/unicode/properties.ex -o lib/elixir/ebin;
085 erl -pa /home/shoz/Elixir/elixir/bin/../lib/elixir/ebin -noshell -s elixir start_cli -extra +elixirc --verbose lib/elixir/unicode/properties.ex -o lib/elixir/ebin
086 bin/elixirc --verbose lib/elixir/unicode/tokenizer.ex -o lib/elixir/ebin;
087 erl -pa /home/shoz/Elixir/elixir/bin/../lib/elixir/ebin -noshell -s elixir start_cli -extra +elixirc --verbose lib/elixir/unicode/tokenizer.ex -o lib/elixir/ebin
088 make[1]: ディレクトリ '/home/shoz/Elixir/elixir' から出ます
089 make app
090 make[1]: ディレクトリ '/home/shoz/Elixir/elixir' に入ります
091 /home/shoz/Elixir/elixir/lib/elixir/generate_app.escript lib/elixir/src/elixir.app.src lib/elixir/ebin/elixir.app 1.9.1-shoz
092 Generated elixir.app
093 make[1]: ディレクトリ '/home/shoz/Elixir/elixir' から出ます
094 ==> eex (compile)
095 cd lib/eex && ../../bin/elixirc --verbose "lib/**/*.ex" -o ebin
096 erl -pa /home/shoz/Elixir/elixir/bin/../lib/elixir/ebin -noshell -s elixir start_cli -extra +elixirc --verbose lib/**/*.ex -o ebin
097 ==> mix (compile)
098 cd lib/mix && ../../bin/elixirc --verbose "lib/**/*.ex" -o ebin
099 erl -pa /home/shoz/Elixir/elixir/bin/../lib/eex/ebin /home/shoz/Elixir/elixir/bin/../lib/elixir/ebin -noshell -s elixir start_cli -extra +elixirc --verbose lib/**/*.ex -o ebin
100 cd lib/mix && ../../bin/elixir -e 'Mix.start(:permanent, [])' -r mix.exs -e 'Mix.Task.run("compile.app", ~w[--compile-path ebin])'
101 erl -pa /home/shoz/Elixir/elixir/bin/../lib/eex/ebin /home/shoz/Elixir/elixir/bin/../lib/elixir/ebin /home/shoz/Elixir/elixir/bin/../lib/mix/ebin -noshell -s elixir start_cli -extra -e Mix.start(:permanent, []) -r mix.exs -e Mix.Task.run("compile.app", ~w[--compile-path ebin])
102 Generated mix app
103 ==> ex_unit (compile)
104 cd lib/ex_unit && ../../bin/elixirc --verbose "lib/**/*.ex" -o ebin
105 erl -pa /home/shoz/Elixir/elixir/bin/../lib/eex/ebin /home/shoz/Elixir/elixir/bin/../lib/elixir/ebin /home/shoz/Elixir/elixir/bin/../lib/mix/ebin -noshell -s elixir start_cli -extra +elixirc --verbose lib/**/*.ex -o ebin
106 cd lib/ex_unit && ../../bin/elixir -e 'Mix.start(:permanent, [])' -r mix.exs -e 'Mix.Task.run("compile.app", ~w[--compile-path ebin])'
107 erl -pa /home/shoz/Elixir/elixir/bin/../lib/eex/ebin /home/shoz/Elixir/elixir/bin/../lib/elixir/ebin /home/shoz/Elixir/elixir/bin/../lib/ex_unit/ebin /home/shoz/Elixir/elixir/bin/../lib/mix/ebin -noshell -s elixir start_cli -extra -e Mix.start(:permanent, []) -r mix.exs -e Mix.Task.run("compile.app", ~w[--compile-path ebin])
108 Generated ex_unit app
109 ==> logger (compile)
110 cd lib/logger && ../../bin/elixirc --verbose "lib/**/*.ex" -o ebin
111 erl -pa /home/shoz/Elixir/elixir/bin/../lib/eex/ebin /home/shoz/Elixir/elixir/bin/../lib/elixir/ebin /home/shoz/Elixir/elixir/bin/../lib/ex_unit/ebin /home/shoz/Elixir/elixir/bin/../lib/mix/ebin -noshell -s elixir start_cli -extra +elixirc --verbose lib/**/*.ex -o ebin
112 cd lib/logger && ../../bin/elixir -e 'Mix.start(:permanent, [])' -r mix.exs -e 'Mix.Task.run("compile.app", ~w[--compile-path ebin])'
113 erl -pa /home/shoz/Elixir/elixir/bin/../lib/eex/ebin /home/shoz/Elixir/elixir/bin/../lib/elixir/ebin /home/shoz/Elixir/elixir/bin/../lib/ex_unit/ebin /home/shoz/Elixir/elixir/bin/../lib/logger/ebin /home/shoz/Elixir/elixir/bin/../lib/mix/ebin -noshell -s elixir start_cli -extra -e Mix.start(:permanent, []) -r mix.exs -e Mix.Task.run("compile.app", ~w[--compile-path ebin])
114 Generated logger app
115 cd lib/eex && ../../bin/elixir -e 'Mix.start(:permanent, [])' -r mix.exs -e 'Mix.Task.run("compile.app", ~w[--compile-path ebin])'
116 erl -pa /home/shoz/Elixir/elixir/bin/../lib/eex/ebin /home/shoz/Elixir/elixir/bin/../lib/elixir/ebin /home/shoz/Elixir/elixir/bin/../lib/ex_unit/ebin /home/shoz/Elixir/elixir/bin/../lib/logger/ebin /home/shoz/Elixir/elixir/bin/../lib/mix/ebin -noshell -s elixir start_cli -extra -e Mix.start(:permanent, []) -r mix.exs -e Mix.Task.run("compile.app", ~w[--compile-path ebin])
117 Generated eex app
118 ==> iex (compile)
119 cd lib/iex && ../../bin/elixirc --verbose "lib/**/*.ex" -o ebin
120 erl -pa /home/shoz/Elixir/elixir/bin/../lib/eex/ebin /home/shoz/Elixir/elixir/bin/../lib/elixir/ebin /home/shoz/Elixir/elixir/bin/../lib/ex_unit/ebin /home/shoz/Elixir/elixir/bin/../lib/logger/ebin /home/shoz/Elixir/elixir/bin/../lib/mix/ebin -noshell -s elixir start_cli -extra +elixirc --verbose lib/**/*.ex -o ebin
121 cd lib/iex && ../../bin/elixir -e 'Mix.start(:permanent, [])' -r mix.exs -e 'Mix.Task.run("compile.app", ~w[--compile-path ebin])'
122 erl -pa /home/shoz/Elixir/elixir/bin/../lib/eex/ebin /home/shoz/Elixir/elixir/bin/../lib/elixir/ebin /home/shoz/Elixir/elixir/bin/../lib/ex_unit/ebin /home/shoz/Elixir/elixir/bin/../lib/iex/ebin /home/shoz/Elixir/elixir/bin/../lib/logger/ebin /home/shoz/Elixir/elixir/bin/../lib/mix/ebin -noshell -s elixir start_cli -extra -e Mix.start(:permanent, []) -r mix.exs -e Mix.Task.run("compile.app", ~w[--compile-path ebin])
123 Generated iex app
ど〜んと出力された。おろおろ(^^;)
しかし、004行〜072行と045行〜072行はコマンド実行のステータス、078行〜最後行は Elixirのツール mix等のビルドなので無視しよう。結局、
(い) 001〜003行 パーサー生成とElixirコンパイラのビルド
(ろ) 038〜043行 アプリケーションファイル生成とブートストラッピング
(は) 074〜077行 stdlibのコンパイル
を調べるだけで良いよーだ(^_-)
先に進む前に、Elixir本体のビルドに関与するディレクトリとファイルの種類を整理しておこう
[ディレクトリ]
elixir
├─ bin/ …… コンパイラ・ドライバ(実体はシェルスクリプト)等が格納されている
├─ lib/
│ ├─ elixir/
: : ├─ ebin/ …… コンパイルで得られたbeamバイナリの格納先
: ├─ lib/ …… Elixirで記述されたソースコードが格納されている
: ├─ src/ …… Erlangで記述されたソースコードが格納されている
: :
: └─ Emakefile …… Erlang make用メイクファイル
└─ Makefile …… make用メイクファィル
[ファイルの種類]
1. *.erl …… Erlangソースコード
2. *.yrl …… パーサー生成器yeccのソースコード
3. *.ex …… Elixirソースコード
4. *.app …… OTPのアプリーケーションファイル
5. ebin/elixir_*.beam …… Erlangソースコードをコンパイルしたbeamバイナリ
6. ebin/Elixir.*.beam …… Elixirソースコードをコンパイルしたbeamバイナリ
(い) 001〜003行 パーサー生成とElixirコンパイラのビルド
001 erlc -o lib/elixir/src/elixir_parser.erl +'{verbose,true}' +'{report,true}' lib/elixir/src/elixir_parser.yrl
002 if [ ! -f lib/elixir/ebin/elixir.app ]; then erl -noshell -eval '{V,_} = string:to_integer(erlang:system_info(otp_release)), io:fwrite("~s", [is_integer(V) and (V >= 20)])' -s erlang halt | grep -q '^true'; if [ $? != 0 ]; then echo "At least Erlang/OTP 20.0 is required to build Elixir"; exit 1; fi; fi
003 cd lib/elixir && mkdir -p ebin && erl -make
● 001行目
yeccと呼ばれるLALR-1パーサー・ジェネレータで、elixir_parser.yrlから Elixirのパーサー(elixir_parser.erl)を生成している。Erlang界隈では、yeccという独立したコマンドはなく、erlcコンパイラ・ドライバがファイルのサフィックスを見て、内部でyeccを呼ぶようになっている。yeccの生成ルールを記述する文法は、Erlangの関数定義に似た文法で、お馴染みのyaccとは似ても似つかない。
ふむ、今回はパーサー回りにはあまり深入りせず、ここのcode readingは老後の楽しみに取っておこう(=^・^=)
とりあえず未来の小生のために、参考情報を残しておく。
▶Erlang Parse Tools Reference Manual
▶Erlang - Leex and Yecc
▶How to use leex and yecc in Elixir
▶Using Leex and Yecc in Elixir
● 002行目:
Erlangのバージョンを確認している。Elixir 1.9.1をビルドするためには、バージョン20.0以上の Erlangが必要なようだ。20.0未満の場合は、ここで makeを中断する。
● 003行目:
先に生成したElixirパーサーと共に、lib/elixir/srcに格納されている Elixir本体のソースコード(.erl)をコンパイルしている。ここで、Elixirコンパイラの実体がビルドされるのだ……きっと、たぶん、そうだよね(^^;)
lib/elixir/srcディレクトリには、今バージョンでは 34個の .erlファイルが格納されている。それら個々のファイルのコンパイルは makeが制御するのではなく、Erlangの make utilityが制御している。lib/elixir/Emakefileは Erlang-make用のMakefileで、対象とするソースコードの指定、コンパイル・オプション、バイナリの格納先ディレクトリなどがタプルを用いて記述されている。
さて、このタスクに相当するMakefileの箇所はココだ。
075 erlang: $(PARSER)
076 $(Q) if [ ! -f $(APP) ]; then $(call CHECK_ERLANG_RELEASE); fi
077 $(Q) cd lib/elixir && mkdir -p ebin && erl -make
078
079 $(PARSER): lib/elixir/src/elixir_parser.yrl
080 $(Q) erlc -o $@ +'{verbose,true}' +'{report,true}' $<
(ろ) 038〜043行 アプリケーションファイル生成とブートストラッピング
038 /home/shoz/Elixir/elixir/lib/elixir/generate_app.escript lib/elixir/src/elixir.app.src lib/elixir/ebin/elixir.app 1.9.1-shoz
039 Generated elixir.app
040 if [ ! -f lib/elixir/ebin/Elixir.Kernel.beam ]; then \
041 echo "==> bootstrap (compile)"; \
042 erl -I lib/elixir/include -noshell -pa lib/elixir/ebin -s elixir_compiler bootstrap -s erlang halt; \
043 fi
● 038〜039行目:
テンプレートlib/elixir/src/elixir.app.srcにバージョンを埋め込んで、elixir.appを作っている。先の(い)でビルドできた Elixirコンパイラを、OTPアプリケーションとして動作させたいのだ。そのためには、.app(application resource file)ファイルが必要なのだ。mixが、プロジェクト作成時に勝手に用意してくれる あれね(.app)。
▶Erlang Kernel Reference Manual/app
▶Learn you some Erlang for great good! 日本語訳
● 040〜043行目:
Elixir取り巻きの標準モジュール(一部)をブートストラップしているらしい(汗)
だが、なんでブートストラップなのだ??? ココまで見てきた通り、Elixirコンパイラはすでにビルドが終わって使える状態だ。事実、このブートストラップのタスクにおいても、Erlangが一モジュールとしてElixirコンパイラを利用して、.exソースコードをコンパイルしているではないか……
ふむ、これは小生の予想だが……これ以降のタスクでは CLI経由で Elixirコンパイラを利用している。つまり、ブートストラップとは、CLIを使用できるように、必要最小のモジュール群を、Erlangのプログラム
でコンパイルするタスクを意味しているのではなかろうか。
このタスクで呼ばれるErlangモジュールのエントリー・ポイントは、下の「Makefile(3)」から分かる通り elixir_compiler:bootstrapだ。同じファイルにコンパイルの対象となる .exソース・ファイルの一覧がリスト(bootstrap_main)で用意されている。それらソースファイル各々に対し、bootstrap()から順に関数が callされ、
bootstrap() ⇒ bootstrap_file(File) ⇒ file(File, Callback) ⇒ string(Contents, File, Callback)
そして string()でソースコードが quoted形式(AST?)に変換される。それから eval_forms()でquoted形式が beamバイナリにコンバートさているようだ。
014 string(Contents, File, Callback) ->
015 Forms = elixir:'string_to_quoted!'(Contents, 1, File, []),
016 quoted(Forms, File, Callback).
017
018 quoted(Forms, File, Callback) ->
019 Previous = get(elixir_module_binaries),
020
021 try
022 put(elixir_module_binaries, []),
023 elixir_lexical:run(File, fun(Pid) ->
024 Env = elixir:env_for_eval([{line, 1}, {file, File}]),
025 eval_forms(Forms, [], Env#{lexical_tracker := Pid}),
026 Callback(File, Pid)
027 end),
028 lists:reverse(get(elixir_module_binaries))
029 after
030 put(elixir_module_binaries, Previous)
031 end.
<途中省略>
047 eval_forms(Forms, Vars, E) ->
048 case (?key(E, module) == nil) andalso allows_fast_compilation(Forms) of
049 true ->
050 Binding = [{Key, Value} || {_Name, _Kind, Key, Value} <- Vars],
051 {Result, _Binding, EE, _S} = elixir:eval_forms(Forms, Binding, E),
052 {Result, EE};
053 false ->
054 compile(Forms, Vars, E)
055 end.
<途中省略>
141 bootstrap_main() ->
142 [<<"lib/elixir/lib/kernel.ex">>,
143 <<"lib/elixir/lib/macro/env.ex">>,
144 <<"lib/elixir/lib/keyword.ex">>,
145 <<"lib/elixir/lib/module.ex">>,
146 <<"lib/elixir/lib/list.ex">>,
147 <<"lib/elixir/lib/macro.ex">>,
148 <<"lib/elixir/lib/code.ex">>,
149 <<"lib/elixir/lib/code/identifier.ex">>,
150 <<"lib/elixir/lib/module/locals_tracker.ex">>,
151 <<"lib/elixir/lib/kernel/typespec.ex">>,
152 <<"lib/elixir/lib/kernel/utils.ex">>,
153 <<"lib/elixir/lib/exception.ex">>,
154 <<"lib/elixir/lib/protocol.ex">>,
155 <<"lib/elixir/lib/stream/reducers.ex">>,
156 <<"lib/elixir/lib/enum.ex">>,
157 <<"lib/elixir/lib/inspect/algebra.ex">>,
158 <<"lib/elixir/lib/inspect.ex">>,
159 <<"lib/elixir/lib/regex.ex">>,
160 <<"lib/elixir/lib/string.ex">>,
161 <<"lib/elixir/lib/string/chars.ex">>,
162 <<"lib/elixir/lib/io.ex">>,
163 <<"lib/elixir/lib/path.ex">>,
164 <<"lib/elixir/lib/file.ex">>,
165 <<"lib/elixir/lib/system.ex">>,
166 <<"lib/elixir/lib/kernel/cli.ex">>,
167 <<"lib/elixir/lib/kernel/error_handler.ex">>,
168 <<"lib/elixir/lib/kernel/parallel_compiler.ex">>,
169 <<"lib/elixir/lib/kernel/lexical_tracker.ex">>
170 ].
このタスクに相当するMakefileの箇所はココとココだ
101 $(APP): lib/elixir/src/elixir.app.src lib/elixir/ebin VERSION $(GENERATE_APP)
102 $(Q) $(GENERATE_APP) $< $@ $(VERSION)
089 $(Q) if [ ! -f $(KERNEL) ]; then \
090 echo "==> bootstrap (compile)"; \
091 $(ERL) -s elixir_compiler bootstrap -s erlang halt; \
092 fi
(は) 074〜077行 stdlibのコンパイル
074 cd lib/elixir && ../../bin/elixirc --verbose "lib/kernel.ex" -o ebin;
075 erl -pa /home/shoz/Elixir/elixir/bin/../lib/elixir/ebin -noshell -s elixir start_cli -extra +elixirc --verbose lib/kernel.ex -o ebin
076 cd lib/elixir && ../../bin/elixirc --verbose "lib/**/*.ex" -o ebin;
077 erl -pa /home/shoz/Elixir/elixir/bin/../lib/elixir/ebin -noshell -s elixir start_cli -extra +elixirc --verbose lib/**/*.ex -o ebin
ふぅ、ゴールは間近だd(^^)
● 074〜077行目:
Elixirコンパイラは、今やコンパイル対象のファイルをコマンドライン引数として受け取ることが出来るようになった。故に、ここでは lib/kernel.ex 及び lib/elixir/lib/以下の全てのディレクトリにある 全.exソースファイル(globパターン lib/** /*.ex)をElixirコンパイラに与えて標準モジュールをコンパイルしているのだ。
ということは、俺々モジュールを標準モジュールに加えたければ、lib/elixir/lib/以下の適当なところにポンと置けばよいのか。なんとお手軽な♬
なにはともあれ、ここは楽勝であった(^^)v
このタスクに相当するMakefileの箇所はココの 094行目と 095行目だ
085 elixir: stdlib lib/eex/ebin/Elixir.EEx.beam mix ex_unit logger eex iex
086
087 stdlib: $(KERNEL) VERSION
088 $(KERNEL): lib/elixir/lib/*.ex lib/elixir/lib/*/*.ex lib/elixir/lib/*/*/*.ex
089 $(Q) if [ ! -f $(KERNEL) ]; then \
090 echo "==> bootstrap (compile)"; \
091 $(ERL) -s elixir_compiler bootstrap -s erlang halt; \
092 fi
093 @ echo "==> elixir (compile)";
094 $(Q) cd lib/elixir && ../../$(ELIXIRC) "lib/kernel.ex" -o ebin;
095 $(Q) cd lib/elixir && ../../$(ELIXIRC) "lib/**/*.ex" -o ebin;
096 $(Q) $(MAKE) unicode
097 $(Q) $(MAKE) app
(に)その他
この後タスクは、unicode、eex、mix、ex_unit、logger、iexのビルドへと続くが……以下同様のタスクであろう。いつの日にか興味が湧けば真面目に調べてみよう。
俺々標準モジュールを追加してみる
試しに、俺々モジュールをElixirの標準モジュールに追加してみよう。上で見たように、次のモジュールをlib/elixir/lib/に置き、リビルドするだけで良い(^^)v
defmodule Oreore do
def say() do
IO.puts("Ore dayo, Ore.")
end
end
shoz@Pavilion-dv6:~/Elixir/elixir$ bin/iex
Erlang/OTP 22 [erts-10.4.4] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe]
Interactive Elixir (1.9.1-shoz) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> Oreore.say()
Ore dayo, Ore.
:ok
iex(2)>
まとめ
- 結局 Elixirコンパイラは、FULLに Erlangで記述されているよーだった。
- ElixirコンパイラはOTPのアプリケーションとして設計されていた。
- Erlangのパーサージェネレータyeccのコンパイラ・ドライバは erlcが兼務していた。
- stdlibブートストラップ・タスクの意図がはっきりと分からなかった。
- 標準モジュールに俺々モジュールを追加するのは超お手軽と分かった。
で、Elixir本体コードを読んでどうしようと言うのかは未だ定かではない(汗)
…まあ、好奇心が満たせて楽しければそれで良いのだ
長〜い復習であった。
この先には、Erlangとyeccの深い森が横たわっている。
進むべきか、引き返すべきか、それが問題だ。
参考文献
1)[上から見るか下から見るか.pdf]
(https://speakerdeck.com/hayabusa333/shang-karajian-rukaxia-karajian-ruka) - ハヤブサさん@hayabusa333
2)Hacking Elixir How-To - おーはら@ohrdevさん
3)Learn you some Erlang for great good! 日本語訳
4)GNU make 日本語訳(Coop編)