「Qtの設定 - CMake」の版間の差分

提供:MochiuWiki - SUSE, Electronic Circuit, PCB
ナビゲーションに移動 検索に移動
217行目: 217行目:
コンパイラやリンカのチェックが正常に終了した後、その結果はキャッシュされて、ビルドディレクトリのCMakeCache.txtファイルに保存される。<br>
コンパイラやリンカのチェックが正常に終了した後、その結果はキャッシュされて、ビルドディレクトリのCMakeCache.txtファイルに保存される。<br>
その後、CMakeは有効化された機能を制御する多くの変数とプロパティをセットアップする。<br>
その後、CMakeは有効化された機能を制御する多くの変数とプロパティをセットアップする。<br>
<br>
<code>project</code>コマンドの値は、変数<code>PROJECT_NAME</code>に自動的に設定される。<br>
例えば、プロジェクトのCMakeLists.txtファイル内で<code>${PROJECT_NAME}</code>を使用することにより、プロジェクト名を参照することができる。<br>
<br><br>
<br><br>



2024年2月11日 (日) 18:41時点における版

概要

CMakeは、アメリカ国立医学図書館の出資により医療系画像解析に利用されるITK(Insight Toolkit)のために開発されたクロスプラットフォーム向けのメタビルドシステムであり、
qmakeと同様、クロスプラットフォーム向けに各種ビルドシステムのためのレシピファイルを生成する。
qmakeがQtのために開発されているのに対して、CMakeはITKのためにと開発されたが、メタビルドシステムとして独立のオープンソースプロジェクトとして単独で提供されている。

このため、KDE、LLVM、OpenCVをはじめ多数のプロジェクトで採用されている。

CMakeは、主に、C/C++等のプログラム言語のビルドに使用される。
CMakeが存在する以前では、C系言語のビルドには多数のビルドシステム(MakefileやNinjaやIDE等)が乱立していた。
これを隠蔽して、包括的に扱える様にしたものがCMakeである。

また、CMakeには、CUI版とGUI版が存在する。

CMakeは、変数、コマンド、マクロ、条件論理、ループ、コメント等の開発者にとって馴染みのある多くのものを備えている。
CMakeで使用されるコマンド名も大文字小文字を区別しないため、以下は全て等価である。
ただし、最近では、コマンド名を全て小文字で記述することが一般的である。(これは、CMakeのドキュメントでビルトインコマンドのために従われている慣習でもある)

 add_executable(myExe main.cpp)
 ADD_EXECUTABLE(myExe main.cpp)
 Add_Executable(myExe main.cpp)



非推奨のコマンドおよび設定

  • include_directoriesコマンド
    指定したディレクトリからヘッダファイルを検索するコマンドである。
    定義した箇所以降の全てのターゲットが指定したディレクトリをインクルードするため、使用は避けるべきである。


  • add_definitionsコマンド


  • add_compile_definitionsコマンド


  • add_compile_optionsコマンド


  • link_directoriesコマンド


  • link_librariesコマンド


  • キャッシュ変数には、必ず接頭辞を付加する
    キャッシュ変数はグローバル変数であるため、名前の衝突を避けるために接頭辞を付加する。


  • 変数CMAKE_<LANG>_FLAGS
    代わりに、target_compile_optionsを使用する。
    target_compile_options(<ターゲット名> PUBLIC -Wall)


  • 変数CMAKE_CXX_FLAGStarget_compile_optionsコマンドに、-std=c++17等を指定しない。
    変数CMAKE_CXX_STANDARDを使用する(CMake 3.1以降)、または、target_compile_featuresコマンドにcxx_std_17を指定する。(CMake 3.8以降)


  • 変数CMAKE_SOURCE_DIRを使用しない
    変数CMAKE_SOURCE_DIRは、トップレベルのディレクトリを指す。
    異なるプロジェクトがネストしている場合、自身のプロジェクトのルートディレクトリ以外のパスを指すため、使用すべきでない。
    代わりに、変数CMAKE_CURRENT_SOURCE_DIR、変数PROJECT_SOURCE_DIR、変数<プロジェクト名>_SOURCE_DIRを使用する。


  • macroコマンドの代わりにfunctionコマンドを使用する
    functionコマンドは、関数を定義するためのコマンドである。
    macroコマンドは、呼び出す側のスコープにある変数を上書きするため、自身のスコープを持つfunctionコマンドを使用する。
    親ディレクトリのスコープにある変数を上書きする場合は、set(<変数名> <値> ... PARENT_SCOPE)コマンドを使用する。


  • file(GLOB)コマンド
    file(GLOB)コマンドは、CMakeを実行するたびに条件に合致するファイルのリストを自動的に作成するコマンドである。
    ただし、特定のIDEでは正常に動作しない可能性があるため、コマンドラインから実行するような場合ではない限り使用すべきではない。
    IDEで使用する場合は、CMakeLists.txtファイルにadd_subdirectoryコマンドとtarget_sourcesコマンドを使用して、再帰的にファイルを明示して追加する。
 # CMakeLists.txtファイル
 
 add_executable(<ターゲット名>)
 add_subdirectory(<ディレクトリ名1>)
 add_subdirectory(<ディレクトリ名2>)
 # 同様に子ディレクトリを追加する


 # <ディレクトリ名1>/CMakeLists.txtファイル
 
 # CMake 3.12以前
 target_sources(<ターゲット名> PRIVATE
    ${CMAKE_CURRENT_SOURCE_DIR}/file1.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/file2.cpp
    # 絶対パスで指定しないとエラーになるため注意すること
 )
 
 # CMake 3.13以降
 cmake_policy(SET CMP0076 NEW)  # CMakeが自動的に相対パスを絶対パスへ変換する
 target_sources(<ターゲット名> PRIVATE
    file1.cpp
    file2.cpp
 )


 # 画像ファイルの取得 (PNG, JPG, SVG)
 execute_process(
    COMMAND find ${CMAKE_CURRENT_SOURCE_DIR}/Image -iname "*.png" -o -iname "*.jpg" -o -iname "*.svg"
    OUTPUT_VARIABLE IMAGES
 )
 
 string(REPLACE "\n" ";" IMAGE_FILES "${IMAGES}")


  • PRIVATEオプション、PUBLICオプション、INTERFACEオプションを適切に使用する。
    これらのオプションは、コマンドのターゲットおよびそのターゲットに依存するターゲットに対する必要性を表す。
    ヘッダファイルのみのライブラリの場合は、INTERFACEオプションを使用する。
