CMake - プリプロセッサ
概要
プリプロセッサの定義
target_compile_definitionsコマンド (推奨)
特定のビルドタイプに基づいて、プリプロセッサ定義を設定することがよくある。
例えば、デバッグビルドの場合にプリプロセッサマクロとして_DEBUGを定義する場合、
target_compile_definitions
コマンドを使用して、その定義をデバッグビルドにのみ適用するように指定する。
# デバッグビルドの場合にのみ_DEBUGを定義する
target_compile_definitions(<プロジェクト名> PRIVATE
$<$<CONFIG:Debug>:_DEBUG>
)
上記の例では、$<CONFIG:Debug>ジェネレータ式を使用している。
これは、現在のビルドがデバッグビルド(Debug)である場合に_DEBUGマクロを定義するものである。
target_compile_definitions
の第2引数にPRIVATE
キーワードを使用しているため、このプリプロセッサはこのターゲットにのみ適用されて、他のターゲットには影響を与えない。
もし、ライブラリ等で他のターゲットにもこの定義を適用する場合は、PUBLIC
またはINTERFACE
キーワードを使用する。
この機能を使用することにより、デバッグビルドとリリースビルドで異なるプリプロセッサマクロを簡単に管理できる。
- PUBLIC
- ターゲット自身とそれをリンクするターゲットの両方に適用される。
- PRIVATE
- ターゲットのソースコードでのみ適用される。
- INTERFACE
- ターゲットをリンクする他のターゲットにのみ適用される。
add_definitionsコマンド (非推奨)
add_definitions
コマンドの使用は避けるべきである。
これは、以下に示すような問題があるからである。
- プロジェクト全体に影響を与えてしまう。
- ビルド依存性の管理が難しくなる。
- コンパイラの最適化を阻害する可能性がある。
推奨されるコマンドは、target_compile_definitions
コマンドである。
このコマンドは、ターゲット単位で定義を設定できるため、上記の問題を回避することができる。
使用例
バージョンによりプリプロセッサを定義する
バージョンが特定の値未満の場合に特定のプリプロセッサを定義する場合、
CMakeのバージョン比較機能とtarget_compile_definitions
コマンドを組み合わせて使用する。
推奨する方法
- まず、プロジェクトのバージョンを定義する。
- 次に、条件分岐でバージョンを比較して、条件に合致する場合にのみプリプロセッサを定義する。
以下の例では、メジャーバージョンが0より大きい場合、または、メジャーバージョンが0でマイナーバージョンが2以上の場合に、処理を実行している。
これにより、将来のバージョンに対して柔軟にソースコードを記述することができる。
project(MyProject VERSION 0.1.0)
target_compile_definitions(MyProject PRIVATE
-DMYPROJECT_VERSION_MAJOR=${PROJECT_VERSION_MAJOR}
-DMYPROJECT_VERSION_MINOR=${PROJECT_VERSION_MINOR}
)
#if MYPROJECT_VERSION_MAJOR > 0 || (MYPROJECT_VERSION_MAJOR == 0 && MYPROJECT_VERSION_MINOR >= 2)
// MyProject 0.2以降の処理を記述
#endif
非推奨の方法
- まず、プロジェクトのバージョンを定義する。
- 次に、条件分岐でバージョンを比較して、条件に合致する場合にのみプリプロセッサを定義する。
project(MyProject VERSION 0.1.0)
# ターゲットを定義
add_executable(MyProject
main.cpp
)
# バージョンを比較して、条件に合致する場合はプリプロセッサを定義
# 以下の例では、バージョンが0.1.0未満かどうかを確認して、合致している場合はMY_VERSION_BELOW_0_1_0プリプロセッサを定義
if(PROJECT_VERSION VERSION_LESS 0.1.0)
target_compile_definitions(MyProject PRIVATE
MY_VERSION_BELOW_0_1_0
)
endif()
上記のようなプリプロセッサは、プリプロセッサディレクティブを使用して条件付きで特定のソースコードを含めたり除外する場合に使用できる。
例えば、C++のソースコードにおいて、以下に示すように使用することができる。
この方法により、異なるバージョンのプロジェクトに対して、条件付きでソースコードをコンパイルする複雑なロジックが実装できる。
#ifdef MY_VERSION_BELOW_0_1_0
// バージョンが0.1.0未満の場合にのみコンパイルされるコード
#endif
※注意
ただし、この方法では新しいバージョンが追加されるごとにプリプロセッサを更新する必要がある。
より柔軟な方法は、バージョン番号を個別の部分に分割することである。