CMake: キャッシュ変数と環境変数

## はじめに

みなさん、こんにちは。CMakeの変数には、いままで使用してきたものの他に、キャッシュ変数と環境変数というものが存在します。今回は、これらについて書いていきます。

## キャッシュ変数

キャッシュ変数とは、文字通りキャッシュされた変数のことで、値がキャッシュファイルに保存されています。そのため、キャッシュファイルが削除されるまで存在し続けます。主な用途として、使用するコンパイラや、依存ライブラリのパスなどの格納です。これらは、CMakeの実行毎に設定する必要はないので、キャッシュ変数を用いて永続化します。また、キャッシュ変数は、通常の変数と違って、スクリプトのどこからでも参照・設定ができます。

このキャッシュ変数の操作方法は以下のようになっています。

### 生成・代入

キャッシュ変数の生成や代入には、`set()`コマンドの`CACHE`オプションを用います。

```cmake
set(<variable> <value> [...] CACHE <type> <docstring> [FORCE])
```

変数名`<variable>`および値`<value> ...`の指定に関しては、通常の変数と同様ですが、その直後にキャッシュ変数であることを示す`CACHE`オプション、キャッシュ変数の種類`<type>`、説明用テキスト`<docstring>`が必要になります。キャッシュ変数の種類`<type>`および説明用テキスト`<docstring>`は、キャッシュファイル編集ツールである`cmake-gui`や`ccmake`のためのもので、変数の性質に関与するものではありません。キャッシュ変数の種類`<type>`は編集用のフォームを生成するためのヒント情報で、説明用テキスト`<docstring>`はそのラベルとなります。キャッシュ変数の種類`<type>`は以下の値をとります。

| 値 | 生成されるフォーム |
|----|----|
| FILEPATH | ファイル選択ダイアログ |
| PATH | ディレクトリ選択ダイアログ |
| STRING | 入力フォーム |
| BOOL | ON/OFFスイッチ |
| INTERNAL | フォームなし |

上記のように、指定しなければいけない値が多い、という以外にも、キャッシュ変数には普通の変数にはない特徴をもっています。その一つが生成はできるが変更はできない、というものです。たとえば以下の例を見てください。

```cmake
set(cache_value 1 CACHE STRING description)
message("cache_value: ${cache_value}") # cache_value: 1

set(cache_value 2 CACHE STRING description)
message("cache_value: ${cache_value}") # cache_value: 1
```

上記において、2回目の`set()`ではキャッシュ変数`cache_value`に`2`を代入しているにもかかわらず、値は`1`のままです。おそらく、ユーザーが`cmake-gui`などのツールで編集した値を、CMakeが勝手に上書きしないためだと思われます。

ですが、時には強制的に上書きしたいときもあります。この場合は、`set()`コマンドに`FORCE`オプションを付与することによりこれが可能になります。

```cmake
set(cache_value 1 CACHE STRING description)
message("cache_value: ${cache_value}") # cache_value: 1

set(cache_value 2 CACHE STRING description FORCE)
message("cache_value: ${cache_value}") # cache_value: 2
```

さきほどの例とは異なり、2回目の`set()`で代入した値が、実際にセットされています。

しかし、変数の種類が`INTERNAL`である場合に限り、上のルールは適用されません。`FORCE`オプションを付与しなくても変更することができます。

```cmake
set(cache_value 1 CACHE INTERNAL description)
message("cache_value: ${cache_value}") # cache_value: 1

set(cache_value 2 CACHE INTERNAL description)
message("cache_value: ${cache_value}") # cache_value: 2
```

これは、変数の種類が`INTERNAL`の場合は、`FORCE`オプションが暗黙的に付与されているからです。このタイプのキャッシュ変数は、`cmake-gui`などのツールでは表示されないので、上書きしないように保護する必要がないからだと思われます。

### 削除

キャッシュ変数の削除には、`unset()`コマンドに`set()`コマンドのときと同様に`CACHE`オプションを付与することで行えます。

```cmake
unset(<variable> CACHE)
```

