はじめに
がっつり遅刻で申し訳ありませんhermit4です。なんか勉強会で記事作成頑張りたいなぁと思っていたのですが、別件の調査がうまく行かなくって結局タイムオーバー。Slint側の記事作成もあってだいぶ遅れました。
土曜日のQt勉強会終わりの忘年会でXに本番とか言って飲み会の写真を投稿してしまった結果、お誘いが増えたのが敗因ですかね・・・。
本日のお題
土曜日にあったQt勉強会で、そろそろCMakeにうつるべきなんだろうけどqmakeで作り込んでいるからねぇという話を聞きました。CMake周りについては以前に記事も上げたのですが、しばらく時間も経っているので、最近の状況をシェアしておこうかと思います。
qmakeユーザーのためのCMake入門補足
ざっくり復習
qmake、CMakeとも各種プラットフォーム上のビルドシステムがビルドに利用するレシピファイルを生成する、いわゆるメタビルドシステムです。
qmakeはQtのために開発されQt開発用ツールとして提供されています。CMakeは、kitware社のITK(Insight Toolkit)向けとしてスタートしましたが、現在は独自のオープンソースソフトウェアとして開発が継続されています。
qmake | CMake | |
---|---|---|
レシピファイル | <dirname>.pro | CMakeLists.txt |
レシピの呼び方 | プロジェクトファイル | CMakeソースコード |
特徴 | 変数ベース | コマンドベース |
ターゲット | 1ファイル1ターゲット | 1ファイル複数ターゲット |
プロジェクト間連携 | 不可 | ターゲット名でリンク等可能 |
Qtのサポート | ◎ | △ |
Qt Creator対応 | ◎ | ファイル追加などの対応が微妙 |
カスタムコンパイラ | △ | ◎ |
Package作成 | △ | ◯ |
qmakeは変数ベースで、必要な変数を設定していくのに対し、CMakeはコマンドベースで必要なコマンド呼び出しを羅列していくという違いがあり、記述量はCMakeのほうがどうしても多くなります。
ちなみに、以前CMakeの記事を上げたころはおそらくversion 3.15でしたが現在は3.31.2まで伸びています。qmakeは3.1のまま…かな。やはりひとつのプロジェクトとして開発が進むためCMakeは進化が早いです。
Qt6以降になって
QtプロジェクトではQt自体をビルドするときのメタビルドシステムにCMakeが採用されました。Qt6以降、Qtアプリのビルドに必要なコマンドが続々と追加されています。Qt Creator自体もCMakeを扱うようになっていますし、CMakeを使うケースも増えているのではないでしょうか。
Qt6がサポートしているCMakeバージョンについて
現在Qt6.8系が最新ですが、6.8系では最低でもCMake 3.16が必要となります。また、以下の環境ではCMake 3.21.1以降が必要とされるため注意が必要です。
- macOS/iOS向けのQt
- WebAssembly向けのQt
- staticビルド用のQt
なお、CMake 3.19以降を使うか否かでfinalize処理周りの遅延実行などの挙動も変わってきます。必要な環境を網羅するということを考え可能なら3.21.1以降を採用することをおすすめします。
基本となる書きっぷり
以前の例では以下のように記述していました
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_minimum_required(VERSION 3.21.1)
project(HelloWorld VERSION 1.0.0 LANGUAGES CXX)
find_package(Qt6 REQUIRED COMPONENTS Core)
qt_standard_project_setup()
qt_add_executable(HelloWorld main.cpp)
- qt_standard_project_setup() を行うことで、automoc/autorccなどが有効になります
- qt_add_executableを使えばQt Coreへの自動的なリンクが設定され、ターゲットプラットフォームに適した処理が行われるようになります。
なお、Qt Creatorを使わずにcmake単体で実行する場合は、CMAKE_PREFIX_PATHを設定しておく必要があります。以下はLinuxでの例です。
export CMAKE_PREFIX_PATH=~Qt/6.8.1/gcc_64/lib/cmake
CMakeコマンド
ロードするモジュールによってCMake API(コマンド)が追加されます。Qt5の時代からいくつか用意はされていますが、Qt6以降増えていっています。
以下はQt6.8以降で利用できるリストです。一部Qt6.8より前にはなかったコマンドもあるので注意してください。
Qt6::Core
qt_add_big_resources (5.12以降)
巨大なリソースファイルを直接オブジェクトファイルへ変換。C++変換を挟まないため効率的。iOSは未対応のためその場合はqt_add_resourcesで代替する必要がある。
qt_add_big_resources(<VAR> file1.qrc [file2.qrc ...]
[OPTIONS ...])
qt_add_binary_resources (5.10以降)
ソースファイルのリストからリソースファイルへ変換
qmakeで便利と思ってたやつですね。CMakeでも使えます。
qt_add_binary_resources(target file1.qrc [file2.qrc ...]
[DESTINATION ...]
[OPTIONS ...])
qt_add_executable (6.0以降)
add_executableのQt版APIです。
Qt::Coreとの自動リンクされます。
必要なファイナライズ処理も自動で行われます。
qt_add_executable(target
[WIN32] [MACOSX_BUNDLE]
[MANUAL_FINALIZATION]
sources...)
qt_add_library (6.2以降)
add_libraryのQt版APIです。
必要なファイナライズ処理が自動で行われます。
Qtライブラリ種別に応じたタイプ自動選択機能。
qt_add_library(target
[STATIC | SHARED | MODULE | INTERFACE | OBJECT]
[MANUAL_FINALIZATION]
sources...
)
qt_add_plugin (6.5以降)
Qtプラグインターゲット作成。
qt_add_plugin(target
[SHARED | STATIC]
[CLASS_NAME class_name]
[OUTPUT_TARGETS variable_name]
[MANUAL_FINALIZATION]
sources...
)
qt_add_resources (6.0以降)
リソースファイル登録。qrcファイル指定と直接リソースファイル列挙とが選択可能。
qt_add_resources(<VAR> file1.qrc [file2.qrc ...]
[OPTIONS ...])
qt_add_resources(<TARGET> <RESOURCE_NAME>
[PREFIX <PATH>]
[LANG <LANGUAGE>]
[BASE <PATH>]
[BIG_RESOURCES]
[OUTPUT_TARGETS <VARIABLE_NAME>]
[FILES ...] [OPTIONS ...])
前者のAPIは変換結果ファイルをVARに保存してqt_add_executableのソースとして指定する方法
set(SOURCES main.cpp)
qt_add_resources(SOURCES example.qrc)
qt_add_executable(myapp ${SOURCES})
後者のAPIは、ターゲット名を指定して自動リンクされます。
qt_add_executable(myapp main.cpp)
qt_add_resources(myapp "images"
PREFIX "/images"
FILES image1.png image2.png)
qt_allow_non_utf8_sources (6.0以降)
Windows 用の一部のコンパイラを使用してビルドする場合、Qt ライブラリにリンクするターゲットは、コンパイラにソースファイルをUTF-8として処理するフラグを立てますが、このAPIでそれを無効化できます。
qt_allow_non_utf8_sources(target)
qt_android_add_apk_target (6.0以降)(6.5以降非推奨)
androiddeployqt を実行して APK を生成するビルド ターゲットを定義します。6.5以降はqt_add_executableを利用してください。
qt_android_apply_arch_suffix (6.0以降)(6.5以降非推奨)
APK用ターゲットのビルドされたバイナリのファイル名に使用されるサフィックスを制御します。6.5以降はqt_add_executableを利用してください。
qt_android_generate_deployment_settings(6.0以降)(6.5以降非推奨)
androiddeployqt用設定ファイル生成。6.5以降はqt_add_executableを利用してください。
qt_deploy_qt_conf (6.3以降)(内部利用)
デプロイメント中にqt.confファイルを生成する必要がある場合に仕様するコマンド。デプロイメントスクリプトでしか機能しないことに注意が必要。このコマンドは通常、直接呼び出す必要はありません。他の上位レベルのコマンドによって内部的に使用されますが、よりカスタマイズされたデプロイメント ロジックを実装したいプロジェクトでは便利な場合があります。
qt_deploy_runtime_dependencies (6.3以降)(内部利用)
アプリケーションが macOS アプリ バンドルまたは Windows 実行可能ファイルの場合、qt_deploy_runtime_dependencies()インストール時スクリプトから呼び出すと、依存するライブラリとプラグインもインストールすることができます。デプロイメントスクリプトでしか機能しないことに注意が必要。このコマンドは通常、直接呼び出す必要はありません。他の上位レベルのコマンドによって内部的に使用されますが、よりカスタマイズされたデプロイメント ロジックを実装したいプロジェクトでは便利な場合があります。
qt_deploy_translations (6.5以降) (テクノロジプレビュー扱い)
アプリケーションをインストールするときに、使用されている Qt モジュールに属する翻訳もインストールすることが望ましい場合があります。 コマンドは、Qt インストールからqt_deploy_translations必要なファイルを収集し、言語ごとに 1 つのファイルにコンパイルします。デプロイメントスクリプトでしか機能しないことに注意が必要。このコマンドは通常、直接呼び出す必要はありません。他の上位レベルのコマンドによって内部的に使用されますが、よりカスタマイズされたデプロイメント ロジックを実装したいプロジェクトでは便利な場合があります。
qt_disable_unicode_defines (6.2以降)
Qt6では、Qtモジュールにリンクするターゲットに対して、UNICODEおよびコンパイラ定義がデフォルトで設定されます(Qt5 CMake挙動と違う)。このコマンドでその設定を無効化できます。
qt_disable_unicode_defines(target)
qt_extract_metatypes (6.0以降) (テクノロジプレビュー扱い)
ターゲットで生成および収集されるメタタイプ情報に依存します。これは通常、Qtコマンドによって内部的に処理されますが、そうでないシナリオではこのコマンドで情報収集することが可能です。
qt_finalize_project (6.3以降)
Qt プロジェクトに関連付けられたさまざまな一般的なプラットフォーム固有のタスクを処理します。
qt_finalize_target (6.3以降) (CMake 3.19未満で必要)
Qtコマンドを使用して作成されたターゲットの中には、CMake構成フェーズの最後に追加アクションを必要とするものがあります。その実行用コマンドで、CMake 3.19未満のバージョンを使う場合は最上位のCMakeLists.txtの最後で呼び出すほうが無難です(Androidプラットフォーム以外ならそれ以外でも問題ないですが)。
CMake 3.19以降を使うのであれば、呼び出す必要はありません。
qt_generate_deploy_app_script(6.3以降)(Linux,macOS,Windows only)
アプリケーションのデプロイメントスクリプトを生成します。
install()を使ってインストールできるのはターゲットのみで、実行可能ファイルが依存するその他のライブラリやプラグインは、明示的にインストールする必要があります。このコマンドはそのプロセスを簡素化するための便利なコマンドです。
qt_generate_deploy_script(6.5以降)
カスタムデプロイスクリプトを生成します。このコマンドは、QtのデプロイメントAPI機能を直接呼び出すカスタムデプロイメントスクリプトを生成します。
qt_generate_moc (5.0以降)
入力ファイルに対してmocを呼び出します。通常、AUTOMOCが有効になっていれば自動でmocは呼び出されますが、明示的にmoc呼び出しを設定するためのコマンドです。
qt_import_plugins (5.14以降)
インポートまたは除外するプラグインのカスタム セットを指定します。
qt_policy (6.5以降)
ポリシーを指定してQtのCMake APIのデフォルトの動作を旧動作へ変更できます。旧動作自体が非推奨なので、どうしてもという時だけの利用を検討してください。
qt_set_finalizer_mode (6.2以降) (テクノロジプレビュー扱い)
ターゲットのファイナライズのモードをカスタマイズします。
qt_standard_project_setup (6.3以降)
このコマンドは、典型的な Qt アプリケーションの設定タスクを簡素化します。通常は、最初のfind_package(Qt6)呼び出しの直後に、通常はトップ レベルCMakeLists.txtファイルで、ターゲットが定義される前に呼び出します。
- Qt関連のautogen機能がすべてデフォルトで有効になる
- GNUInstallDirsモジュール読み込み。インストール用のデフォルトが適切に設定される
- アーキテクチャごとの必要な設定が行われる
- 6.7以降では国際化対応使用言語が指定可能
qt_standard_project_setup(
[REQUIRES <version>]
[SUPPORTS_UP_TO <version>]
[I18N_TRANSLATED_LANGUAGES <language...>]
[I18N_SOURCE_LANGUAGE <language>]
)
qt_wrap_cpp (5.0以降)
ソースから .moc ファイルを作成します。通常はAUTOMOCで自動生成されるため呼び出す必要はありません。
Qt6::DBus
qt_add_dbus_adaptor (6.0以降)
D-Bus インターフェースのアダプタークラスを生成します。
qt_add_dbus_interface (6.0以降)
D-Bus インターフェース記述ファイルのインターフェースを実装するC++ソースを生成します。
qt_add_dbus_interfaces (6.0以降)
D-Bus インターフェース記述ファイルリストからインターフェースを実装するC++ソースを生成します。
qt_generate_dbus_interface (6.0以降)
C++ソースまたはヘッダーファイルを解析し、QObject派生クラス宣言を行い、D-Bus イントロスペクション XML を含むファイルを生成します。
Qt6::Grpc
qt-add_grpc (6.5以降)(テクノロジプレビュー)
protobufスキーマを使用してQtベースのC++サービスを生成します。現状はCLIENTコードのみ対応しています。
Qt6::InterfaceFramework
Qtインターフェイスフレームワークは、独自のインターフェイス定義言語 (IDL) を使用してインターフェイスを記述し、この定義に基づいてQt/QML APIコードを生成する方法を提供します。ドキュメントは存在するのですが、開発中のスナップショットなので、将来に渡って利用できるのかは若干怪しく見えています。
qt_ifcodegen_add_plugin (6.8以降)
qface IDL ファイルを使用して ifcodegen によって生成されるプラグイン ターゲットを追加します。
qt_ifcodegen_add_qml_module (6.8以降)
qface IDLファイルを使用してifcodegenによって生成されるQMLモジュールを追加します。
qt_ifcodegen_extend_target
qface IDLファイルから生成されたファイルでターゲットを拡張します
qt_ifcodegen_generate
qface IDLファイルからファイルを生成します
qt_ifcodegen_import_variables
qface IDL ファイルからファイルを生成し、CMake 内で使用する変数を提供します。
qt_set_ifcodegen_variable
ifcodegenテンプレート内で変数を指定された値に設定します
Qt6::LinguistTools
qt_add_lrelease (6.2以降)(6.7変更)
Qt Linguist .tsファイルを .qm ファイルに変換するためのターゲットを追加します。
ターゲットディレクトリスコープとは異なるスコープで呼び出す場合、CMake 3.18以降が必要になります。
qt_add_lupdate(6.2以降)(6.7変更)
Qt Linguist .ts ファイルを生成または更新するためのターゲットを追加します
qt_add_translation (非推奨)
Qt Linguist .ts ファイルを .qm ファイルにコンパイルします。
ターゲットベースの以下のコマンドへの変更が推奨されています。
- qt6_add_lrelease
- qt6_add_translations
qt_add_translations(6.2以降)(6.7変更)
Qt Linguist .ts ファイルを .qm ファイルに変換して更新するためのターゲットを追加します。
ターゲットディレクトリスコープとは異なるスコープで呼び出す場合、CMake 3.18以降が必要になります。
qt_collect_translation_source_targets (6.7以降)
翻訳の対象となるターゲットを収集します
qt_create_translation (6.7以降)
Qt Linguist翻訳ツールチェーンをセットアップします
Qt6::Multimedia
qt_add_ios_ffmpeg_libraries (6.8以降)
iOS アプリバンドルに FFmpeg バイナリを埋め込む補助機能
Qt6::Protobuf
qt_add_protobuf (6.5以降)(テクノロジプレビュー)
protobuf スキーマを使用して Qt ベースの C++ ソースコードを生成します。
Qt6::Qml
qt_add_qml_module (6.2以降)
QMLモジュールを定義する
qt_add_qml_plugin
QMLモジュールに関連付けられたプラグインを定義します
qt_deploy_qml_imports
実行ファイルに必要なQMLモジュールのランタイムコンポーネントをデプロイする
CMake 3.19より前のバージョンの場合、qt_add_executableのMANUAL_FIANALIZATIONを有効にして適切にqt_finalize_target()を呼ぶよう指定する必要があります。
qt_generate_deploy_qml_app_script (6.3以降)
QMLアプリケーションのデプロイメントスクリプトを生成する
CMake 3.19より前のバージョンの場合、qt_add_executableのMANUAL_FIANALIZATIONを有効にして適切にqt_finalize_target()を呼ぶよう指定する必要があります。
qt_generate_foreign_qml_types
QMLモジュール内の1つのターゲットから型を登録します
qt_import_qml_plugins
ターゲットに必要なQMLプラグインが静的ビルドにインポートされるようにします。
この機能が有効となるのはQtがstaticビルドの場合に限ります。
qt_query_qml_module (6.3以降)
QMLモジュールに関する情報を取得する
qt_target_compile_qml_to_cpp (6.4で廃止)
qt_add_qml_module でqmltcが呼び出せるようになっている。
qt_target_qml_sources (6.2以降)
既存のQMLモジュールターゲットにqmlファイルとリソースを追加する
Qt6::RemoteObjects
qt_add_repc_merged (6.2以降)
.rep ファイルからQt Remote Object ReplicaとQt Remote Object Sourceのヘッダファイルを生成します。
qt_add_repc_replicas (6.2以降)
.rep ファイルからQt Remote Object Replicaヘッダファイルを生成します。
qt_add_repc_sources (6.2以降)
.rep ファイルからQt Remote Object Sourceのヘッダファイルを生成します。
qt_reps_from_headers (6.2以降)
QObject ヘッダーファイルから .rep ファイルを作成します。
Qt6::Scxml
qt_add_statecharts (6.1以降)
.scxml ファイルを読み取り、SCXMLで定義されているステートマシンを実装するクラスを含むC++ソースファイルとヘッダーファイルを生成する
Qt6::ShaderTools
qt6_add_shaders
シェーダーをコンパイルし、Qtリソースに追加します
Qt6::WaylandClient
qt_generate_wayland_protocol_client_sources (6.0以降)
Waylandプロトコル .xmlファイル用のクライアント側C++バインディングを生成します。
Qt6::WaylandCompositor
qt_generate_wayland_protocol_server_sources (6.0以降)
Waylandプロトコル .xmlファイル用のサーバー側C++バインディングを生成します。
Qt6::WebEngineCore
qt_add_webengine_dictionary (6.3以降)
Qt Web Engineのスペルチェッカー用辞書作成のため、hunspell辞書形式をbdictバイナリ形式に変換します。
Qt6::Widgets
qt_add_ui (6.8以降)
User Interface Compiler (uic) を .uiファイルに対して呼び出すためのルールを作成します。
qt_wrap_ui (6.8以降はqt_add_uiを推奨)
User Interface Compiler (uic) を .uiファイルに対して呼び出すためのルールを作成します。
まとめ
今回は、Qt6開発でCMakeを使う人向けに追加されたコマンド群をだらだらと紹介してみました。新しくコマンドも追加されつつあるし、古いバージョンにこだわらないのなら、だいぶ書くコマンド量は減らせるようになりつつあります。
え、古いバージョンも必要・・・・頑張れとしか。
正直qmakeに比べると書く量が増えますし、Ninjaに対応していることとCTest,CPackがなければ積極的に使いたいとは思わないのですけど、Qtユーザーのためには色々追加もされてきて、書く量を減らす方向で頑張ってくれてはいるようです。
Qt自体のビルドにはCMakeが必要になってしまったようですし、避けられないものとしてちょっとずつ勉強してみても良いかと思います。
P.S.
Qtのカレンダーの方はさっぱりうめられていませんが、時間が取れればあと1個か2個くらいは記事を用意したいなと思います。