Posted at
CMakeDay 15

CMake: ポリシー

More than 3 years have passed since last update.


はじめに

みなさん、こんにちは。今回はポリシーについて書いていきます。


ポリシーとはなにか?

ポリシーとは、互換性維持のために過去の挙動を保持する機構のことで、CMake 2.4 から導入されました。このポリシーには様々な種類があり、挙動ごとにCMP<NNNN>という識別子が割り振られます。そして、それぞれ必要に応じて "古い挙動"を表すOLD、"新しい挙動"を表すNEW に設定されます。主な用途として、古いCMakeでしか動作しないプロジェクトを最新のCMakeで動かしたい場合に使用します。

以下、ポリシーの使用方法について述べていきます。


ポリシーの取得

ポリシーの取得には、cmake_policy(GET)コマンドを使用します。

cmake_policy(GET <policy-id> <output-variable>)

<policy-id>はポリシーの識別子CMP<NNNN>を指定し、<output-variable>には代入したい変数名を指定します。この変数にはOLDもしくはNEWが代入されますが、指定のバージョンで導入されていないポリシーを指定した場合は、空文字列が代入されます。

cmake_minimum_required(VERSION 3.0.2)

cmake_policy(GET CMP0007 behavior)
message("CMP0007: ${behavior}") # CMP0007: NEW


ポリシーの設定

ポリシーの設定方法には、以下に示す2種類があります。


個別に指定

ポリシーを個別に設定するには、cmake_policy(SET)コマンドを使用します。

cmake_policy(SET <policy-id> <behavior>)

<policy-id>はポリシーの識別子CMP<NNNN>を指定し、<behavior>には、OLDもしくはNEWを指定します。

cmake_minimum_required(VERSION 3.0.2)

cmake_policy(GET CMP0007 behavior)
message("CMP0007: ${behavior}") # CMP0007: NEW

cmake_policy(SET CMP0007 OLD)

cmake_policy(GET CMP0007 behavior)
message("CMP0007: ${behavior}") # CMP0007: OLD


CMake のバージョンで指定

cmake_policy(VERSION)コマンドを使用すると、CMake のバージョンにもとづいて一括で設定できます。

cmake_policy(VERSION <cmake-version>)

<cmake-version>にはCMakeのバージョンをmajor[.minor[.patch[.tweak]]]というフォーマットで指定します。そうすると、指定したバージョンまでに導入されたすべてのポリシーがNEWに設定されます(導入されていないポリシーには空文字列が設定されます)。

cmake_minimum_required(VERSION 3.0.2)

cmake_policy(GET CMP0007 cmp0007_behavior)
cmake_policy(GET CMP0050 cmp0050_behavior)
message("CMP0007: ${cmp0007_behavior}") # CMP0007: NEW
message("CMP0050: ${cmp0050_behavior}") # CMP0050: NEW

cmake_policy(VERSION 2.8)

cmake_policy(GET CMP0007 cmp0007_behavior)
cmake_policy(GET CMP0050 cmp0050_behavior)
message("CMP0007: ${cmp0007_behavior}") # CMP0007: NEW
message("CMP0050: ${cmp0050_behavior}") # CMP0050:

また、このコマンドは CMake スクリプトの動作する下限バージョンを指定するcmake_minimum_required(VERSION)コマンド内部でも呼び出されます。このコマンドを使用すると、指定したバージョンがCMAKE_MINIMUM_REQUIRED_VERSION変数に設定されるので、こちらの方が使い勝手が良いかもしれません。


ポリシーのデフォルト値

ポリシーはCMAKE_POLICY_DEFAULT_CMP<NNNN>キャッシュ変数を用いて、デフォルト値を設定することができます。たとえば、ポリシーCMP0050は CMake 3.0 から導入されたポリシーなので、CMake 2.8 で動作させても、新しい挙動で動作しません。しかし、キャッシュ変数CMAKE_POLICY_DEFAULT_CMP0050NEWに設定すると、CMake 2.8 でも新しい挙動で動作するようになります。

set(CMAKE_POLICY_DEFAULT_CMP0050 NEW CACHE STRING "" FORCE)

cmake_minimum_required(VERSION 2.8)
cmake_policy(GET CMP0050 current_behavior)
message("CMP0050: ${current_behavior}") # CMP0050: NEW

