cmakeゆとり用
cmakeがこわい
自分は永らくMakefileを書いていて,自分で書くような小〜中規模のプロジェクトではそれで十分だと感じていました.
spin.atomicobject.com
cmakeについては,本来Makefile1枚で済む薄いプロジェクトで大量に中間ファイルや長いMakefileを生成したり,cmake由来の問題でビルドのデバッグが複雑化するなどの想像から永らく敬遠していました.
しかし,最近は複数ターゲットのビルドを手軽にやりたい等の理由でcmakeを利用したいことが増えてきました.
cmake
cmake tutorial (official)
公式が参考になりますが,いきなり複雑すぎる問題があるのでもっとうっすいうっすい用例からみていきたい思います.
CMake Tutorial | CMake
documents
公式が一番良いです.
https://cmake.org/cmake/help/v3.3/
自分はcmakeから検索してます. 詳細はman読んで下さい.
$ cmake --help-command add_executable $ cmake --help-module FindPkgConfig
cmakeの基本
最も単純な例.
proj_root ├── build/ ├── main.cpp └── CMakeLists.txt
CMakeListsを以下のように記述します.
# CMakeLists.txt cmake_minimum_required(VERSION 3.3) add_executable(main main.cpp)
cmake_minimum_required
で利用したいcmakeのバージョンを指定し*1,
add_executable
でソースと,コンパイル先のオブジェクト名を指定します.
add_executable(<name> [WIN32] [MACOSX_BUNDLE] [EXCLUDE_FROM_ALL] source1 [source2 ...])
ビルドはbuild/ディレクトリ以下で行います.
$ mkdir build $ cd build $ cmake .. $ make
cmakeを実行すると,build/以下にMakefileができるのであとはいつものようにmakeすれば良いです.生成物は全てcmake を実行したディレクトリ以下に配置されます.
build ├── CMakeCache.txt ├── CMakeFiles/ ├── Makefile ├── cmake_install.cmake └── main
cmakeのオプション
コンパイラ,フラグの設定
set
命令で様々なフラグを設定できるので,コンパイル時のフラグなどはこれで設定します.
set(CMAKE_CXX_COMPILER clang++) set(CMAKE_CXX_FLAGS "-fPIC -O3 -g -Wall -Wextra") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
インクルードディレクトリの指定
Makefile の -include
命令や,コンパイラでの -I
オプションは include_directories
で代用します.
include_directories(/path/to/includes)
ライブラリのリンク
コンパイラでの -L
オプションによるディレクトリ指定は link_directories
に相当し,リンクはtarget_link_libraries
で行います.
link_directories(/path/to/boost) target_link_libraries(main -lboost_system -lboost_filesystem)
link_directories
は記述行以降に設定されたターゲットにのみ有効であることに注意.
CMakeLists.txt
以上をまとめると大体こんな感じになります.ただこの規模だと正直Makefileを書く手間とあまり変わりません.
# CMakeLists.txt cmake_minimum_required(VERSION 3.3) project(MyProject CXX) set(CMAKE_CXX_COMPILER clang++) set(CMAKE_CXX_FLAGS "-fPIC -O3 -g -Wall -Wextra") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") set(BOOST_INCLUDE_PATH /usr/local/opt/boost/include) set(BOOST_LIBRARY_PATH /usr/local/opt/boost/lib) set(BOOST_LIBS "-lboost_filesystem -lboost_system") include_directories(${BOOST_INCLUDE_PATH}) # -I link_directories(${BOOST_LIBRARY_PATH}) # -L add_executable(main main.cpp) target_link_libraries(main ${BOOST_LIBS}) # -l
find_packageモジュールの利用
自分がMakefile を書いていたときは pkg-config
を利用したり手でパスを指定したりしていましたが,cmakeではある程度ライブラリを賢く探してくれる仕組みがあります.
find_package — CMake 3.0.2 Documentation
$ cmake --help-module-list | less
すれば利用できるリストが確認でき,Find${LIB}
というモジュールがあるものは,find_package(${LIB} REQUIRED)
のように記述するだけで呼び出すことができます.
find_package で boost をみつける
boostを利用するには,FindBoostモジュールが利用できます.
FindBoost — CMake 3.0.2 Documentation
上で書いた例を find_package を利用して書き換えると以下のようになります.こうすると,-lboost_system, -lboost_filesystem
を勝手に${Boost_LIBRARIES} へ格納してくれます.詳細はドキュメントを参照してください.
find_package(Boost COMPONENTS system filesystem REQUIRED) include_directories(${Boost_INCLUDE_DIR}) link_directories(${Boost_LIBRARY_DIR}) add_executable(main main.cpp) target_link_libraries( main ${Boost_LIBRARIES} )
pkg-config で OpenCVをみつける
pkg-configをcmakeから呼び出すこともできます.ここではOpenCVを例にみてみます. いやOpenCVはfind_packageで直接見つけることもできますが.
当然普通に pkg-config が支えて opencv.pc ファイルが pkg-config の検索パスにある必要があります.
FindPkgConfig — CMake 3.0.2 Documentation
find_package(PkgConfig) pkg_check_modules(OPENCV REQUIRED opencv) include_directories(${OPENCV_INCLUDE_DIRS}) link_directories(${OPENCV_LIBRARY_DIRS}) add_executable(main main.cc) target_link_libraries(main ${OPENCV_LIBRARIES})
ここでは,pkg_check_modules
によって $ pkg-config (--cflags|--libs) opencv
を行うように,ライブラリとインクルードパスの検索が行われ,find_package 時と同様に以下の変数に格納されます.
<PREFIX>_FOUND <PREFIX>_LIBRARIES # -l <PREFIX>_LIBRARY_DIRS # -L <PREFIX>_LDFLAGS <PREFIX>_LDFLAGS_OTHER <PREFIX>_INCLUDE_DIRS # -I <PREFIX>_CFLAGS <PREFIX>_CFLAGS_OTHER
PREFIX は上の例では
pkg_check_modules(<PREFIX> [REQUIRED] [QUIET] <MODULE> [<MODULE>]*) checks for all the given modules
で,自由に決められます.詳しくはドキュメントを参照してください.
なんか変に抽象化されてデバッグが面倒になっただけな気がする
それはある
まあ環境依存にならないというメリットは...あるかも
複数ターゲットのプロジェクトを作る
なんか長くなってきた.
add_executable
でターゲットを追加していけば複数ターゲットのビルドは簡単にできるのですが,1個のCMakeListsで色々しすぎるとわけがわからなくなってくるのでディレクトリ階層をまたいだCMakeLists の書き方をみてみます.
ディレクトリ階層を跨ぐcmake
以下のようなプロジェクトで,src1, src2 ごとにbuild/src1, build/src2 へターゲットを生成することを考えます.
proj_root ├── CMakeLists.txt ├── main.cpp ├── src1 │ ├── CMakeLists.txt │ └── main.cpp └── src2 ├── CMakeLists.txt └── main.cpp
この場合,以下のように CMakeLists を記述すればルートディレクトリで cmake
すると全部まとめてビルドされます.
CMakeLists.txt
cmake_minimum_required(VERSION 3.3) find_package(Boost COMPONENTS system filesystem REQUIRED) include_directories(${Boost_INCLUDE_DIR}) link_directories(${Boost_LIBRARY_DIR}) add_executable(main main.cpp) target_link_libraries( main ${Boost_LIBRARIES} ) add_subdirectory(src1) add_subdirectory(src2)
src1/CMakeLists.txt
add_executable(target1 main.cpp) target_link_libraries(target1 ${Boost_LIBRARIES})
src2/CMakeLists.txt
add_executable(target2 main.cpp) target_link_libraries(target2 ${Boost_LIBRARIES})
Vimからcmakeする
もっと良い方法があるかもしれないけどとりあえず以下のようにした.quickfixが使いたいんだけどmakeprgを書き換えるしかない?
augroup cpp au! autocmd FileType cpp ca make !(cd ./build && make) autocmd FileType cpp ca cmake !(cd ./build && cmake ..) augroup END
追記 04/16
ca[bbrev]
でのmappingだとnormalモード全体で展開される(つまり,/make
と検索しようとするとmakeの中身がコマンドに展開されてしまう)ので,以下のようにすると良いのではと教えてもらいました.
cnoreabbrev <expr> make getcmdtype() ==# ':' ? '!(cd ./build && make)' : 'make' cnoreabbrev <expr> cmake getcmdtype() ==# ':' ? '!(cd ./build && cmake)' : 'cmake'
自分は/
をdenite.vimのsearchのmapに使ってたので気づかなかった...
" search nnoremap <silent> / :<C-u>Denite -buffer-name=search -auto-resize line<CR>
quickfix使うのは以下にmappingしてみた.
nnoremap <silent> <Space>m :cexpr system('cd build && make')<CR> :copen<CR> nnoremap <silent> <Space>c :!cd<Space>build;make<Space>clean<CR>
Ninja
cmake は -Gオプションでビルドシステムを指定できます.
Big Sky :: 高速なビルドシステム「ninja」
Ninja を使いたい場合は以下のようにして簡単に導入できます.
$ rm CMakeCache.txt $ cmake .. -G Ninja $ ninja
Makefileも書こう
なんだかんだデバッグするときには,普通にMakefileの知識があった方がいいとは思います
- 作者: Robert Mecklenburg,矢吹道郎(監訳),菊池彰
- 出版社/メーカー: オライリージャパン
- 発売日: 2005/12/01
- メディア: 大型本
- 購入: 4人 クリック: 115回
- この商品を含むブログ (34件) を見る
追記
更に発展した内容がまとまった本です.
CMake Cookbook: Building, testing, and packaging modular software with modern CMake
- 作者: Radovan Bast,Roberto Di Remigio
- 出版社/メーカー: Packt Publishing
- 発売日: 2018/09/26
- メディア: ペーパーバック
- この商品を含むブログを見る
cmake cookbook 掲載のレシピがまとまっていて参考になります.
github.com