CMake: ポリシー

## はじめに

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

## ポリシーとはなにか？

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

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

## ポリシーの取得

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

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

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

```cmake
cmake_minimum_required(VERSION 3.0.2)

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

## ポリシーの設定

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

### 個別に指定

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

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

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

```cmake
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
cmake_policy(VERSION <cmake-version>)
```

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

```cmake
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_CMP0050`を`NEW`に設定すると、CMake 2.8 でも新しい挙動で動作するようになります。

```cmake
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`オプションを用いて、コマンドラインから設定することになります。

```bash
$ cmake -DCMAKE_POLICY_DEFAULT_CMP0050=NEW .
```

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

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

こんなときは、[CMake: 条件分岐](http://qiita.com/mrk_21/items/49d8802dc63a2791bcc3#対象が存在するか) で示した、`if(POLICY)`コマンドを使用することで、存在するときのみに処理を実行することができます。

```cmake
if(POLICY CMP0100)
  cmake_policy(SET CMP0100 NEW)
endif()
```

## ポリシーのスコープ

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

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

```cmake
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
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](http://www.cmake.org/cmake/help/v3.0/manual/cmake-policies.7.html#all-policies) にその一覧があります。

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

```cmake
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: スクリプトモード』です。