Qtのビルドではqmakeというツールを使います。最近はCMakeにも対応しましたが、今のところメインのビルドツールはいまだにqmakeっぽいです。Qt Creatorでプロジェクトを作ると作られるのがqmakeで使えるプロジェクトファイルです。
qmakeも、CMakeやconfigureスクリプトのように、プラットフォームごとのMakefile(gnuのMakefileや、nmake)を生成して、最終的にはそちらがビルドを行います。
プラットフォームの差分の吸収の仕方
プラットフォームの差の吸収の仕方はビルドツールによって作戦がまちまちです。
ツール | ヘッダの有無 | OS名で選択 |
---|---|---|
qmake | ☓ | O |
configure | O | X |
CMake | O | O |
autotoolsで作るconfigureスクリプトの場合は、ヘッダファイルがあるかどうか、実際にビルドしてみて成功するかどうかで判断します。結果はconfig.hみたいなヘッダファイルに、#define文の形で埋め込まれます。プログラム側ではプリプロセッサを使って実際のロジックを分岐したり読み込むヘッダファイルを変えたりします。コンパイラの名前などで分岐させることもできなくもないと思いますが、あまり見たことがありません。歴史的経緯でプラットフォームの栄枯盛衰が激しかった時代だったし、個人では買えないワークステーションなども多かったので有無で判断というパッシブな使われ方がメインになったのだと思います。ソースコード側にも環境に対応するプリプロセッサが氾濫します。
qmakeの場合は、実際にビルドするのではなく、プラットフォーム名を使って分岐させます。環境の差異はヘッダファイルに書き込まれるのではなく、コンパイラオプションとして渡されます(そういうMakefileが生成されます)。プリプロセッサマクロを使って#ifdefで処理を分けることも可能ですが、どちらかというとOSごとにファイルを分けて、コンパイル対象のファイルを選択する方法がメジャーな気がします。ソースコードは比較的きれいに保たれます(ロジック以外の余計な要素が減るという意味で)。
# 行に対してOS設定
win32:DEFINES += USE_WINSOCK2
# ブロックに対してOS設定
unix:!macx {
INCLUDEPATH += ./unix_stuff
SOURCES += pthread_code.cpp
}
CMakeは両方サポートしています。
# ライブラリの有無でチェック
FIND_PACKAGE(ZLIB)
IF (ZLIB_FOUND)
INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIRS})
LINK_LIBRARIES(${ZLIB_LIBRARIES})
ENDIF()
# プラットフォーム名でチェック
IF (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
SET(CMAKE_C_FLAGS "-std=c99 -D_POSIX_C_SOURCE=200112L -D__EXTENSIONS__ -D_POSIX_PTHREAD_SEMANTICS ${CMAKE_C_FLAGS}")
ENDIF()
IF (WIN32 AND NOT CYGWIN)
SET(CMAKE_C_FLAGS_DEBUG "-D_DEBUG")
ENDIF ()
qmakeはできることが少ないですし、一つのプロジェクトファイルでビルド対象が1つだけなので、構造はシンプルで慣れれば処理の流れは分かりやすいです。変数はスカラ変数ではなくて集合なので、要素を足したり引いたりが+と-の演算子でできます。変数の値をループしたり、コンソールに内容を表示したりもできます。
qmakeではどのような名前が使えるの?
Qtはmkspecsというフォルダがあり、サポートしているすべての環境の固有情報のデータベースとなっています。使用するコンパイラから何からが書かれています。C++を使っていてVisual C++以外の環境でも開発をしていれば、一度はビルドツールを作りたいと夢見ると思いますが、その夢を打ち砕いた「バリエーションが多すぎる」という問題を力技で解決しています。
たぶん、ここあたりを元に生成しているのだと思います。実際には、Qt/5.3をインストールしたとすると、~/Qt/5.3/clang_64/mkspecsとか、C:\Qt\5.3\win32-msvc2013\mkspecsといったフォルダを見るとサポート対象の設定が入っています。
qmakeの説明を見ると、プラットフォームの名前のリストは含まれておらず、unix, win32, macxを使えとだけ書かれています。実際に使える名前は、mkspecs内のqmake.confが書かれたすべてのフォルダです。しかも、サポートされている共通のグループ名というものも存在します。親のグループ名は、qmake.confを見れば次に見に行くべきフォルダ名が書かれています。
ここに書かれたPythonスクリプトでこのフォルダを解析したのが以下のリストです。5.3で対応している環境名です。これの見方ですが、先頭のaixという名前からどう処理されるか見ていきます。このaixには4つの環境が含まれています。aix:と書くと、これらの4つのすべての場合にマッチします。aix-g++-64と書くとその環境時にしかマッチしなくなります。uniXやPOSIX, win32が数多くのものにヒットしているのが分かります。何はともあれ、これで必要とする環境名を正確に記述することができるようになりました。昨日書いたパッケージマネージャで、OSごとの差分吸収はqmakeに揃えた方がいいよね?あれ?使える名前のリスト公開されてないよ?というところからが出発点となってできたネタです。
-
aix:
aix-g++, aix-g++-64, aix-xlc, aix-xlc-64 -
android:
android-g++ -
blackberry:
blackberry-armle-v7-qcc, blackberry-armv7le-qcc, blackberry-x86-qcc -
bsd:
freebsd-g++, freebsd-g++46, freebsd-icc, netbsd-g++, openbsd-g++ -
cygwin:
cygwin-g++ -
darwin:
darwin-g++, macx-clang, macx-clang-32, macx-g++, macx-g++-32, macx-g++40, macx-g++42, macx-icc, macx-ios-clang, macx-llvm -
freebsd:
freebsd-g++, freebsd-g++46, freebsd-icc -
hpux:
hpux-acc, hpux-acc-64, hpux-acc-o64, hpux-g++, hpux-g++-64, hpuxi-acc-32, hpuxi-acc-64, hpuxi-g++-64 -
hurd:
hurd-g++ -
ios:
macx-ios-clang -
irix:
irix-cc, irix-cc-64, irix-g++, irix-g++-64 -
linux:
android-g++, linux-arm-gnueabi-g++, linux-clang, linux-clang-libc++, linux-cxx, linux-g++, linux-g++-32, linux-g++-64, linux-g++-maemo, linux-icc, linux-icc-32, linux-icc-64, linux-kcc, linux-llvm, linux-lsb-g++, linux-pgcc -
lynxos:
lynxos-g++ -
mac:
darwin-g++, macx-clang, macx-clang-32, macx-g++, macx-g++-32, macx-g++40, macx-g++42, macx-icc, macx-ios-clang, macx-llvm -
macx:
darwin-g++, macx-clang, macx-clang-32, macx-g++, macx-g++-32, macx-g++40, macx-g++42, macx-icc, macx-llvm -
maemo:
linux-g++-maemo -
mingw:
win32-g++ -
netbsd:
netbsd-g++ -
openbsd:
openbsd-g++ -
osx:
darwin-g++, macx-clang, macx-clang-32, macx-g++, macx-g++-32, macx-g++40, macx-g++42, macx-icc, macx-llvm -
posix:
aix-g++, aix-g++-64, aix-xlc, aix-xlc-64, android-g++, blackberry-armle-v7-qcc, blackberry-armv7le-qcc, blackberry-x86-qcc, cygwin-g++, darwin-g++, freebsd-g++, freebsd-g++46, freebsd-icc, hpux-acc, hpux-acc-64, hpux-acc-o64, hpux-g++, hpux-g++-64, hpuxi-acc-32, hpuxi-acc-64, hpuxi-g++-64, hurd-g++, irix-cc, irix-cc-64, irix-g++, irix-g++-64, linux-arm-gnueabi-g++, linux-clang, linux-clang-libc++, linux-cxx, linux-g++, linux-g++-32, linux-g++-64, linux-g++-maemo, linux-icc, linux-icc-32, linux-icc-64, linux-kcc, linux-llvm, linux-lsb-g++, linux-pgcc, lynxos-g++, macx-clang, macx-clang-32, macx-g++, macx-g++-32, macx-g++40, macx-g++42, macx-icc, macx-ios-clang, macx-llvm, netbsd-g++, openbsd-g++, qnx-armle-v7-qcc, qnx-armv7le-qcc, qnx-x86-qcc, sco-cc, sco-g++, solaris-cc, solaris-cc-64, solaris-cc-64-stlport, solaris-cc-stlport, solaris-g++, solaris-g++-64, tru64-cxx, tru64-g++, unixware-cc, unixware-g++ -
qnx:
blackberry-armle-v7-qcc, blackberry-armv7le-qcc, blackberry-x86-qcc, qnx-armle-v7-qcc, qnx-armv7le-qcc, qnx-x86-qcc -
sco:
sco-cc, sco-g++ -
solaris:
solaris-cc, solaris-cc-64, solaris-cc-64-stlport, solaris-cc-stlport, solaris-g++, solaris-g++-64 -
tru64:
tru64-cxx, tru64-g++ -
unix:
aix-g++, aix-g++-64, aix-xlc, aix-xlc-64, android-g++, blackberry-armle-v7-qcc, blackberry-armv7le-qcc, blackberry-x86-qcc, cygwin-g++, darwin-g++, freebsd-g++, freebsd-g++46, freebsd-icc, hpux-acc, hpux-acc-64, hpux-acc-o64, hpux-g++, hpux-g++-64, hpuxi-acc-32, hpuxi-acc-64, hpuxi-g++-64, hurd-g++, irix-cc, irix-cc-64, irix-g++, irix-g++-64, linux-arm-gnueabi-g++, linux-clang, linux-clang-libc++, linux-cxx, linux-g++, linux-g++-32, linux-g++-64, linux-g++-maemo, linux-icc, linux-icc-32, linux-icc-64, linux-kcc, linux-llvm, linux-lsb-g++, linux-pgcc, lynxos-g++, macx-clang, macx-clang-32, macx-g++, macx-g++-32, macx-g++40, macx-g++42, macx-icc, macx-ios-clang, macx-llvm, netbsd-g++, openbsd-g++, qnx-armle-v7-qcc, qnx-armv7le-qcc, qnx-x86-qcc, sco-cc, sco-g++, solaris-cc, solaris-cc-64, solaris-cc-64-stlport, solaris-cc-stlport, solaris-g++, solaris-g++-64, tru64-cxx, tru64-g++, unixware-cc, unixware-g++
unixware:
unixware-cc, unixware-g++
win32:
win32-g++, win32-icc, win32-msvc2005, win32-msvc2008, win32-msvc2010, win32-msvc2012, win32-msvc2013, wince60standard-armv4i-msvc2005, wince60standard-x86-msvc2005, wince70embedded-armv4i-msvc2008, wince70embedded-x86-msvc2008, winphone-arm-msvc2012, winphone-arm-msvc2013, winphone-x86-msvc2012, winphone-x86-msvc2013, winrt-arm-msvc2012, winrt-arm-msvc2013, winrt-x64-msvc2012, winrt-x64-msvc2013, winrt-x86-msvc2012, winrt-x86-msvc2013 -
wince:
wince60standard-armv4i-msvc2005, wince60standard-x86-msvc2005, wince70embedded-armv4i-msvc2008, wince70embedded-x86-msvc2008 -
winphone:
winphone-arm-msvc2012, winphone-arm-msvc2013, winphone-x86-msvc2012, winphone-x86-msvc2013 -
winrt:
winphone-arm-msvc2012, winphone-arm-msvc2013, winphone-x86-msvc2012, winphone-x86-msvc2013, winrt-arm-msvc2012, winrt-arm-msvc2013, winrt-x64-msvc2012, winrt-x64-msvc2013, winrt-x86-msvc2012, winrt-x86-msvc2013
明日はhermit4さんでQtConcurrentです。Qtにはマルチスレッドを実現する方法が主に3つ(QThread, QThreadPool, QtConcurrent)用意されていますが、一番高級でリッチなやつですね。愉しみです。