CMake: 定義値のエスケープの重要性 - CMP0005 ポリシーとエスケープ方法
CMakeのポリシー CMP0005
は、CMakeが add_definitions
コマンドで追加されたプリプロセッサ定義値をエスケープするかどうかを制御します。このポリシーは、CMake 2.6 で導入され、CMake 3.30 で非推奨になりました。
動作
- NEW: 定義値はエスケープされて生成されます。これは、より安全で堅牢な動作です。
- OLD: (非推奨) 定義値はエスケープされずに生成されます。これは、CMake 2.4 以前の動作と互換性があります。
影響
CMP0005
ポリシーが OLD
に設定されている場合、add_definitions
コマンドで追加された定義値は、エスケープされずに生成されます。これは、特殊文字を含む定義値を使用する場合に問題を引き起こす可能性があります。
例
add_definitions("MY_DEFINE=\"Hello, World!\"")
このコードは、MY_DEFINE
という名前のマクロを定義します。このマクロの値は "Hello, World!" です。しかし、CMP0005
ポリシーが OLD
に設定されている場合、このマクロは次のように展開されます。
#define MY_DEFINE "Hello, World!"
この場合、"
文字はエスケープされません。これは、マクロが正しく展開されない可能性があることを意味します。
解決策
CMP0005
ポリシーを NEW
に設定することで、この問題は解決できます。これにより、CMake は定義値を自動的にエスケープします。
cmake_policy(SET CMP0005 NEW)
add_definitions("MY_DEFINE=\"Hello, World!\"")
このコードは、MY_DEFINE
マクロの値を "Hello, World!" に設定します。しかし、CMP0005
ポリシーが NEW
に設定されているため、このマクロは次のように展開されます。
#define MY_DEFINE "Hello\\, World!"
この場合、"
文字はエスケープされます。これは、マクロが正しく展開されることを意味します。
推奨事項
CMP0005
ポリシーは常に NEW
に設定することをお勧めします。これにより、より安全で堅牢なコードを記述できます。
非推奨化
CMP0005
ポリシーは、CMake 3.30 で非推奨になりました。これは、このポリシーが将来のバージョンで削除される可能性があることを意味します。
代替手段
CMP0005
ポリシーを使用する代わりに、string(ESCAPE)
関数を使用して定義値を明示的にエスケープすることもできます。
string(ESCAPE MY_DEFINE_ESCAPED "Hello, World!")
add_definitions("MY_DEFINE=${MY_DEFINE_ESCAPED}")
このコードは、MY_DEFINE_ESCAPED
という変数に "Hello, World!" のエスケープされた値を格納します。次に、add_definitions
コマンドを使用して、この変数の値を定義値として追加します。
CMP0005 ポリシーを NEW に設定する
cmake_policy(SET CMP0005 NEW)
add_definitions コマンドを使用して定義値を追加する
add_definitions("MY_DEFINE=\"Hello, World!\"")
このコードは、MY_DEFINE
という名前のマクロを定義します。このマクロの値は "Hello, World!" です。
string(ESCAPE) 関数を使用して定義値を明示的にエスケープする
string(ESCAPE MY_DEFINE_ESCAPED "Hello, World!")
add_definitions("MY_DEFINE=${MY_DEFINE_ESCAPED}")
cmake_policy(SET CMP0005 NEW)
add_definitions("MY_DEFINE_NO_ESCAPE=\"Hello, World!\"")
string(ESCAPE MY_DEFINE_ESCAPED "Hello, World!")
add_definitions("MY_DEFINE=${MY_DEFINE_ESCAPED}")
このコードは、2 つの定義値を定義します。
MY_DEFINE
:string(ESCAPE)
関数を使用してエスケープされた定義値MY_DEFINE_NO_ESCAPE
: エスケープされていない定義値
これにより、2 つの定義値を比較して、CMP0005
ポリシーが定義値のエスケープにどのように影響するかを確認できます。
以下の例は、CMP0005
ポリシーがどのように動作するかを示しています。
例 1: CMP0005
ポリシーが OLD
に設定されている場合
cmake_policy(SET CMP0005 OLD)
add_definitions("MY_DEFINE=\"Hello, World!\"")
message(STATUS "MY_DEFINE: ${MY_DEFINE}")
このコードは、次の出力を生成します。
MY_DEFINE: "Hello, World!"
この例では、CMP0005
ポリシーが OLD
に設定されているため、"
文字はエスケープされません。
cmake_policy(SET CMP0005 NEW)
add_definitions("MY_DEFINE=\"Hello, World!\"")
message(STATUS "MY_DEFINE: ${MY_DEFINE}")
MY_DEFINE: "Hello\\, World!"
代替手段
CMP0005
ポリシーの代替手段として、以下の方法があります。
string(ESCAPE)
関数を使用する
この関数は、指定された文字列をエスケープして、プリプロセッサで使用できるようにします。
string(ESCAPE MY_DEFINE_ESCAPED "Hello, World!")
add_definitions("MY_DEFINE=${MY_DEFINE_ESCAPED}")
target_compile_definitions
またはtarget_link_libraries
コマンドを使用する
これらのコマンドは、ターゲットに定義値またはライブラリを追加するために使用できます。これらのコマンドは、自動的にエスケープされた値を使用します。
target_compile_definitions(mytarget "MY_DEFINE=\"Hello, World!\"")
このコードは、mytarget
ターゲットに MY_DEFINE
という名前のマクロを定義します。このマクロの値は "Hello, World!" です。
- CMake 3.30 以降を使用する
CMake 3.30 以降では、CMP0005
ポリシーはデフォルトで NEW
に設定されています。これは、add_definitions
コマンドで追加された定義値が自動的にエスケープされることを意味します。
各方法の比較
方法 | 説明 | 利点 | 欠点 |
---|---|---|---|
string(ESCAPE) 関数を使用する | 定義値を明示的にエスケープする | 柔軟性が高い | 手動でエスケープする必要がある |
target_compile_definitions または target_link_libraries コマンドを使用する | ターゲットに定義値またはライブラリを追加する | 自動的にエスケープされた値を使用する | 柔軟性が低い |
CMake 3.30 以降を使用する | 最新の CMake バージョンを使用する | 自動的にエスケープされた値を使用する | 古いバージョンの CMake を使用している場合は使用できない |
推奨事項
CMP0005
ポリシーを使用する代わりに、以下の方法を使用することをお勧めします。
- CMake 3.30 以降を使用する: 最新の CMake バージョンを使用できる場合
target_compile_definitions
またはtarget_link_libraries
コマンドを使用する: ターゲットに定義値またはライブラリを追加したい場合string(ESCAPE)
関数を使用する: 定義値を個別に制御したい場合