メモの日々


2020年01月04日(土) [長年日記]

[dev][c++] CMakeを使う

CMakeについてメモ。

ドキュメント

リファレンス。

ガイド的な文書。

基本

C++のコードをCMakeでビルドする例を示す。ファイル構成は次の通りとする。

.
├── CMakeLists.txt
├── src
│   ├── CMakeLists.txt
│   ├── hello.cpp
│   ├── hello.h
│   └── main.cpp
└── test
    ├── CMakeLists.txt
    ├── main.cpp
    └── test_hello.cpp

testのビルドは後回しとして、まずsrc配下のプログラムをビルドできるようにする。CMakeLists.txtとsrc/CMakeLists.txtをそれぞれ次のように書く。

cmake_minimum_required(VERSION 2.8)

add_subdirectory(src)
add_executable(hello
  main.cpp
  hello.cpp
)
  • cmake_minimum_requiredは書かないと警告が出るので書く。2.8かかなり昔のバージョンだがCentOS 7のCMakeはこのバージョン。
  • add_subdirectoryでsrc配下のCMakeLists.txtを読み込むことを指示する。
  • add_executableでビルドする実行ファイル名とそれをビルドするためのソースファイル群を指定する。ここにヘッダファイルも記述すべきかはよくわからない。ヘッダファイルを含めないとIDEを使った場合にヘッダファイルが表示されないかもしれないが、ビルドするだけならヘッダファイルを含める必要はない。
    • add_executableにソースファイルを列挙するのはできれば避けたい。fileのGLOB_RECURSEを使うと手動での列挙を避けられるが、これを使うとソースファイルの増減時に手動でのCMakeの再実行が必要になり運用が面倒になる。GLOBはリファレンスマニュアルに「We do not recommend using GLOB to collect a list of source files from your source tree. 」と言及されており、また上でリンクしたEffective Modern CMakeにも「Don't use file(GLOB) in projects.」とあり使用が推奨されていない模様。
    • file(GLOB)にはCONFIGURE_DEPENDSというオプションがある。これはCMake 3.12で追加されておりfile(GLOB)の問題を一部解決するものみたいなのだけれど、試せていない。

CMakeLists.txtとソースコードを用意したら、cmakeコマンドを実行するとビルド環境(LinuxならMakefile)が作られる。

$ mkdir build
$ cd build
$ cmake ..

ビルドを行うにはビルド環境にて--buildオプション付きのcmakeコマンドを実行する。

$ cd build
$ VERBOSE=1 cmake --build .

これで build/src/hello が作られる。環境変数VERBOSEをセットするとビルド時のコマンドが出力されるようになるので指定している。CMake 3.14で--verboseオプションが追加されており、これを使うと環境変数は不要なのかもしれないが試せていない。

cmake --buildはCMakeLists.txtが更新されているとビルド環境の再構築(上記の「cmake ..」相当)もしてくれる。

ビルドタイプの切り替え

ビルド環境構築時にcmakeコマンドにCMAKE_BUILD_TYPEを指定することでコンパイルオプションを切り替えることができる。

$ cmake .. -DCMAKE_BUILD_TYPE=Debug

手元のCMake 2.8だとCMAKE_BUILD_TYPEを切り替えることでGCCのコンパイルオプションが次のように変わる。

未指定オプションなし
Debug-g
Release-O3 -DNDEBUG
RelWithDebInfo-O2 -g -DNDEBUG
MinSizeRel-Os -DNDEBUG

コンパイルオプションの明示的な指定

コンパイルオプションをカスタマイズしたいときにはtarget_compile_optionsを使う。例えば src/CMakeLists.txt に次のように追記する。

