Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
8
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

CMake: ポリシー

はじめに

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

ポリシーとはなにか?

ポリシーとは、互換性維持のために過去の挙動を保持する機構のことで、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: スクリプトモード』です。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
8
Help us understand the problem. What are the problem?