CMake: リスト

## はじめに

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

## リストは値のコレクション型

前回の記事で、変数には文字列型しかないと言いましたが、いくつかの文脈で文字列以外に解釈されることがあります。リストもその一つで、Unquated Argument で値をセミコロン`;`で区切り、`a;b;c`といった形をとると、値がそれぞれ`a`・`b`・`c`からなるコレクションとして認識されます。

## リストの変数への代入

リストは`set()`コマンドを使って変数に代入することができます。

```cmake
set(list_value a;b;c)
message("${list_value}") # a;b;c
```

また、`;`で区切らずに、値を複数渡してもリストとして代入できます。

```cmake
set(list_value a b c)
message("${list_value}") # a;b;c
```

複数のリストを渡すと、連結されます。

```cmake
set(list_value a b c;d "e;f")
message("${list_value}") # a;b;c;d;e;f
```

## リストに使えない文字

リストには以下の文字を含めることはできません。

* `[`
* `]`

また、区切り文字の`;`を含めたい場合は、エスケープしなければなりません。

## リストとコマンド引数

実は、コマンドを呼び出すときにリストを渡すと、あたかも複数の引数が渡されたかのように振る舞います。

```cmake
message(a;b;c) # abc
```

つまり、以下と同様の動作になります。

```cmake
message(a b c) # abc
```

これは、変数に格納されている場合でも同様です。

```cmake
set(list_value a;b;c)
message(${list_value}) # abc
```

この動作が意図したものでないのなら、Quated Argument か Bracket Argument を使用します。

```cmake
message("a;b;c") # a;b;c
message([[a;b;c]]) # a;b;c

set(list_value a;b;c)
message("${list_value}") # a;b;c
```

## リストを操作するコマンド

リストには、`list()`というリスト操作のための便利なコマンドが組み込みで用意されています。この`list()`コマンドは、第一引数にコマンドを指定することで様々な動作を行います。リストの変更をするものは破壊的変更をします。また、リストのインデックスを要求するものは以下のことに注意します。

* インデックスは`0`から始まる
* 負の値を指定すると末尾からのインデックスとなる
* 末尾のインデックスは`-1`であり、`-0`ではない

### `list(LENGTH <list> <output variable>)`

リスト`<list>`の長さを、`<output variable>`変数に代入します。

```cmake
set(list_value a;b;c)
list(LENGTH list_value list_length)
message(${list_length}) # 3
```

### `list(GET <list> <element index> [<element index> ...] <output variable>)`

リスト`<list>`からインデックス`<element index> ...`の値を持つリストを`<output variable>`に代入します。

```cmake
set(list_value a;b;c;d)

list(GET list_value 0 value)
message(${value}) # a

list(GET list_value 0 -1 2 value)
message("${value}") # a;d;c
```

### `list(APPEND <list> [<element> ...])`

リスト`<list>`の末尾に値`<element> ...`を追加します。

```cmake
set(list_value a;b)
list(APPEND list_value c d)
message("${list_value}") # a;b;c;d
```

### `list(FIND <list> <value> <output variable>)`

リスト`<list>`の中の値`<value>`の最初のインデックスを`<output variable>`に代入します。このとき、指定した値がリストになければ`-1`が代入されます。

```cmake
set(list_value a;b;b;c)

list(FIND list_value b index)
message("${index}") # 1

list(FIND list_value d index)
message("${index}") # -1
```

### `list(INSERT <list> <element_index> <element> [<element> ...])`

リスト`<list>`のインデックス`<element_index>`に`<element> ...`を挿入します。

```cmake
set(list_value a;b;c)

list(INSERT list_value 2 b2)
message("${list_value}") # a;b;b2;c

list(INSERT list_value 3 b3 b4 b5)
message("${list_value}") # a;b;b2;b3;b4;b5;c
```

### `list(REMOVE_ITEM <list> <value> [<value> ...])`

リスト`<list>`から値`<value> ...`をすべて削除します。

```cmake
set(list_value a;b;c;b;c;d)

list(REMOVE_ITEM list_value b)
message("${list_value}") # a;c;c;d

list(REMOVE_ITEM list_value a c)
message("${list_value}") # d
```

### `list(REMOVE_AT <list> <index> [<index> ...])`

リスト`<list>`からインデックス`<index> ...`の値を削除します。

```cmake
set(list_value a;b;c;d;e)

list(REMOVE_AT list_value 1)
message("${list_value}") # a;c;d;e

list(REMOVE_AT list_value 1 3)
message("${list_value}") # a;d
```

### `list(REMOVE_DUPLICATES <list>)`

リスト`<list>`から重複している値をすべて削除します。

```cmake
set(list_value a;b;c;b;d;e;c;a)
list(REMOVE_DUPLICATES list_value)
message("${list_value}") # a;b;c;d;e
```

### `list(REVERSE <list>)`

リスト`<list>`を逆順にします。

```cmake
set(list_value a;b;c;d;e)
list(REVERSE list_value)
message("${list_value}") # e;d;c;b;a
```

### `list(SORT <list>)`

リスト`<list>`を辞書順にソートします。

```cmake
set(list_value aa;a;e;aaa;a;g;zz)
list(SORT list_value)
message("${list_value}") # a;a;aa;aaa;e;g;zz
```

## リストの用途

リストには様々な用途がありますが、もっとも多用するのはソースファイルのリストになるでしょう。たとえば、以下ではディレクトリのソースファイル一覧をリストとして取得し、各ソースファイルの実行ファイルを作成する例です。

```cmake
file(GLOB sources RELATIVE ${PROJECT_SOURCE_DIR} *.cpp) #[[
  project-dir/
    +-- a.cpp
    +-- b.cpp
    +-- c.cpp
]]
message("${sources}") # a.cpp;b.cpp;c.cpp

foreach(path IN LISTS sources)
  get_filename_component(target ${path} NAME_WE)
  add_executable(${target}_bin ${path})
endforeach()
```

## おわりに

以上、リストでした。リストは普段、無意識に使っていますが、思いもよらない挙動をすることもあり、その仕様を知っておくことは重要です。

明日は、mrk_21 さんの『CMake: 条件分岐』です。