CMake: モジュール

## はじめに

みなさん、こんにちは。今回はモジュールについて書いていきます。

## モジュールとは何か？

モジュールとは、定義したコマンドなどを複数のスクリプトで再利用するためのもので、`include()`コマンドや`find_package()`コマンドなどで読み込まれます。

```cmake:/path/to/module.cmake
function(func)
  message(func)
endfunction()
```

```cmake
include(/path/to/module.cmake)
func() # func
```

また、モジュール内の各コマンドは読み込み元の文脈で評価されるので、モジュール内での変数の設定は読み込み元に影響します。

```cmake:/path/to/module.cmake
set(value 2)
```

```cmake
set(value 1)
message("value: ${value}") # value: 1
include(/path/to/module.cmake)
message("value: ${value}") # value: 2
```


## include() コマンド

モジュールを読み込む方法はいくつかありますが、本エントリーでは`include()`コマンドについて述べます。

`include()`コマンドとはモジュールを読み込む基本的な方法で、定義は以下となります。

> ```cmake
> include(<file|module> [OPTIONAL] [RESULT_VARIABLE <VAR>]
>                       [NO_POLICY_SCOPE])
> 
> ```
> <cite>[include — CMake 3.0.2 Documentation](http://www.cmake.org/cmake/help/v3.0/command/include.html)</cite>

### モジュールの指定

使用するモジュールの指定は`<file|module>`引数で行いますが、これには以下の2つの方法があります。

#### 絶対パスで指定

モジュールのファイルへの絶対パスで指定する方法です。

```cmake
include(/path/to/module.cmake)
```

#### モジュール名で指定

モジュール名で指定する方法です。モジュール名とは、変数(もしくはキャッシュ変数)`CMAKE_MODULE_PATH`に登録されているパスからモジュールへの相対パスで、拡張子`.cmake`を除去したものです。

```cmake
set(CMAKE_MODULE_PATH /path/to)
include(module)

set(CMAKE_MODULE_PATH /path)
include(to/module)
```

また、変数`CMAKE_MODULE_PATH`には複数のパスをリストとして設定することが可能です。この場合、リストの先頭から線形探索していき、最初に見つかったモジュールをロードします。

```cmake
# /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`の中に存在する場合、その標準モジュールは隠蔽されてしまいます。

```cmake
# /path/to
# └── Documentation.cmake

include(Documentation) # 標準ライブラリ Documentation

set(CMAKE_MODULE_PATH /path/to)
include(Documentation) # /path/to/Documentation.cmake
```

### OPTIONAL オプション

`OPTIONAL`オプションを付与すると、モジュールが見つからない場合でもエラーになりません。

```cmake
include(/not/existing/module.cmake) # エラー
include(/not/existing/module.cmake OPTIONAL) # OK
```

### RESULT\_VARIABLE オプション

`RESULT_VARIABLE`オプションを付与すると、ロードしたモジュールの絶対パスが指定の変数に設定されます。

```cmake
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`が設定されます。

```cmake
include(/not/existing/module.cmake OPTIONAL RESULT_VARIABLE module_path)
message("module_path: ${module_path}") # module_path: NOTFOUND
```

### NO\_POLICY\_SCOPE オプション

`include()`コマンドは、ポリシースコープを生成するので、モジュール内でのポリシーの設定が呼び出し元に影響することはありません。

```cmake:/path/to/module.cmake
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
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
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()`コマンドは同じモジュールを何回でも読み込むことに注意してください。

```cmake:/path/to/module.cmake
set(value 1)
message(module)
```

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

## おわりに

以上、モジュールでした。よく使う共通の処理をモジュールとしておくことで、コードを再利用することができます。

明日は、mrk_21 さんの『CMake: Generator Expressions』です。