オプション ターゲットが必要とする ターゲットに依存するターゲットが必要とする
PRIVATE
PUBLIC
INTERFACE


  • ライブラリの種類を指定しない
    ビルドするユーザがスタティックライブラリまたはダイナミックライブラリを指定できるようにする。
    BUILD_SHARED_LIBSオプションを付加して選択することもできるが、各ライブラリごとにオプションを設定すべきである。
 option(<変数名1> "build library as a shared library" ON)
 
 if(<変数名1>)
    add_library(<ターゲット名> SHARED)
 else()
    add_library(<ターゲット名> STATIC)
 endif()



ソースツリーおよびビルドツリー

ビルドに使用されるソースコードを含むトップレベルディレクトリをソースツリーと呼ぶ。
ソースツリーは、トップレベルのCMakeLists.txtが配置され、プロジェクトが定義される。

ビルドの生成物を保管するためのディレクトリをビルドツリーと呼ぶ。
ビルドツリーは、CMakeCache.txtが作成・保管され、キャッシュ変数やオプション等が格納される。

同一ディレクトリを両方の用途に使用することも可能であるが、ビルドツリーをソースツリーと分離(out-of-source build)して、ソースツリーを綺麗に保つことが推奨されている。

cmakeコマンドの実行中において、実行時の情報を提供する変数が多数提供されている。
例えば、処理中の位置を取得する場合は、次のような変数がある。

  • CMAKE_SOURCE_DIR (非推奨)
    ソースツリーの絶対パス

  • CMAKE_BINARY_DIR
    バイナリツリーの絶対パス

  • CMAKE_CURRENT_SOURCE_DIR
    ソースツリーにおける現在処理中のディレクトリの絶対パス

  • CMAKE_CURRENT_BINARY_DIR
    ビルドツリー内における現在処理中のディレクトリの絶対パス



CMakeのバージョン管理 (cmake_minimum_requiredコマンド)

CMakeは、新しいツールやプラットフォーム、機能のサポートを追加するために継続的に更新および拡張されている。
開発者は、新しいリリースごとに後方互換性を維持することに非常に注意を払うことになる。
そのため、ユーザがCMakeの新しいバージョンに更新した時、プロジェクトは以前と同じようにビルドし続けることができる。

時には、CMakeの特定の動作を変更する必要があったり、より厳しいチェックや警告が新しいバージョンで導入されたりすることがある。
全てのプロジェクトに直ちにこのような対応を要求するのではなく、CMakeは、プロジェクトが"CMakeのバージョン X.Y.Zのように振る舞う"というようなポリシー機構を提供する。

CMakeのバージョン動作の詳細を指定する主な方法は、cmake_minimum_requiredコマンドを使用することである。
これは、CMakeLists.txtファイルの最初の行で記述するべきであり、プロジェクトの要件が他の何よりも先にチェックされ、確立されるようにするものである。
cmake_minimum_requiredコマンドは、以下に示す2つのことを実行する。

  • プロジェクトが必要とするCMakeの最小バージョンを指定する。
    CMakeLists.txtファイルが指定されたバージョンより古いCMakeのバージョンで処理された場合、エラーが発生して直ちに停止する。
    これにより、処理を進める前に、CMakeの機能の特定の最小セットが利用可能であることが保証される。
  • CMakeの動作を指定されたバージョンに一致させるためのポリシー設定を強制するものである。


もし、cmake_minimum_requiredコマンドを記述しなかった場合は警告を出力する。
ほとんどのプロジェクトでは、cmake_minimum_requiredコマンドは、単に必要最小限のCMakeのバージョンを指定するものとして扱えば十分である。

cmake_minimum_required(VERSION <メジャー番号>.<マイナー番号>[.<バグフィックス番号>.<ビルド番号>])


VERSIONオプションは常に必要であり、<メジャー番号>および<マイナー番号>も記述する必要がある。
ほとんどのCMakeプロジェクトでは、<バグフィックス番号>および<ビルド番号>を指定する必要はない。(CMakeの新しい機能はマイナー番号のアップデートでのみ現れるため)
特定のバグフィックスが必要な場合のみ、<バグフィックス番号>を指定すべきである。
さらに、CMake 3.0以降では、<ビルド番号>を使用していないため、指定する必要はない。

詳細を知りたい場合は、Qtの設定 - CMake#CMakeのポリシー設定において、cmake_minimum_requiredコマンドの挙動を調整する方法を参照すること。
これにより、CMakeは内部でバグを修正し、新しい機能を導入しながらも、過去の特定のリリースの期待される振る舞いを維持することができる。


projectコマンド

全てのCMakeプロジェクトは、projectコマンドを含む必要があり、cmake_minimum_requiredコマンドの後に記述する。

 # CMake 3.0以降
 project(
    <プロジェクト名>
    [VERSION <メジャー番号>[.<マイナー番号>.<バグフィックス番号>.<ビルド番号>]]
    [LANGUAGES <プログラム言語名  例. CやCXX等>]
 )
 
 # CMake 2.x以前
 project(
    <プロジェクト名>
    [<プログラム言語名  例. CやCXX等>]
 )


