こんにちは。田原です。

QtでWindows用のプログラムを作っていて時々困ってしまうのですが、ビルドしたプログラムはQtCreatorからしか実行できません。時として直接exeを叩いて起動したいのですが、その時はQtの該当バージョンのbinフォルダにPATHを通すなどの一工夫が必要になり面倒です。そこで今回は起動に必要なQtライブラリを集める*deployqtと、それをビルド時に自動的に呼び出す方法について解説します。これにより集まったフォルダを他のPCへ持っていって起動することもできるようになります。

1.*deployqtとは

Qt公式のバイナリには、windeployqt.exe と macdeployqt が含まれています。それぞれ、Windows用、Mac用のターゲット・プログラムが必要としているQtライブラリ(やQMLファイル等)を集めてくるプログラムです。

linux用のものはQt公式には含まれていないのですが、有志の方々により、macdeployqt をベースに linuxdeployqt が開発されていますのでこれを使います。

linuxdeployqtの入手について
上記のページに記載がありますように、GitHubのリリースから linuxdeployqt-*-x86_64.AppImage をダウンロードし、実行属性を与えて(chmod a+x linuxdeployqt-*-x86_64.AppImage)使います。

なお、以下では、”~/Qt/linuxdeployqt-*-x86_64.AppImage” へ保存しているものとして説明しています。他のフォルダへ保存された方は適宜読み換えて下さい。

2.必要なファイルだけを集める

何も指定しないと、ビルド・フォルダ/releaseやビルド・フォルダ/debugに全ての中間ファイル群とターゲットのプログラム・ファイルが出力されます。これらのうち中間ファイル群は不要なので必要なファイルだけを抽出してみます。

2-1. releaseビルドとdebugビルドの切り替え対応

まずは、releaseビルドとdebugビルドへの対応です。QtCreatorの右下の設定を使ってreleaseビルドとdebugビルドを切り替えることができます。

