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
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
に書く。