QtのCMakeデプロイメントAPI

本稿は「Qt's CMake deployment API」の抄訳です。

Qtを始めたばかりと仮定しましょう。Qtのオンラインインストーラーを使用して最新バージョンをインストールし、C++とQML APIを使いこなし、自分にとって使いやすい素敵なデスクトップアプリケーションができました。そして、この素晴らしい作品を他の人と共有したい、永遠の名声または利益を得たいと思うかもしれません!ただし、コンパイラやQtをインストールせずに、エンドユーザーがインストールして実行できるバイナリを共有したいのですが・・・どうすればいいのでしょうか?

一般的な方法は、アプリケーションの実行可能ファイルだけでなく、Qtのライブラリやプラグインも含むフォルダを作成し、適切な構造で配置することです。Qtは、windeployqtmacdeployqtandroiddeployqtなどのコマンドラインツールを使用して、フォルダの設定をサポートしています。しかし、CMakeからこれらのツールをどのように使用するのでしょうか?それがQtのCMakeデプロイメントAPIの登場です!

通常のデプロイプロセスは、以下の手順で行われます。

  1. アプリケーションをステージングエリアにインストールします。ステージングエリアは、CMAKE_INSTALL_PREFIX(またはクロスビルドでは一般的にCMAKE_STAGING_PREFIX)によって決定されます。
  2. windeployqtやmacdeployqtを呼び出して、Qtのライブラリ、プラグイン、アセットをステージングエリアにコピーします。
  3. *deployqtツールで行わないカスタムな調整を行います。

これらの手順の後、ステージングエリアはアーカイブ化および出荷の準備が整っています。

通常、全体のデプロイメントを処理するためにユーザーが別のスクリプトを作成することが求められます。

バージョン6.3以降、Qtではインストール時に実行される追加のデプロイメント手順を定義する方法が提供されています。この記事の目的は、QtのCMakeデプロイメントAPIを使用してQtアプリケーションをデプロイする方法を示すことです。

シンプルなQtアプリケーションを使って始めましょう。

cmake_minimum_required(VERSION 3.22)
project(MyApp)

find_package(Qt6 REQUIRED COMPONENTS Widgets)
qt_standard_project_setup()

qt_add_executable(MyApp main.cpp)
target_link_libraries(MyApp PRIVATE Qt::Widgets)

アプリケーションのターゲットをインストールするためのステートメントを追加します。macOSでは、${CMAKE_INSTALL_PREFIX}に直接インストールし、他のプラットフォームではその配下の bin ディレクトリにインストールします。

install(TARGETS MyApp
BUNDLE DESTINATION .
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)

qt_standard_project_setupGNUInstallDirs.cmakeを呼び出すことに注意してください。これがCMAKE_INSTALL_BINDIR変数を定義しています。

cmake --install ...の呼び出しでは、アプリケーションの実行可能ファイル(またはバンドル)のみがインストール先にインストールされます。

ここまでは特に新しいことはありません。しかし、今度はCMakeにmacdeployqtまたはwindeployqtの呼び出し方法を指示します。それを行うために、CMakeのインストール時にCMakeスクリプトを実行する機能を使用します。

qt_generate_deploy_app_script(
TARGET MyApp
OUTPUT_SCRIPT deploy_script
NO_UNSUPPORTED_PLATFORM_ERROR
)
install(SCRIPT ${deploy_script})

これにより、アプリケーションターゲット上で macdeployqt または windeployqt を呼び出す CMake スクリプトファイルが生成されます。生成されたスクリプトファイルのファイル名は deploy_script に格納されます。install(SCRIPT) 呼び出しで、インストール時にスクリプトを実行するように CMake に指示します。

これでプロジェクトの設定、ビルド、インストールができます。Windowsでは次のようになります。

D:\projects\myapp\build>D:\Qt\bin\qt-cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=D:/TEMP/myapp ..
...
D:\projects\myapp\build>ninja
...
D:\projects\myapp\build>ninja install
-- Install configuration: "Release"
-- Installing: D:/TEMP/myapp/bin/myapp.exe
-- Writing D:/TEMP/myapp/bin/qt.conf
-- Running Qt deploy tool for bin/myapp.exe in working directory 'D:/TEMP/myapp'
'D:/Qt/bin/windeployqt.exe' 'bin/myapp.exe' '--dir' '.' '--libdir' 'bin' '--plugindir' 'plugins' '--force'
D:\TEMP\myapp\dist\bin\myapp.exe 64 bit, release executable
Direct dependencies: Qt6Core Qt6Gui Qt6Widgets
All dependencies : Qt6Core Qt6Gui Qt6Widgets
To be deployed : Qt6Core Qt6Gui Qt6Widgets
Updating Qt6Core.dll.
Updating Qt6Gui.dll.
Updating Qt6Widgets.dll.
Updating d3dcompiler_47.dll.
Updating vc_redist.x64.exe.
Creating directory D:/TEMP/myapp/plugins/imageformats.
Updating qgif.dll.
Updating qico.dll.
Updating qjpeg.dll.
Creating directory D:/TEMP/myapp/plugins/platforms.
Updating qwindows.dll.
Creating directory D:/TEMP/myapp/plugins/styles.
Updating qwindowsvistastyle.dll.

