CMake: 便利なコマンド・変数

## はじめに

みなさん、こんにちは。今回は CMake スクリプトを記述する上で便利なコマンドや変数について紹介していきます。

## `list()`コマンド

リストの長さ取得や追加など、リスト操作の便利な機能を提供するコマンドです。[CMake: リスト](http://qiita.com/mrk_21/items/082bae48a5ef2ac1564c#リストを操作するコマンド)で詳しく述べられています。

## `file()`コマンド

`file()`コマンドは、各種ファイル操作やファイルの検索に加えて、ダウンロードやアップロード機能など様々な機能を提供します。よく使いそうな機能を下記に示します。

### ファイル生成・削除と入出力

#### WRITE・APPEND

```
file(WRITE <filename> [<content1> ...])
file(APPEND <filename> [<content1> ...])
```

`WRITE`はファイル`<filename>`に指定した文字列`<content1> ...`を出力します。このとき、指定したファイルが存在しない場合は作成します。`APPEND`はファイル末尾への追加となります。

```cmake
file(WRITE hoge.txt a b c) #[[
  $ cat hoge.txt
  abc
]]
file(APPEND hoge.txt a b c) #[[
  $ cat hoge.txt
  abcabc
]]

```

#### READ

```cmake
file(READ <filename> <variable> [LIMIT <length>] [OFFSET <offset>] [HEX])
```

ファイル`<filename>`を変数`<variable>`に読み込みます。`HEX`オプションが付与されると16進表記のバイト列として読み込みます。また、読み込む範囲を`<offset>..(<offset> + <length> - 1)`で指定できます。

```bash
$ echo abc > hoge.txt
```

```cmake
file(READ hoge.txt content) # abc
file(READ hoge.txt content HEX) # 616263
file(READ hoge.txt content LIMIT 2 OFFSET 1) # bc
```

#### MAKE\_DIRECTORY

```cmake
file(MAKE_DIRECTORY [<directory1> ...])
```

ディレクトリ`<directory1> ...`を作成します。

#### REMOVE・REMOVE\_RECURSE

```cmake
file(REMOVE [<file1> ...])
file(REMOVE_RECURSE [<file1> ...])
```

ファイル`<file1> ...`を削除します。ディレクトリを削除するためには`REMOVE_RECURSE`を使用します。

### ファイルの探索と取得

#### GLOB・GLOB\_RECURSE

```cmake
file(GLOB <variable> [RELATIVE <path>] [<globbing_expressions>]...)
file(GLOB_RECURSE <variable> [RELATIVE <path>]
     [FOLLOW_SYMLINKS] [<globbing_expressions>]...)
```

指定されたGLOBパターン`<globbing_expressions>`にマッチするファイルを探索し、その一覧をリストとして変数`<variable>`に格納します。このとき絶対パスで格納されますが、`RELATIVE <path>`が指定されていると、このパスからの相対パスとなります。`add_executable()`などに指定する依存ソースファイルの一覧を取得するのに使い、おそらく`file()`コマンドの中でもっともよく使うことになるでしょう。以下の正規表現のメタ文字に似たものが使えます。

* 任意の文字の0回以上の繰り返し`*`
* 任意の文字`?`
* ブラケット内で指定された文字`[ ]`とその範囲`-`

```
/path/to/
├── CMakeLists.txt
├── test1.txt
└── .txt
```

```cmake
file(GLOB files *.txt)      # /path/to/CMakeListx.txt;/path/to/test1.txt;/path/to/.txt
file(GLOB files test1*.txt) # /path/to/test1.txt
file(GLOB files [t-z]*.txt) # /path/to/test1.txt
file(GLOB files test?.txt)  # /path/to/test1.txt
```

### ファイル情報の取得

#### MD5・SHA1・SHA224・SHA256・SHA384・SHA512

```cmake
file(<MD5|SHA1|SHA224|SHA256|SHA384|SHA512> <filename> <variable>)
```

指定のハッシュ関数により、ファイル`<filename>`のダイジェストを生成し、変数`<variable>`に出力します。

### パスの変換

#### RELATIVE\_PATH

```cmake
file(RELATIVE_PATH <variable> <directory> <file>)
```

基準となるパス`<directory>`からの対象のパス`<file>`への相対パスを変数`<variable>`に格納します。

### ファイルのダウンロード

#### DOWNLOAD

```cmake
file(DOWNLOAD <url> file [INACTIVITY_TIMEOUT <timeout>]
     [TIMEOUT <timeout>] [STATUS <status>] [LOG <log>] [SHOW_PROGRESS]
     [EXPECTED_HASH ALGO=<value>] [EXPECTED_MD5 <sum>]
     [TLS_VERIFY on|off] [TLS_CAINFO <file>])
```

指定したURL`<url>`のリソースをファイル`<file>`にダウンロードします。外部プロジェクトやデータなどをダウンロードするのに使用するでしょう。

---

これ以外のコマンドについては、[file — CMake 3.0.2 Documentation](http://www.cmake.org/cmake/help/v3.0/command/file.html) に記述されています。

## `get_filename_component()`コマンド

```cmake
get_filename_component(<variable> <path> <comp> [CACHE])
```

`get_filename_component()`コマンドはファイル名や拡張子、ディレクトリなど指定したパス`<path>`の構成要素`<comp>`を取得し、変数`<variable>`に格納します。指定できる構成要素は以下のとおりです。

* ディレクトリのパス`DIRECTORY`
* ファイル名`NAME`
* 拡張子`EXT`
* 拡張子を除いたファイル名`NAME_WE`
* 絶対パス`ABSOLUTE`
* シンボリックリンクのリンク先の絶対パス`REALPATH`

```
/path/to/
├── script.cmake
└── script-alias.cmake -> script.cmake
```

```cmake
get_filename_component(result "/path/to/script.cmake" DIRECTORY) # /path/to
get_filename_component(result "/path/to/script.cmake" NAME)      # script.cmake
get_filename_component(result "/path/to/script.cmake" EXT)       # .cmake
get_filename_component(result "/path/to/script.cmake" NAME_WE)   # script
get_filename_component(result "script.cmake" ABSOLUTE)           # /path/to/script.cmake
get_filename_component(result "script-alias.cmake" REALPATH)     # /path/to/script.cmake
```

## `configure_file()`コマンド

```cmake
configure_file(<input> <output>
               [COPYONLY] [ESCAPE_QUOTES] [@ONLY]
               [NEWLINE_STYLE [UNIX|DOS|WIN32|LF|CRLF] ])
```

`configure_file()`コマンドは、元となるファイルからファイルに埋め込まれた変数を展開し、新しいファイルを生成する、いわゆるテンプレートエンジンの機能を提供するコマンドです。

@osamu0329 さんの [CMake:configure\_fileコマンド](http://qiita.com/osamu0329/items/edc66e2e1b6c96947771) で詳しく解説されています。

## `string()`コマンド

`string()`コマンドは、各種文字列処理を行うためのコマンドです。よく使いそうな機能を下記に示します。

### 文字列の探索と置換

#### REGEX MATCH・REGEX MATCHALL

```cmake
string(REGEX MATCH <regexp> <variable> <input1> [...])
string(REGEX MATCHALL <regexp> <variable> <input1> [...])
```

文字列`<input1> ...`が正規表現`<regexp>`にマッチするかを判定し、マッチした場合は、マッチした部分の文字列が変数`<variable>`に格納されます。`REGEX MATCH`では最初にマッチした部分しか格納されませんが、`REGEX MATCHALL`ではマッチした部分すべてがリストとして格納されます。

#### REGEX REPLACE・REPLACE

```cmake
string(REGEX REPLACE <pattern> <replace> <variable> <input> [...])
string(REPLACE <pattern> <replace> <variable> <input> [...])
```

文字列`input`でパターン`<pattern>`と一致した部分を文字列`<replace>`で置換します。`REGEX REPLACE`と`REPLACE`の違いは正規表現が使えるかどうかです。また、`REGEX REPLACE`の場合、置換する文字列`<replace>`の中で`\1`の形で後方参照することができます。

#### SUBSTRING

```cmake
string(SUBSTRING <string> <begin> <length> <variable>)
```

文字列`<string>`の中から`<begin>..(<begin> + <length>)`の範囲の文字列を変数`<variable>`に格納します。

### 文字列の情報取得

#### MD5・SHA1・SHA224・SHA256・SHA384・SHA512

```cmake
string(<MD5|SHA1|SHA224|SHA256|SHA384|SHA512> <variable> <input>)
```

`file(<MD5|SHA1|SHA224|SHA256|SHA384|SHA512>)`と同じですが、こちらはファイルの内容ではなく、任意の文字列を指定します。

#### LENGTH

```cmake
string(LENGTH <string> <variable>)
```

文字列`<string>`の長さを変数`<variable>`に格納します。

### 文字列の変換

#### STRIP

```cmake
string(STRIP <string> <variable>)
```

文字列`<string>`の両端から空白文字を削除し、変数`<variable>`に格納します。

#### TOUPPER・TOLOWER

```cmake
string(TOUPPER <string> <variable>)
string(TOLOWER <string> <variable>)
```

文字列`<string>`をすべて大文字/小文字にして、変数`<variable>`に格納します。

### 文字列の生成

#### CONFIGURE

```cmake
string(CONFIGURE <string> <variable>
       [@ONLY] [ESCAPE_QUOTES])
```

`configure_file()`コマンドと同様の動作をしますが、こちらはファイルではなく任意の文字列`<string>`に対して処理を行い、その結果を変数`<variable>`に出力します。

#### RANDOM

```cmake
string(RANDOM [LENGTH <length>] [ALPHABET <alphabet>]
       [RANDOM_SEED <seed>] <variable>)
```

乱数を生成し、変数`<variable>`に格納します。

#### TIMESTAMP

```cmake
string(TIMESTAMP <variable> [<string>] [UTC])
```

ファイル`<filename>`のタイムスタンプを変数`<variable>`に出力します。通常はローカル時間(たとえばJST)ですが、`UTC`オプションを指定すると標準時間となります。また、`<format>`により、任意のフォーマットに変換できます。指定できるフォーマットは下記のとおりとなります。

* 年(4桁)`%Y`
* 年(2桁)`%y`
* 月(01-12)`%m`
* 日(01-31)`%d`
* 日(001-336)`%j`
* 週(00-53)`%U`
* 曜日(0-6)`%w`
* 時(00-24)`%H`
* 時(00-12)`%I`
* 分(00-59)`%M`
* 秒(00-60)`%S`

---

これ以外のコマンドについては、[string — CMake 3.0.2 Documentation](http://www.cmake.org/cmake/help/v3.0/command/string.html) に記述されています。

## `math()`コマンド

```
math(EXPR <variable> <expr>)
```

`math()`コマンドは、四則演算やビット演算を行うコマンドです。`<expr>`に式を指定し、`<variable>`に出力される変数を指定します。`math()`コマンドでは、以下の演算が可能です。また、括弧`()`も使用できます。

* 可算`+`
* 減算`-`
* 乗算`*`
* 除算`/`
* 剰余`%`
* ビット和`|`
* ビット積`&`
* ビット排他的論理和`^`
* 右シフト`>>`
* 左シフト`<<`

注意点として、負の数を指定するとエラーになってしまうようです。また、ビット否定`~`が使えるはずですが、同じくエラーになってしまいます。

```cmake
math(EXPR result "1 + 1")           # 2
math(EXPR result "1 - 5")           # -4
math(EXPR result "2 * 3")           # 6
math(EXPR result "2 * 3 + (2 + 4)") # 12
math(EXPR result "1 << 3")          # 8
math(EXPR result "8 >> 2")          # 2
math(EXPR result "12 % 10")         # 2
math(EXPR result "8 | 15")          # 15
math(EXPR result "8 & 15")          # 8

math(EXPR result "-1 + 2") # エラー
math(EXPR result "~1")     # エラー
```

## `execute_process()`コマンド

```
execute_process(COMMAND <cmd1> [<arg1> ...]]
                [COMMAND <cmd2> [<arg2> ...] [...]]
                [WORKING_DIRECTORY <directory>]
                [TIMEOUT <seconds>]
                [RESULT_VARIABLE <variable>]
                [OUTPUT_VARIABLE <variable>]
                [ERROR_VARIABLE <variable>]
                [INPUT_FILE <file>]
                [OUTPUT_FILE <file>]
                [ERROR_FILE <file>]
                [OUTPUT_QUIET]
                [ERROR_QUIET]
                [OUTPUT_STRIP_TRAILING_WHITESPACE]
                [ERROR_STRIP_TRAILING_WHITESPACE])
```

`execute_process()`コマンドは、外部コマンドを実行するコマンドです。

```cmake
set(build_dir ${CMAKE_CURRENT_LIST_DIR}/build)
file(MAKE_DIRECTORY ${build_dir})
execute_process(
  COMMAND ${CMAKE_COMMAND} ..
  WORKING_DIRECTORY ${build_dir}
)
```

## `cmake_host_system_information()`コマンド

```
cmake_host_system_information(RESULT <variable> QUERY <key> ...)
```

`cmake_host_system_information()`とは、メモリ量やCPUのコア数、ホスト名などシステム情報を取得するコマンドです。`QUERY <key> ...`に取得したい情報を指定し、`<variable>`に出力する変数を指定します。指定できる情報には以下があります。なお、`<key>`を複数指定した場合は、対応する値がリストとして格納されます。

* CPUの論理コア数`NUMBER_OF_LOGICAL_CORES`
* CPUの物理コア数`NUMBER_OF_PHYSICAL_CORES`
* ホスト名`HOSTNAME`
* ドメイン名`FQDN`
* 全仮想メモリ量`TOTAL_VIRTUAL_MEMORY`
* 空き仮想メモリ量`AVAILABLE_VIRTUAL_MEMORY`
* 全メモリ量`TOTAL_PHYSICAL_MEMORY`
* 空きメモリ量`AVAILABLE_PHYSICAL_MEMORY`

中でもコア数を示す`NUMBER_OF_LOGICAL_CORES`・`NUMBER_OF_PHYSICAL_CORES`は、`make`コマンドや`ctest`コマンドの並列数の指定によく使うでしょう。

```cmake
cmake_host_system_information(RESULT cores QUERY NUMBER_OF_LOGICAL_CORES)
execute_process(COMMAND make -j ${cores})
execute_process(COMMAND ctest -j ${cores})
```

## CMAKE\_COMMAND・CMAKE\_CTEST\_COMMAND 変数

これらの変数は、`cmake`コマンドや`ctest`コマンドのフルパスを返します。

```cmake
message("CMAKE_COMMAND: ${CMAKE_COMMAND}") # /usr/bin/cmake
message("CMAKE_CTEST_COMMAND: ${CMAKE_CTEST_COMMAND}") # /usr/bin/ctest
```

## CMAKE\_CURRENT\_LIST\_FILE・CMAKE\_CURRENT\_LIST\_DIR 変数

これらの変数は、現在のCMakeスクリプトファイルのディレクトリ・パスを返します。スクリプトモードでよく使うでしょう。

```cmake
message("CMAKE_CURRENT_LIST_FILE: ${CMAKE_CURRENT_LIST_FILE}") # /path/to/current.cmake
message("CMAKE_CURRENT_LIST_DIR: ${CMAKE_CURRENT_LIST_DIR}") # /path/to
```

## おわりに

以上、CMake スクリプトで使う便利なコマンド・変数でした。これらのコマンドは必須ではありませんが、CMake スクリプトを書く上で便利に使用することができるでしょう。

明日は、mrk_21 さんの『CMake でのメタプログラミングとその応用』です。