<プロジェクト名>オプションは必須であり、英数字、アンダースコア(_)、ハイフン(-)が使用できる。
ただし、空白は使用できない。
<プロジェクト名>は、引用符で囲む必要はない。

VERSIONオプションは、CMake 3.0以降でのみサポートされている。

LANGUAGESオプションは、CMakeプロジェクトで有効にするプログラム言語を指定する。(CCXXFortranASMJava等)
複数の言語を指定する場合は、それぞれをスペースで区切る。

特殊な状況下では、CMakeプロジェクトはプログラム言語を使用しないことを示す必要がある場合が存在する。
このような場合、LANGUAGES NONEを指定する。
CMake 2.x以前では、LANGUAGESオプションがサポートされていないため、<プロジェクト名>の後にプログラム言語を指定する。

projectコマンドの重要な役割の1つは、有効化された各言語のコンパイラをチェックして、正常にコンパイルとリンクを行えるかどうかを確認することである。
コンパイラやリンカのチェックが正常に終了した後、その結果はキャッシュされて、ビルドディレクトリのCMakeCache.txtファイルに保存される。
その後、CMakeは有効化された機能を制御する多くの変数とプロパティをセットアップする。

projectコマンドの値は、変数PROJECT_NAMEに自動的に設定される。
例えば、プロジェクトのCMakeLists.txtファイル内で${PROJECT_NAME}を使用することにより、プロジェクト名を参照することができる。


実行ファイルの構築 (add_executableコマンド)

add_executableコマンドは、CMakeにソースファイルのセットから実行ファイルを作成する。

 add_executable(
    <ターゲット名>
    <ソースコードファイル名 1>
    <ソースコードファイル名 2>
    # ...略
 )
 
 # 例. myAppまたはmyApp.exeという名前の実行ファイルが生成される
 add_executable(
    myApp
    main.cpp
 )


<ターゲット名>には、英数字、アンダースコア、ハイフンを使用することができる。
CMakeプロジェクトのビルドが正常に終了すると、<ターゲット名>として実行ファイルが作成される。

ターゲット名は、CMakeの機能であるターゲットプロパティでカスタマイズすることもできる。
また、ターゲット名を変更して、add_executableコマンドを複数回呼び出すことにより、1つのCMakeLists.txtファイル内で複数の実行ファイルを定義することができる。

ただし、同じターゲット名を複数のadd_executableコマンドで使用する場合は、CMakeは失敗してエラーがハイライトされる。

 # CMakeプロジェクトの例
 cmake_minimum_required(VERSION 3.2)
 
 # C++コンパイラは未使用とするため、プラットフォームが持っていない場合に備えて、projectコマンドで使用されないように指定する
 project(MyApp VERSION 4.7.2 LANGUAGES C)
 
 # メインの実行ファイルを生成
 add_executable(
    mainTool
    main.c
    debug.c # リリースビルドのために最適化される
 )
 
 # サブの実行ファイルの生成
 add_executable(
    testTool
    testTool.c
 )



ライブラリの構築 (add_libraryコマンド)

CMakeは様々な種類のライブラリのビルドをサポートしている。
ライブラリを作成して使用する場合、add_libraryコマンドを使用する。

 add_library(
    <ターゲット名>
    [<STATIC または SHARED または MODULE>]
    [EXCLUDE_FROM_ALL]
    <ソースコードファイル名 1>
    <ソースコードファイル名 2>
    # ...略
 )


<ターゲット名>は、CMakeLists.txtファイル内でライブラリを参照するために使用される。
ビルドされたライブラリ名は、<ターゲット名>となる。

EXCLUDE_FROM_ALLオプションは、add_executableコマンドと同様、ライブラリがデフォルトのALLターゲットに含まれないようにする。
構築するライブラリの種類は、STATICSHAREDMODULEのうちの1つを指定する。

  • STATICオプション
    静的ライブラリまたはアーカイブを指定する。
    Windowsでは、デフォルトのライブラリ名は、<ライブラリファイル名>.libとなる。
    Linuxでは、デフォルトのライブラリ名は、lib<ライブラリファイル名>.aとなる。
  • SHARED
    共有ライブラリまたは動的ライブラリを指定する。
    Windowsでは、デフォルトのライブラリ名は、<ライブラリファイル名>.dllとなる。
    Linuxでは、デフォルトのライブラリ名は、lib<ライブラリファイル名>.soとなる。
  • MODULE
    共有ライブラリに似ているが、ライブラリや実行ファイルに直接リンクされるのではなく、実行時に動的にロードされることを意図しているライブラリを指定する。
    これらは、ユーザがロードするかどうかを選択できるプラグイン、または、オプションのコンポーネントである。
    Windowsでは、DLL向けのインポートライブラリは作成されない。


よほどのことがない限り、STATICまたはSHAREDオプションは、それが必要であることが分かるまで付加しないことを推奨する。
これにより、プロジェクト全体を通して、スタティックライブラリかダイナミックライブラリかをより柔軟に選択することができる。

ビルドするライブラリの種類を定義するオプションを省略することも可能である。
特定のライブラリが必要でない限り、プロジェクトファイルには指定せずに、プロジェクトの構築時に開発者が選択できるようにすることが望ましい。
そのような場合、ライブラリはSTATICSHAREDのどちらかになり、変数BUILD_SHARED_LIBSの値により選択される。

変数BUILD_SHARED_LIBStrueを代入する場合はライブラリのターゲットはダイナミックライブラリ、それ以外の場合はスタティックライブラリとなる。
add_libraryコマンドを呼び出すたびに変更する必要がなく、1ヶ所に記述するだけで設定を変更することができる。
ただし、add_libraryコマンドを呼び出す前に、変数BUILD_SHARED_LIBSを記述する必要がある。

 set(BUILD_SHARED_LIBS YES)


変数BUILD_SHARED_LIBSを設定する方法として、cmakeコマンドに-DBUILD_SHARED_LIBSオプションを付加する方法がある。

