premake4によるライブラリ管理

Windows上のC++で開発をする場合、わりと依存ライブラリの調達がネックになる。単一のライブラリにだけに依存している場合は使うライブラリが提供しているdllをそのまま組み込めばよかったりするのだが、スタティックライブラリで単一のexeにしたかったり、出所の違う(コンパイルオプションとかコンパイラが違う)dllを組み合わせるようになってくると事態は悪化する。欲しいバイナリが無かったりリンクに失敗する。
解決策としては、全部を同じオプションでソースからビルドすればいい。

autotoolsで頑張る

Windowsでもconfigureでやりきるという方向性。
cygwinもしくはmsysの上にあらゆるライブラリのconfigure, make, make installが動く環境を構築するわけですが、茨の道です。やってみると楽しかったりするけど、msysでsdlとかgtkとかOCamlをビルドしてみた経験から言うと、膨大な時間がかかる。修正無しでビルドできるときもありますが、msysだとlibtool周りが一筋縄でいかないことが多い。cygwin前提のものとmsysでもOKのものがあり、パスの扱いでドライブレターがあるとだめな時とそうじゃないときがあったりでconfigure実行後にlibtoolを文字列置換したりとかの多数のバッドノウハウが必要でした。同様にmingwのmake(msys.dllもしくはcygwin.dllとリンクしていない)じゃないとだめとかカオス。

vcで頑張る

ライブラリでプロジェクトが提供されているときはそれ使えば問題ないが、そもそも無かったりする。ランタイム設定(/MD、/MT、/LDなど)をチェックするのがめんどくさかったり、サブプロジェクトがいっぱいあってどれが必要かわからないときがある。IDE苦手なのだ・・・。

そこでpremake4

premake4だとmingw, vc両対応にできるし設定は単なるテキストなので取り回しが楽になる。ただし、滅多にライブラリ側でpremake4の設定が提供されていることはない(bulletは2.79からpremake設定が提供されている)ので新規にpremake4.luaを作成する必要がある。新規に作成する場合は、ひととおりの設定を作らなければならないが、テンプレートを用意しておいてプロジェクト毎に違うところを記入するだけなので慣れれば簡単な作業になる。プロジェクト毎に違うのは、名前とソースの一覧くらい。ソースはディレクトリの中身を全部入れるときは"**.cpp"とか書けるので変える必要がないときもある。あと、premake4を使えばdebugビルドとreleaseビルドを同じディレクトリに違う名前で出力する(debug版にdのsufixをつけるとか)か、違うディレクトリに同じ名前で出力するかを自分の好みで制御できる。ソリューションの定義で出力場所を設定しておいてサブプロジェクトをインクルードするようにすれば設定の共通化ができるので、出力場所やUNICODE defineの一括定義なども柔軟に設定できる。

で、実践したところこんな感じになった。
https://bitbucket.org/ousttrue/onibi

  • Irrlichtのpremake4.lua設定を作成して、ライブラリ毎にスタティックライブラリを作成して最後にdllに合体するようにした。
  • bulletのpremake4を改造してデモと本体を別々にした。
  • bulletdemosで使うfreeglutのpremake4を作成
  • debugはrootディレクトリのdebugに、releaseはrootディレクトリのdebugに出力するようにした

など

今のところ、Windowsでのライブラリ管理はオールインワンのリポジトリを作ってpremake4で管理するのが手頃だと思う。部品は全部スタティックで最後に一個だけexeかdllにまとめるのが簡単に出来ます(ライセンス注意)。

premake4.lua sample

-- A solution contains projects, and defines the available configurations
solution "SolutionName"
configurations { "Release", "Debug" }

-- mingw
configuration "gmake"
do
  buildoptions { "-Wall" }
end

-- vs各バージョン
configuration "vs*"
do
  buildoptions { "/wd4996" }
end

configuration "Debug"
do
  defines { "DEBUG" }
  flags { "Symbols" }
  targetdir "debug"
end

configuration "Release"
do
  defines { "NDEBUG" }
  flags { "Optimize" }
  targetdir "release"
end

-- gamkeでかつdebug
configuration "gmake Debug"
do
    buildoptions { "-g" }
    linkoptions { "-g" }
end

-- windows上で実行時
configuration "windows"
do
    defines {
      "WIN32",
      "_WINDOWS",
      --"_USRDLL",
      --"_CRT_SECURE_NO_DEPRECATE",
      --"_IRR_WCHAR_FILESYSTEM",
    }
end

-- A project defines one build target
project "ProjectName"
--kind "WindowedApp"
--kind "ConsoleApp"
--kind "SharedLib"
kind "StaticLib"
language "C"
files { 
    '*.c', '*.h',
}

configurationのあとの
do, endはluaのブロック。
インデントのために入れているだけで意味は無い。
configurationはブロックと無関係に次のconfigurationが出てくるまで有効。