target_compile_options(hello
  PRIVATE $<$<CXX_COMPILER_ID:MSVC>:/source-charset:utf-8>
  PRIVATE $<$<CXX_COMPILER_ID:GNU>:-Wall>
  PRIVATE $<$<AND:$<CXX_COMPILER_ID:GNU>,$<CONFIG:Debug>>:-ggdb3>
)
  • 条件に応じて異なるオプションを指定するためにgenerator-expressionsを使用している。
  • Visual C++に対しては/source-chasetオプションを使用してソースコードの文字コードがUTF-8であることを通知している。
  • GCCに対しては、CMAKE_BUILD_TYPEがDebugの場合にオプションを追加するようにしている。
  • target_compile_optionsで指定したオプションは、CMAKE_BUILD_TYPEにより付与されるオプションより後ろに指定される。

インクルードディレクトリとリンクするライブラリの指定

インクルードディレクトリはtarget_include_directories、リンクするライブラリはtarget_link_librariesで指定できる。

Boost.Logを使用する例を記す。

src/CMakeLists.txt に次を追記する。

find_package(Boost 1.71 REQUIRED COMPONENTS log thread)
find_package(Threads REQUIRED)

target_include_directories(hello
  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
  PRIVATE ${Boost_INCLUDE_DIR}
)
target_link_libraries(hello
  PRIVATE ${Boost_LIBRARIES}
  PRIVATE ${CMAKE_THREAD_LIBS_INIT}
)
  • find_package(Boost)でBoostを見つけさせる。また、ライブラリとしてlibboost_logとlibboost_threadを使うことを指示している(libboost_threadを指定しているのはlibboost_logが必要としているため。CMakeのバージョンが高い場合はthreadを明記しなくてもこの依存関係は自動的に解決される)。
  • libboost_threadをリンクするにはスレッドライブラリが必要になるため、find_package(Threads)で見つけさせる。これもCMakeのバージョンが高い場合は明記しなくて大丈夫そう。
  • target_include_directoriesでインクルードディレクトリを指定する。find_package(Boost)が変数Boost_INCLUDE_DIRにBoostのインクルードディレクトリを設定するのでそれを使っている。
  • target_link_librariesでリンクするライブラリを指定する。find_packageが変数Boost_LIBRARIESと変数CMAKE_THREAD_LIBS_INITを設定するのでそれを使っている。CMakeのバージョンが高い場合は ${CMAKE_THREAD_LIBS_INIT} と書く代わりに Threads::Threads と書ける。

また、本質的な記述ではないが、CMakeLists.txt の方には次を追記している。add_subdirectory()より上に書く必要がある。

set(Boost_USE_STATIC_LIBS ON)
set(Boost_NO_BOOST_CMAKE ON)
  • Boost_USE_STATIC_LIBSはBoostのライブラリを静的に(libboost_log.aを)リンクするためのもの。動的リンクする場合は不要。
  • Boost_NO_BOOST_CMAKEはBoostに添付されているCMake設定ファイルを読み込まないようにするもの。手元の環境ではこうしないとfind_package(Boost)が失敗してしまうために指定した。

テストのビルド

後回しにしていたtestディレクトリ配下のビルドをできるようにする。まず CMakeLists.txt に次を追記する。

enable_testing()
add_subdirectory(test EXCLUDE_FROM_ALL)
  • enable_testingはビルドするだけなら不要だが、CMakeを通じてテストの実行も行うのなら必要になる。Visual Studioを使うならこれでVisual Studioのテスト機能と連携するようになるので便利。
  • add_subdirectoryに指定しているEXCLUDE_FROM_ALLは、デフォルトのビルド時にはテストのビルドを行わないようにするため。これによりtest配下のコードはビルドターゲットを明示的に指定した時だけビルドされるようになる。

test/CMakeLists.txtは次のように書く。src/CMakeLists.txtとそれほど変わらない。

find_package(Boost 1.71 REQUIRED COMPONENTS unit_test_framework)

add_executable(hello-test
  main.cpp
  test_hello.cpp
  ../src/hello.cpp
)
target_compile_options(hello-test
  PRIVATE $<$<CXX_COMPILER_ID:MSVC>:/source-charset:utf-8>
  PRIVATE $<$<CXX_COMPILER_ID:GNU>:-Wall>
  PRIVATE $<$<AND:$<CXX_COMPILER_ID:GNU>,$<CONFIG:Debug>>:-ggdb3>
)
target_include_directories(hello-test
  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../src
  PRIVATE ${Boost_INCLUDE_DIR}
)
target_link_libraries(hello-test
  PRIVATE ${Boost_LIBRARIES}
)