cmake -DBUILD_SHARED_LIBS=YES /path/to/source



ターゲットのリンク

例えば、ライブラリAがライブラリBを必要とする時、ライブラリ間に存在し得る依存関係には、いくつかの異なる種類がある。

  • PRIVATE
    プライベートな依存関係とは、ライブラリAが自身の内部実装でライブラリBを使用することを指定する。
    ライブラリAにリンクする他のものは、ライブラリBについて知る必要はない。なぜなら、ライブラリBはライブラリAの内部実装だからである。
  • PUBLIC
    パブリックな依存関係とは、ライブラリAが内部でライブラリBを使用するだけでなく、そのインターフェイスでもライブラリBを使用することを指定する。
    例えば、ライブラリAで定義された関数が、ライブラリBで定義・実装された型のパラメータを少なくとも1つ持つ場合、
    ライブラリBの型を持つパラメータを提供しなければ、ライブラリAからその関数を呼び出すことができない。
  • INTERFACE
    インターフェースな依存関係とは、ライブラリAを使用するために、ライブラリBの一部も使用しなければならないことを指定する。
    これは、ライブラリAが内部的にライブラリBを必要とせず、そのインターフェイスでライブラリBを使用するだけであるという点で、PUBLICとは異なる。

    add_libraryコマンドのINTERFACE形式を使用して定義されたライブラリターゲットを使用する場合に便利である。
    例えば、ヘッダファイルのみのライブラリの依存関係を表すためにターゲットを使用する場合等が挙げられる。


target_link_librariesコマンドを使用して、リンクだけではなく、依存関係を捉えることができる。

 target_link_libraries(
    <ターゲット名>
    <PRIVATE または PUBLIC または INTERFACE>
    item 1
    item 2
    # ...略
    [<PRIVATE または PUBLIC または INTERFACE>]
    item 3
    item 4
    # ...略
 )


これにより、あるライブラリが他のライブラリにどのように依存するかをプロジェクトで定義することができる。
CMakeは、この方法でリンクされたライブラリの連鎖を通して依存関係を管理する。

以下の例では、uiライブラリはPUBLICとしてcollectorライブラリとリンクされているため、myAppが直接collectorにリンクしているだけでも、そのPUBLIC関係からmyAppはuiにもリンクされる。
一方、algoライブラリとengineライブラリは、PRIVATEとしてcollectorにリンクされているため、myAppはそれらに直接リンクされるわけではない。
これらのコマンドにより、ターゲット間でtarget_link_librariesコマンドで接続された場合、コンパイラやリンカのフラグやヘッダ検索パスもターゲット間で伝達されるようになる。

 add_library(collector src1.cpp)
 add_library(algo src2.cpp)
 add_library(engine src3.cpp)
 add_library(ui src4.cpp)
 add_executable(myApp main.cpp)
 
 target_link_libraries(
   collector
   PUBLIC ui
   PRIVATE algo engine
 )
 
 target_link_libraries(myApp PRIVATE collector)


このような場合、target_link_librariesコマンドで使用するターゲット名は、
target_link_librariesコマンドが呼び出されている同じディレクトリにおいて、add_executableコマンドまたはadd_libraryコマンドにより定義されている必要がある。


非ターゲットのリンク (target_link_librariesコマンド)

target_link_librariesコマンドを呼び出す時は、常に、PRIVATEPUBLICINTERFACEオプションを指定する。

target_link_librariesコマンドは、CMakeのターゲット以外にも、以下に示すものを指定することが可能である。

  • ライブラリファイルへのフルパス
    CMakeはそのライブラリファイルをリンカコマンドに追加する。
    ライブラリファイルが変更された場合、CMakeはその変更を検出して、ターゲットを再リンクする。
    CMake 3.3以降では、リンカコマンドは指定されたフルパスを使用するが、CMake 3.2以前では、CMakeが代わりにライブラリを検索するようリンカに要求する場合がある。
    (例. /usr/lib/libfoo.soファイルを-lfooに置換する等)
  • 単純なライブラリ名
    パスなしでライブラリ名だけを指定する場合、リンカコマンドはそのライブラリを検索する。
    (例えば、プラットフォームによって、fooは-lfoo、または、foo.libになる)
  • リンクフラグ
    特殊なケースとして、-lや-framework以外のハイフンで始まる項目は、リンカコマンドに追加されるフラグとして扱われる。

    ただし、これらはPRIVATE項目にのみ使用するように警告している。
    PUBLICINTERFACEとして定義する場合は、他のターゲットに引き継がれてしまい、必ずしも安全とは言えないからである。


上記に加え、項目の前にdebugoptimizedgeneralのいずれかのオプションを付加することができる。
これらのオプションは、ビルドがデバッグビルドとして設定されているかどうかに基づいて、それに続く項目が含まれるべき時をさらに絞り込むことである。
ただし、これらのオプションは、同様の機能をより明確で柔軟かつ堅牢な方法が別に存在するため、現在は使用しない方がよい。

  • debugオプション
    項目の前にdebugオプションを付加する場合、そのビルドがデバッグビルドである場合にのみ追加される。
  • optimizedオプション
    項目の前にoptimizedオプションを付加する場合、ビルドがデバッグビルドでない場合のみ追加される。
  • generalオプション
    項目の前にgeneralオプションを付加する場合、全てのビルド構成に対して項目を追加するように指定する。



非ターゲットのインクルード (target_include_directoriesコマンド)

find_packageコマンドは、includeコマンドで手動で行う設定を、REQUIREDオプション等を使用して自動で行うことができる。

find_package(PkgConfig)


find_packageコマンドは、pkg_search_moduleコマンドよりも柔軟でより多くのオプションが存在する。
また、CMakeには豊富なパッケージ定義が付属しており、パッケージ管理システムからインストールしたソフトウェアは/usr/share/cmake/Modules/Find*.cmakeファイルにある。

