CMake
CMakeは、C/C++プロジェクト向け、マルチプラットフォームのビルドツールです。
僕が初めてプログラミングに触りC/C++を勉強していた当時、難しそうという雰囲気から避けてしまっていたツールでもありました。しかし、現在はPythonライブラリ開発で高速化のためにpybindを用いて処理をC++化したり、大学でのロボコンではOpenCV、ROSなどを用いた大きめのプロジェクト開発をしたりするため、CMakeをガッツリ利用しています。
CMakeは10年以上前から開発されているツールですが、現在も、これから先も使い続けられるツールだと思います。今回はQiitaの「Qiita 10周年記念イベント - 10年後のために今勉強しておきたい技術」というテーマで、難しそうと思っていた過去の自分にも分かるように、そして今からCMakeを勉強しようと思っている人の資料となるように、という思いを込めて投稿しようと思います。
プロジェクトにCMakeを導入する時には、ガイドラインをまとめてくれている記事に目を通した上で、レファレンスを適宜参照することを推奨する。
-
CMake Reference Documentation
- 利用しようとしているコマンドはそのCMakeのバージョンで利用できるのかはドキュメントとよく照らし合わせるようにしたほうが良い。新しいバージョンでは利用しやすいように機能が増えていることもあり、それはドキュメントを読めば書いてあり、古いバージョンを利用する場合にはどうするかも書いてある1。
-
CMakeスクリプトを作成する際のガイドライン - Qiita
- 参考文献も合わせて、眺めておいて、何が良くないとされているのかは知っておくと良い
環境
ちなみに、CMake 3.13.0のリリースは2018年11月20日。
$ cmake --version
cmake version 3.13.4
対象とするプロジェクトの構造
今回は、ライブラリをビルドし、ヘッダファイルと共に適切な場所に配置するようなプロジェクトを作成する。
また、このライブラリをリンクさせるソースコードのサンプルも任意で生成出来るようにする。
ここでは以下のようなmylib
ライブラリのプロジェクト構造を作成したとして進める。
- include : ライブラリのヘッダファイル
-
#include "mylib/mylib.h"
としてインクルードすることを想定している
-
- lib : ライブラリのヘッダファイルに対応するソースファイル
-
include/mylib/mylib.h
とlib/mylib/mylib.c
が対応する
-
- examples :
mylib
ライブラリを利用したソースコードのサンプル
.
├── CMakeLists.txt
├── include
│ └── mylib
│ └── mylib.h
├── lib
│ └── mylib
│ ├── CMakeLists.txt
│ └── mylib.c
└── examples
├── CMakeLists.txt
└── main.c
基本的なビルド方法
基本的なビルド方法としては、以下のような流れを想定している。
後ほど、#高度なビルド方法で、ビルドする際に利用できる重要なオプションについて触れる。
$ mkdir build
$ cd build
$ cmake ..
$ make #ライブラリのビルド
$ make examples #ライブラリを利用したサンプルをビルドしてバイナリを生成する
ライブラリをインストールする際には、CMAKE_INSTALL_PREFIX
を定義する。詳細は# ビルドしたライブラリのインストール方法で書いている。
$ cmake .. -DCMAKE_INSTALL_PREFIX='$HOME/.local'
$ make install
オブジェクトファイル*.o
やライブラリ、バイナリなどの成果物を削除する場合はclean
ターゲットが用意されている。
$ make clean
ビルド環境を作成しなおす場合、大抵の場合はcmake ...
のコマンドで作り直しが出来る。
しかし、オプションをつけている場合、cmake ...
のコマンドでは消えない場合があるので、何か変だと思った場合は、一度build
フォルダを削除してやり直すことも良い。
$ rm -rf build
$ mkdir build
...
CMakeのデバッグ方法
手順を追っている時に、詰まった時の助けになるであろうデバッグ方法をいくつか紹介する。
CMakeがキャッシュしている変数の値を表示する
$ cmake .. -LAH
キャッシュしている変数を-L
、ADVANCEDに設定された変数も表示するときは-A
、変数名についている説明を表示するなら-H
をつける
cmake(1) - CMake Documentation #Generate a Project Buildsystem の-L[A][H]
の項目を参照。
traceする
$ cmake .. --trace-source=CMakeLists.txt
CMakeLists.txtに記述された内容のどこを実行したのかをtraceできる。
$ cmake .. --trace
CMakeが自動的に生成して読み込んで実行したものも含めてtraceする。
また、それぞれで、変数が展開された状態でログ出力をしたい場合は--trace-expand
が利用できる。
$ cmake .. --trace-source=CMakeLists.txt --trace-expand
$ cmake .. --trace --trace-expand
cmake(1) - CMake Documentation #Generate a Project Buildsystem の--trace
の項目辺りを参照。
makeした時のコンパイルされるコマンドを出力する
$ make VERBOSE=1
gcc -I./include -o mylib.c.o -c mylib.c
みたいなのを眺めたかったらVERBOSE=1
をつける
プロジェクトルートのCMakeLists.txt
# 動作確認できたCMakeのバージョン
cmake_minimum_required(VERSION 3.13)
# プロジェクトの情報
project(mylib
VERSION 1.0
DESCRIPTION "mylib project"
LANGUAGES C
)
# in-source ビルドガード
if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR})
message(FATAL_ERROR "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there. You may need to remove CMakeCache.txt. ")
endif()
# 以下のフォルダにあるCMakeLists.txtも読み込んでビルドターゲットを生成する
add_subdirectory(lib/mylib)
add_subdirectory(examples EXCLUDE_FROM_ALL)
基本的にはこんな感じ。
cmakeのバージョン
cmake_minimum_required(VERSION 3.13)
まずは、CMakeのバージョンを設定しておく。ここには、動作確認出来ている最も低いバージョンを記載しておくべきで、動作を保証するための何よりも一番最初に設定しておくポリシーでもある。ここでは、今回利用したCMakeのバージョンが3.13だったので、このようにしている。
プロジェクトの情報
project(mylib
VERSION 1.0
DESCRIPTION "mylib project"
LANGUAGES C
)
プロジェクトの情報をprojectに記載しておく。これによって、PROJECT_NAME
など、いくつかの変数名がセットされる。
project - CMake Documentation
in-sourceビルドガード
# guard against in-source builds
if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR})
message(FATAL_ERROR "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there. You may need to remove CMakeCache.txt. ")
endif()
この部分を説明する前に、まず、ビルドの方法には、in-sourceと呼ばれる方法とout-of-sourceと呼ばれる方法の2種類があるという話をしておく。
cmakeを使った方法として、以下のようにbuild
フォルダを作成してからビルドする方法をよく見かけると思う。これはout-of-sourceと呼ばれるビルド方法で、out-of-sourceビルドをすることを強く推奨する。
$ mkdir build
$ cd build
$ cmake ..
$ make
in-sourceの問題点はCMake : out-of-sourceビルドで幸せになる - Qiitaから引用すると、以下の通り。out-of-sourceを使った時はこれらの問題を解決でき、その話はQiitaの記事を参照してほしい。
# in-sourceビルド (
cmake .
) の何が問題なのか?
まずは、in-source ビルドのどこが問題なのか、確認します。
- git でソース管理する際に、ビルド成果物や cmakeコマンドが生成する各種ファイルによってリポジトリが汚れしまい
git status
した際に、本来追加すべきファイルを見落とす可能性がある- cmakeが生成したファイルをまとめて消したいときにディレクトリごとに
CMakeCache.txt
やCMakeFiles
ディレクトリを消すのが苦痛- Debugビルド、Releaseビルドを分けたい時に、ソースツリーを別々に用意する必要がある
https://qiita.com/osamu0329/items/7de2b190df3cfb4ad0ca#in-sourceビルド-cmake--の何が問題なのか
さて、本題に戻るが、ここではin-sourceビルドガードを記述していた。
前述のような問題で開発者を悩ませないようにするための対策で、これはC++向けライブラリEigenのCMakeLists.txtで利用されていたin-sourceビルドガードをそのまま利用している。
subdirectoryを追加する
add_subdirectory(lib/mylib)
add_subdirectory(examples EXCLUDE_FROM_ALL)
プロジェクトルートに配置したCMakeLists.txt以外にも、lib/mylib
以下やexamples
以下にもCMakeLists.txtを配置している。
このように、CMakeLists.txtを配置したディレクトリをadd_subdirectory
に追加することで、それらをビルドの対象として読み込む事ができ、CMakeLists.txtを分割することが出来るものだと思えば良い。
add_subdirectory(examples EXCLUDE_FROM_ALL)
のように、EXCLUDE_FROM_ALL
という引数が与える事ができる。
これの効果を理解するために、CMakeを用いてビルドする手順をもう一度見てみる。
$ mkdir build
$ cd build
$ cmake ..
$ make
このmake
を実行する際に、ビルドターゲットを指定することが出来るのだが、ターゲットを指定しなかった場合はDEFAULT_TARGET
として指定されているターゲットが利用されることになる。
通常は、CMakeによって用意されるall
がDEFAULT_TARGET
としてセットされている2。
CMakeでは、CMakeLists.txtにライブラリやバイナリの生成ルールを記述した場合、通常、それらはall
ターゲットに追加されることになる。all
ターゲットに追加したくない場合は、EXCLUDE_FROM_ALL
を指定することで出来る。
今回のように、add_subdirectory
にEXCLUDE_FROM_ALL
を与えると、そのディレクトリ内で記述されている全ての生成ルールがall
ターゲットから除外される。もっと細かく、ライブラリやバイナリ単位でも可能である。
examples
をall
ターゲットから除外した理由についても触れておくと、今回のmylib
プロジェクトはライブラリを提供しているものを想定している。何らかのCLIツールのバイナリを生成するプロジェクトではない。
なので、ライブラリだけ生成できればOK。
ライブラリを利用したサンプルとしてexamples
のバイナリを生成するルールを用意しているけど、ライブラリを使いたいだけならバイナリを生成する必要は無く、ユーザが必要に応じて生成出来るようにしたい、という理由になる。3
では、次はそれぞれのディレクトリに配置されているCMakeLists.txtを見ていく。
lib/mylib/CMakeLists.txt
lib/mylib
ディレクトリには、ライブラリのヘッダファイルに対応するソースファイルが配置されている。
ここに配置されるCMakeLists.txtでは、ライブラリの生成ルール、インストール方法を記述している。
# mylibを共有ライブラリとして生成するためのオプションの定義
option(MYLIB_BUILD_SHARED_LIBS "build mylib as a shared library" OFF)
# オプション定義によって生成するライブラリの種類(SHARED/STATIC)を切り替える部分
if (MYLIB_BUILD_SHARED_LIBS)
add_library(mylib SHARED)
else()
add_library(mylib STATIC)
endif()
# mylibを生成するために必要なソースファイル
target_sources(mylib
PRIVATE # mylib.cはmylibライブラリを生成するためだけに必要なのでPRIVATE
mylib.c
)
# mylibを生成するために必要なヘッダファイルが配置されているディレクトリを指定する
target_include_directories(mylib
# mylib.hなどはmylibライブラリを生成するのに加えて、
# mylibライブラリを利用する他のバイナリなどでも利用するのでPUBLIC
PUBLIC
${PROJECT_SOURCE_DIR}/include
)
# GNUのディレクトリ構造で定義されたCMAKE_INSTALL_<dir>の変数を読み込む
include(GNUInstallDirs)
# install mylib library
install(TARGETS mylib
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
# install headers
install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
FILES_MATCHING PATTERN "*.h"
)
mylibを共有ライブラリとして生成するためのオプションの定義
option(MYLIB_BUILD_SHARED_LIBS "build mylib as a shared library" OFF)
まず、前提として、ライブラリを静的ライブラリで生成するのか、共有ライブラリで生成するのかはユーザが指定出来るようにするべきである4。そして、それを指定するためのオプションとしてBUILD_SHARED_LIBS
が既に用意されており、それがON
かOFF
かを判断して切り替えてくれている。
しかし、これはプロジェクト全体に適用されてしまうため、通常はライブラリごとにオプションを用意する。
ここでは、mylib
ライブラリに対して、共有ライブラリとして生成するためのオプションとして、MYLIB_BUILD_SHARED_LIBS
定義し、オプションの説明と、デフォルト値を与えておく5。
このオプションの値を変更したい時は以下のように-D
オプションを利用する。
$ cmake .. -DMYLIB_BUILD_SHARED_LIBS=ON
ライブラリの生成ルール
if (MYLIB_BUILD_SHARED_LIBS)
add_library(mylib SHARED)
else()
add_library(mylib STATIC)
endif()
前述のオプション定義によって生成するライブラリの種類を分岐させると同時に、ライブラリの生成ルールを記述している。
ライブラリをビルドするにはadd_library
を用い、引数にはライブラリ名と、そのライブラリの種類を指定する。
SHARED
を指定すると共有ライブラリとしてlibmylib.so
、STATIC
を指定すると静的ライブラリとしてlibmylib.a
が生成される。
add_library #normal-libraries - CMake Documentation
mylibを生成するために必要なソースファイル
target_sources(mylib
PRIVATE
mylib.c
)
ライブラリの生成に必要なソースファイルの定義はadd_library
でも出来るが、target_sources
を利用して記述することも出来る6。
target_sources
にリストアップしたソースファイルやヘッダファイルには、PRIVATE
、PUBLIC
、INTERFACE
を指定出来る。それぞれの意味を完結に表した表をCMakeスクリプトを作成する際のガイドライン#PRIVATE/PUBLIC/INTERFACEを適切に使うから引用したものが以下の表である。
キーワード | ターゲットが必要とする | ターゲットに依存するターゲットが必要とする |
---|---|---|
PRIVATE | ||
PUBLIC | ||
INTERFACE |
mylib.c
はmylib
ライブラリの生成に必要とする。
しかし、mylib
をリンクして利用するターゲットでは成果物であるlibmylib.a
やヘッダファイルが必要となるのであって、mylib.c
自体は必要ではない。なのでここではPRIVATE
が適切である。
今回、mylib
ライブラリのビルドに必要なソースファイルはmylib.c
だけであったが、実際のライブラリでは多くのソースファイルに依存することになる。そういった場面では、*.c
でパターンにマッチしたファイルを自動で収集させたい、という需要が出てくると思う。
自動で収集する機能は、file(GLOB ...)
7やaux_source_directory
8が利用できる。
しかし、それらのドキュメントに記載されているように、自動で収集する機能を利用することは推奨していない。
その内容をここでも記載しておくと、その理由は、これを利用した場合、CMakeは新しいソースファイルがいつ追加されたかを認識することが出来ないからである。
本来、新しいファイルが追加された場合にはCMakeLists.txt
が編集されるため、CMakeがビルド環境を再生成してくれる。しかし、自動で収集させた場合、新しいファイルを追加してもCMakeLists.txt
を編集しないため、CMakeはビルド環境を再生成するタイミングを検出することが出来なくなる。
結果、新しいファイルを追加した場合は手作業でビルド環境を再生成させることになる。ということらしい9。
mylibを生成するために必要なヘッダファイルが配置されているディレクトリを指定する
target_include_directories(mylib
PUBLIC
${PROJECT_SOURCE_DIR}/include
)
mylib.h
などはmylibライブラリを生成するのに加えて、mylib
ライブラリをリンクする他のバイナリなどでも利用するのでPUBLIC
が適切。
先程、ファイルを自動で収集するのは良くないといいつつ、ここではディレクトリを指定してるのは良いのか?と思うかもしれない。これは各ソースファイル内で正しくincludeできるようにするための記述であり、ヘッダファイルの依存関係の解決をしているわけではない(たぶん。あと、依存関係は別でちゃんと解決されている)10。
これは、gcc -I./include -c mylib.c -o mylib.o
とするように、ソースファイル内でincludeしているファイルをどこから探すか?という情報を与える-I
オプションに対応している。
ここまで記述すれば、ライブラリのビルドが出来るようになっていると思う。#基本的なビルド方法の手順を利用して、試してみてもいいかも。
GNU Coding Standardsとして定義されているインストールディレクトリを利用する
include(GNUInstallDirs)
ここからは、インストール方法の記述になる。まずここでは、GNU Coding Standardsとして定義されているインストールディレクトリをCMAKE_INSTALL_<dir>
の変数で利用できるように読み込んでいる。今回利用する変数は以下の3つで、具体的には以下のような値が環境に合わせてセットされる。
-
CMAKE_INSTALL_LIBDIR
=lib
orlib64
orlib/<multiarch-tuple>
on Debian -
CMAKE_INSTALL_INCLUDEDIR
=include
-
CMAKE_INSTALL_PREFIX
=/usr/local
on UNIX orc:/Program Files/${PROJECT_NAME}
on Windows.
GNUInstallDirs - CMake Documentation
CMAKE_INSTALL_PREFIX - CMake Documentation
ビルドしたライブラリのインストール方法
install(TARGETS mylib
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
ビルドしたライブラリのインストール方法を記述している。インストールするターゲットはmylib
をビルドして出来た成果物であり、その成果物のタイプごとにインストール方法を記述できる。
今回の成果物としては、静的ライブラリ(ARCHIVE
)か共有ライブラリ(LIBRARY
)の場合がある。それらをインストールする作業としては、ファイルを適切な場所に配置ができれば良いので、DESTINATION
だけ指定しておけば良い。
配置場所としてDESTINATION
に${CMAKE_INSTALL_LIBDIR}
を指定すると、${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}
に配置されることになる。
変数を展開してみると、/usr/local/lib
となるので、ライブラリは/usr/local/lib/libmylib.a
と配置される。
/usr/local/
に配置するにはroot権限が必要になるので11、その代わりとして$HOME/.local/
がよく利用される。
そこで、${CMAKE_INSTALL_PREFIX}
を上書きしてビルド環境を作成した上でmake install
すると良い。
$ cmake .. -DCMAKE_INSTALL_PREFIX='$HOME/.local'
$ make install
ヘッダファイルのインストール
install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
FILES_MATCHING PATTERN "*.h"
)
今回は、include
内の全てのヘッダファイルをインストールする。この場合は、install(DIRECTORY ...)
が利用できる。
ディレクトリは再帰的にファイルが検索される。
配置場所は、ライブラリの時同様に${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}
に配置されることになるので、/usr/local/include/mylib/mylib.h
のようにヘッダが配置される。
また、FILES_MATCHING PATTERN "*.h"
で、ヘッダファイルだけ配置するようにしている。
include
の中の一部だけをインストールしたい場合、PUBLIC_HEADER
にヘッダ一覧を指定しておいて、前述のinstall(TARGET mylib ...)
でインストールする方法が使えると思う12。
ここまでの記述で、ライブラリとヘッダファイルのインストールが出来るようになった。
examples/CMakeLists.txt
add_custom_target(examples)
add_dependencies(examples
main
)
add_executable(main main.c)
target_link_libraries(main
mylib
)
mylib
ライブラリを利用したサンプルをビルドするルールを記述している。
examplesターゲットを作成する
add_custom_target(examples)
make
コマンドでは、ターゲットを引数として与えることができた。
make examples
とすれば、examples
をターゲットとして指定したことになり、このコマンドでmylib
ライブラリを利用したサンプルを全てビルドするものを用意したい。
examples
ターゲットのような独自のターゲットはadd_custom_target
を用いて作成できる。この時点ではまだ何も生成されないので、以下のadd_dependencies()
で定義する。
examplesターゲットで生成したいものを定義する
add_custom_target(examples)
add_dependencies(examples
main
)
今回は、サンプルとしてmain
を用意しようとしていて、これをexamples
ターゲットの依存関係としてadd_dependencies
を利用して定義する。
これによって、examples
ターゲットを実行するためにはmain
を生成する必要がある→main
が生成されていなければ生成する、という動作が実現できる。
今回はmain
しか用意していないが、サンプルとして複数用意しており、それらもexamples
ターゲットで生成されてほしい場合はadd_dependencies
のmain
に続けて記述していけば良い。
mainのビルド方法を定義する
add_executable(main main.c)
ライブラリの時はadd_library
であったが、実行ファイルを生成する場合はadd_executable
を指定する13。
1つのソースファイルから1つの実行ファイルを生成する場合は、target_source
に分離させず、add_executable
にまとめて書いてしまう方が冗長にならなくて良いと思う。
add_executable - CMake Documentation
mainにmylibライブラリをリンクする
target_link_libraries(main
mylib
)
main.c
はmylib.h
をincludeしていたので、実行ファイルを生成する際にmylib
ライブラリをリンクする必要がある。
そのときにはtarget_link_libraries
を利用する。
これによって、main
を生成する時に、mylib
ライブラリがまだビルドされていない場合は先にmylib
ライブラリをビルドするように、よしなにされる。
target_link_libraries - CMake Documentation
これで、make examples
でサンプルをビルドすることが出来るようになった。
#基本的なビルド方法に帰って、一通りの流れを試してみてほしい。
高度なビルド方法
コンパイラを指定する
MacではXCodeが持っているコンパイラやgcc
だと思っているclang
がいたりするため、コンパイラを上手く指定したい場面がある。
しかし、CMakeLists.txt
内でコンパイラを直接指定することは良くない。なぜなら、その部分が環境依存になってしまうため、異なる環境の差異を吸収出来なくなてしまうからである。
これはCMakeによって解決させる、もしくは、以下のようにCMakeを実行する際に、ユーザが適切にコンパイラを指定するべきである。
cmake .. -DCMAKE_C_COMPILER=/usr/bin/gcc -DCMAKE_CXX_COMPILER=/usr/bin/g++
How can I make CMake use GCC instead of Clang on Mac OS X? - stack overflow
ビルドタイプにDebug,Releaseなどを指定する
gdbデバッグを行う目的で-g
オプションをつけてビルドしたいときにはDebug
でのビルドを行う。一方、Release
は最適化した状態でビルドしたい時に用いる。
このDebug
、Release
をCMAKE_BUILD_TYPE
に指定する。
# デバッグするために、-gオプションをつける
$ cmake .. -DCMAKE_BUILD_TYPE=Debug
# 最適化をするために、-O3オプションをつける
$ cmake .. -DCMAKE_BUILD_TYPE=Release
Debug
やRelease
は、大文字から始まるCamel caseを利用するようにした方が良い。CMAKE_BUILD_TYPE
自体は大文字小文字でも関係なく正しく切り替わる。
しかし、CMakeLists.txtの記述によっては、${CMAKE_BUILD_TYPE}
にセットされている値がDebug
と一致するか、のように、大文字小文字を区別して利用している場合がある14。
CMAKE_BUILD_TYPEのUPPER CASEにご用心 - Qiita
新しいバージョンのCMakeを使ってcmake --install
を使う
CMake 3.13では${CMAKE_INSTALL_PREFIX}
を上書きしてビルド環境を作成した上でmake install
する必要があった。
$ cmake .. -DCMAKE_INSTALL_PREFIX='$HOME/.local'
$ make install
しかし、より新しいバージョン15のCMakeでは、この2行を以下の1行で実行出来るようになっている。
$ cmake --install . --prefix="$HOME/.local"
Step 4: Installing and Testing - CMake Documentation
その他
mylibを別のプロジェクトから利用したい
# Build mylib
include(FetchContent)
FetchContent_Declare(
mylib
GIT_REPOSITORY https://github.com/hoge/mylib.git
GIT_TAG origin/main
)
FetchContent_GetProperties(mylib)
if(NOT mylib_POPULATED)
FetchContent_Populate(mylib)
add_subdirectory(${mylib_SOURCE_DIR} ${mylib_BINARY_DIR})
endif()
add_subdirectory(src)
例えば、GitHubなどでmylibのプロジェクトが公開されているのであれば、CMakeからFetchContent
を利用して取得する事ができる。
FetchContentを利用できるようにする
include(FetchContent)
includeする。
取得したいリポジトリの情報
FetchContent_Declare(
mylib
GIT_REPOSITORY https://github.com/hoge/mylib.git
GIT_TAG origin/main
)
ここに取得したいGitHubリポジトリの情報を記述する。ここではGIT_TAG
にブランチ名を指定しているが、コミットハッシュやv1.0
などのタグも利用できるようになっている。
2重で読み込んでしまうのを防ぐ
FetchContent_GetProperties(mylib)
if(NOT mylib_POPULATED)
FetchContent_Populate(mylib)
add_subdirectory(${mylib_SOURCE_DIR} ${mylib_BINARY_DIR})
endif()
FetchContent_GetProperties(<name>)
で、<name>_POPULATED
、<name>_SOURCE_DIR
や<name>_BINARY_DIR
の3つの変数を読み込んでいる。
これらの変数は、FetchContent_Populate()
によって定義されるため、初回の読み込みではif(NOT mylib_POPULATED)
の条件がtrueになるが、以降はfalseとなるので、2重で読み込んでしまうのを防ぐ事ができる。
注意することとしては、FetchContent_Populate()
に渡した名前はlower caseに変更され、mylib_POPULATED
、mylib_SOURCE_DIR
やmylib_BINARY_DIR
のようにprefixとして利用されることである16。
FetchContent #command:fetchcontent_populate - CMake Documentation
ちなみに…、新しいCMakeのバージョンなら、以下の一行で出来るっぽい。
FetchContent_MakeAvailable(mylib)
FetchContent #overview - CMake Documentation
threadライブラリを利用する場合
# pthreadを使う
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
add_executable(main main.c)
target_link_libraries(main
Threads::Threads
)
threadライブラリをリンクしてmain
をコンパイルしたい場合は、上記のようにすればいい。
こういった有名なライブラリに関しては、CMakeであればfind_package
を用いて簡単に読み込むことが出来るようになっている。
threadライブラリが見つかった場合はターゲットとしてThreads::Threads
が読み込まれるので、target_link_libraries
でThreads::Threads
を指定すればリンクできる。
また、コンパイル時に-pthread
オプションをつけたい場合はset(THREADS_PREFER_PTHREAD_FLAG ON)
とすると良い17。
FindThreads - CMake Documentation
cmake and libpthread - stack overflow
おわりに
柔軟に記述出来る反面、どう書くのが良いのか分かりにくいのがCMakeの難しいところで、そういったポイントを丁寧にカバーしてみました。
ここまでの手順を追えば、CMakeの基本的な使い方は十分だと思います。後はそのプロジェクト固有の処理や、CMakeはマルチプラットフォームですので、OSごとの分岐処理などを使いこなせれば最高だと思います。(ここが一番むずかしい…。)
最後になりましたが、Qiita10周年おめでとうございます
これからもよろしくお願いします
-
例えば、FetchContentを見てみると、
FetchContent_MakeAvailable
を使えば1つのコマンドで出来ると書いてあるが、「CMake 3.14以前をサポートするなら、次のようにする必要がある」という文面とともに、例が記載されている。FetchContent #command:fetchcontent_populate - CMake Documentation ↩ -
ようするに、
make
はmake all
とした時と同じ、ということ。生成されたMakefile
を見てみると、default_target: main
という記述が見つかると思う。また、そのmain
ターゲットの詳細はmain: cmake_check_build_system ...
に書かれている。 ↩ -
伝われ...! ↩
-
CMakeが持っている変数の一覧を説明付きで表示するコマンドである
cmake .. -LH
を叩いた時などに、オプションの説明が利用される。 ↩ -
というかこうした方がメンテしやすそうだし、こうした方がいい。 ↩
-
ビルド環境を再生成するタイミングを意識するのが嫌か、手作業でファイルを追加するのが嫌か、どっちを取るか?という話。これくらいなら、まだ手作業でファイルを追加する方が楽。めちゃめちゃ大きいプロジェクトだと実際どうしているか、はまだコードを眺めていないので、どうしているかは分からない。分かったら教えて。 ↩
-
ちゃんと依存関係解決しないと、どのオブジェクトファイルを再生成する必要があるのか分からんはず。実は、ちゃんと依存関係が解決されていて、
build/lib/mylib/CMakeFiles/mylib.dir/depend.make
を見てみると、その中に各オブジェクトファイルをtargetとして、そのprereqにヘッダファイルがリストアップされているのが分かると思う。g++
はMake用の依存関係を出力できるので、g++ -MM *.cpp > depend.make
として、depend.make
をincludeすることで利用できる。 ↩ -
sudo make install
のように、sudo
をつけないとpermission deniedする。prefixを上書きする理由は、sudo
を使うのを避けるためではなくて、単純に配置する場所を変えたかっただけ、という好みの話もある。 ↩ -
install - CMake Documentation の
PUBLIC_HEADER
を使っているサンプルを見てほしい。 ↩ -
実行ファイルを生成するのは、いっちばん基本的な話になると思うんだけど、思った以上に最後になってしまった…。 ↩
-
CMakeLists.txtの中で
DEBUG
と一致するか、で利用していたらどうするんだ、という話もあるかもしれないが、これの解決方法としては何かに統一しておくのが良いということになると思う。 ↩ -
いつから使えるようになったの? ↩
-
例えば、
FetchContent_Populate(MyLib)
としても、mylib_POPULATED
、mylib_SOURCE_DIR
やmylib_BINARY_DIR
が定義される。 ↩ -
This variable has no effect if the system libraries provide the thread functions, i.e. when CMAKE_THREAD_LIBS_INIT will be empty.
って書いてあるののは実はよく分かってない。 ↩