3/16更新: qtpmがなくても使えるように生成されるCMakeLists.txtを変更しました
qtpmはCMakeラッパーではあるんですが、それ以外もいろいろやっているので、それを整理します。qtpmで生成したCMakeLists.txtがどうなっているのか(qtpm以外での利用での注意点など)も紹介します。
プロジェクト作成
# アプリケーションモードで初期化
$ qtpm init app
# ライブラリモードで初期化
$ qtpm init lib
これは各種フォルダを作ったりデフォルトのファイルを生成します。これらのコマンドではCMakeはキックされません。CMakeLists.txtもまだ生成されません。
i18n関連
# 翻訳ソースファイルを追加
$ qtpm i18n add ja
# 翻訳ソースファイルを更新
$ qtpm i18n update
# 翻訳ソースファイルを編集(linguist起動)
$ qtpm i18n edit
.tsから.qmの生成なんかはCMakeを使って行うこともできるんですが、qtpmでは直接lupdateやlreleaseコマンドを起動しています。ライブラリ側では.tsだけを持っているということを想定しているんですが、.qmの置き場はルートのプロジェクトのresources/translations以下になるので、プロジェクトが単独で使われているか、そうじゃないかで出力先が変わってきますし、.qmファイルはリソースファイルに置く前提のコードになっているんですが、リソースファイル自身は自動生成する(CMake実行前に作られる)ので、.qmファイルもその前に作られる必要があるため、CMake内での実行はしていません。
クリア
# 作業フォルダのクリア
$ qtpm clear
作業フォルダをクリアします。とはいっても、生成されたCMakeLists.txtを使ってビルドするときに必要になるフィアルは一部残します。具体的には自動生成したリソースファイルと、バイナリ形式に変換された翻訳ファイル、アイコン画像などです。
ビルド
# ビルド
$ qtpm build
# インストーラ作成 (まだMacのみ)
$ qtpm pack
# テスト実行
$ qtpm test
# (qtpackage.tomlに依存ライブラリが設定されている場合)必要なファイルを一括取得
$ qtpm get
# 依存ライブラリのダウンロード(qtpackage.tomlがあれば必要なモジュールとして追加)
$ qtpm get github.com/qtpm/QtCBOR
ビルドではCMakeLists.txtが作られてCMakeコマンドを実行します。その前に次のようなことをしています。
- 翻訳ファイル(.ts)を実行プログラムから利用可能なファイル(.qm)に変換してリソースフォルダ(トッププロジェクトのresources/translations)に出力
- リソースフォルダのファイルを収集して、リソースファイルを作成
- アプリケーションであればresources/icon.pngがあればそれを元にアイコンを生成、なければデフォルトのQtの画像からアイコンを生成
- ソースファイル、テストの情報を収集。サフィックス(_windows.cppなど)による、環境ごとにどんな条件ビルドがあるかの整理もする。
これらの情報を元にCMakeLists.txtを生成します。一度生成してしまったら、qtpmがなくても動作に支障はないCMakeLists.txtが生成されます。CMakeLists.txtを生成するのがめんどくさいよ、という人はここで生成してからqtpmは捨てて自分のプロジェクトを作ることもできます。
階層構造を持ったプロジェクトになっていますが、最終的にはすべてトッププロジェクトのvendor以下にヘッダとライブラリが格納されます。getを実行すると子供のプロジェクトのビルドが走りますが、依存ライブラリの読み込みにはこのトッププロジェクトの情報が書き込まれていたりするので(# project global settingと書かれている中のinclude_directories/link_directories)、もし自前でCMakeLists.txtをメンテするならこのあたりを書き換えればOKです。
CMake実行時は以下の3つのオプションを引数に渡しています。前者2つはCMakeのデフォルトの設定であるので省略しても支障はありません。最後のQTDIRも、Qt開発者であれば設定しているかもしれません。
- CMAKE_INSTALL_PREFIX: トッププロジェクトのvendor/releaseもしくはvendor/debug(指定しなければOSのデフォルトの場所になる)
- CMAKE_BUILD_TYPE: リリースビルドか、デバッグビルドか
- QTDIR環境変数: Qtの処理系が入っているところ。Ubuntuなどでインストールした(/usr/lib)場合は何も渡さない。
qtpm自身も、QTDIR環境変数を参照します。もし設定してなければ、Qtのバイナリインストーラがインストールしそうなパスを探してQtの処理系のパスを取得します。それでもなければ空欄のままCMakeを起動します。
ライブラリの場合は最後にCMAKE_INSTALL_PREFIXに格納しておしまいですが、アプリの場合で、packコマンドで実行された場合は、Macであれば、ビルド完了後にmacdeployqt
コマンドを起動して、必要なフレームワークを集めてきて.dmgファイルを生成するところまで行います。これはCMake外で実行しています。
3/16追記: 生成されるCMakeLists.txtそのものは、特殊な引数に依存せずに素のCMakeで処理できるファイルなので、Qt Creatorから読み込ませることができます。
まとめ
CMakeでQtというのはなかなか情報が見つけにくく、あっても少し古かったり(CMAKE_AUTOMOC
などを使わない)することもあるのですが、このツールの生成するCMakeLists.txtを見れば、どれが必要な処理なのか、QMakeと対応するのはどれかが分かりやすいかもしれません。僕も一つ一つ手探りで調べて行ってテンプレート化してますので。もしよりよい方法があればお知らせください。最後にqtpmが生成するCMakeLists.txt(かいぎょうだけ修正)を貼っておきます。QtKeychain用に生成したものです。
C++11をデフォルトで使うようにしていたり、MacOS用の設定がもろもろ入っていたりします。CMAKE_POSITION_INDEPENDENT_CODE
やCMAKE_CXX_STANDARD
といったオプションを使っているため、ネットによくある情報よりも、cmake_minimum_required
が若くなっています。
cmake_minimum_required(VERSION 3.1.0)
# project global setting
project(qtcbor)
set(QTCBOR_MAJOR_VERSION 1)
set(QTCBOR_MINOR_VERSION 0)
set(QTCBOR_PATCH_VERSION 0)
set(QTCBOR_VERSION 1.0.0)
if(DEFINED QTPM_IS_ROOT_PROJECT)
set(READ_SUBDIRECTORIES FALSE)
else()
set(READ_SUBDIRECTORIES TRUE)
set(QTPM_IS_ROOT_PROJECT FALSE)
endif()
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/../../vendor)
set(VENDOR_PATH ${CMAKE_CURRENT_SOURCE_DIR}/..)
else()
set(VENDOR_PATH ${CMAKE_CURRENT_SOURCE_DIR}/vendor)
endif()
# compiler setting
set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
set(CMAKE_CXX_STANDARD 11)
if(MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
endif()
if(APPLE)
set(CMAKE_MACOSX_RPATH ON)
set(CMAKE_SKIP_BUILD_RPATH FALSE)
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/lib" isSystemDir)
if("${isSystemDir}" STREQUAL "-1")
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
endif()
endif()
# enable Qt
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
set(CMAKE_INCLUDE_CURRENT_DIR ON)
if(DEFINED ENV{QTDIR})
set(CMAKE_PREFIX_PATH "$ENV{QTDIR}")
endif()
find_package(Qt5Core REQUIRED)
# extra setting
include("CMakeExtra.txt" OPTIONAL)
# build setting
include_directories(src)
set(sources src/qtcbor/private/cbor.h
src/qtcbor/private/decoder.cpp
src/qtcbor/private/decoder.h
src/qtcbor/private/encoder.cpp
src/qtcbor/private/encoder.h
src/qtcbor/private/endian.h
src/qtcbor/private/input.cpp
src/qtcbor/private/input.h
src/qtcbor/private/listener.h
src/qtcbor/private/listener_debug.cpp
src/qtcbor/private/listener_debug.h
src/qtcbor/private/log.h
src/qtcbor/private/output.h
src/qtcbor/private/output_dynamic.cpp
src/qtcbor/private/output_dynamic.h
src/qtcbor/private/output_static.cpp
src/qtcbor/private/output_static.h
src/qtcbor/qtcbor.cpp
src/qtcbor/qtcbor.h)
add_library(qtcbor STATIC ${sources})
qt5_use_modules(qtcbor Core)
# enable tests
enable_testing()
find_package(Qt5Test REQUIRED)
set(tests test/qtcbor_test.cpp)
foreach(file IN LISTS tests)
get_filename_component(execname ${file} NAME_WE)
add_executable(${execname} ${file} ${extra_test_sources})
add_test(NAME ${execname} COMMAND ${execname})
qt5_use_modules(${execname} Test Core)
target_link_libraries(${execname} qtcbor )
endforeach()
# install setting
install(TARGETS qtcbor ARCHIVE DESTINATION lib)
install(FILES ${public_headers} DESTINATION include/qtcbor/)