target_include_directories(<ターゲット名> PUBLIC ...)コマンドは、
<ターゲット名>を使用する全てのターゲットにおいて、自動的にインクルードディレクトリが使用されるようになる。
ただし、CMakeLists.txtファイル内にターゲットにのみ有効であり、pkg_search_moduleコマンドで取得したライブラリに対しては機能しない。

 find_package(PkgConfig REQUIRED)
 pkg_check_modules(<任意の変数名> <.pcファイル内の[Name]セクション名  例. sdl2> REQUIRED IMPORTED_TARGET)
 
 # ...略
 
 target_link_libraries(<ターゲット名>
    ${<任意の変数名>_LIBRARIES}
 )
 
 target_include_directories(<ターゲット名> PUBLIC
    ${<任意の変数名>_INCLUDE_DIRS}
 )
 
 target_compile_options(<ターゲット名> PUBLIC
    ${<任意の変数名>_CFLAGS}
    # または
    ${<任意の変数名>_CFLAGS_OTHER}
 )



CMAKEキャッシュ変数

CMAKE_PREFIX_PATH

 # ライブラリのインストールディレクトリを指定
 ## ライブラリのインストールディレクトリを1つ指定する場合
 set(CMAKE_PREFIX_PATH "<ライブラリのパス>")
 
 ## ライブラリのインストールディレクトリを複数指定する場合
 set(CMAKE_PREFIX_PATH "<ライブラリのパス 1>;<ライブラリのパス 2>;<ライブラリのパス 3>")
 
 # find_packageコマンドを使用してライブラリを検索
 find_package(<ライブラリ名> REQUIRED)
 
 if(<ライブラリ名>_FOUND)
    # ライブラリが見つかった場合
 else()
    # ライブラリが見つからなかった場合
 endif()



find_packageコマンド

find_packageコマンドとは

find_packageコマンドは、特定の名前のCMakeスクリプトを検索して、該当のスクリプトを実行するコマンドである。

対象となるCMakeスクリプトは、その役割に応じて、ModuleとConfigの2種類に分類される。
find_packageコマンドは、Module -> Configの順で検索を行う。

具体的な動作は以下のとおりです。

  • Module
    <パッケージ名>.cmakeファイル、または、Find<パッケージ名>.cmakeファイルを検索する。
    • CMakeキャッシュ変数である変数CMAKE_MODULE_PATHで指定されたパス
    • CMakeにデフォルトで付いてくるモジュールの配置ディレクトリ (/usr/local/share/cmake/Modulesディレクトリ等)

  • Config
    <パッケージ名>Config.cmakeファイル、または、<パッケージ名(小文字)>-config.cmakeファイルを検索する。
    • CMake変数<PackageName>_DIRで指定されたパス
    • CMakeキャッシュ変数である変数<パッケージ名>_ROOTで指定されたパス
    • 環境変数<パッケージ名>_ROOTで指定されたパス
    • CMakeキャッシュ変数である変数CMAKE_PREFIX_PATH、変数CMAKE_FRAMEWORK_PATH、変数CMAKE_APPBUNDLE_PATHで指定されたパス
    • 環境変数<パッケージ名>_DIR、環境変数CMAKE_PREFIX_PATH、環境変数CMAKE_FRAMEWORK_PATH、環境変数CMAKE_APPBUNDLE_PATHで指定されたパス
    • 環境変数PATHで指定されたパス (binディレクトリおよびsbinディレクトリで終わるディレクトリの場合は、その親ディレクトリに読み替える)
    • その他


なお、コマンド終了時において、CMake変数<パッケージ名>_FOUND0または1(FALSEまたはTRUE)が格納されるため、
正常に検索できたかどうかを条件分岐として記述することもできる。

Module (CMakeスクリプト)

Moduleは、以下の2種類に分類される。

  • Utility Modules
    よく使用するCMake関数をまとめたもの。
    例えば、CheckLanguage.cmake(あるプログラミング言語の開発ツールがインストールされているか確認するための機能をまとめたもの)等がある。
    Utility ModulesのCMakeスクリプトを読み込む時、check_languageコマンド、get_bundle_main_executableコマンド等の関数が定義されるため、
    それらを呼び出すことで機能を使用することができる。

  • Find Modules
    外部ライブラリの情報を収集するものであり、ファイル名はFind<パッケージ名>.cmakeファイルである。
    例えば、C++で記述された外部ライブラリをリンクする場合、ヘッダファイルのディレクトリ、ライブラリファイルのディレクトリ、ライブラリファイル名の3つの情報が必要になる。
    これらの情報を自動的に収集するものがFind Modulesである。

    CMakeでは、Boost、BLAS、GLEW、OpenGL、X11、Qt4等のライブラリのFind Modulesがデフォルトで用意されている。

    ほとんどのFind Modulesは、以下に示すようなCMake変数に値が自動的に代入される。
    • 変数<パッケージ名>_INCLUDE_DIRS
      インクルードディレクトリのパス
    • 変数<パッケージ名>_LIBRARY_DIRS
      ライブラリファイルがあるディレクトリのパス
    • 変数<パッケージ名>_LIBRARIES
      ライブラリファイル名のリスト
    • 変数<パッケージ名>_DEFINITIONS
      コンパイル時のdefinitionフラグ

    find_packageコマンドを使用することにより、上記の変数は自動的に代入されるため、その変数をtarget_include_directoriesコマンド等に設定を記述する。
    ただし、上記の変数名は必ずしもこの通りになっているわけではないため、ライブラリの情報を変数にまとめるのではなくターゲット(ライブラリ)を作成してそのプロパティにまとめる場合もある。
    そのため、各ライブラリのFind Modulesを確認することが必要である。


CMakeでは、初期状態でいくつかのModuleがインストールされている。

