はじめに
みなさん、こんにちは。今回はモジュールについて書いていきます。
モジュールとは何か?
モジュールとは、定義したコマンドなどを複数のスクリプトで再利用するためのもので、include()
コマンドやfind_package()
コマンドなどで読み込まれます。
function(func)
message(func)
endfunction()
include(/path/to/module.cmake)
func() # func
また、モジュール内の各コマンドは読み込み元の文脈で評価されるので、モジュール内での変数の設定は読み込み元に影響します。
set(value 2)
set(value 1)
message("value: ${value}") # value: 1
include(/path/to/module.cmake)
message("value: ${value}") # value: 2
include() コマンド
モジュールを読み込む方法はいくつかありますが、本エントリーではinclude()
コマンドについて述べます。
include()
コマンドとはモジュールを読み込む基本的な方法で、定義は以下となります。
include(<file|module> [OPTIONAL] [RESULT_VARIABLE <VAR>] [NO_POLICY_SCOPE])
モジュールの指定
使用するモジュールの指定は<file|module>
引数で行いますが、これには以下の2つの方法があります。
絶対パスで指定
モジュールのファイルへの絶対パスで指定する方法です。
include(/path/to/module.cmake)
モジュール名で指定
モジュール名で指定する方法です。モジュール名とは、変数(もしくはキャッシュ変数)CMAKE_MODULE_PATH
に登録されているパスからモジュールへの相対パスで、拡張子.cmake
を除去したものです。
set(CMAKE_MODULE_PATH /path/to)
include(module)
set(CMAKE_MODULE_PATH /path)
include(to/module)
また、変数CMAKE_MODULE_PATH
には複数のパスをリストとして設定することが可能です。この場合、リストの先頭から線形探索していき、最初に見つかったモジュールをロードします。
# /path/to
# ├── module.cmake
# └── dir
# └── module.cmake
set(CMAKE_MODULE_PATH /path/to /path/to/dir)
include(module) # /path/to/module.cmake
include(dir/module) # /path/to/dir/module.cmake
set(CMAKE_MODULE_PATH /path/to/dir /path/to)
include(module) # /path/to/dir/module.cmake
include(dir/module) # /path/to/dir/module.cmake
ここで注意したいのは、CMake が提供している標準モジュールは最後に探索されるということです。つまり、標準モジュールと同名のモジュールがCMAKE_MODULE_PATH
の中に存在する場合、その標準モジュールは隠蔽されてしまいます。
# /path/to
# └── Documentation.cmake
include(Documentation) # 標準ライブラリ Documentation
set(CMAKE_MODULE_PATH /path/to)
include(Documentation) # /path/to/Documentation.cmake
OPTIONAL オプション
OPTIONAL
オプションを付与すると、モジュールが見つからない場合でもエラーになりません。
include(/not/existing/module.cmake) # エラー
include(/not/existing/module.cmake OPTIONAL) # OK
RESULT_VARIABLE オプション
RESULT_VARIABLE
オプションを付与すると、ロードしたモジュールの絶対パスが指定の変数に設定されます。
set(CMAKE_MODULE_PATH /path/to)
include(module RESULT_VARIABLE module_path)
message("module_path: ${module_path}") # module_path: /path/to/module.cmake
また、OPTIONAL
オプションも付与し、かつ指定したモジュールが見つからない場合には、指定の変数にNOTFOUND
が設定されます。
include(/not/existing/module.cmake OPTIONAL RESULT_VARIABLE module_path)
message("module_path: ${module_path}") # module_path: NOTFOUND
NO_POLICY_SCOPE オプション
include()
コマンドは、ポリシースコープを生成するので、モジュール内でのポリシーの設定が呼び出し元に影響することはありません。
cmake_policy(GET CMP0007 cmp0007_behavior)
message("cmp0007_behavior: ${cmp0007_behavior}")
cmake_policy(SET CMP0007 OLD)
cmake_policy(GET CMP0007 cmp0007_behavior)
message("cmp0007_behavior: ${cmp0007_behavior}")
cmake_policy(GET CMP0007 cmp0007_behavior)
message("cmp0007_behavior: ${cmp0007_behavior}") # cmp0007_behavior: NEW
include(/path/to/module.cmake) #[[
cmp0007_behavior: NEW
cmp0007_behavior: OLD
]]
cmake_policy(GET CMP0007 cmp0007_behavior)
message("cmp0007_behavior: ${cmp0007_behavior}") # cmp0007_behavior: NEW
しかし、NO_POLICY_SCOPE
オプションを付与すると、ポリシースコープを生成しなくなるので、モジュール内でのポリシーの設定が呼び出し元に影響します。
cmake_policy(GET CMP0007 cmp0007_behavior)
message("cmp0007_behavior: ${cmp0007_behavior}") # cmp0007_behavior: NEW
include(/path/to/module.cmake NO_POLICY_SCOPE) #[[
cmp0007_behavior: NEW
cmp0007_behavior: OLD
]]
cmake_policy(GET CMP0007 cmp0007_behavior)
message("cmp0007_behavior: ${cmp0007_behavior}") # cmp0007_behavior: OLD
このオプションは、ポリシーの設定を目的とするモジュールをロードするときに必要となるでしょう。
なお、include()
コマンドは同じモジュールを何回でも読み込むことに注意してください。
set(value 1)
message(module)
include(/path/to/module.cmake) # module
message("value: ${value}") # value: 1
set(value 2)
message("value: ${value}") # value: 2
include(/path/to/module.cmake) # module
message("value: ${value}") # value: 1
モジュールの種類
モジュールには、自分や第三者が作成したものの他に、CMake が標準で提供しているものが多数あります。この標準モジュールの詳細は、cmake-modules(7) にあります。
おわりに
以上、モジュールでした。よく使う共通の処理をモジュールとしておくことで、コードを再利用することができます。
明日は、mrk_21 さんの『CMake: Generator Expressions』です。