CMakeを使ってビルドする(1)
今まで、プログラムをビルドするのにmakeを使用していましたが、GCCとVC++でそれぞれ異なるMakefileを用意しなければならないのでなかなか面倒でした。CMakeを使うことで、別々のMakefileを用意する手間を省いて共通化できそうだったので、試しに使ってみました。そのメモです。
CMakeとは?
CMakeは、CMakeLists.txt
という設定ファイルにビルド用の設定を記述しておくことで、複数の異なるビルドシステム用の設定ファイル(make用のMakefile
やVisual Studio用の*.sln
など)の生成を自動で行ってくれるツールです。DebugビルドとReleaseビルドを切り替えたり、使用するコンパイラを切り替えることも可能です。
CMakeはout-of-sourceビルドというものを基本としており、ソースと異なるディレクトリ内でビルドすることが推奨されています。一般的には、プロジェクトのルートにビルド用のディレクトリを作成し、そのディレクトリに移動してからcmakeすることが多いようです。リビルドする際には、ビルド用のディレクトリ自体を削除します。
CMakeLists.txtを作成する
使用するCMakeのバージョン
> cmake --version
cmake version 3.19.3
やりたいこと
まずは、次ができることを目指しました。
- WindowsでC++のプログラムをビルドする
- make用の
Makefile
を生成する - DebugビルドとReleaseビルドで異なるコンパイルオプションを設定する
- コンパイラはGCCとVC++(cl.exe)の2つに対応させる
CMakeもなかなか複雑なので、まずは簡単なディレクトリ構成から始めてみました。
想定しているディレクトリ構成
プロジェクトのディレクトリ直下にソースが置かれている単純なケースを考えます。
project1/
|- test1.h
|- test1.cpp
|- CMakeLists.txt
|- build/ (ビルド用のディレクトリ)
サブディレクトリが存在する場合は、こちらもご覧ください。
簡単なCMakeLists.txt
実際にCMakeの設定ファイルであるCMakeLists.txt
を作成します。まずはプログラムをビルドするための簡単な設定を行います。
cmake_minimum_required(VERSION 3.1)
project(cmake_test_1 CXX)
aadd_executable(cmake_test test1.cpp)
1行目cmake_minimum_required()
1行目ではこのプロジェクトで必要とするCMakeのバージョンを指定しています。これより古いバージョンのCMakeを使用した場合、ビルドシステム用の設定ファイルを生成できません。
2行目project()
2行目ではプロジェクト名を指定しています。ここで指定したプロジェクト名は、Visual Studio(MSBuild)用のソリューションファイル名で使われるようです。
2つ目のCXX
はこのプロジェクトで使用される言語がC++であることを示します。省略した場合、CMakeはCとC++両方に対応しようとします。
ちなみに、project()
を省略すると、プロジェクトの開発者向けに下記の様な警告が出ます。
CMake Warning (dev) in CMakeLists.txt:
No project() command is present. The top-level CMakeLists.txt file must
contain a literal, direct call to the project() command. Add a line of
code such as
project(ProjectName)
near the top of the file, but after cmake_minimum_required().
CMake is pretending there is a "project(Project)" command on the first
line.
This warning is for project developers. Use -Wno-dev to suppress it.
これによると、project()
は省略せずにファイルの先頭付近、かつその位置はcmake_minimum_required()
の後に記述しなければならないようです。
プロジェクト名は省略せずにつけておきましょう。
3行目add_executable()
3行目で実行ファイルの生成に関する設定を行っています。次のような書式で記述します。
add_executable(ターゲット名 [EXCLUDE_FROM_ALL] 依存ファイル ...)
ターゲット名が生成する実行ファイルの名前に相当します。Windowsであれば勝手に.exe
を付加してくれます。
依存ファイルにはビルドに必要なソースファイルなどを指定します。複数指定可能。ソースファイルでincludeしているヘッダファイルは、自動的に依存関係に追加されるので省略できます。
EXCLUDE_FROM_ALL
はデフォルトターゲット(all
)にターゲット名を含めないときに指定します。
Makefileの生成とビルド
Makefileの生成
先のCMakeLists.txt
を用いて、WindowsでMakefile
を生成するには、build
ディレクトリに移動したあとでcmake
を実行します。
project> cd build
project\build> cmake -G "MinGW Makefiles" ..
Windowsでは-G "MinGW Makefiles"
をつけないと、初期設定であるVisual Studio(MSBuild)用の設定ファイルが生成されます。Linuxではmake
が初期設定なので、このようなオプションは不要です。
-G "MinGW Makefiles"
をつけた場合、コンパイラはGCCが初期設定として選択されます。コンパイラをVC++(cl.exe)に切り替えるときには-D
オプションを使って、変数CMAKE_CXX_COMPILER
に値を設定します。
cmake -DCMAKE_CXX_COMPILER=cl -G "MinGW Makefiles" ..
ビルド
生成されたMakefile
を使ってビルドするには、直接make
を使う方法のほか、cmake
に--build
オプションを付けて呼び出す方法もあります。
cmake --build Makefileのディレクトリ [--target ターゲット名]
--target
はadd_executable()
で指定したターゲット名を指定します。省略した場合はall
がターゲット名として指定されます。EXCLUDE_FROM_ALL
を指定していなければ、all
ターゲットに含まれているはずなので、省略して良いです。
ビルドタイプの切り替えとコンパイルオプションの設定
ビルドタイプの切り替え
DebugビルドやReleaseビルドなどのビルドタイプを切り替えるには、次のように-D
オプションを使って変数CMAKE_BUILD_TYPE
にビルドタイプを指定します。
使用可能な値として、Debug
, Release
, RelWithDebInfo
, MinSizeRel
の4種類があらかじめ用意されていますが、独自に定義も可能なようです。
cmake -DCMAKE_BUILD_TYPE=ビルドタイプ -G "MinGW Makefiles" ..
CMakeではコンパイルオプションを保持する変数として、共通で使われるCMAKE_CXX_FLAGS
の他に、各ビルドタイプに対応する変数CMAKE_CXX_FLAGS_DEBUG
, CMAKE_CXX_FLAGS_RELEASE
, CMAKE_CXX_FLAGS_RELWITHDEBINFO
, CMAKE_CXX_FLAGS_MINSIZEREL
が用意されていて、それぞれOSやコンパイラに応じた初期値が与えられています。
各ビルドタイプのコンパイルオプション初期値を調査したものをここに示しました。
コンパイルオプションの設定
コンパイルオプションの設定は、上記の変数を直接変更するのではなく、target_compile_options()
, target_compile_features()
, target_compile_definitions()
等を使って設定することが推奨されています。
コンパイルオプションの追加
target_compile_options(ターゲット名 [BEFORE] PRIVATE|PUBLIC|INTERFACE オプション ...)
コンパイルオプションを追加します。次のようにコンパイラに渡すオプション文字列をそのまま設定します。
target_compile_options(cmake_test PRIVATE -Wall -Wextra)
BEFORE
を指定した場合、コンパイルオプションを最後に付加するのではなく、最初に挿入します。
言語機能の指定
target_compile_features(ターゲット名 PRIVATE|PUBLIC|INTERFACE オプション)
コンパイルする際に必要な言語機能を指定します。C++の言語仕様と値、コンパイルオプションの対応は以下の表を参照してください。
仕様 | 値 | GCCオプション | VC++オプション |
---|---|---|---|
C++98 | cxx_std_98 | -std=gnu++98 | (なし) |
C++11 | cxx_std_11 | -std=gnu++11 | (なし) |
C++14 | cxx_std_14 | -std=gnu++14 | /std:c++14 |
C++17 | cxx_std_17 | -std=gnu++17 | /std:c++17 |
C++20 | cxx_std_20 | -std=gnu++2a | /std:c++latest |
GCCの場合、-std=c++xx
ではなくGNU拡張の有効な-std=gnu++xx
が指定されます。GNU拡張を無効にする場合には、CXX_EXTENSIONS
というプロパティをあらかじめOFFにする必要があります。
set_property(TARGET cmake_test PROPERTY CXX_EXTENSIONS OFF)
target_compile_features(cmake_test PRIVATE cxx_std_17)
cxx_std_xx
の代わりに、必要とする言語の機能を指定することも可能です。たとえば、cxx_constexpr
と指定することで、その機能をサポートしている仕様をオプションとして設定してくれます。
どのような機能が指定可能かは、公式ドキュメントを参照してください。
プリプロセッサ定義の追加・削除
target_compile_definitions(ターゲット名 PRIVATE|PUBLIC|INTERFACE オプション ...)
コンパイル時オプションとして、プリプロセッサの定義を追加または削除します。たとえばFOO
を定義する場合は、
target_compile_definitions(cmake_test PRIVATE FOO)
逆にFOO
を削除する場合は、
target_compile_definitions(cmake_test PRIVATE -DFOO)
とします。-D
と削除するプリプロセッサの間にスペースを入れてはいけません。
PRIVATE
, PUBLIC
, INTERFACE
これら3種類のキーワードはオプションの有効な範囲を示すために使われます。ターゲット自身に有効な設定の場合にはPRIVATE
かPUBLIC
を、ターゲットに依存する他のターゲットに有効なものはINTERFACE
かPUBLIC
を指定します。
キーワード | ターゲット自身のビルドに必要 | ターゲットに依存するターゲットのビルドに必要 |
---|---|---|
PRIVATE | 〇 | × |
PUBLIC | 〇 | 〇 |
INTERFACE | × | 〇 |
自分自身のビルドに使うのであれば、PRIVATE
を使用します。
Debugビルド用とReleaseビルド用のコンパイルオプションの設定
Debugビルド用とReleaseビルド用のコンパイルオプションを設定してみます。今回は、次のような内容とします。
- Debugビルド
- プリプロセッサの定義に
USE_HOGE
を指定する
- プリプロセッサの定義に
- Releaseビルド
- 追加なし
- 共通
- GCCでは警告オプション
-Wall
と-Wextra
を付加する - VC++では警告オプション
/W4
を付加する - C++17に対応する
- GCCでは警告オプション
作成したCMakeLists.txt
は次の通りです。
cmake_minimum_required(VERSION 3.1)
project(cmake_test_1 CXX)
add_executable(cmake_test test1.cpp)
target_compile_options(
cmake_test PRIVATE
$<$<CXX_COMPILER_ID:MSVC>: /W4>
$<$<CXX_COMPILER_ID:GNU>: -Wall -Wextra>
)
target_compile_features(cmake_test PRIVATE cxx_std_17)
target_compile_definitions(
cmake_test PRIVATE
$<$<CONFIG:Debug>: USE_HOGE>
)
唐突に見慣れない$<...>
が出てきましたが、これはGenerator-Expressionsというもので、ビルドタイプやコンパイラによって設定する値を変更するための仕掛けです。
Generator-Expressionsについては、エントリを改めて説明したいと思います。
まとめ
プロジェクトのディレクトリ直下にソースが置かれている単純なケースで、GCCとVC++の2つのコンパイラに対応し、DebugビルドとReleaseビルドで異なるコンパイルオプションを設定できるようなCMakeLists.txt
を作成してみました。
WindowsでMinGW用のMakefile
を生成するには、コマンドラインで次を入力します。GCCの場合には-DCMAKE_CXX_COMPILER
は不要です。
project\build> cmake -DCMAKE_CXX_COMPILER=コンパイラ -DCMAKE_BUILD_TYPE=ビルドタイプ -G "MinGW Makefiles" ..
(参考)各コンパイラにおけるビルドタイプごとのオプション初期値
GCC(MinGW含む)
GCCの場合-Wall
等の警告関係のオプションが設定されていないので注意。
変数 | オプションの初期設定 |
---|---|
CMAKE_CXX_FLAGS | (なし) |
CMAKE_CXX_FLAGS_DEBUG | -g |
CMAKE_CXX_FLAGS_RELEASE | -O3 -DNDEBUG |
CMAKE_CXX_FLAGS_RELWITHDEBINFO | -O2 -g -DNDEBUG |
CMAKE_CXX_FLAGS_MINSIZEREL | -Os -DNDEBUG |
VC++
変数 | オプションの初期設定 |
---|---|
CMAKE_CXX_FLAGS | /DWIN32 /D_WINDOWS /W3 /GR /EHsc |
CMAKE_CXX_FLAGS_DEBUG | /MDd /Zi /Ob0 /Od /RTC1 |
CMAKE_CXX_FLAGS_RELEASE | /MD /O2 /Ob2 /DNDEBUG |
CMAKE_CXX_FLAGS_RELWITHDEBINFO | /MD /Zi /O2 /Ob1 /DNDEBUG |
CMAKE_CXX_FLAGS_MINSIZEREL | /MD /O1 /Ob1 /DNDEBUG |
Clang(Windows)
変数 | オプションの初期設定 |
---|---|
CMAKE_CXX_FLAGS | (なし) |
CMAKE_CXX_FLAGS_DEBUG | -g -Xclang -gcodeview -O0 -D_DEBUG -D_DLL -D_MT -Xclang –dependent-lib=msvcrtd |
CMAKE_CXX_FLAGS_RELEASE | -O3 -DNDEBUG -D_DLL -D_MT -Xclang –dependent-lib=msvcrt |
CMAKE_CXX_FLAGS_RELWITHDEBINFO | -O2 -g -DNDEBUG -Xclang -gcodeview -D_DLL -D_MT -Xclang –dependent-lib=msvcrt |
CMAKE_CXX_FLAGS_MINSIZEREL | -Os -DNDEBUG -D_DLL -D_MT -Xclang –dependent-lib=msvcrt |
Clang(Linux)
Linux版のClangはWindows版と比べるとシンプルで、GCCと同じ。
変数 | オプションの初期設定 |
---|---|
CMAKE_CXX_FLAGS | (なし) |
CMAKE_CXX_FLAGS_DEBUG | -g |
CMAKE_CXX_FLAGS_RELEASE | -O3 -DNDEBUG |
CMAKE_CXX_FLAGS_RELWITHDEBINFO | -O2 -g -DNDEBUG |
CMAKE_CXX_FLAGS_MINSIZEREL | -Os -DNDEBUG |
ディスカッション
コメント一覧
まだ、コメントがありません