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 ポリシーの代替手段として、以下の方法があります。

  1. string(ESCAPE) 関数を使用する

この関数は、指定された文字列をエスケープして、プリプロセッサで使用できるようにします。

string(ESCAPE MY_DEFINE_ESCAPED "Hello, World!")
add_definitions("MY_DEFINE=${MY_DEFINE_ESCAPED}")
  1. target_compile_definitions または target_link_libraries コマンドを使用する

これらのコマンドは、ターゲットに定義値またはライブラリを追加するために使用できます。これらのコマンドは、自動的にエスケープされた値を使用します。

target_compile_definitions(mytarget "MY_DEFINE=\"Hello, World!\"")

このコードは、mytarget ターゲットに MY_DEFINE という名前のマクロを定義します。このマクロの値は "Hello, World!" です。

  1. 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) 関数を使用する: 定義値を個別に制御したい場合