インストールによって、自己完結のディレクトリが生成され、例えばcpackによってパッケージ化する準備が整いました。


内部仕組み

qt_generate_deploy_app_scriptの呼び出しは、ビルドディレクトリにCMakeスクリプトファイルを生成し、次の2つのことを行います。

  • QtDeploySupport.cmakeファイルを含める
  • インストールされた実行可能ファイルに対してqt6_deploy_runtime_dependenciesを呼び出す

QtDeploySupport.cmakeファイルは、デプロイメントを補助するためのCMakeコマンドを提供します。また、QT_DEPLOY_PREFIXなどの変数も設定します。

qt_deploy_runtime_dependenciesコマンドは、デプロイメントプロセスの実際のドライバーです。mac/windeployqtを呼び出し、必要に応じてqt.confファイルを生成します。

カスタマイズが必要なセットアップでは、スクリプトの生成を自分で行う必要があります。

set(deploy_script "${CMAKE_CURRENT_BINARY_DIR}/deploy_MyApp.cmake")
file(GENERATE OUTPUT ${deploy_script} CONTENT "
include(\"${QT_DEPLOY_SUPPORT}\")

qt_deploy_runtime_dependencies(
EXECUTABLE \"\${QT_DEPLOY_BIN_DIR}/$<TARGET_FILE_NAME:MyApp>\"
GENERATE_QT_CONF
)")

初期のincludeステートメントでは、設定時に使用可能なCMake変数QT_DEPLOY_SUPPORTを展開する必要があります。

スクリプトの残りの部分では、インストール時に利用可能なQT_DEPLOY_BIN_DIR変数にアクセスします。そのため、変数の代入を設定時に行わないように、エスケープする必要があります。

最後に、generator式を使用してMyAppターゲットのファイル名を抽出します。

プロジェクトが独自のqt.confを提供している場合、qt_deploy_runtime_dependenciesがそれを生成しないようにしたい場合は、GENERATE_QT_CONFを削除できます。

 

ステージング領域のレイアウト

Qt の CMake デプロイメント API は、GNUInstallDirs.cmake で定義された CMAKE_INSTALL_BINDIR 変数と CMAKE_INSTALL_LIBDIR 変数を使用してステージング領域のレイアウトを決定します。

インストール時に、これらのディレクトリ変数は QT_DEPLOY_BIN_DIRQT_DEPLOY_LIB_DIR として利用できます。

実行可能ファイルのインストール・コマンドの DESTINATION には CMAKE_INSTALL_BINDIR を、ライブラリのインストール・コマンドの DESTINATION には CMAKE_INSTALL_LIBDIR を必ず使用してください。

CMAKE_INSTALL_BINDIR や CMAKE_INSTALL_LIBDIR の値を変更するには、Qt6Core パッケージが見つかる前に変数を設定してください。

Qt プラグインは QT_DEPLOY_PLUGINS_DIR に配置され、デフォルトは 「plugin」です。QML モジュールは QT_DEPLOY_QML_DIR に配置され、デフォルトは「qml」です。

QtQuickプロジェクトの配布

QtQuickプロジェクトのデプロイは少し複雑ですが、ユーザーにとっては幸いなことに、そうではありません。詳細はqt_generate_deploy_qml_app_scriptコマンドによって隠ぺいされています。

QtQuick アプリケーションの CMakeLists.txt を作成する方法を見てみましょう。

まず、QtQuickアプリケーションを作成します。

qt_add_executable(MyApp main.cpp)
qt_add_qml_module(MyApp
URI Application
VERSION 1.0
QML_FILES main.qml MyThing.qml
)

通常のinstallコマンドを追加する必要があります。

install(TARGETS MyApp
BUNDLE DESTINATION .
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)

そして、qt_generate_deploy_qml_app_scriptを呼び出します。

qt_generate_deploy_qml_app_script(
TARGET MyApp
   OUTPUT_SCRIPT deploy_script
)
install(SCRIPT ${deploy_script})

インストール時に、QML ファイルとプロジェクトで使用する Qt の部分を含むアプリケーションバイナリがデプロイされます。

qt_generate_deploy_qml_app_scriptqt_deploy_runtime_dependencies に加えて qt_deploy_qml_imports を呼び出します。詳細はこれらのコマンドのドキュメントを参照してください。

 

展望

現時点では、Qt の CMake デプロイメント API は Windows と macOS でのみサポートされています。Android はビルドターゲット経由で処理されます。

将来的には Linux (QTBUG-74940) を含む、より多くのプラットフォームを追加する予定です。

CMake デプロイメント API は当分の間テクニカルプレビューです。お好きなプロジェクトでお試しいただき、https://bugreports.qt.io/ にフィードバックをお寄せください。


Blog Topics:

Comments