ちょっと便利なdubパッケージの作り方紹介（copyFilesでWindowsでも快適な開発をする）

# はじめに
今回は、主にWindowsでD言語をお使いの皆さんにお伝えしたい超絶便利(個人の感想です)なdubパッケージの使い方紹介です。

ざっくり言ってしまうと、一切ソースコードを含まない「作業自動化を目的としたdubパッケージ」の作り方です。

別に環境に関係なく便利なので、ぜひ色々応用してもらえればと思います。


# TL;DR
- Windowsでderelict系のライブラリを使うのがちょっと大変だったよ
- dubパッケージは別にソースファイルを含んでいなくても作れるよ
- `dub.json`で`copyFiles`を設定すると「リソースだけのパッケージ」が作れるよ
  - `preGenerateCommands`を使えば任意のコマンドが使えて作業が自動化できるよ
  - Windowsでだけ行う設定は`copyFiles-windows-x86_64`みたいに書くよ
- 使うときは作ったパッケージを参照してビルドするだけだよ
- 細かい設定を忘れたらこの辺を見てね
 - [http://code.dlang.org/package-format?lang=json#build-settings](http://code.dlang.org/package-format?lang=json#build-settings)
- 以下の記事も参考になるよ
  - [ローカルのdubプロジェクトに依存するdubプロジェクト](https://qiita.com/kotet/items/4ef5039d17c521ec5dc7)
  - [D言語でdubを使ってビルド時に、最新のコミットのハッシュをバイナリに組み込む](https://qiita.com/alpha_kai_NET/items/7b6c888646f57cd8a409)
- 慣れるとすごく便利だよ


# 状況
昔の私「derelict-sdl2使ってプログラム書いたー！動いたー！ヤッター！」
今の私「derelict-sdl2使うかー、DLLどこだっけ…？パッケージもDLLも更新されてるな！これか！？⇒残念ランタイムエラーです😇」

- Windowsで開発をしていて、`derelict-sdl2`とかを使いたいときに毎回DLLを探してくるのが面倒だった
- `derelict-*`と実際のOSSのバージョン対応付けを忘れ、違うバージョンのDLL放り込みつらい思いをした
- 二度とこんな思いはすまいと自動化を決意した


# 解決方法
`derelict-*`を参照しつつ、実際のDLLを含んだ「設定オンリーのdubパッケージ」を作って対処しました。
（.NET界隈だとNuGetのメタパッケージ、Microsoft.AspNetCore.Allとかあのあたりと同じようなもの）

そして、使うときはこのパッケージを参照するだけで、目的のパッケージが参照され、なおかつビルド時にちょうど良いDLLが自動でコピーされてくる、という寸法です。

このパッケージは主にderelictの参照バージョン固定と対応したDLLだけが目的なので、自身のソースコードは一切含んでいません。

なお、ライセンスとかバイナリ配布の兼ね合いを色々調べるのが面倒だったので、実際に公開することはしていません。
以下は、そのあたり気にしなくて良いローカルパッケージにする方法です。
（derelict公式で対応してもらえるとありがたいのだけど…）


# 手順
細かく説明すると長くなりそうなので、画像ファイルを扱うFreeImageと、そのD言語用バインディングである`derelict-fi`を扱った例について、作成手順をざっくりまとめます。

1. パッケージを作る
2. DLLを用意する
3. 設定ファイルを書く
4. ローカルパッケージとして登録する
5. 使うプロジェクトで参照設定をする

⇒ 参照してビルドするとDLLがコピーされてる！快適！

## 1. パッケージを作る
derelict-fiのDLLを用意するということで、`derelict-fi-dll`というパッケージにします。
適当なプロジェクトを置くディレクトリを用意して、そこをカレントにして以下のコマンドを実行します。
Windowsなのでコマンドプロンプトを起動してやります。
大体いつものやつです。

```bash
mkdir derelict-fi-dll
cd derelict-fi-dll
dub init -n
```

## 2. DLLを用意する
dubのパッケージ一覧から使うパッケージを見繕って、対応の取れたライブラリのバージョンを確認後、DLLを拾ってきます。
もちろん自分でビルドしてDLLを作るところからやってもOKです。~~大抵面倒なのでやりません。~~

今回の例では、`derelict-fi@2.0.3`が安定版なので、それに対応した`FreeImage 3.17`を使います。
derelict系はトップ画面に大体依存バージョンが書いてあるので親切です。

- FreeImage DLL -- Download FreeImage 3.17.0 [WIN32/WIN64]
  - http://downloads.sourceforge.net/freeimage/FreeImage3170Win32Win64.zip
  - 中に`Dist`フォルダ、更に`x32`と`x64`があり、両方に`FreeImage.dll`が入っています（x32…？）
    - それぞれdubの`--arch`で言うところの`x86`向けと`x86_64`向けです

ダウンロードしたら以下のパスに配置します

* `derelict-fi-dll/lib/win32`に`x32/FreeImage.dll`
* `derelict-fi-dll/lib/win64`に`x64/FreeImage.dll`

## 3. 設定ファイルを書く
dub.jsonができているはずなので、以下のように設定します。
（authorsほかは削りました）

ついでにsourceフォルダは不要なので丸ごと消し去ります。

```json:dub.json
{
	"name": "derelict-fi-dll",
	"targetType": "none",
	"dependencies": {
		"derelict-fi": "~>2.0.3"
	},
	"copyFiles-windows-x86": [
		"lib/win32/FreeImage.dll"
	],
	"copyFiles-windows-x86_64": [
		"lib/win64/FreeImage.dll"
	]
}
```

### ポイント
- `dependencies`に`~>`でバージョン指定して参照設定を書く
  - 互換性が壊れるのが怖いのでパッチバージョンだけ上げて良い設定にする
- `copyFiles-windows-x86``copyFiles-windows-x86_64`でアーキテクチャごとにDLLを変える
  - ハイフンで環境に合わせた条件を設定することができ、マッチした項目しか適用されなくなる
- `"targetType": "none"`にしてビルドしないようにする（任意）

## 4. ローカルパッケージとして登録する
GitHubやdubで公開しなくても参照できるようにします。
詳細は以下の記事が参考になります。

- [ローカルのdubプロジェクトに依存するdubプロジェクト](https://qiita.com/kotet/items/4ef5039d17c521ec5dc7)

dubの`add-local`コマンドを使って以下のようにすれば良いです。

```bash
cd derelict-fi-dll
dub add-local .
```

実行後、`dub list`コマンドを実行して、`derelict-fi-dll`というのが一覧に表示されていれば成功です。


## 5. 使うプロジェクトで参照設定をする
参照するプログラムをdub initから作り、dub.jsonに以下のような記述を追加します。

なお、ローカルパッケージでもGitのタグバージョンが指定できるのですが、
その辺やらずにadd-localする場合は`"*"`を指定しないといけません。（以前は`~master`だった）

```json:dub.json
{ 
  "dependencies": {
    "derelict-fi-dll": "*"
  }
}
```

あとは適当にビルドすれば適切なDLLがコピーされる、という状況になります。
`--arch`を切り替えたりLDCを使っても大丈夫です。

```bash:どれでも良い
dub build
dub build --arch=x86_64
dub build --compiler=ldc2
```


# 発展形
今回の例の類似手法としては、画像処理のサンプルにレナさん画像をコピーしてくるとかがあります。案外便利。

また、copyFile以外にも、dub.jsonに設定できる項目として、`preBuildCommands`や`postBuildCommands`、`preGenerateCommands`というのもあります。

これらを使えばビルド時に任意のコマンドを実行できるのですが、例としては以下URLなどが分かりやすいです。

- [D言語でdubを使ってビルド時に、最新のコミットのハッシュをバイナリに組み込む](https://qiita.com/alpha_kai_NET/items/7b6c888646f57cd8a409)


なお、`Build`は「実際にビルドするとき」、`Generate`は「常に実行する」という程度の違いです。
（dubは前回のビルド結果をキャッシュしているので）

`preBuildCommands`でddoxなどを使ったドキュメントの更新とか設定すると色々と捗るかもしれませんね！

以上です！