add_test(NAME test-hello
  COMMAND hello-test --log_level=test_suite --report_level=short
)
  • テストにBoost.Testを使ったのでfind_packageによりlibboost_unit_test_frameworkを検索している。
  • add_testでテスト名と実行するコマンドを指定する。ここではコマンドとしてadd_executableによりビルドした実行ファイルを指定している。log_levelなどはBoost.Testの方のオプション。

上述の通りtestディレクトリにはEXCLUDE_FROM_ALLを指定しているので、テストをビルドするには次のようにターゲットを明示する必要がある。

$ cd build
$ VERBOSE=1 cmake --build . --target hello-test

テストの実行にはctestコマンドが使えるが、特にこのコマンドを使う必要はない気がする。

インストールの設定

ビルドされた実行ファイルを既定のディレクトリへインストールする設定を追加する。src/CMakeLists.txtに次を追記する。

install(TARGETS hello RUNTIME DESTINATION bin)

これで、生成されるMakefileにインストール用のターゲット(install, install/strip, install/local)が追加される。上のように書くと、インストール先は /usr/local/bin 配下になる。

$ make -C build install

CMake 3.15でcmakeコマンドに--installオプションが追加されているので、このバージョン以降ならインストールにもcmakeコマンドが使えるみたい。

ビルド用のMakefileを用意する

cmakeコマンドの発行は色々面倒なので、Makefileを用意したくなる。次のような感じ。

SHELL := /bin/bash
CMAKEFLAGS := -DCMAKE_BUILD_TYPE=Release

build_release_dir := build-release
build_debug_dir := build-debug

.PHONY: release
release: $(build_release_dir)/Makefile
	cd $(<D); VERBOSE=1 cmake --build .

.PHONY: debug
debug: CMAKEFLAGS := -DCMAKE_BUILD_TYPE=Debug
debug: $(build_debug_dir)/Makefile
	cd $(<D); VERBOSE=1 cmake --build .

.PHONY: test
test: CMAKEFLAGS := -DCMAKE_BUILD_TYPE=Debug
test: $(build_debug_dir)/Makefile
	cd $(<D); VERBOSE=1 cmake --build . --target hello-test
	$(<D)/test/hello-test --log_level=test_suite --report_level=short

.PHONY: install
install: release
	$(MAKE) -C $(build_release_dir) install/strip

.PHONY: clean
clean:
	rm -rf $(build_release_dir) $(build_debug_dir)

$(build_release_dir)/Makefile $(build_debug_dir)/Makefile:
	mkdir -p $(@D)
	cd $(@D); cmake .. $(CMAKEFLAGS)

最終的な状態

最終的なCMakeList.txtは次の通り。

cmake_minimum_required(VERSION 2.8)

set(Boost_USE_STATIC_LIBS ON)
set(Boost_NO_BOOST_CMAKE ON)

add_subdirectory(src)

enable_testing()
add_subdirectory(test EXCLUDE_FROM_ALL)

最終的な src/CMakeList.txt は次の通り(行の順序は入れ替えた)。

find_package(Boost 1.71 REQUIRED COMPONENTS log thread)
find_package(Threads REQUIRED)

add_executable(hello
  main.cpp
  hello.cpp)
target_compile_options(hello
  PRIVATE $<$<CXX_COMPILER_ID:MSVC>:/source-charset:utf-8>
  PRIVATE $<$<CXX_COMPILER_ID:GNU>:-Wall>
  PRIVATE $<$<AND:$<CXX_COMPILER_ID:GNU>,$<CONFIG:Debug>>:-ggdb3>
)
target_include_directories(hello
  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
  PRIVATE ${Boost_INCLUDE_DIR}
)
target_link_libraries(hello
  PRIVATE ${Boost_LIBRARIES}
  PRIVATE ${CMAKE_THREAD_LIBS_INIT}
)

