dub v1.23.0のx86_omfについて

こんにちは。
[ついに2.094.0がリリースされましたね！](https://qiita.com/lempiji/items/eaf99abc35fe0a55a3d1) <sub>
なんかアナウンスないけど…
</sub>←[されました！！](https://forum.dlang.org/thread/yiwfozsqnbakmbgzriiq@forum.dlang.org)

さて、このリリースには私の携わったdubの改良があります。それが[`x86_omf`というアーキテクチャ指定](https://dlang.org/changelog/2.094.0.html#add_x86_omf)。
この記事ではどうやって使うのかというところから、なぜそれが必要になったか、という点について触れたいと思います。

# x86_omfとは何ぞや
D言語のWindowsのdmdコンパイラでは、32bitと64bitのバイナリをコンパイルすることができますが、その中でも32bitには、MSVC(Microsoft Visual Studio)のランタイムを使うものと、DegitalMars謹製のランタイムを使うものが2種類あります。
まあ、性能面で言えばMSVCのランタイム一択なのですが、DigitalMars製のランタイムがデフォルトなのです。ちなみに64bitは選択の余地なくMSVCのランタイムを使用します。

以下にWindowsでmain.dから各種バイナリを得る方法を記載します

```sh:32bit-DigitalMarsランタイム
dmd main.d
```

```sh:32bit-MSVCランタイム
dmd -m32mscoff main.d
```

```sh:64bit-MSVCランタイム
dmd -m64 main.d
```

ビルド中、MSVCのランタイムを使用する場合は、内部のフォーマットとしてCOFF形式のライブラリとリンクします。※正確にはCOFF形式のオブジェクトを集めた静的リンクライブラリ
ではDigitalMarsのものはというと、OMF形式のライブラリとリンクします。
ここまで説明するとお分かりいただけると思いますが、x86_omfというのは、dubでビルドする際にx86(32bit)のOMF形式のライブラリとリンクしてビルドするよ、というアーキテクチャ指定なのです。

| ビット数 | ランタイム  | オブジェクト形式 | 使用するリンカ  | コンパイル指定     | アーキテクチャ |
|:-------:|:-----------:|:--------------:|:--------------:|:----------------:|:-------------:|
| 32bit   | DigitalMars | OMF            | optlink.exe    | -m32 または無指定 | x86_omf       |
| 32bit   | MSVC        | COFF           | (MSの)link.exe | -m32mscoff       | x86_mscoff    |
| 64bit   | MSVC        | COFF           | (MSの)link.exe | -m64             | x86_64        |




# x86_omfの使い方
x86_omfの使い方はx86_mscoffと同じです。(x86_mscoffは従来からあった)

## dubビルド時にコマンドライン引数で指定する

```sh:今回追加された仕様
dub build -a=x86_omf
```

または

```sh:従来からある仕様
dub build -a=x86
```

上記のようにすることで、明示的にDigitalMarsの(OMF形式の)ランタイムを使用するように指定できます。
ちなみに、dubのデフォルト(無指定)では、ビルド環境のビット数のMSVCランタイムが使用されるようになっています。(64bit Windowsを使ってビルドすればx86_64、32bit Windowsを使ってビルドすればx86_mscoffが選択される)

この使い方は従来とほとんど変わらないので割愛します。


## dubのプロジェクト設定ファイル(dub.json/dub.sdl)に記載する
問題はこちらです。JSON形式で設定ファイルを記載した場合で説明します。

今回のアーキテクチャ指定は主に`libs`や`sourceFiles`でライブラリとのリンク指定を行う際に効果を発揮します。
先述の通り、リンクすべきライブラリの種類は3種類あるので、3種類それぞれをアーキテクチャに応じて分けて指定する必要があります。その例が以下。

```json:dub.json
{
  "sourceFiles-windows-x86_omf-dmd":    ["libs/windows-x86_omf/test.lib"],
  "sourceFiles-windows-x86_mscoff-dmd": ["libs/windows-x86_mscoff/test.lib"],
  "sourceFiles-windows-x86_64-dmd":     ["libs/windows-x86_64/test.lib"]
}
```

上記のようにすることで、それぞれ別のディレクトリに格納した3種類のライブラリをアーキテクチャ別に指定することができます。

また、先述の表にある通り、ライブラリの種類が異なれば、使用するリンカも異なります。OMFはDigitalMars製のリンカを、MSCOFFはMSVCのリンカを使用する必要があります。リンカが異なれば、リンカに指定するオプションも異なります。そう、`lflags`なんかにも役立つのです。以下の指定はGUIアプリケーションをビルドする際にコンソール画面がでなくなるようにする設定です。

```json:dub.json
{
  "lflags-windows-x86_omf-dmd":    ["/exet:nt/su:windows:6.0"],
  "lflags-windows-x86_mscoff-dmd": ["/SUBSYSTEM:WINDOWS", "/ENTRY:mainCRTStartup"],
  "lflags-windows-x86_64-dmd":     ["/SUBSYSTEM:WINDOWS", "/ENTRY:mainCRTStartup"]
}
```

# x86_omfはなぜ必要なのか？
ここまでの説明でお分かりいただけると思いますが、3種類のアーキテクチャ(ビット数×オブジェクト形式)に対しては、3種類のアーキテクチャ指定ができるのが自然です。が、従来は`x86_omf`がありませんでした。従来はどのようにして上記問題を解決していたのでしょうか？答えは`configurations`です。

```json:dub.json
{
  "configurations": [
    {
      "name": "application",
      "lflags-windows-x86-dmd":    ["/exet:nt/su:windows:6.0"],
      "lflags-windows-x86_64-dmd": ["/SUBSYSTEM:WINDOWS", "/ENTRY:mainCRTStartup"]
    },
    {
      "name": "application_mscoff",
      "lflags-windows-x86-dmd":    ["/SUBSYSTEM:WINDOWS", "/ENTRY:mainCRTStartup"]
    }
  ]
}
```

上記のようにmscoff用に`"configurations"`で分けて、

```
dub build -a=x86 -c=application
dub build -a=x86_mscoff -c=application_mscoff
dub build -a=x86_64 -c=application
```

のようにして3種類のアーキテクチャに対応していたのです。

できるなら必要ないじゃないか？いいえ、問題はこれにとどまりません。
依存関係がある場合はもっと複雑になります。というか、依存先がこのことを知ってて`configurations`を分けてくれているかどうかはライブラリの作者依存ですし、プロジェクト固有にある`configurations`の設定を調べるのは大変でした。

```json:dub.json
{
  "dependencies": {
    "vibe-d": "~>0.8.0"
  }
  "configurations": [
    {
      "name": "application",
      "lflags-windows-x86-dmd":    ["/exet:nt/su:windows:6.0"],
      "lflags-windows-x86_64-dmd": ["/SUBSYSTEM:WINDOWS", "/ENTRY:mainCRTStartup"],
    },
    {
      "name": "application_mscoff",
      "lflags-windows-x86-dmd":    ["/SUBSYSTEM:WINDOWS", "/ENTRY:mainCRTStartup"]
      "subConfigurations": {
        "vibe-d": "win32_mscoff"
      }
    }
  ]
}
```

というか、まじめに対応しようとすると普通に`configurations`が2倍必要で面倒くさいことこの上ない。

```json:dub.json
{
  "dependencies": {
    "vibe-d": "~>0.8.0"
  }
  "configurations": [
    {
      "name": "application",
      "lflags-windows-x86-dmd":    ["/exet:nt/su:windows:6.0"],
      "lflags-windows-x86_64-dmd": ["/SUBSYSTEM:WINDOWS", "/ENTRY:mainCRTStartup"],
    },
    {
      "name": "application_mscoff",
      "lflags-windows-x86-dmd":    ["/SUBSYSTEM:WINDOWS", "/ENTRY:mainCRTStartup"]
      "subConfigurations": {
        "vibe-d": "win32_mscoff"
      }
    },
    {
      "name": "library",
      "targetType": "library"
    },
    {
      "name": "library_mscoff",
      "targetType": "library",
      "subConfigurations": {
        "vibe-d": "win32_mscoff"
      }
    },
    {
      "name": "unittest",
      "dependencies": { "silly": "~>1.0.2" }
    },
    {
      "name": "unittest_mscoff",
      "dependencies": { "silly": "~>1.0.2" },
      "subConfigurations": {
        "vibe-d": "win32_mscoff"
      }
    }
  ]
}
```

`x86_omf`があれば以下のようにすっきりします。依存先の`configurations`の指定も必要ありません。

```json:dub.json
{
  "dependencies": {
    "vibe-d": "~>0.X.0"
  }
  "configurations": [
    {
      "name": "application",
      "lflags-windows-x86_omf-dmd":    ["/exet:nt/su:windows:6.0"],
      "lflags-windows-x86_mscoff-dmd": ["/SUBSYSTEM:WINDOWS", "/ENTRY:mainCRTStartup"]
      "lflags-windows-x86_64-dmd":     ["/SUBSYSTEM:WINDOWS", "/ENTRY:mainCRTStartup"],
    },
    {
      "name": "library",
      "targetType": "library"
    },
    {
      "name": "unittest",
      "dependencies": { "silly": "~>1.0.2" }
    }
  ]
}
```

※現時点では引き合いに出したvibe.dが`x86_omf`に対応した書き方をしていないので無理だが、いずれ上記のようになる(ことが期待できる)

# 最後に
今回私は上記のような複雑な設定に嫌気がさし、[プルリクエストを出した](https://github.com/dlang/dub/pull/1983)ところ、議論もなくすぐにマージされたのでした。(あとでChagneLog必要じゃない？という話が出たくらい)
ものは言ってみるものだなぁ。
今回の変更によって、全部のコンパイラ・アーキテクチャに対応させるのもそれほど難しくなくなったので、ぜひ活用してみてくださいね。