こんにちは。
ついに2.094.0がリリースされましたね! なんかアナウンスないけど…←されました!!
さて、このリリースには私の携わったdubの改良があります。それがx86_omf
というアーキテクチャ指定。
この記事ではどうやって使うのかというところから、なぜそれが必要になったか、という点について触れたいと思います。
x86_omfとは何ぞや
D言語のWindowsのdmdコンパイラでは、32bitと64bitのバイナリをコンパイルすることができますが、その中でも32bitには、MSVC(Microsoft Visual Studio)のランタイムを使うものと、DegitalMars謹製のランタイムを使うものが2種類あります。
まあ、性能面で言えばMSVCのランタイム一択なのですが、DigitalMars製のランタイムがデフォルトなのです。ちなみに64bitは選択の余地なくMSVCのランタイムを使用します。
以下にWindowsでmain.dから各種バイナリを得る方法を記載します
dmd main.d
dmd -m32mscoff main.d
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ビルド時にコマンドライン引数で指定する
dub build -a=x86_omf
または
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種類それぞれをアーキテクチャに応じて分けて指定する必要があります。その例が以下。
{
"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アプリケーションをビルドする際にコンソール画面がでなくなるようにする設定です。
{
"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
です。
{
"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
の設定を調べるのは大変でした。
{
"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倍必要で面倒くさいことこの上ない。
{
"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
の指定も必要ありません。
{
"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
に対応した書き方をしていないので無理だが、いずれ上記のようになる(ことが期待できる)
最後に
今回私は上記のような複雑な設定に嫌気がさし、プルリクエストを出したところ、議論もなくすぐにマージされたのでした。(あとでChagneLog必要じゃない?という話が出たくらい)
ものは言ってみるものだなぁ。
今回の変更によって、全部のコンパイラ・アーキテクチャに対応させるのもそれほど難しくなくなったので、ぜひ活用してみてくださいね。