install(TARGETS hello RUNTIME DESTINATION bin)

test/CMakeList.txtは上述のまま。


2019年12月30日(月) [長年日記]

[life] 蕁麻疹を発症した

11月の末頃に、朝方に足や腰が痒くなるようになった。1円玉くらいの結構大きな発疹がたくさんできる。虫刺されかと思っていたが、自分だけ毎日出るのはおかしい気がする。

皮膚科を受診すると、発疹の場所が変わるかと聞かれた。意識していなかったのでわからないと答えたが、場所が変わるなら蕁麻疹で、大人の蕁麻疹はほとんどが原因不明だと言われる。ベポタスチンベシル酸塩錠という薬を処方され、これを飲むと発疹がおさまった。

で、この薬を1か月飲み続けている。初期より症状は改善しているが、発疹はまだ出る。そういうものみたい。

[life] 腰痛を発症した

12月末から腰が痛い。痛みにより歩くときにどうしても腰が曲がってしまい、腰に負担がかかり大変。4年前に椎間板ヘルニアの手術を受けており、再発かと心配。

安静にすることで少しずつ良くなっている気はするけど、こちらもなかなか治らない。


2019年12月27日(金) [長年日記]

[dev][windows] Visual StudioでCMakeを使う

Visual Studio 2017にはCMakeがバンドルされており、IDEと連携してCMakeを使えるようになっている。公式ドキュメントが

にある。

プロジェクトを開く

Visual Studio 2017では次のようにしてCMakeList.txtからプロジェクト開くことができる(ソリューションファイルは使わない)。

  • [ファイル]→[開く]→[CMake]

CMakeList.txtを開くとcmakeコマンドが自動的に実行され、CMakeList.txtの内容に応じて[CMake]メニューから次のような操作を行えるようになる。

  • ビルド
  • コード分析
  • インストール
  • テスト
  • デバッグ
  • CMakeのキャッシュの操作

ビルドの成果物の出力先

ビルドの成果物の出力先は、デフォルトでは C:\Users\ユーザ名\CMakeBuilds のサブフォルダになる。サブフォルダのフォルダ名が分かりにくいので、メニューの [CMake]→[キャッシュ]→[キャッシュフォルダーを開く] を使って開くのがよさそう。

cmakeコマンドの再実行

cmakeコマンドを再実行したいときには、[CMake]→[キャッシュ]→[キャッシュフォルダーの削除]を実行した後[CMake]→[キャッシュ]→[生成]を行えばよい。

ビルド構成の設定

リリースビルドやデバッグビルドなどの設定や切り替えを行うには、CMakeSetting.jsonファイルを作成する。[CMake]→[CMakeの設定を変更]と辿るとファイルを生成できる。

CMakeSetting.jsonに複数の「構成」を定義することで、ツールバーのプルダウンメニューから構成を切り替えてビルドやデバッグができるようになる。

CMakeSetting.jsonで何を設定できるのかは次のドキュメントに書かれている。


2019年12月20日(金) [長年日記]

[life] toto当せんした(6)

また3か月ぶりに5等が当せん。100円BIGを毎週買うとこのくらいの周期で5等は当たるようだ。当せん金は870円…


2019年09月27日(金) [長年日記]

[windows] Windows 10でファイル共有ができない原因

Windows 10でフォルダを共有しても、他のPCからアクセスできず困った。

設定の[ネットワークとインターネット]→[共有オプション]にある設定を色々変更してみたが解決しない。

共有ができない原因は、ネットワークアダプターの設定にあった。設定の[ネットワークとインターネット]→[アダプターのオプションを変更する]ででる画面で対象アダプタのプロパティを表示すると、

  • Microsoftネットワーク用ファイルとプリンター共有

のチェックが外れていた。ここをチェックすると共有フォルダへ他のPCからアクセスできるようになった。