Moduleの一覧は、CMakeの公式Webサイトから確認することができる。

また、Moduleが存在しないライブラリでは、開発者がModuleを作成することもできる。

Config (CMakeスクリプト)

Configファイルは、CMakeプロジェクトをビルドしてライブラリをインストールした時に生成される設定ファイルである。
ファイル名は、<パッケージ名>Config.cmakeファイル、または、<パッケージ名(小文字)>-config.cmakeファイルである。

ライブラリ情報を提供する点ではFindモジュールと同様であるが、Configファイルはライブラリの開発元が直接提供している設定ファイルである。
ライブラリをインストールした時、cmakeディレクトリやshareディレクトリの中に含まれていることが多い。

Findモジュールと同様、多くのConfigファイルは以下に示すようなCMake変数が存在する。
また、これらの情報を1つのターゲットにまとめている場合もある。

  • 変数<パッケージ名>_INCLUDE_DIRS
    インクルードディレクトリのパス
  • 変数<パッケージ名>_LIBRARY_DIRS
    ライブラリファイルがあるディレクトリのパス
  • 変数<パッケージ名>_LIBRARIES
    ライブラリファイル名のリスト
  • 変数<パッケージ名>_DEFINITIONS
    コンパイル時のdefinitionフラグ


Configファイルを使用する場合、開発元が公開しているドキュメントやConfigファイルの内容から、各変数名や各ターゲット名を確認する必要がある。

Linuxにおいて、Configファイルの場所は以下に示すようなディレクトリに配置される。
また、初期設定より、/usr/binディレクトリは環境変数PATHに設定されているため、CMakeは自動的にConfigファイルを見つけることができる。

  • /usr/lib64/cmake/<パッケージ名>
  • /usr/share/cmake/<パッケージ名>


したがって、Linuxでは、パッケージ管理システムからインストールしたライブラリは、find_packageコマンドで見つけることができる。
もし、ライブラリにConfigファイルが付属していない場合、開発者自身でFindモジュールを作成する必要がある。

また、他の場所にライブラリをインストールしている場合は、CMakeキャッシュ変数<パッケージ名>_DIRにライブラリのパスを設定することにより、Configファイルを見つけることができる。


CMakeとqmakeの特徴

qmake CMake
レシピファイル <Qtプロジェクト名>.pro CMakeLists.txt
レシピの呼び方 プロジェクトファイル CMakeソースコード
特徴 変数ベース コマンドベース
ターゲット 1ファイル 1ターゲット 1ファイル 複数ターゲット
プロジェクト間の連携 不可 ターゲット名でリンク可能
Qtのサポート
Qt Creatorの対応 ファイルの追加等の対応がイマイチ
カスタムコンパイラの対応
Packageの作成


レシピファイルの違い

qmakeとCMakeは、レシピの考え方が異なる。
qmakeは、変数SOURCES等のような変数に適切な値を設定していくことにより、対応するアーキテクチャの設定ファイル等から取得した値を使用して、
MakefileやVisual Studio / XCode向けのプロジェクトファイルを生成する。
CMakeは、コマンドと呼ばれる関数の引数に、ターゲットの情報、Define情報、インクルードパス等をプログラムして、Makefileや各種プロジェクトファイルを生成する。

 # qmakeの場合
 TEMPLATE = app
 CONFIG -= qt        # Qtライブラリを使用しない場合は、変数CONFIGからqtを削除する
 SOURCES = main.cpp
 
 # CMakeの場合
 cmake_minimum_required(VERSION 3.15)
 
 project(HelloWorld LANGUAGES CXX)
 add_executable(helloworld main.cpp)


以下の例は、Qt Coreライブラリのみを使用するQtソフトウェアにおける最低限のレシピファイルである。

CMakeは汎用的なツールのため、Qtライブラリを使用する場合、CMakeのコマンド量が増加する。
find_package関数を使用してQtライブラリ全体からQt Coreライブラリを要求することにより、
Qt Coreライブラリを変数Qt5::Core(Qt6では、Qt6::Coreとなる)経由で、target_link_librariesに指定してリンクを指示する。

 # qmakeの場合
 TEMPLATE = app
 QT -= gui           # qmakeの初期状態はGUIが有効なため、変数QTからguiを削除することにより、Qt Coreライブラリのみとなる
 SOURCES = main.cpp hello.cpp
 HEADERS = hello.h
 
 # CMakeの場合
 cmake_minimum_required(VERSION 3.15)
 
 project(HelloWorld LANGUAGES CXX)
 
 set(CMAKE_AUTOMOC ON)
 set(CMAKE_AUTORCC ON)
 
 find_package(Qt5 COMPONENTS Core REQUIRED)
 
 add_executable(helloworld
    main.cpp
    hello.cpp
    hello.h
 )
 
 target_link_libraries(helloworld 
    Qt5::Core
 )


共通の機能

  • サブディレクトリ(サブプロジェクト)に対応
  • ファイルの読み書き
  • 独自関数の定義
  • 別ファイルに分割したレシピをインクルードできる
  • 自動的にmocを呼び出す
  • Qtのリソースファイル対応
  • インストールの定義
  • 繰り返し文や条件分岐文
  • プラットフォームの判定
  • インストール
  • テストの設定および実行
  • 追加機能およびモジュール等の提供


CMakeの強みは、カスタムコンパイラ、機能の拡張性、パッケージ作成まで行う機能が提供されていることである。
qmakeでは、QTestによりテストの設定および実行もできるが、ビルドを補助するツールとしての特色が強い。
CMakeでは、CTest、CPack等の機能により、テストやパッケージ作成も可能である。

qmakeもCMakeも制御構文を持ち複雑なプロジェクトの作成も可能であるが、プロジェクトでの採用検討という視点から将来性も含めて考慮した場合、CMakeの方が有力である。


変数

変数の宣言と代入