`CACHE`オプションがついている以外は普通の変数のときと同じように使用できます。

### 参照

キャッシュ変数の参照には、通常の変数と同様の`${}`を使う方法と、`$CACHE{}`の2種類があります。

```cmake
set(cache_value 1 CACHE STRING description)
message("\${cache_value}: ${cache_value}") # ${cache_value}: 1
message("\$CACHE{cache_value}: $CACHE{cache_value}") # $CACHE{cache_value}: 1
```

これらは、基本的に同じ動作を行いますが、キャッシュ変数と同名の通常の変数が存在したときの動作が異なります。この場合、通常の変数と同様の形式、`${}`を用いたときは、キャッシュ変数ではなく通常の変数の値を参照します。

```cmake
set(cache_value 1 CACHE STRING description)
set(cache_value 2)
message("\${cache_value}: ${cache_value}") # ${cache_value}: 2
message("\$CACHE{cache_value}: $CACHE{cache_value}") # $CACHE{cache_value}: 1
```

しかし、その後再度キャッシュ変数を`set()`すると、この同名の通常の変数は削除され、再びキャッシュ変数が参照できるようになります。このときに、種類が`INTERNAL`でないのなら、`FORCE`オプションを付与しなければならないことに注意してください。

```cmake
set(cache_value 1 CACHE STRING description)
set(cache_value 2)
message("\${cache_value}: ${cache_value}") # ${cache_value}: 2
message("\$CACHE{cache_value}: $CACHE{cache_value}") # $CACHE{cache_value}: 1

set(cache_value 1 CACHE STRING description FORCE)
message("\${cache_value}: ${cache_value}") # ${cache_value}: 1
message("\$CACHE{cache_value}: $CACHE{cache_value}") # $CACHE{cache_value}: 1
```

この規則は複雑ですが、いくつかの例で役に立ちます。たとえば、コンパイラオプションのキャッシュ変数の値を、あるサブディレクトリで違うものに上書きしたい場合です。キャッシュ変数はどこからでも変更できるので、サブディレクトリでの値の変更はすべてのディレクトリに影響します。これを回避するために同名の通常の変数に上書きしたい値をセットし、`${}`の形で参照することによって、値を上書きしつつも影響をサブディレクトリ内に留めることが可能となります。

```cmake:CMakeLists
set(CMAKE_CXX_FLAGS "-std=gnu++14 -stdlib=libc++ -Wall -Wextra" CACHE STRING
  [[Flags used by the compiler during all build types.]] FORCE)

message("CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}") # -std=gnu++14 -stdlib=libc++ -Wall -Wextra
add_subdirectory(subdir)
message("CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}") # -std=gnu++14 -stdlib=libc++ -Wall -Wextra
```

```cmake:subdir/CMakeLists
set(CMAKE_CXX_FLAGS "$CACHE{CMAKE_CXX_FLAGS} -Wno-unused-parameter")
message("CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}") # -std=gnu++14 -stdlib=libc++ -Wall -Wextra -Wno-unused-parameter
```

## 環境変数

CMakeは、OSの環境変数の参照と設定も行うことができます。キャッシュ変数同様に、どこからでも参照・設定ができます。

環境変数の操作は以下のようになっています。

### 生成・代入

環境変数の生成・代入は、キャッシュ変数の時とは違い通常の変数と同じように行います。ただし、変数名の指定には`ENV{}`という形式でなければなりません。

```cmake
set(ENV{<variable>} <value> [...])
```

### 削除

環境変数の削除も通常の変数同様に行います。ここでも変数名の指定には`ENV{}`としなければなりません。

```cmake
unset(ENV{<variable>})
```

### 参照

環境変数の参照には、`$ENV{}`を使用します。

```cmake
set(ENV{PATH} /usr/local/bin)
message("PATH: $ENV{PATH}") # /usr/local/bin
```

## おわりに

以上、キャッシュ変数と環境変数でした。キャッシュ変数の規則はなかなか複雑なので注意する必要があります。

明日は、osamu0329 さんの『out-of-sourceビルドについて』です。