しゃけのブログ

某大学院で研究している人の備忘録

CMakeのチュートリアルをやる(Step2)

CMake Tutorial

CMakeをC++プロジェクトなどで使うことは多いのですが、付け焼刃の知識しかなくて正しい書き方がいまだにわからない... なので初心に戻って、CMakeの公式が作ったチュートリアルをやっていこうと思います。

この記事はその時のメモみたいなものです。

参考はこちら。 チュートリアルの元となるプロジェクトファイルたちもここからダウンロードできます。

CMake Tutorial — CMake 3.25.0-rc3 Documentation

Step2: 自作ライブラリの参照と制御

次の要素が含まれています。

  • 自作ライブラリ(cppファイルとヘッダファイル)の作成
  • ライブラリの参照
  • 使用するライブラリのCMakeLists.txtからの制御

ディレクトリ構成

メインのプロジェクトの下にライブラリディレクトリがある構造です。

Step2
 |-- MathFunctions
 |     |-- CMakeLists.txt
 |     |-- MathFunctions.h
 |     `-- mysqrt.cxx
 |-- CMakeLists.txt
 |-- tutorial.cxx
 `-- TutorialConfig.h.in

ライブラリの作成

Step2/MathFunctions/CMakeLists.txt

add_library(MathFunctions mysqrt.cxx)

add_library

add_library(MathFunctions mysqrt.cxx)

MathFunctionsというライブラリをmysqrt.cxxから作るコマンド。

ここでは、mysqrt.cxxが参照する自作のヘッダファイルはないので、target_include_directoriesなどは不要。

Linuxでビルドしてみると、ビルドディレクトリの下にMathFunctionsというディレクトリができて、libMathFunctions.aという静的ライブラリが作られます。

動的、静的の違いはまた説明するとして、ここではライブラリが作れるんだな~ぐらいの理解でとどめておきます。

ライブラリの参照

メインのCMakeLists.txtから、ライブラリディレクトリのCMakeLists.txtを参照して、実行ファイルへ紐づけます。

Step2/CMakeLists.txt

cmake_minimum_required(VERSION 3.10)

project(Tutorial VERSION 1.0)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

configure_file(TutorialConfig.h.in TutorialConfig.h)

add_subdirectory(MathFunctions)

add_executable(Tutorial tutorial.cxx)

target_link_libraries(Tutorial PUBLIC MathFunctions)

target_include_directories(Tutorial PUBLIC
    "${PROJECT_BINARY_DIR}"
    "${PROJECT_SOURCE_DIR}/MathFunctions"
    )

add_subdirectory

add_subdirectory(MathFunctions)

別のCMakeLists.txtを追加することができます。この時、CMakeLists.txtを含むディレクトリを相対パスで指定しています。

ちなみに、subdirectoryではないディレクトリ(例えば親ディレクトリにあるディレクトリなど、../MathFunctions2とか)を指定すると次のようなエラーが出ます。

  add_subdirectory not given a binary directory but the given source
  directory
  ".../cmake-3.25.0-rc3-tutorial-source/MathFunctions2"
  is not a subdirectory of
  ".../cmake-3.25.0-rc3-tutorial-source/Step2".
  When specifying an out-of-tree source a binary directory must be explicitly
  specified.

階層が異なる時は親ディレクトリにもCMakeLists.txtを置いて、本ディレクトリとMathFunctions2ディレクトリをadd_subdirectoryしてもらう方法をとる方がよい。

MathFunctions2は親ディレクトリの管轄、というイメージ。

target_link_libraries(Tutorial PUBLIC MathFunctions)

このコマンドはかなり重要で今後も頻出する予定です。

Tutorialというターゲットに対して、MathFunctionsというライブラリを紐づけています。今回の場合は、libMathFunctions.aという静的ライブラリを紐づけていることになります。

例によって、PUBLICというキーワードの紹介は保留です。

target_include_directories

target_include_directories(Tutorial PUBLIC
    "${PROJECT_BINARY_DIR}"
    "${PROJECT_SOURCE_DIR}/MathFunctions"
    )

Tutorialがインクルードするヘッダファイルを探索するディレクトリ情報を追加します。

このコマンドは前回も登場しましたが、今回のように複数を一度に指定することもできます。

今回は、MathFunctionsにあるMathFunctions.hの場所を知りたいので、CMakeLists.txtが存在するディレクトリを指す変数である${PROJECT_SOURCE_DIR}を使って、MathFunctionsディレクトリを指定しています。

これによって、${PROJECT_SOURCE_DIR}/MathFunctionsがインクルードの探索ディレクトリとして追加されているので、C++ファイル側では #include "MathFunctions.h"と、MathFunctionsディレクトリから見た相対パスでヘッダファイルを指定できるようになっています。

ビルドの制御

cmake変数を使って、ソースファイルで使用する関数を制御します。

具体的には、CMakeLists.txtでUSE_MYMATHという変数を用意して、これがONならライブラリをビルドしてTutorialターゲットに紐づけ、#ifdefによってライブラリの関数を呼び出すようにします。

逆にOFFなら、MathFunctionsライブラリはビルドせず紐づけも行わず、#ifdefによって標準関数であるsqrtを呼び出すように変更します。

最終的なCMakeLists.txt(Step2の下の)は次の通りです。

cmake_minimum_required(VERSION 3.10)

project(Tutorial VERSION 1.0)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

option(USE_MYMATH "Use tutorial provided math implementation" ON)

configure_file(TutorialConfig.h.in TutorialConfig.h)

if(USE_MYMATH)
    add_subdirectory(MathFunctions)
    list(APPEND EXTRA_LIBS MathFunctions)
    list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")
endif()

add_executable(Tutorial tutorial.cxx)

target_link_libraries(Tutorial PUBLIC "${EXTRA_LIBS}")

target_include_directories(Tutorial PUBLIC
    "${PROJECT_BINARY_DIR}"
    "${EXTRA_INCLUDES}"
    )

option

option(USE_MYMATH "Use tutorial provided math implementation" ON)

変数を追加することができます。

setと混同しそうで注意したいところですが、optionはbool値を扱う時に利用されます。

また、単純なsetを使うと変数の内容が呼び出された瞬間に決まりますが、optionではcmake .. -DUSE_MYMATH=OFFのように内容をコマンド呼び出し時に変更することができます。

ここで、"単純な" といったのはsetにもCACHEという引数を追加することで、cmakeコマンドから内容を制御することができるようになるからです。

ただ、optionはbool値に特化した関数で、依存関係を扱えるようになるなど多機能なので、bool値でコマンドラインから制御したいときには使うべきということですね。

詳細はキャッシュ変数というキーワードで扱われるようです。詳しくはここでは扱わないことにします。

if - endif

if文です。

elseもあります。

list(APPEND...)

list(APPEND EXTRA_LIBS MathFunctions)

EXTRA_LIBSというリスト変数にMathFunctionsを追加します。

もしEXTRA_LIBSがない場合は新たにリスト変数が作られます。

ここで作られたリスト変数は参照されたとき展開される特性を持ちます。

つまり、

list(APPEND EXTRA_LIBS MathFunctions1)
list(APPEND EXTRA_LIBS MathFunctions2)

と変数を作って、

target_link_libraries(Foo PUBLIC "${EXTRA_LIBS}")

とすると次のように扱われます。

target_link_libraries(Foo PUBLIC MathFunctions1 MathFunctions2)

cmakedefine

#cmakedefine USE_MYMATH

値を持たない変数に対して、#ifdefを使うために変数を用意するためのコマンド。 configure_fileの対象になる、TutorialConfig.h.inに書く。