releaseビルドとdebugビルドではターゲット・プログラムを起動するのに必要なファイルが異なる(一般にdebugビルドの方が数が多い)ので、出力先を分けておくと便利です。以下の記述により、BUILD_TYPE変数にdebugビルドの時はdebugが設定され、releaseビルドの時はreleaseが設定されます。(Qt公式のリファレンス

CONFIG(debug,debug|release){
    BUILD_TYPE=debug
}else{
    BUILD_TYPE=release
}

2-2. 出力先ファオルダの指定

ターゲット・プログラムの出力先フォルダは、DESTDIR変数で指定することができます。(指定しなければデフォルトのフォルダへ中間ファイル群と共に出力されます。ここではターゲット・プログラムだけを抽出するために指定します。)
2-1で定義したBUILD_TYPE変数を使って出力先フォルダを指定します。

DESTDIR = $$OUT_PWD/deploy/$$BUILD_TYPE

ここで使っている$$OUT_PWDCMakeのCMAKE_CURRENT_BINARY_DIRと同じく「当プロジェクトのビルド用フォルダ」のパスが入っています。

3.*.proファイルでリンク後に*deployqtを呼び出す

多くのビルド・システムは、ターゲットをリンクする前(PRE_LINK)やリンクした後(=POST_LING)に指定コマンドを呼び出せますが、qmakeも同じことができます。QMAKE_POST_LINK変数に実行したいコマンドを設定するだけです。

この機能を使って、リンク後に*deployqtを呼び出すようにします。

3-1. ターゲット・プログラムのパス

*deployqtにはターゲット・プログラムのパスを指定する必要があります。
ターゲット・プログラムのファイル名やパスは既に決まっているので、qmake変数のどれかに入っていそうな気がするのですが、私は見つけることができませんでした。 そこで以下のようにして作りました。

win32 {
    TARGET_NAME = $${TARGET}.exe
}
else: macx {
    TARGET_NAME = $${TARGET}.app
}
else: linux {
    TARGET_NAME = $${TARGET}
}
TARGET_PATH = $$DESTDIR/$${TARGET_NAME}

win32やmax、linuxはプラットフォームを指定しています。これらは Qtの該当バージョンの実体をインストールしたフォルダの下音mkspecs で定義されています。例えばWindowsならC:\Qt\5.15.0\msvc2019_64\mkspecsに入っています。

3-2. リンク後コマンドの指定

CMakeのadd_custom_commandのPOST_LINK、Visual Studioのビルド・イベントの「ビルド後のイベント」と同様にターゲット・プログラムをリンクした後で任意のコマンドを実行できます。
ここでは、このタイミングで *deployqt を呼び出して必要なQtのライブラリ群などをかき集めます。

win32 {
    QMAKE_POST_LINK =  $$dirname(QMAKE_QMAKE)/windeployqt $${TARGET_PATH} -qmldir=$$PWD --no-translations$$escape_expand(\n\t)
    QMAKE_POST_LINK += cd$$escape_expand(\n\t)
}
else: macx {
    QMAKE_POST_LINK =  $$dirname(QMAKE_QMAKE)/macdeployqt $${TARGET_PATH} -qmldir=$$PWD$$escape_expand(\n\t)
    QMAKE_POST_LINK += pwd$$escape_expand(\n\t)
}
else: linux {
    QMAKE_POST_LINK =  ~/Qt/linuxdeployqt-7-x86_64.AppImage $${TARGET_PATH} -qmldir=$$PWD$$escape_expand -unsupported-allow-new-glibc$$escape_expand(\n\t)
    QMAKE_POST_LINK += pwd$$escape_expand(\n\t)
}

Windows, Mac, Linux、それぞれでコマンドが異なります(windeployqt.exe, macdeployqt, linuxdeployqt)し、それぞれのパラメータも微妙に異なるので各プラットフォーム毎に分岐して定義しています。
それぞれの1行目のQMAKE_POST_LINKで *deployqt を呼び出しています。

ちょっとコツが必要ですが、QMAKE_POST_LINK変数には複数のコマンドを指定することもできます。Windowsとlinuxでは「改行」、Macでは「タブ」で区切ることで複数コマンドを指定できました。「改行」や「タブ」を指定する際 `”\n\t”` と書いてもダメです。$$escape_expand(\n\t) と書いてエスケープ・シーケンスを展開する必要があるようです。

2行目のQMAKE_POST_LINKはおまけです。①複数のコマンドを書く方法の例、②この時のカレント・フォルダを表示しています。

4.サンプルの.proファイル

前回のサンプル・プログラムの .pro ファイルだけを書き換えて、必要ファイルを deploy フォルダの下に集めるようにしてみました。
前回のプロジェクトのcpp-qml-if.proを下記へ書き換え、①QtCreatorの左下でdebug/releaseを設定し、②qmakeを行い、③ビルドしてみて下さい。ビルド・フォルダのdeploy/debug、もしくは、deploy/releaseの下に必要なファイル一式が揃っています。
それらを、zip圧縮するなどして他のPCへ持っていって起動することもできます。

cpp-qml-if.pro
QT += quick

CONFIG += c++11

# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Refer to the documentation for the
# deprecated API to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
    WindowBase.cpp \
    main.cpp \
    MainWindow.cpp

HEADERS += \
    MainWindow.h \
    WindowBase.h

RESOURCES += qml.qrc

# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH =

# Additional import path used to resolve QML modules just for Qt Quick Designer
QML_DESIGNER_IMPORT_PATH =

# 抽出先フォルダ
CONFIG(debug,debug|release){
    BUILD_TYPE=debug
}else{
    BUILD_TYPE=release
}
DESTDIR = $$OUT_PWD/deploy/$$BUILD_TYPE

# ターゲット・バイナリのパス
win32 {
    TARGET_NAME = $${TARGET}.exe
}
else: macx {
    TARGET_NAME = $${TARGET}.app
}
else: linux {
    TARGET_NAME = $${TARGET}
}
TARGET_PATH = $$DESTDIR/$${TARGET_NAME}

# リンク後処理の登録
win32 {
    QMAKE_POST_LINK =  $$dirname(QMAKE_QMAKE)/windeployqt $${TARGET_PATH} -qmldir=$$PWD --no-translations$$escape_expand(\n\t)
    QMAKE_POST_LINK += cd$$escape_expand(\n\t)
}
else: macx {
    QMAKE_POST_LINK =  $$dirname(QMAKE_QMAKE)/macdeployqt $${TARGET_PATH} -qmldir=$$PWD$$escape_expand(\n\t)
    QMAKE_POST_LINK += pwd$$escape_expand(\n\t)
}
else: linux {
    QMAKE_POST_LINK =  ~/Qt/linuxdeployqt-7-x86_64.AppImage $${TARGET_PATH} -qmldir=$$PWD$$escape_expand -unsupported-allow-new-glibc$$escape_expand(\n\t)
    QMAKE_POST_LINK += pwd$$escape_expand(\n\t)
}

message("DESTDIR        =$$DESTDIR")
message("TARGET         =$$TARGET")
message("TARGET_PATH    =$$TARGET_PATH")
message("QMAKE_POST_LINK=$$QMAKE_POST_LINK")

なお、windeployqtは、releaseフォルダの下に vc_redist.x64.exe(Visual C++の再頒布可能パッケージ) をコピーするようです。古いバージョンのWindows等でインストーラでVisual C++のランタイムが入っていない場合は、これを使ってインストールできます。

5.まとめ

Macやlinuxでは大丈夫なようですが、Windowsではデバッグ中のプログラムを起動するのはQtCreatorからでないと面倒なので結構ストレスが溜まっていました。他にも同様な方がいるような気もしましたので、順序的にどうなのかと思いつつ、先に *deployqt の解説をしてみました。
ついでにせっかく必要なものを集めるのなら、(中間ファイルを除き)必要なものだけを集めて、簡易的なパッケージ化できるようにしてみました。

このように、qmakeも結構強力ですので使い方も複雑です。そしてCMakeに比べるとちょっと癖が強い感じがします。そのため手懐けるのに苦労しました。ですので私の解説もまだ拙いかもしれませんが、他の方にとっても有用であれば幸いです。

それでは今回はこの辺で終わります。お疲れさまでした。