読者です 読者をやめる 読者になる 読者になる

CMakeを使ってみた(2)もう少しまともなプロジェクト

前回は簡単なアプリケーションを作り、CMakeでビルドしてみた。もちろん実際のプロジェクトはもっと複雑で、ソースファイルも複数あるだろうし、各種パスの設定も必要だろう。そこで今回は以下を設定してみる。

  • デバッグ版ビルドとリリース版ビルドができるようにする
  • コンパイラ・リンカへの基本的な設定を行う
    • インクルードパス (-I)
    • マクロ定義 (-D, -U)
    • ライブラリパス (-L)
    • ライブラリ (-l)

デバッグオプションをつける

多くのプロジェクトではデバッグビルドとリリースビルドを用意しているだろう。デバッグビルドはデバッグシンボルをつけ、またデバッガによる追跡がしやすいよう最適化オプションをあまりつけないようにし、一方リリースビルドでは逆にデバッグシンボルをつけず、最適化も最大限行う、というのが一般的だと思う。

CMakeでは変数CMAKE_BUILD_TYPEを設定することでデバッグ版/リリース版の設定を行う。変数の設定はCMakeLists.txt中でsetコマンドで設定する方法と、コマンドラインオプションで行う方法がある。

CMakeLists.txt中で指定

cmake_minimum_required(VERSION 2.8)
add_executable(myapp main.cpp)
set(CMAKE_BUILD_TYPE Debug)

コマンドラインオプションで指定

> cmake -DCMAKE_BUILD_TYPE=Debug .

また、Debug、Releaseの他にもRelWithDebInfoやMinSizeRelなどもある。詳細はCMakeのWikiのCompilers and Toolsの項を参照。ここを見ると、例えばRelWithDebInfoのときはCMAKE_C_FLAGS_RELWITHDEBINFOまたはCMAKE_CXX_FLAGS_RELWITHDEBINFOがつく、などのことが分かる。

変数の内容を調べる

ではCMAKE_CXX_FLAGS_RELWITHDEBINFOには何がセットされているのか?そういうことは実際に表示させてみれば分かる。messageコマンドを使えばメッセージを表示することができ、また変数の内容は${変数名}で参照できる。つまり、以下のように書いてcmakeを実行すれば表示される。

cmake_minimum_required(VERSION 2.8)
add_executable(myapp main.cpp)
message(STATUS "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
> cmake .
-- -O2 -g
-- Configuring done
-- Generating done
-- Build files have been written to: /home/ws/study-cmake/test

RelWithDebInfoでは-O2 -gが指定されるようだ。変数の表示はCMakeLists.txt自体を調べたいときに便利なので覚えておくといいと思う。

コマンドやオプションを指定する

CMakeが自動的に設定したコンパイラあるいはオプションなどが望むものでない場合は手動で設定することになる。これもsetコマンドや-Dオプションで指定する。どのような変数があるかはやはりWikiのCMake Useful Variablesが役に立つだろう。

コンパイラ・リンカの基本設定

ソースファイルを増やす

普通のプロジェクトではソースファイルが1つということはないだろう。ソースファイルを増やすには単にadd_executableコマンドに追加すればよい。

add_executable(myapp main.cpp sub1.cpp sub2.cpp)

これだけならどうということのない話だが、同じターゲットに対してadd_executableは1回しか呼べないということに注意。つまり、

add_executable(myapp main.cpp)
add_executable(myapp sub1.cpp sub2.cpp)

のようなことはできない。CMakeでも条件分岐を行うifコマンドがあるが(詳細は後述)、条件によってソースファイルを追加したい場合はこれでは困る。以下のようにソースファイルは変数に入れておき、最後にまとめてセットした方が良いだろう。

set(myapp_sources main.cpp sub1.cpp)
if (USE_FEATURE_X)
  set(myapp_sources ${myapp_sources} sub2.cpp)
endif ()
add_executable(myapp ${myapp_sources})

インクルードパスを指定する

インクルードパスを指定するときはinclude_directoriesコマンドを使う。

cmake_minimum_required(VERSION 2.8)
include_directories(/path/to/include)
add_executable(myapp main.cpp)

なお、include_directoriesは複数回指定可能で、デフォルトでは後で指定したものは後ろにつく。ただし、オプションでBEFOREを指定したときは前につく。例えば、

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

としたときは"-I/path1/include -I/path2/include"となり、

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

としたときは"-I/path2/include -I/path1/include"となる。

マクロの指定

マクロを指定するときはadd_definitionsを使う。

add_definitions(-DFOO -DBAR=xyz)

include_directoriesのときは単にディレクトリ名を指定するだけで-Iはつけなかったが、add_definitionsのときは-Dをつける必要があることに注意。マクロは指定するだけでなく、定義をなくす(-U)を使うことも可能であるためだ。

add_definitions(-DFOO -UHOGE)

ちなみに-Dや-Uの部分は環境に合わせて適切に変えてくれる。例えばVisual C++用に生成する場合はちゃんと/Dや/Uが設定される。

なお、add_definitionsも複数回呼ぶことが可能。また、同じマクロを複数回定義した場合はそのまま複数回定義したものが生成され、CMake自体は警告などは出さない。ただし、多くのコンパイラはそのようなマクロ定義に対して警告を出すだろう。

ライブラリの追加

リンクするライブラリを追加するときはtarget_link_librariesだ。結果は-lオプションに反映される。

cmake_minimum_required(VERSION 2.8)
add_executable(myapp main.cpp)
target_link_libraries(myapp foo)

ライブラリ名だけでなく、ターゲット名も指定する必要がある。このため、target_link_librariesはadd_executableより後になければならず、先に指定するとエラーになる。

ライブラリパスの指定

リンクするライブラリがあるパスを指定するにはlink_directoriesコマンドを使う。

cmake_minimum_required(VERSION 2.8)
link_directories(/path/to/lib)
add_executable(myapp main.cpp)

もちろん、結果は-Lオプションに反映される。注意点としては、link_directoriesはそれまでに既に作成されたターゲットのみに効くということだ。先の例で言えば、link_directoriesの行とadd_executableの行を入れ替えると無効になる。そして、target_link_librariesの場合と異なり、先に置いた場合はエラーも警告も出ない。

ちなみにinclude_directoriesやadd_definitionsはどこに置いても有効になる。

まとめ

コマンド名が覚えにくい感じがするのでまとめておく。

指定するもの コマンド 備考
ソースファイル add_executable add_executable(myapp src1.cpp src2.cpp) ソースファイルは変数に一度変数に入れておこう。
インクルードパス include_directories include_directories(/path/to/include) CMakeLists.txt中のどこに置いても有効。
ライブラリパス link_directories link_directories(/path/to/lib) それ以降に作成されたターゲットに対してのみ有効。
ライブラリ target_link_libraries target_link_libraries(myapp foo) ターゲット作成後でないと指定できない。

呼ぶ順番はとりあえず以下のようにしておけばよいだろう。

cmake_minimum_required(VERSION 2.8)
add_definitions(-DFOO)
include_directories(/path1/to/include /path2/to/include)
link_directories(/path1/to/lib /path2/to/lib)
add_executable(myapp main.cpp sub1.cpp)
target_link_libraries(foo bar)

また、include_directories、link_directories、target_link_librariesは複数回使って追加することができる。