makeとconfigureと、もっとナウいやつ

この記事は[eeic Advent Calendar 2017](https://qiita.com/advent-calendar/2017/eeic) の17日目の記事として書かれたものです。昨日はsatewnさん担当でした。明日はasaiさんの単語ベクトルの話らしいです。

この記事では、ビルド補助ツールとしてのmakeと、configureとその先の話を軽くしたいなと考えています。去年の学科合宿LTでやった内容ですが、ゆるして……

# make

eeicの人たちならたぶん2年生で講義で習(った|う)のではと思う[^1]のですが、makeコマンドというものがありまして、大雑把に言うと、たくさんの実行するコマンドを必要なものだけまとめて実行してくれる、小さくない規模のC/C++開発には無いと困る (ことが多かった) 便利ツールです。別にC/C++に限った話でもないですが。

ここではとりあえず、

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

int main(void) {
  printf("生命、宇宙、そして万物についての究極の疑問の答え: %d\n", answer());
}
```

```c:ans.h
int answer(void);
```

```c:ans.c
int answer(void) {
  return 32; // ←打ち間違えてしまった……！
}
```

というanswer42プロジェクトがあったとします。

このプロジェクトに

```make:Makefile
all: ans.o main.o
	$(CC) $^ -o main
```

Makefileを用意してあげれば、makeコマンドを打てば

```shell-session
$ make
cc    -c -o ans.o ans.c
cc    -c -o main.o main.c
cc ans.o main.o -o main
```

みたいなことやってくれます。

間違いに気がついて、ans.cを

```c:ans.c
int answer(void) {
  return 42; // ←修正しました
}
```

に修正しても、makeコマンドは

```shell-session
$ make
cc    -c -o ans.o ans.c
cc ans.o main.o -o main
```

のように、変更していないmain.cから作成されるmain.oの必要のない再生成を省いてくれ[^2]、必要のないコンパイルを行わないのでちょっとだけ開発→手動テストサイクルを早く回せるわけです。

## メリット
* 必要のないコマンドは省いてくれる
* 実行する内容はただのシェルスクリプト
* デフォルトで、Cコンパイル等のルールが定義されてある

## デメリット
* 一行ごとにシェルを呼び出すので、遅い
* サブディレクトリはmakeを再帰的に呼び出す構造になっていることが多く、遅い



# configure

C/C++で書かれたソフトウェアをソースコードからコンパイルしてローカルにインストールしたいとなったら、たいてい定型文句を唱えればよく、それが

```shell-session
$ ./configure
長い出力
$ make
やっぱり長い出力
$ sudo make install
比較的短い出力
```

でした[^3]。今はこの呪文を唱えることも少なくなってしまったように感じます[^4]がいまだにこの呪文が通じる状況は少なくありません。

configureは基本的にシェルスクリプトですが、結局シェルから実行できるようになっていれば[^5]pythonでもなんでもいいです。基本的にはshが一番使われます。

で、このconfigureが何やっているのかといえば、ビルドに必要なコンパイラのチェック、ライブラリのチェック、機能のオンオフ、ビルドに必要なファイルの生成など片手で数えられないくらいのことはやっています。

でもチェックするコードを手で書くのはだるいので、autotoolsと呼ばれるフレームワークが使われることが多いです[^6]。

## autotools

autotoolsは、configureを生成するautoconfと、Makefileを生成するautomakeと、その他もろもろする諸々コマンドを含むフレームワークです。

configure.ac (または非推奨configure.in) があればたいていautoconfが使われています。Makefile.amがあればautomakeも使われています。個人的なプロジェクトならよっぽどの理由がなければ、automakeも使ったほうが楽でしょう。

answer42の場合は、例えばこんなファイルが必要になるでしょう。

```:configure.ac
#                                               -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.

AC_PREREQ([2.60])
AC_INIT([FULL-PACKAGE-NAME], [VERSION], [BUG-REPORT-ADDRESS])
AC_CONFIG_SRCDIR([main.c])
AC_CONFIG_HEADERS([config.h])

# Checks for programs.
AC_PROG_CC

# Required by automake
AM_INIT_AUTOMAKE([foreign subdir-objects])

AC_CONFIG_FILES([Makefile])
AC_OUTPUT
```

```make:Makefile.am
bin_PROGRAMS = main
main_SOURCES = ans.c main.c
```

ファイルを配置して、

```shell-session
$ autoreconf -iv  # autoconfやautomake等の必要なコマンドをいい感じの順番で呼び出してくれます
$ ./configure
$ make
$ ./main
```

してあげれば、動くことが確認できると思います。

### 特徴

* m4というマクロ言語でconfigure.ac (やMakefile.am) を書く
* 昔のデファクトスタンダード、今でも広く使われている

### メリット

#### だいたいのパッケージにautotoolsが使われているので、参考にするコードは多い

たとえば三つ列挙すれば、git、vim [^8]、emacs……古くからあるプロジェクトはMakefile単体かautotoolsのどちらかが使われていることが多いです。

[^8]: configure.acがあるのに拡張子inなしのMakefileがあったりしてちょっと変則的？

#### makeやshell scriptに詳しくなれる……かも

m4がいくらマクロ言語だとしても、書くのはMakefileとシェルスクリプトなので……

#### クロスコンパイルがしやすい

configureに--host=x86_64-w64-mingw32を渡してあげるだけでクロスコンパイルしてくれます。嘘です。してくれる場合もあります。別途設定が必要な場合もあります。

それでも後に述べる二つより簡単だと思っています。PC環境向けならば。残念なことにマイコン向けとかのクロスコンパイルをautotoolsで行ったことがないので、それ以上のことはわかりません。

### デメリット

#### とにかく書くのが辛い

あるフラグが対応しているかどうかの判定すら、標準そのままだと辛い [^7] ので、ライブラリをincludeして使うなり自分で書くことが多くなると思います。たとえば、GNUの[コンパイラが特定のフラグに対応しているかを判定するライブラリ](http://git.savannah.gnu.org/gitweb/?p=autoconf-archive.git;a=blob_plain;f=m4/ax_check_compile_flag.m4)

[^7]: フラグを付けて、ソースコードを書いてコンパイルして、コンパイルに失敗したらフラグをもとに戻して、という段階を踏む必要があります (間違ってるかも)

また、m4のマクロに引数を渡す時、角括弧を付けて渡すことが推奨されています。たとえば下のように。

```m4
AX_CHECK_COMPILE_FLAG([-std=c++14],
       [AX_APPEND_FLAG([-std=c++14], [extra_cxxflags])], [
       AC_MSG_WARN([$CXX is not supported -std=c++14. check your compiler version])
       AX_CHECK_COMPILE_FLAG([-std=c++1y],
               [AX_APPEND_FLAG([-std=c++1y], [extra_cxxflags])], [
               AC_MSG_WARN([$CXX is not supported -std=c++1y. may not be supported C++14 features!!])])])
```

まだこれは楽な方なので大したことはないのですが、

> The most common problem with existing macros is an improper quotation. This section, which users of Autoconf can skip, but which macro writers must read, (後略)
> -- https://www.gnu.org/software/autoconf/manual/autoconf.html#M4-Quotation 

と公式で書かれるくらいには、この角括弧の付け方もろもろはトリッキーです。

#### 必要なファイルが多い

[中間ファイルがたくさんある図](https://commons.wikimedia.org/wiki/File:Autoconf-automake-process.svg)

#### 出力ファイルが大きい

例えば、Subversionのトップディレクトリのconfigureは、858KB 28706行です。多いか少ないかは個人の判断によりますが、経験的に言えばconfigureだけで2MB [^9] 行くこともままある気がします。まあ画像よりは遥かに小さいんですけど。

[^9]: 例えば、gcc-7.2.0のlibstdc++-v3/configureの2.2M 84842行

#### Windowsのようにfork-execが遅い環境だと辛い

辛いです。mingw/msysを使ってる人はmingw-w64/msys2を使うと若干早くなります。それでも辛いです。

[^1]: そもそも記憶違いかもしれないし、教官とカリキュラムが変わってるから習わないかもしれないです。
[^2]: 本来ならばmain.oはmain.cとans.hが更新されたら再生成するべきですが、今回のサンプルコードではそうなっていません。
[^3]: ./configure --prefix=${HOME}/installのようにしてroot権限が必要のないところへのインストールを指示すればmake installにsudoは必要ないですし、理由がなければそうするべきだと思っていますが、本質ではないので省いています。
[^4]: パッケージマネージャーから入れることが出来るのでソースからコンパイルする必要がなくなったり、後述のconfigureではないビルド設定コマンドを使うソフトが増えてきたのが理由です。
[^5]: つまり、基本的には、[shebang](https://ja.wikipedia.org/wiki/%E3%82%B7%E3%83%90%E3%83%B3_(Unix))が書いてあって実行権限があるファイルのことです。
[^6]: 逆に、当然ではありますが、autotoolsであることは必須ではないので、ffmpegのように./configureがシェルスクリプトでありながら手でメンテナンスするプロジェクトは存在します。

# cmake

autotoolsを使ったconfigureが辛いものだったので、置き換えを目指したのか目指さなかったのか、CMakeとかいうものが世の中にはあります。

```cmake:CMakeLists.txt
# 雰囲気だけ読み取ってください。
cmake_minimum_required(VERSION 3.2)

ENABLE_LANGUAGE(CXX)
project(proj)

set(CMAKE_CXX_STANDARD 14)
set(CXX_STANDARD_REQUIRED ON)
if (NOT MSVC)
    set(DX_GCC_COMPILE ON)
endif ()
add_definitions(-DHAVE_CONFIG_H)
if (CMAKE_BUILD_TYPE STREQUAL Debug)
    add_definitions(-D_DEBUG)
else()
    add_definitions(-DNDEBUG)
endif()

include_directories("${proj_BINARY_DIR}/src" "${proj_SOURCE_DIR}/src")

file(GLOB_RECURSE libproj_files RELATIVE ${proj_SOURCE_DIR} src/*.cpp src/*.cxx src/*.h)
list(REMOVE_ITEM libproj_files src/WinMain.cpp)
list(APPEND libproj_files "${proj_BINARY_DIR}/src/config.h")
add_library(projcore ${libproj_files})

add_dependencies(projcore dxlib)
target_link_libraries(projcore
    DxLibW DxUseCLibW DxDrawFunc jpeg png zlib tiff theora_static
    vorbis_static vorbisfile_static ogg_static bulletdynamics bulletcollision bulletmath
    liblua g3logger)
```

## 特徴

### 必要なファイルはCMakeLists.txtのみ

autotoolsに比べて、ファイルのスリム化を計ったみたいですね。

### autotoolsみたいに標準で出来ないことが多い、なんてことはない

autotoolsならGNUのライブラリを使わないと出来なかった、C++14の利用 (-std=c++14) が、CMakeなら一行で出来るよ！

## メリット

### 最近広がっている

autotoolsの代替として、それなりの勢いで広まっていると思います。例えば、LLVMやOpenCV、MySQLはCMakeです。

### 実行が軽い

シェルスクリプトじゃないので、比較的configure相当のプロセスが速いです。

### WindowsのVisual Studio向け、macOSのXcode向けのプロジェクトが作れる

cmakeの引数にそれ用のオプションを渡してあげれば、mingw-w64とかを入れなくてもWindowsでビルドできるようになるのは結構良いと思います。

あと、Makeが遅くて嫌になった人向けにNinjaというビルドツール [^10] でもビルドできるようにしてくれたりもします。

[^10]: 速くて軽い、ナウいヤング向けのツール。https://ninja-build.org/

### 指定してあげればファイルのダウンロード、展開、ビルドもやってくれる

らしいです (ExternalProject_Add)。使ったことがそこまでないのであまり触れられません。

## デメリット

まあ大体は[GStreamer and Meson: A New Hopeのコメント](http://blog.nirbheek.in/2016/05/gstreamer-and-meson-new-hope.html?showComment=1464011145671#c8053239224284015105)あたりに書いてありますが。

### 記法が新しい()

```cmake
if (NOT MSVC)
    set(DX_GCC_COMPILE ON)
endif () # ←endにも括弧をつけるのか……
```

```cmake
check_include_files("windows.h;versionhelpers.h" HAVE_VERSIONHELPERS_H)

set(list_var a b c) # ←リストが作られる
message("${list_var}") # a;b;c
message(${list_var}) # abc (三つの引数として渡される = message(a b c)と一緒 (らしい)、慣れないと混乱する
# (え、シェルスクリプトもそうだって？くぁｗせｄｒｆｔｇ……
```

> CMake (中略) trades whitespace-safety for semicolon-safety: ok, it's not common to put semicolons in filenames, but if you do you'll have a nightmake in CMake
> -- http://blog.nirbheek.in/2016/05/gstreamer-and-meson-new-hope.html?showComment=1464020031868#c3890433598738944056

### 引数で挙動が変わる

```cmake
string(FIND <string> <substring> <output variable> [REVERSE])
string(REPLACE <match_string> <replace_string> <output variable> <input...>)
string(REGEX MATCH <regular_expression> <output variable> <input…>)
string(APPEND <string variable> [<input>...])
string(CONCAT <output variable> [<input>...])
```

上は[公式のドキュメント](https://cmake.org/cmake/help/latest/command/string.html)より。string_findじゃだめだったんですか。

### 一つのことをやるのに複数のやり方を提供する

コンパイルフラグを指定するのに複数のやり方があるので、覚えきれなかった **印象** があります。~~ごめん最近CMake触ってないからもう忘れた……~~

上のブログだとtarget_link_librariesが例に出されています。ちょっとよくわからなかったから割愛。

### 変数/プロパティありすぎ問題

[変数一覧](https://cmake.org/cmake/help/v3.0/manual/cmake-variables.7.html)、[プロパティ一覧](https://cmake.org/cmake/help/v3.0/manual/cmake-properties.7.html)。

## CMakeのまとめ

書いててだいぶCMakeの記憶が抜けてたことに気がつきました。間違ってたこと書いてたらごめんなさい。

まあなんというか、結局のところこの言葉に落ち着くと思います。

> CMake works great for many people, but it doesn't work for everyone. 
> -- http://blog.nirbheek.in/2016/05/gstreamer-and-meson-new-hope.html?showComment=1464011145671#c8053239224284015105

個人的にはautotoolsと同じくらいクソという感じでした。CMake使っていたときはLinuxでWindows向けにクロスコンパイルしているから余計なのかもしれませんが……

[CMakeの公式ドキュメントの通り](https://www.vtk.org/Wiki/CMake_Cross_Compiling)クロスコンパイル設定ファイルを作ったら、windresがない (x86_64-w64-windresとしてインストールされていたので) エラーが出てひっかかったとか、cotireというCMakeのライブラリ (？) を使わないとプリコンパイル済みヘッダをよしなに出来ないとか、そのくせして[コンパイラのいわゆる-flto向けのオプションがある](https://cmake.org/cmake/help/latest/variable/CMAKE_INTERPROCEDURAL_OPTIMIZATION.html)とか、そうじゃないだろ感があって…………

# Meson

というわけで、このままクソばっかり言っててもアレなので、もう一つくらい紹介したいと思います。

~~[ロゴが可愛い](http://mesonbuild.com/assets/images/meson_logo.png)~~ 新しいロゴが可愛くない……

[まだそこまで広く使われているわけではない](http://mesonbuild.com/Users.html)のですが、特に[GNOME界隈で人気です](https://wiki.gnome.org/Initiatives/GnomeGoals/MesonPorting)

```meson:meson.build
# こいつも雰囲気だけで。
project('hogefuga', 'cpp',
	default_options: [
		'cpp_std=c++14',
		'warning_level=3',
		'default_library=static',
	],
	version: '4.1',
	license: 'MIT')

cpp_compiler = meson.get_compiler('cpp')

project_warnings = []
if get_option('warning_level') == '3'
	extra_warnings = [
		'-Wctor-dtor-privacy',
		'-Wdelete-non-virtual-dtor',
		'-Wdisabled-optimization',
		'-Wfloat-equal',
	]
	foreach warn : extra_warnings
		if cpp_compiler.has_argument(warn)
			add_project_arguments(warn, language: 'cpp')
		endif
	endforeach
endif

if get_option('buildtype').contains('debug')
	if cpp_compiler.has_argument('-rdynamic')
		add_project_link_arguments('-rdynamic', language: 'cpp')
	endif
endif

boost_dep = dependency('boost', modules: ['exception'])
g3logger_dep = dependency('g3logger',
	fallback: ['g3log', 'g3logger_dep'],
	default_options: ['dynamic_logging=true']
)

libavcodec_dep = dependency('libavcodec')
libavutil_dep = dependency('libavutil')
libswscale_dep = dependency('libswscale')
ffmpeg_dep = declare_dependency(
	dependencies: [libavcodec_dep, libavutil_dep, libswscale_dep])
```

どれくらい使われていないかというと、このようにソースコードに色付けしてくれないくらいには広く使われていないのですが、それはそれとして。

なんか見た目からもうわかりやすくて心躍りませんか？躍らないですかそうですか……

## 特徴
### makeサポートを完全に撤廃している

make？そんな古いものは知らんですよ

makeは古くから使われているので、私が持っているmakeとあなたが持っているmakeが違うということが稀によくあります [^11]。あと、上にも書いたけど遅い。

[^11]: いわゆるGNU MakeとBSD Make

mesonではNinjaというヤングでナウいツールを使ってビルドします。モチロンVisual StudioやXcodeにも対応してますよ？

### in-treeビルドに対応していない

mesonではout-of-treeビルドを強制されます。必ず新しいディレクトリを作って、その中でビルドする必要があるということです。

in-treeビルド、つまりソースtarを落として展開してその中にcdしてmake！は出来ないということです。~~説明が分かりにくいな~~

## メリット
### ビルドが速い (ことになっている)
[CMake+Ninjaに比べて、Meson+Ninjaのほうが速いということになっているらしいです](http://mesonbuild.com/Simple-comparison.html)。

Ninja自体が一般的な条件下ではMakeより速いので、CMake+Makeよりはずっと速いですが、CMake+NinjaとMeson+Ninjaを比べてもそこまで大きくは変わらないと思います。~~どうせ実行するコマンドは大体一緒なので~~

### 記法が読みやすい
個人の感想ですが、autotoolsやCMakeに比べて圧倒的に読みやすいです。

### 指定してあげればファイルのダウンロード、展開、ビルドもやってくれる (CMakeとカブり)

……のですが、Mesonの場合はmeson.buildがビルドするパッケージの中にある必要があります ([wrap file](http://mesonbuild.com/Wrap-dependency-system-manual.html))。zip/tar.gzだったら、別途meson.build等の必要なファイルを用意してあげて、新規に必要なファイルだけをzipで固めてあげればいいです。[参考例](https://github.com/turenar/ipphone/blob/master/subprojects/g3log.wrap)。gitだとフォークしてあげる必要がありそう。

~~………………ところで、このwrap fileなんですが、ハッシュエラーとかmeson.buildの構文エラーとかがあると死ぬほどわかりにくいエラーメッセージで死にやがるんで、ちょっとそこだけはありえないと思います。~~2018年3月5日にリリースされていた0.45.0で[理由が表示されるようになっていました](https://github.com/mesonbuild/meson/commit/f35606f61ea19d1d71b670b34ed8dc108b492d7f)。

## デメリット
### あまり広く使われていない
まあ前述のとおりです。それでもナウヤングに大人気 (？) のsystemdはautotoolsからmesonに完全移行しました。


### 勝手にccacheが使われる
デメリットなのかどうかはしらないですが、ccacheがPATH内にあると勝手に使ってくれます。

### シンプルな分、凝ったことで出来ることが少ない
たとえば、src/以下のファイルを全てビルド対象に追加、なんてことはできず、基本的にはspecificである必要があります。[公式によればre-configure時にあるファイルなら出来ます](http://mesonbuild.com/FAQ.html#why-cant-i-specify-target-files-with-a-wildcard)が。

あと、[Meson言語はチューリング完全ではない](http://mesonbuild.com/FAQ.html#why-is-meson-not-just-a-python-module-so-i-could-code-my-build-setup-in-python)ので、functionが定義できません (！！！！！)。foreachとかで頑張りませう。

### 日本語のドキュメントが少ない
英語の公式ドキュメントは比較的読みやすいので、そこまで苦にはならないと思っていますが、読みたくないものは読みたくない…………

hello world的なものならば[去年のGStreamer Advent Calendar](https://qiita.com/advent-calendar/2016/gstreamer)の[Hello Library World: The Meson Build System](http://hellolibraryworld.blogspot.jp/2016/12/the-meson-build-system.html)が参考になると思います。あとは公式の[WrapDB](https://wrapdb.mesonbuild.com/)に登録されているプロジェクトのwrap fileの、patch_urlを落として見てみるのもいいかもしれません。

# 全体のまとめ
新しいプロジェクトを立てるならば、Meson使えばいいんじゃないかな？(予定調和)(これがいいたかっただけ)(ありがとうございました)