はじめに
みなさん、こんにちは。今回は CMake のカスタムターゲットという機能を用いてターゲットのグループ化を行う方法について書いていきます。
カスタムターゲットとは何か?
まず、カスタムターゲットとは何かについて述べます。CMake では、add_executable()
やadd_library()
などによって、実行ファイルやライブラリなどを生成するための各ビルドシステムのターゲットを作成します。カスタムターゲットとは、生成を目的としない任意のターゲットを作成するためのもので、以下のような構文になっています。
add_custom_target(Name [ALL] [command1 [args1...]] [COMMAND command2 [args2...] ...] [DEPENDS depend depend depend ... ] [WORKING_DIRECTORY dir] [COMMENT comment] [VERBATIM] [SOURCES src1 [src2...]])
各引数の意味は以下となります。
Name
<Name>
にはターゲット名を指定します。ここで指定した名前がビルドシステムのターゲットとして追加されます。
cmake_minimum_required(VERSION 3.0.2)
add_custom_target(my_target)
$ cmake .
$ make my_target
Scanning dependencies of target my_target
Built target my_target
ここで、ALL
オプションを追加すると、デフォルトターゲット(Unix Makefiles では all)にも追加されます。
cmake_minimum_required(VERSION 3.0.2)
add_custom_target(my_target ALL)
add_executable(src src.cpp)
$ cmake .
$ make
Scanning dependencies of target my_target
[ 0%] Built target my_target
Scanning dependencies of target src
[100%] Building CXX object CMakeFiles/src.dir/src.cpp.o
Linking CXX executable src
[100%] Built target src
COMMAND
command<N>
・arg1 ...
には、カスタムターゲットが実行されたときに呼び出されるコマンドとその引数を指定します。COMMAND ...
セクションは何回でも指定できます。また、一番最初に限り、COMMAND
識別子を省略することができます。
cmake_minimum_required(VERSION 3.0.2)
add_custom_target(my_target echo @@ command_1
COMMAND echo @@ command_2
COMMAND echo @@ command_3
)
$ cmake .
$ make my_target
Scanning dependencies of target my_target
@@ command_1
@@ command_2
@@ command_3
Built target my_target
DEPENDS
depend ...
には、依存するターゲット名を指定します。また、DEPENDS ...
セクションは何回でも指定できます。
cmake_minimum_required(VERSION 3.0.2)
add_custom_target(my_target1 echo @@ command_1)
add_custom_target(my_target2 echo @@ command_2)
add_custom_target(my_target3 echo @@ command_3)
add_custom_target(my_target4 echo @@ command_4
DEPENDS my_target1 my_target2
DEPENDS my_target3
)
$ cmake .
$ make my_target4
Scanning dependencies of target my_target3
@@ command_3
Built target my_target3
Scanning dependencies of target my_target1
@@ command_1
Built target my_target1
Scanning dependencies of target my_target2
@@ command_2
Built target my_target2
Scanning dependencies of target my_target4
@@ command_4
Built target my_target4
WORKING_DIRECTORY
dir
には、カスタムターゲットの動作ディレクトリを指定します。
cmake_minimum_required(VERSION 3.0.2)
add_custom_target(my_target pwd WORKING_DIRECTORY ~)
$ cmake .
$ make my_target
Scanning dependencies of target my_target
/path/to/home
Built target my_target
COMMENT
コメントとは、ビルド前に表示される[100%] Building CXX object ...
のようなメッセージのことで、comment
に指定した内容が表示されます。
cmake_minimum_required(VERSION 3.0.2)
add_custom_target(my_target
echo @@ my_target
COMMENT [[!! my_target comment !!]]
)
$ cmake .
$ make my_target
Scanning dependencies of target my_target
[100%] !! my_target comment !!
@@ my_target
[100%] Built target my_target
VERBATIM
VERBATIM
オプションを指定すると、各引数の値がビルドシステムでそのまま出力されるようにエスケープされます。例えば、下記の例ではコマンドの引数に${PATH}
という文字列がありますが、これは、ビルドシステムが Unix Makefiles の場合、環境変数参照の形となっています。したがって、実行すると環境変数PATH
の値が出力されてしまいます。VERBATIM
オプションを指定すると、${PATH}
と指定したとおりに出力されます。
cmake_minimum_required(VERSION 3.0.2)
add_custom_target(my_target1 echo @@ PATH: [[${PATH}]])
add_custom_target(my_target2 echo @@ PATH: [[${PATH}]] VERBATIM)
$ cmake .
$ make my_target1 my_target2
Scanning dependencies of target my_target1
@@ PATH: /usr/local/bin:/usr/bin
Built target my_target1
Scanning dependencies of target my_target2
@@ PATH: ${PATH}
Built target my_target2
SOURCES
このオプションは、add_custom_command(OUTPUT)
コマンドとセットで使用するためのものです。add_custom_command(OUTPUT)
は、独自の生成方法を定義するためのものですが、そのままでは呼び出されません。そこで、add_custom_target()
コマンドにSOURCES
オプションを付与することでこれを可能とします。add_custom_command(OUTPUT output1 ...)
の出力ファイルoutput1 ...
で指定したファイル名をadd_custom_target(Name SOURCES src1 ...)
コマンドのsrc1 ...
に指定するとName
ターゲットが呼び出された時にadd_custom_command()
が呼び出されます。
cmake_minimum_required(VERSION 3.0.2)
add_custom_target(my_target
SOURCES copied_src.cpp
)
add_custom_command(OUTPUT copied_src.cpp
COMMAND cp ${PROJECT_SOURCE_DIR}/src.cpp copied_src.cpp
)
#include <iostream>
int main() {
std::cout << "src" << std::endl;
return 0;
}
$ cmake .
$ make my_target
Scanning dependencies of target my_target
[100%] Generating copied_src.cpp
[100%] Built target my_target
$ cat copied_src.cpp
#include <iostream>
int main() {
std::cout << "src" << std::endl;
return 0;
}
カスタムターゲットによるグループ化
さて、カスタムターゲットの使い方が分かりましたが、このコマンドの使用頻度は高くないと思ったかもしれません。生成を目的としないタスクとしてのターゲットで思いつくのはテストの実行くらいでしょうか。しかし、テストの実行には CTest という強力なツールがありますし、そもそもenable_testing()
コマンドによって、test
ターゲットが生成されます。かといって、add_custom_command()
と連携するにも、独自の生成ルールなど普段使うものではありません。
しかし、カスタムターゲットには、任意のターゲットをグループとしてまとめる、という機能があり、これは普段よく使うものです。例えば、以下の構成を持つプロジェクトがあったとします。
project-root/
├── CMakeLists.txt
├── mylib
│ ├── hoge.hpp
│ └── foga.hpp
├── src
│ ├── CMakeLists.txt
│ ├── hoge.cpp
│ └── fuga.cpp
└── test
├── CMakeLists.txt
├── hoge_test.cpp
└── foga_test.cpp
#ifndef MYLIB_HOGE_HPP
#define MYLIB_HOGE_HPP
namespace mylib {
inline int hoge() {
return 1;
}
}
#endif
#ifndef MYLIB_FUGA_HPP
#define MYLIB_FUGA_HPP
namespace mylib {
inline int fuga() {
return 2;
}
}
#endif
#include <iostream>
#include <mylib/hoge.hpp>
int main() {
std::cout << mylib::hoge() << std::endl;
return 0;
}
#include <iostream>
#include <mylib/fuga.hpp>
int main() {
std::cout << mylib::fuga() << std::endl;
return 0;
}
#define BOOST_TEST_MAIN
#include <boost/test/included/unit_test.hpp>
#include <mylib/hoge.hpp>
BOOST_AUTO_TEST_SUITE(mylib)
BOOST_AUTO_TEST_CASE(mylib) {
BOOST_CHECK_EQUAL(hoge(), 1);
}
BOOST_AUTO_TEST_SUITE_END()
#define BOOST_TEST_MAIN
#include <boost/test/included/unit_test.hpp>
#include <mylib/fuga.hpp>
BOOST_AUTO_TEST_SUITE(mylib)
BOOST_AUTO_TEST_CASE(mylib) {
BOOST_CHECK_EQUAL(fuga(), 2);
}
BOOST_AUTO_TEST_SUITE_END()
cmake_minimum_required(VERSION 3.0.2)
find_package(Boost REQUIRED)
add_compile_options(-std=gnu++1y)
include_directories(${Boost_INCLUDE_DIRS})
include_directories(${PROJECT_SOURCE_DIR})
enable_testing()
add_subdirectory(src)
add_subdirectory(test)
add_executable(hoge hoge.cpp)
add_executable(fuga fuga.cpp)
add_executable(hoge_test hoge_test.cpp)
add_test(NAME hoge COMMAND $<TARGET_FILE:hoge_test>)
add_executable(fuga_test fuga_test.cpp)
add_test(NAME fuga COMMAND $<TARGET_FILE:fuga_test>)
このプロジェクトをビルドするときは、下記のようにすると思います。
$ cmake .
$ make
Scanning dependencies of target fuga
[ 25%] Building CXX object src/CMakeFiles/fuga.dir/fuga.cpp.o
Linking CXX executable fuga
[ 25%] Built target fuga
Scanning dependencies of target hoge
[ 50%] Building CXX object src/CMakeFiles/hoge.dir/hoge.cpp.o
Linking CXX executable hoge
[ 50%] Built target hoge
Scanning dependencies of target fuga_test
[ 75%] Building CXX object test/CMakeFiles/fuga_test.dir/fuga_test.cpp.o
Linking CXX executable fuga_test
[ 75%] Built target fuga_test
Scanning dependencies of target hoge_test
[100%] Building CXX object test/CMakeFiles/hoge_test.dir/hoge_test.cpp.o
Linking CXX executable hoge_test
[100%] Built target hoge_test
しかし、開発時はテストを実行するのがほとんどではないでしょうか?そして、src 以下を実行することはないので、ビルドする必要はありません。これは、src 以下をビルドするときも同様です。このときはテストコードをビルドする必要はありません。しかし、現在のままだと両方ともビルドしてしまいます。かといって、いちいち$ make hoge_test fuga_test
とするのは面倒なことです。
そこで、カスタムターゲットの出番です。カスタムターゲットで、テスト用のグループと実行コマンド用のグループを表すターゲットを作成します。まず、src/CMakeLists.txt
とtest/CMakeLists.txt
を次のようにします。
add_custom_target(build_src) # 追加
add_executable(hoge hoge.cpp)
add_dependencies(build_src hoge) # 追加
add_executable(fuga fuga.cpp)
add_dependencies(build_src fuga) # 追加
add_custom_target(build_test) # 追加
add_executable(hoge_test hoge_test.cpp)
add_test(NAME hoge COMMAND $<TARGET_FILE:hoge_test>)
add_dependencies(build_test hoge_test) # 追加
add_executable(fuga_test fuga_test.cpp)
add_test(NAME fuga COMMAND $<TARGET_FILE:fuga_test>)
add_dependencies(build_test fuga_test) # 追加
ここで、add_dependencies(<target> <target-dependency> ...)
コマンドとは、指定したターゲット<target>
に、依存ターゲット<target-dependency> ...
を追加するものです。上記では、テストコマンドと実行コマンドを表すターゲットをそれぞれbuild_test
・build_src
とし、依存するターゲットに各テストコマンド・実行コマンドを指定しています。これによって、テストコマンドだけビルドするターゲットと実行コマンドだけビルドするターゲットが生成されました。つまり、テストコマンドだけビルドしたい場合は、$ make build_test
とし、実行コマンドだけビルドしたい場合は、$ make build_src
とすればよいのです。このようなことはよくあることなので、ターゲットのグループを行うコマンドとして、カスタムターゲットは重宝されるのではないでしょうか。
$ make build_test
Scanning dependencies of target fuga_test
[ 50%] Building CXX object test/CMakeFiles/fuga_test.dir/fuga_test.cpp.o
Linking CXX executable fuga_test
[ 50%] Built target fuga_test
Scanning dependencies of target hoge_test
[100%] Building CXX object test/CMakeFiles/hoge_test.dir/hoge_test.cpp.o
Linking CXX executable hoge_test
[100%] Built target hoge_test
Scanning dependencies of target build_test
[100%] Built target build_test
$ make build_src
Scanning dependencies of target fuga
[ 50%] Building CXX object src/CMakeFiles/fuga.dir/fuga.cpp.o
Linking CXX executable fuga
[ 50%] Built target fuga
Scanning dependencies of target hoge
[100%] Building CXX object src/CMakeFiles/hoge.dir/hoge.cpp.o
Linking CXX executable hoge
[100%] Built target hoge
Scanning dependencies of target build_src
[100%] Built target build_src
おわりに
以上、カスタムターゲットによるグループ化でした。カスタムターゲットを使用することで、状況に応じて必要なものだけビルドすることができます。これによってビルド時間が短縮され、効率よく開発できます。
明日は、mrk_21 さんの『CMake: プリコンパイル済みヘッダーの作成と利用』です。