CMake - Qt5 / Qt6の互換性

提供:MochiuWiki : SUSE, EC, PCB
ナビゲーションに移動 検索に移動

概要

Qt 5およびQt 6のCMakeコマンドのセマンティクスは、ほぼ互換性がある。
しかし、Qt 5.14以前では、インポートされたQtライブラリのターゲットとコマンドは、全て名前の一部としてバージョン番号を含んでいた。

そのため、Qt 5とQt 6の両方で動作するCMakeコマンドを記述することは煩雑であった。

Qt 5.15以降では、バージョンレスターゲットおよびバージョンレスとコマンドを導入しており、異なるQtのバージョンにほとんど依存しないCMakeコマンドが記述できるようになっている。


Qt 5 / Qt 6ライブラリの検索

システムからQt 5 / Qt 6モジュールを検索する場合、以下に示すように記述する。
これは、Qt 6への移行を容易にしつつ、Qt 5を使用しているシステムとの互換性も維持することができる。

 find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core Network Xml)


  1. まず、Qt 6モジュールを探す。
  2. Qt 6モジュールが見つからない場合、Qt 5モジュールを探す。
  3. いずれかが存在する場合、該当するバージョンのCore、Network、Xmlコンポーネントを探す。
  4. REQUIREDフラグがあるため、どちらのバージョンも存在しない場合はエラーになる。


つまり、システムにQt 6とQt 5の両方がインストールされている場合、Qt 6が優先して使用される。


バージョンレスターゲット

既存のインポートターゲットに加えて、Qt 5.15以降ではバージョンレスターゲットが使用できる。
例えば、Qt Coreライブラリとリンクする場合、Qt6::CoreとQt::Coreの両方を参照することができる。

バージョンレスターゲットはデフォルトで定義されている。
もし、バージョンレスターゲットを無効にする場合は、最初のfind_packageコマンドの実行前に、QT_NO_CREATE_VERSIONLESS_TARGETSを設定する必要がある。

  • QT_NO_CREATE_VERSIONLESS_TARGETS
    Qt::で始まるインポートされたターゲットを隠す。
    代わりに、Qt6::で始まるターゲットを使用する必要がある。


※注意
例えば、インポートされたQt::Coreターゲットには、Qt6::Coreターゲットで利用可能なターゲットプロパティは無いことに注意する。

以下の例では、まず、Qt 6ライブラリを検索する。
Qt 6ライブラリの検索に失敗した場合は、Qt 5.15ライブラリを検索する。
これにより、Qt 5 / Qt 6に関係なく、インポートされたQt::Coreターゲットを使用することができる。

 # バージョンレスターゲットを有効にする場合
 # デフォルトで有効なため、設定は不要である
 find_package(Qt6 COMPONENTS Core)
 if (NOT Qt6_FOUND)
    find_package(Qt5 5.15 REQUIRED COMPONENTS Core)
 endif()
 
 add_executable(myApp
    # ...略
 )
 
 target_link_libraries(myApp PRIVATE
    Qt::Core
 )


 # バージョンレスターゲットを無効にする場合
 set(QT_NO_CREATE_VERSIONLESS_TARGETS OFF)
 find_package(Qt6 COMPONENTS Core)
 
 # ...略



バージョンレスコマンド

Qt 5.15以降、Qtモジュールはバージョンレスのコマンドも提供している。
例えば、Qt5とQt6のどちらを使用していても、qt_add_translationコマンドを使用して、翻訳ファイルをコンパイルすることができる。

最初のfind_packageコマンドの実行の前に、QT_NO_CREATE_VERSIONLESS_FUNCTIONSを設定して、バージョンレスコマンドの作成を防ぐ。

  • QT_NO_CREATE_VERSIONLESS_FUNCTIONS
    qt_で始まるコマンドを非表示にして、qt6_で始まるバージョン管理されたコマンドのみを残す。


このマクロを定義することで、Qtのバージョン付きのシンボルのみが使用されるようになる。
これは、特にQtの異なるバージョンを混在させる場合や明示的にバージョン管理を行う場合に有用である。

以下の例では、find_packageコマンドの前にsetコマンドでQT_NO_CREATE_VERSIONLESS_FUNCTIONSONまたはTRUEに設定している。
これにより、Qtのバージョンなしのシンボルが生成されないようになる。
CMake 3.16以降が必要な場合があるため、cmake_minimum_requiredコマンドを適切に設定する必要がある。

 cmake_minimum_required(VERSION 3.16)
 
 # find_packageコマンドの前にQT_NO_CREATE_VERSIONLESS_FUNCTIONSを設定する
 set(QT_NO_CREATE_VERSIONLESS_FUNCTIONS ON)
 
 project(MyQtProject)
 
 # 使用するQtライブラリ
 find_package(Qt6 REQUIRED COMPONENTS Core Widgets)
 
 # 実行ファイルのソースファイルおよびヘッダファイル
 add_executable(MyApp main.cpp)
 
 # Qtライブラリのリンク
 target_link_libraries(MyApp Qt6::Core Qt6::Widgets)