unset(CMAKE_POLICY_DEFAULT_CMP0050 CACHE)

cmake_minimum_required(VERSION 2.8)
cmake_policy(GET CMP0050 current_behavior)
message("CMP0050: ${current_behavior}") # CMP0050:

このCMAKE_POLICY_DEFAULT_CMP<NNNN>キャッシュ変数は、コマンドラインにて設定することが強く奨励されているので、通常は以下のように-Dオプションを用いて、コマンドラインから設定することになります。

$ cmake -DCMAKE_POLICY_DEFAULT_CMP0050=NEW .


ポリシーが存在しているかどうか

ポリシーの取得・設定には、cmake_policy(GET)コマンド、およびcmake_policy(SET)コマンドを用いますが、存在しないポリシーを指定するとエラーになってしまいます。

こんなときは、CMake: 条件分岐 で示した、if(POLICY)コマンドを使用することで、存在するときのみに処理を実行することができます。

if(POLICY CMP0100)

cmake_policy(SET CMP0100 NEW)
endif()


ポリシーのスコープ

ポリシーはcmake_policy(PUSH)コマンドおよびcmake_policy(POP)コマンドを用いてスコープを生成することができます。これによって、ポリシーの変更による影響を最小限度に抑えることができます。また、これらのコマンドは対になっていなければなりません。

ポリシーのスコープは、変数のスコープと同様に親スコープから設定を引き継ぎ、子スコープでのポリシーの変更が親スコープに影響することはありません。

cmake_minimum_required(VERSION 3.0.2)

cmake_policy(GET CMP0050 current_behavior)
message("CMP0050: ${current_behavior}") # CMP0050: NEW

cmake_policy(PUSH)
cmake_policy(SET CMP0050 OLD)
cmake_policy(GET CMP0050 current_behavior)
message("CMP0050: ${current_behavior}") # CMP0050: OLD
cmake_policy(POP)

cmake_policy(GET CMP0050 current_behavior)
message("CMP0050: ${current_behavior}") # CMP0050: NEW

また、include()コマンドとfind_package()コマンドは、自動的にスコープを生成するので、呼び出し元のポリシーが変更されることはありません。しかし、NO_POLICY_SCOPEオプションをこれらのコマンドに付与すると、スコープの生成を抑制することができます。これによって、呼び出し元のポリシーの設定を変更することが目的のモジュールを作成することができます。


コマンド定義におけるポリシー

function()macro()といったコマンド定義を行うコマンドでは、コマンド定義時のポリシーの設定を保持するので注意が必要です。

cmake_minimum_required(VERSION 3.0.2)

function(display_cmp0007_behavior)
cmake_policy(GET CMP0007 current_behavior)
message("CMP0007: ${current_behavior}")
endfunction()

display_cmp0007_behavior() # CMP0007: NEW

cmake_policy(SET CMP0007 OLD)
display_cmp0007_behavior() # CMP0007: NEW


ポリシーの種類と例

CMake 3.0.2 現在CMP0000からCMP0050までの51種類のポリシーがあり、cmake-policies(7) — CMake 3.0.2 Documentation, All Policies にその一覧があります。

大量のポリシーがありますが、一例としてCMP0007を見てみましょう。このポリシーではlist()コマンドの挙動を管理しています。list()コマンドは、古い挙動ではリストの空要素を認識しませんが、新しい挙動では空要素を認識します。

function(verify_cmp0007 behavior)

cmake_policy(SET CMP0007 ${behavior})
cmake_policy(GET CMP0007 current_behavior)
message("CMP0007: ${current_behavior}")

set(list_value "a;b;;c")
list(LENGTH list_value length)
message("list: ${list_value}")
message("length: ${length}")
message("")
endfunction()

verify_cmp0007(OLD) #[[
CMP0007: OLD
list: a;b;;c
length: 3
]]

verify_cmp0007(NEW) #[[
CMP0007: NEW
list: a;b;;c
length: 4
]]


おわりに

以上、ポリシーでした。外部ライブラリの CMake スクリプトを自分のプロジェクトの CMakeスクリプトで使用するときに活躍するでしょう。

明日は、mrk_21 さんの『CMake: スクリプトモード』です。