変数は、setコマンドを使用して、変数を宣言および代入することができる。
CMakeでは、全ての変数を文字列として扱っており、変数名には、英数字、_./-+を含めることができる。
また、大文字と小文字が区別される。

変数の値を代入する時、値にスペースが含まれていない限り、引用符で囲む必要はない。

 set(value 1)  # 値が1の変数valueを宣言


既存の変数への代入もsetコマンドを使用する。
変数の値の参照は、入れ子にすることもできる。

 set(value 1)  # 値が1の変数valueを生成
 set(value 2)  # 既存の変数valueに値1を代入
 set(value_name value)  # 変数value_nameに変数valueの値を代入
 
 message(${${value_name}}) # ${value_name}が展開されて、${${value_name}}が${value}になり、これが展開されて2になる


1つの変数に複数の値を指定する場合、各値はセミコロンで区切って結合される。(文字列リストとして代入される)
また、未定義の変数を参照する場合は、空の文字列となる。

 set(myVar a b c)    # myVarの値: a;b;c
 set(myVar a;b;c)    # myVarの値: a;b;c
 set(myVar "a b c")  # myVarの値: a b c
 set(myVar a b;c)    # myVarの値: a;b;c
 set(myVar a "b c")  # myVarの値: a;b c


 set(USE_CCACHE OFF CACHE BOOL "")


変数の削除

変数の削除する場合は、unsetコマンドを使用する。

 set(value 1)  # 変数valueを宣言
 unset(value)  # 変数valueを削除


キャッシュ変数

キャッシュ変数とは、ビルドディレクトリのCMakeCache.txtに記録される変数のことである。
グローバルスコープで参照可能であり、生存期間がcmakeコマンドの終了後も継続する。

次のcmakeコマンドの実行でも値を保つため、生成したビルドの構成情報を保存するような用途にも使用される。

例えば、キャッシュ変数VAR1の値は$CHACHE{VAR1}で参照できる。
同名の通常変数が存在しない場合は、${VAR1}でも参照することができる。

set(CACHE)コマンドで初回の変数が定義ができる。

既に定義されているキャッシュ変数を変更する場合は、FORCEオプションが必要である。
指定されていない場合、set(CACHE)コマンドは無視される。
また、cmakeコマンドの実行時に-D<オプション名>でも定義や変更が可能である。

他の変数と異なり、キャッシュ変数は型を持つ。
cmakeコマンドの-D<オプション名>オプションで型を指定せずに定義することにより、型のない状態で使用することもできる。
この場合、set(CACHE)コマンドを実行した時点で型が確定する。

リスト(配列)

変数は全て文字列として扱われるが、複数の変数をまとめて扱う場合はリストを使用する。
これは、複数の文字列をセミコロン;で連結したものである。

リストという型は無く文字列であるが、関数の引数が暗黙的に連結されてリストとして扱うことができ、操作関数により言語レベルでのサポートがあるため、
不定個数の変数をまとめて扱う場合に便利である。

 set(LIST1 "AA" "BB" "CC") # 3つの文字列からなるリストを作成
 message("${LIST1}")       # -> AA;BB;CC
 
 list(APPEND LIST1 "DD")   # リスト末尾に要素を追加
 message("${LIST1}")       # -> AA;BB;CC;DD
 
 list(LENGTH LIST1 LEN)    # リスト要素の個数を取得
 message("${LEN}")         # -> 4
 
 list(GET LIST1 2 ITEM)    # 2番目の要素を取得
 message("${ITEM}")        # -> CC


定義された全ての変数を出力

 get_cmake_property(variableNames VARIABLES)
 
 foreach(variableName ${variableNames})
    message(STATUS "${variableName}=${${variableName}}")
 endforeach()



条件分岐

if()コマンドおよびelseif()コマンドの引数には、そのブロックを実行する条件式を記述する。
この条件式には、以下に示す種類がある。

  • ブール定数
    CMakeの値は全て文字列型であるが、条件式では以下に示す値がBooleanとして認識される。
    これらの値は、大文字・小文字は区別されないことに注意する。


真と解釈される値

  • 0でない数値
  • ON
  • YES
  • TRUE
  • Y
# 例
if(1)
if(TRUE)
if(on)
if(Yes)


偽と解釈される値

  • 空文字列
  • 0
  • OFF
  • NO
  • FALSE
  • N
  • IGNORE
  • NOTFOUND
  • 末尾が-NOTFOUNDである文字列


# 例
if(0)
if(FALSE)
if(off)
if(No)
if(SamplePackage-NotFound)


ブール演算子

論理積を表すAND、論理和を表すOR、否定を表すNOTが使用できる。
これらは、全て大文字でなければならないことに注意する。

# 例
if(true AND true)
if(true OR false)
if(NOT false)


変数の確認

変数が定義済みかどうかの確認、および、数値の比較等を行うことができる。

# 例
if(DEFINED variable)
if(1 LESS 2)


  • 対象の存在の確認
    対象の存在を判定することができる。
# 例
if(DEFINED value)    # value という変数がセットされているかどうか
if(COMMAND message)  # message というコマンドが存在するかどうか
if(POLICY CMP0011)   # CMP0011 というポリシーが存在するかどうか
if(TARGET test)      # test というターゲットが定義されているかどうか
if(EXISTS src/a.cpp) # src/a.cpp というパスが存在しているかどうか


  • 対象の性質の確認
    対象の性質を確認することができる。
# 例
if(IS_DIRECTORY src/)  # src/ がディレクトリかどうか
if(IS_SYMLINK /bin/sh) # /bin/sh がシンボリックリンクかどうか
if(IS_ABSOLUTE /)      # / が絶対パスかどうか


数値および文字列等の比較

  • 数値の比較
    数値の比較することができる。