Qt 5とQt 6の混在

Qt 5およびQt 6の両方を1つのCMakeファイルに記述する場合、バージョンレスターゲットおよびバージョンレスコマンドは、find_packageコマンドで検索された最初のQtバージョンが暗黙的に参照される。
ただし、1つのライブラリや実行ファイルにQtのバージョンを混在させることはサポートされていない。

バージョンを明示的にするには、最初のfind_packageコマンドを実行する前に、CMake変数QT_DEFAULT_MAJOR_VERSIONを設定する必要がある。

  • QT_DEFAULT_MAJOR_VERSION
    Qt 5プロジェクトとQt 6プロジェクトが混在している場合、qt_コマンドで進むQtのバージョンを指定する。
    find_packageコマンドを実行する前に、Qt 5かQt 6のいずれかに設定する必要がある。

    5に設定する場合、qt_で始まるコマンドはqt5_で始まるコマンドを呼び出す。
    6に設定する場合、qt_で始まるコマンドはqt6_で始まるコマンドを呼び出す。

    設定されていない場合、最初のfind_packageコマンドを実行すると、デフォルトのバージョンが定義される。



レガシーなQt 5のサポート

Qt 5.14以前のバージョンもサポートする必要がある場合は、CMake変数に現在のバージョンを格納することで対応することができる。

以下の例では、find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core)では、まず、Qt 6を検索、見つからない場合はQt 5をQTという名前で検索している。
いずれかの検索に成功した場合は、CMake変数QT_VERSION_MAJORは5または6のいずれかに定義される。

次に、Qt${QT_VERSION_MAJOR}という名前をオンザフライで作成することにより、決定されたQtバージョンのパッケージを再度読み込む。
これは、CMAKE_AUTOMOCがパッケージ名がQt5あるいはQt6であることを期待しており、異なる場合はエラーを表示するためである。

 find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core)
 find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core)
 
 add_executable(myApp
    # ...略
 )
 
 target_link_libraries(myApp PRIVATE
    Qt${QT_VERSION_MAJOR}::Core
 )


同様に、インポートするQtライブラリ名も指定することができる。
target_link_librariesコマンドにQt${QT_VERSION_MAJOR}::Widgetsと記述することにより、Qt5::WidgetsまたはQt6::Widgetsのいずれかのライブラリをリンクすることができる。


推奨事項

  • 可能であれば、CMakeコマンドのバージョンレスバリアントを使用する。
  • 同じプロジェクトでQt 5およびQt 6をサポートする必要がある場合を除き、バージョン付きのターゲットを使用する。
  • バージョンレスターゲットを使用しなければならない場合は、バージョンレスターゲットを使用する際のデメリットに注意する。
  • Qt 5.15より古いバージョンのQt 5をサポートする必要がある場合や、
    QT_NO_CREATE_VERSIONLESS_FUNCTIONSQT_NO_CREATE_VERSIONLESS_TARGETSが定義されているコンテキストでCMakeコードがロードされるかどうかを制御できない場合は、
    バージョン管理されたバージョンのCMakeコマンドやターゲットを使用する。

    この場合でも、変数を通して実際のコマンド名やターゲット名を決定することにより、コードを簡素化することができる。



バージョンレスターゲットのデメリット

バージョンレスターゲットには、いくつかのデメリットがある。

バージョンレスターゲットはALIASターゲットであり、バージョン付きターゲットのターゲットプロパティが無い。

プロジェクトは、バージョンレスターゲットを公開するターゲットをエクスポートしてはならない。
例えば、他のプロジェクトにより消費されるライブラリは、バージョンレスターゲットに対して公にリンクするターゲットをエクスポートしてはならない。
これは、推移的依存関係が壊れたり、そのライブラリのユーザがQt 5とQt 6のターゲットを不本意に混在させたりする可能性がある。


WindowsのUnicodeサポート

Qt 6では、Qtモジュールに対してリンクするターゲットに対して、UNICODE_UNICODEコンパイラ定義がデフォルトで有効化されている。
これは、qmakeコマンドの動作と同様であるが、Qt 5のCMake APIの動作と差異がある。

ターゲット上において、qt_disable_unicode_definesコマンドを実行して、UNICODE_UNICODEコンパイラ定義を無効にする。

 find_package(Qt6 COMPONENTS Core)
 
 add_executable(myApp
    # ...略
 )
 
 qt_disable_unicode_defines(myApp)