CMakeでお手軽ビルド環境構築

# はじめに

この記事は[長野高専 Advent Calendar 2021](https://qiita.com/advent-calendar/2021/nnct)の25日目の記事です。

改めてこんにちは、しゅんです。3日目ぶりの投稿です。当初は書く予定がありませんでしたが、アドベントカレンダーなのに25日に記事がないのもさみしいので投稿します。

さて皆さんはC言語でプログラミングをするとき、どのようにソースコードのビルドを行っていますか？C言語を学校で習った方は`gcc`コマンドでビルドを行っているかもしれません。しかし`gcc`コマンドのみを使用した場合、ソースコードが複数あった場合や多数のコンパイラオプションを使用しているときに非常にコマンドが長くなったり複数のコマンドを実行する必要があったりします。

今回はこれらの煩雑な作業を軽減することができるCMakeを紹介します。CMakeを用いてビルドの効率を上げ、開発速度を上げていきましょう！

# CMakeとは

[Wikipedia](https://ja.wikipedia.org/wiki/CMake)にはこのように書いてあります。

> CMakeは、コンパイラに依存しないビルド自動化のためのフリーソフトウェアであり、様々なオペレーティングシステムで動作させることができる。CMakeは階層化ディレクトリや複数のライブラリを利用するアプリケーションをサポートするよう設計されている。実際のビルドにおいては、make、Xcode、Visual Studioのようなネイティブのビルド環境が利用される。CMake自身は最小限の依存関係を持つよう設計されており、ビルドするにはC++コンパイラのみを必要とする。

ここに書いてあるようにCMakeはビルド自動化のためのソフトウェアです。実はCMake自体はビルドを行いません。実際にビルドする場合はCMakeによって作られたビルド環境を使用してビルドを行います。`make`コマンドや`Visual Studio`は聞いたことがある人も多いと思います。しかしこれらのビルド環境は特定のプラットフォームのみのサポートのことが多く、多数のOSに対応させることは難しいです。そこでCMakeというマルチプラットフォームなソフトウェアを使用することによってそれぞれのビルド環境の差異を吸収し、簡単にビルド環境のマルチプラットフォーム化ができます。

# CMakeによるビルドの流れ

まずCMakeには`CMakeLists.txt`というファイルが必要です。このファイルにはどのようなファイルをコンパイルするかやライブラリ、オプションなどの各種設定を記述します。
`CMakeLists.txt`を書いたら`cmake`コマンドを使用してビルド環境を構築します。このときどのようなビルド環境を使用するかを設定することができ、`make`や`Ninja`、`Visual Studio`など様々なビルド環境を選択することができます。私の手元のmacOS環境では以下のビルド環境が使用できるようです。

```terminal
$ cmake -G
CMake Error: No generator specified for -G

Generators
* Unix Makefiles               = Generates standard UNIX makefiles.
  Ninja                        = Generates build.ninja files.
  Ninja Multi-Config           = Generates build-<Config>.ninja files.
  Watcom WMake                 = Generates Watcom WMake makefiles.
  Xcode                        = Generate Xcode project files.
  CodeBlocks - Ninja           = Generates CodeBlocks project files.
  CodeBlocks - Unix Makefiles  = Generates CodeBlocks project files.
  CodeLite - Ninja             = Generates CodeLite project files.
  CodeLite - Unix Makefiles    = Generates CodeLite project files.
  Eclipse CDT4 - Ninja         = Generates Eclipse CDT 4.0 project files.
  Eclipse CDT4 - Unix Makefiles= Generates Eclipse CDT 4.0 project files.
  Kate - Ninja                 = Generates Kate project files.
  Kate - Unix Makefiles        = Generates Kate project files.
  Sublime Text 2 - Ninja       = Generates Sublime Text 2 project files.
  Sublime Text 2 - Unix Makefiles
                               = Generates Sublime Text 2 project files.
```

ビルド環境ができれば後は好みのビルド環境のコマンドを使用してビルドを行えます。また、`cmake`コマンド自体にもビルドのコマンドがあり、それぞれのビルド環境専用のコマンドを実行しなくともビルドすることができます。

# CMakeのインストール

CMakeは各OSのパッケージマネージャにてインストールすることができます。Windowsの場合は[scoop](https://scoop.sh/)を使用してインストールすることもできますし、[公式サイト](https://cmake.org/download/)からダウンロードすることも可能です。

# CMakeを使ってみる

それではCMakeを使ってみましょう。今回は単純な3ファイル構成のプロジェクトを使用して解説します。先にソースコード全文を掲載します。

```c:add.h
#pragma once

int add(int, int);
```

```c:add.c
#include "add.h"

int add(int x, int y) { return x + y; }
```

```c:main.c
#include <stdio.h>

#include "add.h"

int main(void) {
  int x = 3;
  int y = 5;

  printf("%d + %d = %d\n", x, y, add(x, y));

  return 0;
}
```

それでは`CMakeLists.txt`を作りましょう。

```cmake:CMakeLists.txt
cmake_minimum_required(VERSION 3.1)
project(hello-cmake LANGUAGES C)

add_executable(main main.c add.c)
```

`cmake_minimum_required`はこの`CMakeLists.txt`が必要としているCMakeのバージョンを指定します。あまり新しい文法を使わないのであれば`3.1`あたりを指定しておけば問題ありません。

`project`はプロジェクト名や使用する言語、バージョンなどのプロジェクト全体の設定を行います。
`hello-cmake`がプロジェクト名、`LANGUAGES C`が言語の指定です。詳細なオプションに関しては[公式リファレンス](https://cmake.org/cmake/help/v3.22/command/project.html)を参照してください。
`add_executable`で実行形式ファイルのビルドを設定します。はじめの名前が出力ファイル名、後がソースコードのパス名です。`add_executable`や後に紹介する`add_library`などで設定するビルド対象のことをCMakeではターゲットと言います。複数のターゲットがある場合、CMakeでビルドするときにターゲットを絞ってビルドすることが可能になります。

`CMakeLists.txt`ができたら後はビルドするだけです。先程も言いましたが、CMake自体はビルドを行わないため、まずビルドツリーを構築する必要があります。

`cmake <CMakeLists.txtが存在するディレクトリパス>`でビルドツリーを構築できます。デフォルトではワーキングディレクトリ上にビルドツリーを構築するので`cmake .`みたいにするとソースコードがある場所にビルドツリーができてしまいやり直したいときなどに非常に面倒です。そこで`build/`のようなディレクトリを追加で作り、そこにビルドツリーを構築しましょう。CMakeの`-B`オプションを使用することでビルドツリーの構築先を指定できるため、`cmake . -B build`というように実行するのがおすすめです。

```terminal
$ cmake . -B build
-- The C compiler identification is AppleClang 13.0.0.13000029
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /Library/Developer/CommandLineTools/usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/shun_shobon/tmp/hello-cmake/build
```

このとき`-G`オプションでビルド環境の指定をできます。macOSやLinuxではデフォルトで`Unix Makefiles`が使用されますが、Ninjaなどが使用したい場合はお好みで設定してください。

ビルドツリーが構築できたらあとはビルドするだけです。ビルドはビルドツリー上でビルド環境独自のコマンドを入力してもビルドできますし、CMake上からもビルドできます。CMake上でビルドする場合は`cmake --build <ビルドツリーへのパス>`でビルドできます。

```terminal
$ cmake --build build
[ 33%] Building C object CMakeFiles/main.dir/main.c.o
[ 66%] Building C object CMakeFiles/main.dir/add.c.o
[100%] Linking C executable main
[100%] Built target main
```

なお、デフォルトではターゲット全てがビルドされますが、特定のターゲットのみにしたい場合は`-t <ターゲット名>`で行えます。

ビルド成果物はビルドツリー下に生成されるのでお好みで実行してください。

```terminal
$ ./build/main
3 + 5 = 8
```

## コンパイルオプションを設定する

コンパイルオプションを設定したい場合は`add_compile_options`を使用します。

```cmake
cmake_minimum_required(VERSION 3.1)
project(hello-cmake LANGUAGES C)

add_compile_options(-Wall -O3)

add_executable(main main.c add.c)
```

## ライブラリを作る

複数の実行ファイルがある場合、いちいち`add_executable`の後にソースコードを並べるのは効率が悪いです。`add_library`を使えばライブラリを作成することができ、`target_link_libraries`で各種ターゲットにライブラリをリンクできます。

```cmake
cmake_minimum_required(VERSION 3.1)
project(hello-cmake LANGUAGES C)

add_compile_options(-Wall -O3)

add_library(add add.c)

add_executable(main main.c)
target_link_libraries(main add)
```

# 終わりに

CMakeを使えばプラットフォームに依存しないビルド環境を作ることができ、保守性も高まります。`CMakeLists.txt`のコマンドは非常に多く、今回はその一部を紹介しましたが、[公式リファレンス](https://cmake.org/cmake/help/v3.22/manual/cmake-commands.7.html)に詳細が載っているため詳しく見たい方はそちらを参照してください。[公式チュートリアル](https://cmake.org/cmake/help/v3.22/guide/tutorial/index.html)には多数のユースケースに向けたチュートリアルがあるためこちらを見ることもおすすめします。