D言語で書かれたD言語コンパイラー・DDMDをビルドしてみる (挫折編)

[D言語 Advent Calendar 2014](http://qiita.com/advent-calendar/2014/dlang)の9日目の記事です．

# DDMD?

現在，D言語のリファレンス実装である dmd は，C++で書かれています．
これに対し，ddmd はD言語で書かれたDコンパイラーなのですが，まだ[dmd のリポジトリ](https://github.com/D-Programming-Language/dmd)には含まれておりません．
これを解決する Pull Request が[Issue3410](https://github.com/D-Programming-Language/dmd/pull/3410)で，これがマージされれば， dmd と ddmd の両方を公式のリポジトリからビルドできるようになります．

# この記事では
一足先に ddmd を体験するために，~~ddmd をビルドする方法を解説します~~解説しようと思っていましたが，**ビルドできませんでした！**

なのでここでは，ddmd をビルドする際の注意点や試してみたことを列挙します．
ddmd にチャレンジする際のヒントになれば幸いです．


これ以降は，次の環境での試行錯誤の結果です．

- Arch Linux (64bit)
- gcc 4.9.2
- druntime/Phobosのバージョン: trunk

# ビルド方法 (試行錯誤含む)
ddmd は，D言語のコミッターの[Daniel Murphyさんのリポジトリ](https://github.com/yebblies)で公開されています．
ddmd の開発ブランチは `newmagic` です．

```
$ git clone https://github.com/yebblies/dmd.git
$ cd dmd
dmd $ git co newmagic
dmd $ git log -n 1
commit e4445baf3c4571fc5d2b99e28057b0605e8a8ed1
Author: Daniel Murphy <yebblies@gmail.com>
Date:   Sat Nov 8 19:45:37 2014 +1100

    enable gc by default
```

[Issue3410](https://github.com/D-Programming-Language/dmd/pull/3410)の説明には，「`make ddmd` でビルドできるよ！」と書いてありますが，自分の環境ではいくつかの問題に遭遇しました．以下は遭遇した順です．

### object.d を読んでくれない
```
dmd $ cd src
dmd/src $ make -f posix.mak ddmd
...
./dmd -m64 -ofmagicport/magicport2 magicport/magicport2.d magicport/ast.d magicport/scanner.d magicport/tokens.d magicport/parser.d magicport/dprinter.d magicport/typenames.d magicport/visitor.d magicport/namer.d
Error: cannot find source code for runtime library file 'object.d'
       dmd might not be correctly installed. Run 'dmd -man' for installation instructions.
       config file: /home/tm_tn/.denv/shims/dmd.conf
Specify path to file 'object.d' with -I switch
posix.mak:481: ターゲット 'magicport/magicport2' のレシピで失敗しました
make: *** [magicport/magicport2] エラー 1

```
これは ddmd をビルドするための dmd が， dmd.conf の内容を読んでくれないのが原因です．
dmd.conf をリポジトリ内にコピーしても読まないため，環境変数 DFLAGS と MODEL_FLAG をしていすることで解決しました(MODEL_FLAGを指定しないと libphobos2 のリンクに失敗します)．

### "Error: static assert(0) is false"
`/path/to/foo` は foo のあるディレクトリの場所を表しています． 環境に応じて変更してください．

```
dmd/src $ make -f posix.mak ddmd DFLAGS="-I/path/to/phobos -I/path/to/druntime/import -L-L/path/to/lib64 -L--export-dynamic" MODEL_FLAG=-L-L/path/to/lib64
...
root/rmem.d(54): Error: static assert  (0) is false
posix.mak:519: ターゲット 'ddmd' のレシピで失敗しました
make: *** [ddmd] エラー 1
```
非Windows環境では， root/rmem.d に含まれる static assert に引っかかるためビルドできません．
これを解決するために以下の3パターンを試してみましたが，結果はどれも同じでした…

- `static assert(0)` の行をコメントアウトする．
- root/rmem.d の6行目の`version=GC;` をコメントアウトする．
  - これにより，GCを使わずにメモリ管理をするようになり，`static assert(0)`のコードをコンパイルせずに済みます．
- 環境に合わせて `operator_new` 等の各関数を実装する．
  - gccでのマングル名(`_Z12operator_newm`等)を`pragma(mangle, ...)` として指定しました．関数の中身は Windows 版と同様です．

### "Error: variable p is shadowing variable..."

```
dmd/src $ make -f posix.mak ddmd DFLAGS="-I/path/to/phobos -I/path/to/druntime/import -L-L/path/to/lib64 -L--export-dynamic" MODEL_FLAG=-L-L/path/to/lib64
...
cppmangle.d(332): Error: variable p is shadowing variable cppmangle.CppMangleVisitor.cpp_mangle_name.p
posix.mak:519: ターゲット 'ddmd' のレシピで失敗しました
make: *** [ddmd] エラー 1
```
これはそのままですね．該当箇所のスコープで使われている `p` を，別の名前に置き換えました．

### ddmd のバイナリ生成はできた！
```
dmd/src $ make -f posix.mak ddmd DFLAGS="-I/path/to/phobos -I/path/to/druntime/import -L-L/path/to/lib64 -L--export-dynamic" MODEL_FLAG=-L-L/path/to/lib64
...
dmd/src $ ls ddmd
ddmd
dmd/src $ ./ddmd
DMD v2.067 DEBUG
DMD64 D Compiler v2.067
Copyright (c) 1999-2014 by Digital Mars written by Walter Bright
Documentation: http://dlang.org/
Config file: (null)
Usage:
  dmd files.d ... { -switch }
...
```
ビルドできたよ！引数なしならちゃんと動くよ！やったね！

# 早速コンパイル！
以下のプログラムをコンパイルしてみます．

```d:sample.d
module sample;
import std.stdio;

void main() {
    writeln("Hello!");
}
```

```
dmd/src $ cp ddmd ~
dmd/src $ cd
$ cat dmd.conf
[Environment64]
DFLAGS=-I/path/to/phobos -I/path/to/druntime/import -L-L/path/to/lib64 -L--export-dynamic
$ ./ddmd sample.d
DMD v2.067 DEBUG
zsh: segmentation fault (core dumped)  ./ddmd sample.d

```
おや？

# どこがおかしいのか
GDBを使って調べると， `root.rmem.Mem.realloc` 辺りでこけている様子．
ちゃんとコードを読まないと解決は難しそう．

# 終わりに
後は偉い人達に任せました！ビルドできたら記事書いてくれると私が喜びます！



## おまけ
### Q. DDMDってフルスクラッチで書かれてるの？
**A. 違うよ． ビルド時に，C++ で書かれた dmd のソースをD言語に変換してるよ．**

`make ddmd` のビルドプロセスは，大まかには:

- C++のソースをD言語に変換するツール `magicport/magicport2` のビルド
- `magicport/magicport2` を使って dmd のソースをD言語に変換 (`root/rmem.d`等の一部例外あり)
- 変換したD言語のソースをビルドして ddmd を生成

という大変ロマンにあふれた構成になっています．

### Q. ddmdのビルドに，インストール済みの dmd は使えないの？
A. 環境変数 `DC` に，インストール済みの dmd を設定すればそちらを使ってくれます．
注意点としては， ddmd のビルドに `id.c` と `impcnvtab.c` が必要にもかかわらず makefile に記述がないため，以下のようにこれら２つもターゲットとして指定する必要があります．

```
$ make -f posix.mak id.c impcnvtab.c ddmd ...
```
また，DCにインストール済みの dmd を指定した場合でも dmd.conf は見てくれないようなので，DFLAGS等の指定は必要です．