はじめに
やや遅れてしまいましたが Qt Advent Calendar 2019 の 9 日目です。
昨日は、 @shin1_okada さんによる「Qt Creator で モデリングをしてみよう」でした。Qt Creatorは、プラグインにより機能をいろいろ追加できるように設計されており、実はインストールされているけれど有効になっていないプラグインもいくつかあったりします。興味がある方はプラグインをいろいろ有効にして試してみましょう。
実は、担当日は10日のつもりでいまして、すっかり準備をしていませんでした。ごめんなさい。
とりあえず、今日はCMakeについてできる範囲で簡単に説明して、明日(というかすでに本日ですが)、もう少し詳しく解説したいと思います・・・orz。
Qtのメタビルドシステム
Qtは、クロスプラットフォーム向けのアプリケーションフレームワークですが、コンパイラなどのツールチェーンは基本的に同梱されていません。ビルド環境は各プラットフォーム固有のものを利用するわけですが、ビルドシステムが異なるとそのためのレシピとなるファイル(プロジェクトファイルだったり、Makefileだったり)は異なっています。
そのため、Qt自体のビルドやQtを利用するアプリケーションのために、qmakeという独自のメタビルドシステムを提供されてきたのですが、qmakeはQt自体がほとんど利用できない状況でビルドできなくてはならないソフトウェアで、黎明期に作られ、コードを読む限り場当たり的に対処され続けてきたソフトウェアであるため、メンテナンス性に難がありninja等の最新のビルドシステムにも対応できておらず、メンテナも少ない状況だったようです。
そのため、巨大になったQtのビルドはqmakeから離れるべきだとの意見が主流となり、Qbsも含めて検討が進められたものの、Qbsを推し進めるためのさらなる投資がThe Qt Companyとしては難しかったこともあり、Qt6に関するディスカッションの中では、今後Qt自体をビルドするためのメタビルドシステムにはCMakeを採用しようという方針に決まったようです。
なお、qmake自体は非推奨化や廃止という情報は今のところ無く、QbsもThe Qt Companyが開発を停止したといってもコミュニティとしてはメンテナンスを維持していることから、qmake, Qbsなどを利用しているQtユーザー側があわててCMakeへ移行する必要は無いかと思います。とはいえ、Qt自体をビルドせざるを得なくなった場合にはCMakeを知っていないと困るときも出てくるかもしれません。qmake入門の著者としては複雑な心境ですが、そのようなわけで、CMakeについて少しだけ解説しておきます。
CMakeとは
CMakeは、アメリカ国立医学図書館の出資により医療系画像解析に利用されるITK(Insight Toolkit)のために開発されたクロスプラットフォーム向けのメタビルドシステムです。qmakeと同じく、クロスプラットフォーム向けに各種ビルドシステムのためのレシピファイルを生成するためのメタビルドシステムです。qmakeがQtのために開発されQtに同梱されているのに対し、CMakeはITKのためにと開発されましたが、メタビルドシステムとして独立のオープンソースプロジェクトとして単独で提供されています。
このため、LLVM, OpenCV, KDEをはじめ多数のプロジェクトで採用されています。
両者の特徴
qmake, CMake両者のソースコードを読むと感じるのは、qmakeは、Makefileを書くのが面倒だからとりあえず、最低限のことだけ書いたらMakefileができるようにしたいという意図からとりあえず作った感が強いのに対し、CMakeは、拡張性、メンテナンス性も含めてきちんと設計されて作られた感が強い実装になっています。
ユーザー視点で見ると、qmakeは(めんどくさいの嫌だから)必要最低限の変数に値を設定したプロジェクトファイルで動くよう実装されているのに対し、CMakeは少々長い名前のコマンドを書くことで変換できるというような実装になっています。
qmake | CMake | |
---|---|---|
レシピファイル | <dirname>.pro | CMakeLists.txt |
レシピの呼び方 | プロジェクトファイル | CMakeソースコード |
特徴 | 変数ベース | コマンドベース |
ターゲット | 1ファイル1ターゲット | 1ファイル複数ターゲット |
プロジェクト間連携 | 不可 | ターゲット名でリンク等可能 |
Qtのサポート | ◎ | △ |
Qt Creator対応 | ◎ | ファイル追加などの対応が微妙 |
カスタムコンパイラ | △ | ◎ |
Package作成 | △ | ◯ |
レシピファイルの違い
両者の差分としては、まず根本的なレシピの考え方が異なります。qmakeはSOURCESを始めとする変数に適切な値を設定していくことで、対応するアーキテクチャ設定ファイルなどから拾い上げた値を使いMakefileやVisual Studio/XCode向けのプロジェクトファイルなどを生成します。
これに対し、CMakeはコマンドと呼ぶ関数の引数でターゲットの情報、Define情報、インクルードパスなどをプログラムしていき、Makefileや各種プロジェクトファイルを生成していきます。
シンプルな例(Qtを利用しない場合)
思想の違いから、同じメタビルドシステムでもレシピの書きっぷりは大きく異なります。例えば、main.cppが1つきりのQtを利用しないアプリケーションをビルドするとしましょう。
CONFIG -= qt
SOURCES = main.cpp
qmakeでは、上記のようにシンプルにSOURCES変数にmain.cppを代入するだけです。デフォルトではQtを利用する設定のため、Qtを使わないなら、CONFIGからqtを削除する必要がありますが、これも簡単に記述できます。
一方、CMake(3.15)の場合以下のような書きっぷりとなります。
project(HelloWorld)
add_executable(helloworld main.cpp)
多くのCMakeのサンプルでは、先頭行に
cmake_minimum_required(VERSION 3.15)
のような記述があるかと思いますが、最近のバージョンでは数行程度のCMakeソースでは警告を出さないように実装されています。その代わり、3.15ではprojectの指定がないとエラーになりました。
大雑把に省略してしまったので、もう少ししっかり記述すると
TEMPLATE = app
CONFIG -= qt
SOURCES = main.cpp
cmake_minimum_required(VERSION 3.15)
project(HelloWorld LANGUAGES C++)
add_executable(helloworld main.cpp)
となるでしょうか。実行ファイル名は、helloworld(+環境に応じてexe等の拡張子)となります。
qmakeの場合、デフォルトではアプリケーションテンプレートが選択され、アプリがビルドされます。CMakeの場合は、executableを作るターゲットを追加するコマンドを使い、引数にファイルを指定することになります。
シンプルな例(Qt Coreを利用する場合)
Qt Coreとだけリンクするアプリケーションの作成を書こうと思うとqmakeでは
TEMPLATE = app
QT -= gui
SOURCES = main.cpp
となります。qmakeのデフォルトはGUIありのため、QT変数からguiを抜くことでCoreモジュールのみとなります。一方、CMakeの場合は少し複雑になります。
cmake_minimum_required(VERSION 3.15)
project(HelloWorld LANGUAGES C++)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
find_package(Qt5 COMPONENTS Core REQUIRED)
add_executable(helloworld main.cpp)
target_link_libraries(helloworld Qt5::Core)
CMakeは汎用的なツールで、Qtのためのツールではないため、Qt5を使うために若干コマンドが増えることになります。
find_package()を使ってQt5モジュールを呼び出し、Coreを要求しています。結果、その環境にあるQt5 CoreのライブラリをQt5::Core変数経由でtarget_link_libraries()に指定してリンクを指示しています。
ファイルが増えた場合
さて、実際にアプリケーションを書くとなるとmain.cppファイル一つ切りということはありません。hello.cpp, hello.h が追加されたとしましょう。
qmakeでは変数は空白区切りのリストを保持しますので、以下のように設定します。
TEMPLATE = app
QT -= gui
SOURCES = main.cpp hello.cpp
HEADERS = hello.h
CMakeでは以下のようになります。
cmake_minimum_required(VERSION 3.15)
project(HelloWorld LANGUAGES C++)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
find_package(Qt5 COMPONENTS Core REQUIRED)
add_executable(helloworld main.cpp hello.cpp hello.h)
target_link_libraries(helloworld Qt5::Core)
CMakeの場合、1つの実行ファイルを構成するソースファイルが増えると()内の引数の数が増えていくことになるので、少し読みにくくなりますよね。
共通的な機能
こうして並べてユーザー視点で見る限り、書きつける量が少ないため、qmakeの方がシンプルで素敵な感じなのですね。
CMakeは、コマンド文字列が意図を伝えやすいように定義されているため、レシピを見れば何をしようとしているのかざっくりと理解できるようになっていますが、自分で手書きで書くにはめんどくさいと思ってしまいます。
なお、共通的な機能としては
- サブディレクトリ(サブプロジェクト)に対応
- ファイルの書き出しや読み込み
- 独自関数を定義可能
- 別ファイルに分割したレシピをinclude可能
- 自動的なmoc呼び出し
- Qtのリソースファイル対応
- インストール定義
- ループや条件分岐
- プラットフォーム判定
- インストール
- Test設定/実行
- 追加機能・モジュールなどの提供
等の機能は、qmakeでもCMakeでも提供されています。
ではCMakeの強みはというと、カスタムコンパイラや、機能の拡張性、パッケージ作成まで行う機能が提供されているなどの部分が特に優れています。CMake自体のソースコードもきれいに整えられており、メンテナンス性が高くなっています。
qmakeもQTestもある程度考慮されてテストまでは実行できますが、基本的にはビルドを補助するツールとしての特色が強いのに対し、CMakeはCTest, CPackといった機能としてあらかじめテストやパッケージ作成を視野に入れて作られています。
qmakeもCMakeも制御構文を持ち、複雑なプロジェクトの作成もできるのですが、プロジェクトでの採用検討という視点から将来性も含めて両者を比較した場合は、CMakeに軍配があがるのも仕方の無いところかもしれません。
まとめ
本日は予期せず、駆け足で1つ記事を書く必要があったため、かなりざっくりとqmakeとCMakeの違いを書き出しました。明日も引き続きCMakeについて、もう少し有益な情報となるように、こちらの記事も含めて編集するつもりです。あまり期待せずお待ちください。