# 例
if(1 EQUAL 1)
if(1 LESS 2)
if(2 GREATER 1)


  • 文字列のパターンマッチ
    <文字列> MATCHES <正規表現のパターン>
    文字列正規表現のパターンにマッチするかどうかを確認することができる。
    もし、文字列が空文字列、または、存在しない場合は、falseを返す。
# 例
# 文字列がa(1文字以上)から始まり、aの次がbで終わる場合
if("aaaab" MATCHES "^a+b$")


  • 文字列の比較
    文字列を比較することができる。
    辞書順に基づく。
# 例
if("a" STREQUAL "a")   # 文字列が同じ
if("a" STRLESS "aa")   # "a" < "aa"
if("b" STRGREATER "a") # "b" > "a"


  • バージョンを比較
    バージョンを比較することができる。
    バージョンのフォーマットは、<メジャー>[.<マイナー>[.<パッチ>[.<ビルド>]]]である。
    数値的な順序や辞書順には基づかない。
    例えば、0.1という値と0.1.0.0という値は等しいと評価される。
    また、1.19は、1.2より大きいと評価される。
# 例
if(0.1 VERSION_EQUAL 0.1.0.0)  # 0.1 == 0.1.0.0
if(0.1 VERSION_LESS 0.1.1)     # 0.1 < 0.1.1
if(1.19 VERSION_GREATER 1.2)   # 1.19 > 1.2


  • ファイルのタイムスタンプの比較
    ファイル1とファイル2のタイムスタンプを比較することができる。
    ファイル1がファイル2より新しい場合、trueを返す。
    また、いずれかのファイルが存在しない場合も、trueを返す。
# 例
if(build/my_exe IS_NEWER_THAN /usr/local/bin/my_exe)



ログメッセージの出力

messageコマンドとは

 message([<mode>] "message to display" ...)


下表に、<mode>に指定できる項目を示す。(一部)

modeの種類 説明 処理の継続 出力先
省略する場合 重要な情報 CMakeの処理を継続する STDERR
STATUS 情報 CMakeの処理を継続する STDOUT
WARNING 警告 CMakeの処理を継続する STDERR
SEND_ERROR エラー CMakeの処理を継続する STDERR
FATAL_ERROR 致命的なエラー CMakeの処理を終了する STDERR


以下の例では、messageコマンドにおいて、第1引数にFATAL_ERRORを指定することにより、エラーメッセージを出力して、CMakeコマンドを終了している。

 message(FATAL_ERROR "cmake to terminate.")


また、messageコマンドにおいて、第1引数にSEND_ERRORを指定することにより、エラーメッセージを出力して動作を継続する。

 message(SEND_ERROR "Continue cmake.")


以下の例では、Linux以外のOSの場合は、エラーメッセージを出力して、cmakeコマンドを終了している。

 if(NOT (UNIX AND NOT APPLE))
    message(FATAL_ERROR "ERROR! Only Linux can build this software.")
 else()
    set(LINUX TRUE)
 endif()


以下の例では、CMakeの特殊変数であるCMAKE_COMMANDCMAKE_CTEST_COMMANDを出力している。

 message("${CMAKE_COMMAND}")        # /usr/bin/cmake
 message("${CMAKE_CTEST_COMMAND}")  # /usr/bin/ctest



インクルードパスの指定

インクルードパスを指定する場合、include_directoriesコマンドを使用する。

 include_directories(/path/to/include)


include_directoriesコマンドを複数使用する場合、デフォルトでは、指定したパスは最後尾となる。
ただし、include_directoriesコマンドにBEFOREオプションを付加した場合は最前となる。

以下の例では、"-I/path1/to/include -I/path2/to/include"となる。

 include_directories(/path1/to/include)
 include_directories(/path2/to/include)


以下の例では、"-I/path2/to/include -I/path1/to/include" となる。

 include_directories(/path1/to/include)
 include_directories(BEFORE /path2/to/include)



ライブラリパスの指定

ライブラリパスを指定する場合、link_directoriesコマンドを使用する。

 link_directories(/path/to/lib)


link_directoriesコマンドを複数使用する場合、デフォルトでは、指定したパスは最後尾となる。
ただし、link_directoriesコマンドにBEFOREオプションを付加した場合は最前となる。

以下の例では、"-I/path1/to/lib -I/path2/to/lib"となる。

 link_directories(/path1/to/lib)
 link_directories(/path2/to/lib)


以下の例では、"-I/path2/to/lib -I/path1/to/lib" となる。

 link_directories(/path1/to/lib)
 link_directories(BEFORE /path2/to/lib)



qt5_create_translation

TSファイルやTSファイルがあるディレクトリを指定して、Qt LinguistからTSファイルを生成する。
TSファイルは、ビルドディレクトリに保存される同じベースネームのQMファイルにコンパイルされる。

生成されたQMファイルへのパスは、<変数名>に追加される。

作成または更新する翻訳ファイルの拡張子は.tsである必要がある。
与えられたTSファイルのパスが相対パスの場合、現在のプロジェクトのトップディレクトリからの相対パスで解決される。

TSファイルが存在しない場合、qt5_create_translationは何もしない。

lupdateコマンドは、ソースファイルまたはディレクトリを入力として受け付ける。
<オプション>は、lupdateコマンドの実行時に使用するオプションを指定することができる。
指定可能なオプションは、Qtの設定 - Qt Linguist#lupdateコマンドを参照すること。

 qt5_create_translation(<変数名> <TSファイル 1> <TSファイル 2> <TSファイル 3> ... <オプション>)
 
 # 例.
 # まず、プロジェクトのトップディレクトリにあるTSファイルを検索する。(helloworld_en.tsファイルとhelloworld_ja.tsファイル)
 # 次に、lupdateコマンドを実行してTSファイルをコンパイルすることにより、helloworld_en.qmファイルとhelloworld.ja.qmファイルを生成または更新する。
 qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} helloworld_en.ts helloworld_de.ts)