D言語環境構築 2018年版

# はじめに

D言語のリファレンス実装である[dmd](https://dlang.org/download.html)は、インストールが簡単で、気軽に扱える言語処理系になっています。
D言語に触ってみたいと思ったときは、とりあえずdmdをインストールするのがおすすめです。

(最近[GCCにD言語フロントエンドが取り込まれて](https://www.phoronix.com/scan.php?page=news_item&px=GCC-9-Merges-D-Language)、Linux等ではそちらの方が気軽に使えるようになっていくかもしれませんが……)

この記事では、dmdをインストールし、Visual Studio Codeで開発環境を構築する方法について解説します。

# dmdのインストール

さっそくdmdのインストールです。D言語コンパイラのインストールは、我々D言語erの間で**人生最良の行動**と呼ばれているものです。

あなたも是非すぐに**人生最良の行動**を実践しましょう。

## Linuxの場合

### `install.sh`の使用

まずは、[公式のインストールスクリプト](https://dlang.org/install.html)を使用するのがおすすめです。

以下のコマンドで最新安定版のdmdがユーザー個人の環境にインストールされます。ルート権限等は不要です。

```console
$ curl -fsS https://dlang.org/install.sh | bash -s dmd
```

ユーザー環境を汚さないよう、インストールしてもそのままではパス等が通っていません。環境変数等を`source`することにより、dmdが使えるようになります。

```console
$ source $(~/dlang/install.sh dmd -a)
(dmd-X.XXX.X)$ dmd --version #dmdが使えるようになった
Copyright (C) 1999-20XX by The D Language Foundation, All Rights Reserved written by Walter Bright
(dmd-X.XXX.X)$ dub --version #dub(後述)も入っている
DUB version X.XX.X, built on XXX  X 20XX
```

有効になった`dmd`へのパスは、`deactivate`で元に戻せます。

```console
(dmd-X.XXX.X)$ deactivate
$ #元にもどる
```

#### 参考記事

kotetさんによる詳しい解説があります。

[#dlang インストールスクリプトの使い方](https://blog.kotet.jp/2017/12/d-install-script/)

### パッケージマネージャを使用する

[公式ダウンロードページ](https://dlang.org/download.html)の下の方に各ディストリビューション向けのコマンドが載っています。こちらを参考にしてみてください。

## Macの場合

基本的にはLinuxの場合と同様に`install.sh`が使えます。

また、公式のdmgやHomebrewパッケージも存在します。やはり[こちら](https://dlang.org/download.html)を参照です。

## Windowsの場合

### Windows Installerを使用する

公式のWindows Installerが個人的にはおすすめです。

（わざわざインストールしたくない場合は、もちろん7zで固められたパッケージも使えますが、環境変数などの設定は手動です）

#### ランタイムライブラリの選択

Windows版インストーラーでは、途中でVisual Studioをインストールするか、またはMinGWを使用するかの選択肢が出てきます。
これは、D言語プログラムのビルド時にC言語のランタイムライブラリおよびリンカー(64bit版)が必要になるためで、いずれかのインストールが必要になります。

Visual Studioをインストールする予定がなければ、MinGWを使用で問題ありません。

![無題.png](https://qiita-image-store.s3.amazonaws.com/0/3735/a39a5053-8053-1902-37fe-4b057b3f1dd8.png)

以前はWindowsで64bit実行ファイルを作成するためにはVisual Studioがどうしても必要でした。（64bit対応版のCOFF形式オブジェクト用のリンカーが必要なため）
それが最近、[LLVMのリンカー(`lld-link`)を同梱することで不要になった](https://dlang.org/dmd-windows.html#linking)とのことです。

### 7zファイルをダウンロードして手動で展開する

Windows Installerとは別に、実行ファイルやライブラリ一式を7z形式で圧縮したダウンロードファイルも公開されています。
こちらでは環境変数の設定が手動になりますが、より環境に影響を与えずにdmdを使用できます。
この場合は当然Visual Studioなどのインストールはされず、MinGWの使用がデフォルトになります。

ダウンロード元は[Windows Installer版と同じ](https://dlang.org/download.html#dmd)(7zの方を選択)です。

#### PATHの設定

`dmd`や後述する`code-d`の実行に必要な設定は、環境変数`PATH`への`bin`ディレクトリの追記のみです。
msys2などを使用する場合も、@kari_techさんからコメントを頂いている通り`PATH`を通せば良いようです。

# D言語プロジェクトを`dub`で作る

D言語の各種インストーラー等には、パッケージマネージャーでありビルドツールである[DUB](https://code.dlang.org/)が同梱されていて、すぐ使えるようになっています。

DUBはちょうどJavaScriptにおける`npm`のような存在です。`dub`コマンドを使うと、簡単にD言語プロジェクトの作成・ビルド・実行・テストが行えます。

`npm`のようにライブラリ等の依存関係の管理も行えます。設定ファイル(テキスト形式の`dub.json`または`dub.sdl`)に使用ライブラリとバージョンを記載しておくことで、自動的にダウンロードおよびビルド時のリンクを行ってくれます。

## プロジェクトの作成

まずディレクトリを作成して、中で`dub init`を行います。
(`install.sh`を使った人は`source`を忘れないでください)

すると、コンソールでプロジェクトの初期化設定について聞かれます。あとで簡単に書き換えられるので、基本的にEnter連打で問題ありません。

```console
$ mkdir test
$ cd test
$ dub init
Package recipe format (sdl/json) [json]: #設定ファイルの形式。jsonで問題ないでしょう。
Name [test]: #プロジェクト名
Description [A minimal D application.]: #プロジェクトの説明
Author name [Cloud User]: #開発者名
License [proprietary]: #ライセンス。dmdと同じライセンスにする場合は BSL-1.0 とか
Copyright string [Copyright © 2018, D-MAN]: #著作権表記
Add dependency (leave empty to skip) []: #追加する依存ライブラリ
Successfully created an empty project in '/home/dman/test'.
Package successfully created in .
```

これで以下のファイルが生成されます。

```
$ find .
.
./source #ソースディレクトリ
./source/app.d #main関数のあるソース
./dub.json #設定ファイル。先ほどの選択内容が記載されている
./.gitignore #中間ファイル等の.gitignore
```

## Windowsで64bit版ビルドを行いたい場合

なおWindowsでは、32bit版実行ファイルのビルドがデフォルトになってしまっています。
そのままでは、64bit版のビルドをするためにコマンドラインオプションでの指定が必要です。
(`dub build --arch=x86_64`といった具合です)

もし64bit版ビルドをデフォルトにしたい場合は、設定ファイルの追加を行います。

[参考](https://github.com/dlang/dub-docs/pull/11)

以下のような`settings.json`を、

```json:settings.json
{
    "defaultArchitecture": "x86_64",
    "defaultCompiler": "dmd"
}
```

以下のいずれかに配置します。

* `%ProgramData%\dub\settings.json`
* `{dub実行ファイルのあるフォルダー}\..\etc\dub\settings.json`
* `%APPDATA%\dub\settings.json`

下のパスに置かれたものほど優先順位が高いです。

### Windowsの64bit版ビルドで`Native PDB Error`が出る場合

私の環境では以下のようなエラーが64bit版ビルドの時に発生しました。

```console
Native PDB Error: The entry already exists.  The specified module already exists`
```

これは、`dub.json`/`dub.sdl`の`lflags`に`/nopdb`を追加すると一応解消します。今後デバッガ関連で困るかもしれませんが……。

```json:dub.json
{
    // 中略
    "lflags": ["/nopdb"] // これを追加
}
```

### Windowsの64bit版ビルドで`NOLOGO.d`のエラーが出る場合

再現が行えていませんが、たまに`NOLOGO.d`のリンクエラーが発生するようです。
このエラーは、私の環境では再ビルド連打で直りました。

Windowsの64bitはどうもまだ鬼門ですね……。

## プロジェクトの実行

自動生成されたプロジェクトは`dub run`ですぐ動かせます。メッセージが出るだけですが……。

```console
$ dub run
Performing "debug" build using /home/dman/dlang/dmd-X.XXX.X/linux/bin64/dmd for x86_64.
test ~master: building configuration "application"...
Linking...
Running ./test 
Edit source/app.d to start your project.
```

`./source/app.d`の中身はHelloWorldになっています。見ればすぐわかるコードです。

```d:source/app.d 
import std.stdio;

void main()
{
	writeln("Edit source/app.d to start your project.");
}
```

## プロジェクトのテスト

D言語の気持ち良い点として、**ユニットテストを書くのがすごい楽**というのがあります。
なので、最初のうちからユニットテストの書き方を覚えておくととても捗ります。

### `unittest`を書く

`app.d`に早速ユニットテストを追加します。

```d:source/app.d
import std.stdio;

void main()
{
    writeln("Edit source/app.d to start your project.");
}

// 以下を追加
unittest
{
    assert(1 + 1 == 2);
}
```

C言語などと同様、`assert`の中に`bool`に評価される式を書きます。その式が`false`になると失敗と見なされ、テストがエラーになります。

今の所はきっと成功するはず……。

### `dub test`で実行する

このテストは`dub test`コマンドで実行できます。

```console
$ dub test
No source files found in configuration 'library'. Falling back to "dub -b unittest".
Performing "unittest" build using /home/centos/dlang/dmd-2.083.0/linux/bin64/dmd for x86_64.
test ~master: building configuration "application"...
Linking...
Running ./test 
Edit source/app.d to start your project.
```

先ほどと何も変わっていないのは成功した証拠です。
テストが失敗するようにしてみます。

```d:source/app.d
unittest
{
    assert(1 + 1 == 3); //よくある間違い
}
```

```console
$ dub test
No source files found in configuration 'library'. Falling back to "dub -b unittest".
Performing "unittest" build using /home/dman/dlang/dmd-X.XXX.X/linux/bin64/dmd for x86_64.
test ~master: building configuration "application"...
Linking...
Running ./test 
core.exception.AssertError@source/app.d(10): unittest failure
----------------
??:? _d_unittestp [0x43eed5]
source/app.d:10 void app.__unittest_L8_C1() [0x43dc80]
??:? void app.__modtest() [0x43e224]
??:? int core.runtime.runModuleUnitTests().__foreachbody2(object.ModuleInfo*) [0x446f17]
??:? int object.ModuleInfo.opApply(scope int delegate(object.ModuleInfo*)).__lambda2(immutable(object.ModuleInfo*)) [0x43ecde]
??:? int rt.minfo.moduleinfos_apply(scope int delegate(immutable(object.ModuleInfo*))).__foreachbody2(ref rt.sections_elf_shared.DSO) [0x44075d]
??:? int rt.sections_elf_shared.DSO.opApply(scope int delegate(ref rt.sections_elf_shared.DSO)) [0x4407ec]
??:? int rt.minfo.moduleinfos_apply(scope int delegate(immutable(object.ModuleInfo*))) [0x4406e9]
??:? int object.ModuleInfo.opApply(scope int delegate(object.ModuleInfo*)) [0x43ecb5]
??:? runModuleUnitTests [0x446ced]
??:? void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).runAll() [0x43f528]
??:? void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).tryExec(scope void delegate()) [0x43f4af]
??:? _d_run_main [0x43f417]
??:? main [0x43e249]
??:? __libc_start_main [0x361c5444]
1/1 unittests FAILED
Program exited with code 1
```

何か色々出てくるようになりましたね。
`core.exception.AssertError@source/app.d(10): unittest failure`の部分がユニットテスト失敗の箇所を指しています。

### `assert`にメッセージを付ける

このままだとわかりにくいので、メッセージを表示させてみます。

```d:source/app.d
unittest
{
    assert(1 + 1 == 3, "1 + 2が3にならなかった");
}
```

```console
$ dub test
No source files found in configuration 'library'. Falling back to "dub -b unittest".
Performing "unittest" build using /home/dman/dlang/dmd-X.XXX.X/linux/bin64/dmd for x86_64.
test ~master: building configuration "application"...
Linking...
Running ./test 
core.exception.AssertError@source/app.d(10): 1 + 2が3にならなかった
# 後略
```

**以上がD言語の`unittest`のほぼ全てです。**

`unittest`について、あとは、

* ソースコードの中に何回も書ける
* クラスや構造体の中にも書ける
* `///`で`unittest`直前にコメントを付けると、ドキュメントツール`ddoc`で生成したドキュメントにテストコード全体が載る
    * ドキュメントのサンプルコードの記載が`unittest`でできる

程度のことを知っておけば大丈夫だと思います。

他の言語のユニットテストツールにあるような、綺麗な結果表示とかカッチョいいassert構文とか何も無いですが、とにかく書きやすいです。
テストをどんどん書きながら楽しくコーディングしましょう。

# Visual Studio Codeを使ってみる

D言語用のIDEは何種類かあるのですが、どれも完成度という点で一抹の不安が残ります。
(そもそも、IDEが必須になるような大規模開発があまりなかったりします……)

しかし今年、[code-d](https://github.com/Pure-D/code-d)へのファンドが募集され、Visual Studio CodeとAtomでのサポートが改善されたようです。

私もごく少額を支援したりしたので、Visual Studio CodeでD言語を動かしてみたいと思います。

## Visual Studio Codeのインストール

まずは[ここ](https://code.visualstudio.com/)から普通にインストールします。

## 拡張機能パックのインストール

`code-d`を含むD言語拡張機能パックが公開されているようです。

コマンドラインで下記を実行するか、マーケットプレイスの画面で検索してインストールします。

```cmd
$ code --install-extension webfreak.dlang-bundle
```

## 再起動

拡張機能を有効化するために再起動し、適当なD言語プロジェクトを開いてみると、下記のようになります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/3735/b0ab7e3a-7d76-25b2-4ef1-b4bf088b6370.png)

ちゃんとシンタックスがハイライティングされたり、インテリセンス(と今どきも言うのだろうか……)されたりしますね。

## 確認されている問題

### 組み込みのタスクがうまく動かない場合がある

私のWindows環境だとなぜかバックグラウンドでdubが強制終了してしまったり、VSCodeのタスクで`code-d: dub build`できなかったりしました……。まあターミナルから直接`dub run`とか`dub test`と実行すれば何も問題ないですね！

### インストールしている`dub`と`code-d`の`dub`とのバージョン不一致

@mono_shooさんがコメントして下さった通り、一時期の`dub`と`code-d`のバージョン不一致で不具合が発生するケースがあるようです。
おそらく最新版では解消されていると思いますが、類似の事例があれば注意が必要です。
（おそらく過去バージョンのdubを再インストールするか、ビルドをcode-dだけで行う必要がある?）

# まとめ

IDEの完成度がやはりまだいまいちですが、少なくとも書く分には問題なくなってきたようです。
IDE好きな人は選択肢として相対的には十分ありだと思うので、Visual Studio Codeを試してみてください！

# ライセンス

<a rel="license" href="http://creativecommons.org/licenses/by/4.0/"><img alt="クリエイティブ・コモンズ・ライセンス" style="border-width:0" src="https://i.creativecommons.org/l/by/4.0/88x31.png" /></a><br />この 作品 は <a rel="license" href="http://creativecommons.org/licenses/by/4.0/">クリエイティブ・コモンズ 表示 4.0 国際 ライセンス</a>の下に提供されています。