CMakeを使ってみた(1)経緯と簡単なアプリケーション

はじめに

CMakeを調べた経緯

CやC++でプログラムを書くときは普通何らかのビルドツールを使う。Unix/LinuxならMakeが多いだろう。Makefileにビルド手順を書くわけだ。一方WindowsでVisual Studio(以下VS)を使うときはVS上でビルドの設定を行い、その結果はプロジェクトファイル(.vcproj)に保存される。

そのため、Unix/LinuxとWindows両方に対応しようとすると、例えばソースファイルを追加するたびにMakefileとVSのプロジェクトファイルの両方を修正することになる。さらにEclipse CDTでも開発できるようにしようと考えたり、あるいは古いVS用のプロジェクトファイルが必要になったりすると、サポートするビルド環境がどんどん増えていく。

当然管理も面倒になり普段使わないものが段々放置され、ついにはビルドできなくなる。そしてそのことを知らないユーザが、もう使われなくなったプロジェクトファイルを使ってビルドしようとして「ビルドできないんですが」という質問メールを送るようになる。

という流れがいい加減いやになったので、何とか自動化したいと考えた。ビルド環境は違っていても必要な設定はだいたい同じようなもので、

  • ソースファイルのリスト
  • インクルードパス
  • ライブラリパス
  • リンクするライブラリ
  • マクロの値

といったあたりだ。つまり、これらの情報から各種ビルドツール用の設定ファイルは自動生成できる。

もちろんこういうことを考える人は昔からいて、各種ツールが存在している。有名なのはGNUのAutotools(AutomakeやAutoconfなど)だろう。Makfile.amに実行ファイル名やソース・ヘッダーファイル名を書くと、Makefileを自動生成してくれる。AutotoolsはUnix/Linux環境では便利に使え、特にプラットフォーム毎の微妙な差を埋めてくれるAutoconfがあるので、Unix環境全般(Solaris, FreeBSD, NetBSD, Linuxなど)で使えるプログラムを作るときは第一候補に挙がるだろう。しかし、VS環境については考慮されていないため、今回の目的からすると除外される。

そこでGNU Autotools以外のビルドツールを調べたところWikipediaのページにたくさん載っていた。その中で、「何となく聞いたことのある」「ある程度以上実績のあるもの」ということでCMakeを試してみることにした。

CMakeの情報源

なお、Mastering CMakeという本も出版されており、多分これが一番詳しい文書と思われる。ただし、$59というなかなかの値段。使うと決めたわけでもないのものにはちょっと出せない。ということで、この記事はCMakeの一番詳しい本を全く読まずに書いている。そんなわけでここには間違っている内容もあれば、「もっと簡単にできる」ということもあるだろう。その辺は勘弁して欲しい。

実行環境

CMakeはクロスプラットフォームのアプリケーションであり、今回CMakeを調べたきっかけもWindowsとLinuxの両方で動かしたいからである。が、まずはLinuxで試してみることにする。手元の環境はUbuntu 11.10、CMakeのバージョンは2.8.5だ。

簡単なアプリケーション

ソースファイルと最初のCMakeLists.txt

まずは簡単なアプリケーションということで、適当なディレクトリ(myapp01)を用意し、こんなソースファイル(main.cpp)を置く。

#include <cstdio>

int main() {
    printf("hello\n");
    return 0;
}

CMakeの設定ファイルは基本的にCMakeLists.txtという名前にする(CMakeList.txtではなくCMakeLists.txtだ)。今回は2行でよい。

cmake_minimum_required(VERSION 2.8)
add_executable(myapp main.cpp)

最初の行は、このCMakeLists.txtを使うのに必要なCMakeのバージョンだ。ここで指定したバージョンより低いと動作しないようになる。GNU Autoconfでも似たような設定(AC_PREREQ)があるが、こういう設定をしなければならないのは正直困る。というのも、今回作ったファイルがCMakeのどのバージョン以降サポートされたものか、なんてことはいちいち気にしていないからだ。今回も特に理由はなく、何となく2.8にしてみただけだ。

という文句はさておき、次の行のadd_executableは実行ファイルの作成を指示するものである。引数として実行ファイル名とソースファイル名を指定する。

CMakeによるビルド

上記CMakeLists.txtをmyapp01に置き、myapp01ディレクトリ上でcmakeを実行する。引数はディレクトリ名だが、すでにmyapp01にいるので引数は . になる。すると、各種ディレクトリ・ファイルが生成される。

> cmake .
-- The C compiler identification is GNU
-- The CXX compiler identification is GNU
-- Check for working C compiler: /usr/bin/gcc
-- Check for working C compiler: /usr/bin/gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/wagavulin/01/myapp01
> ls
CMakeCache.txt  CMakeFiles/  CMakeLists.txt  Makefile  cmake_install.cmake  main.cpp

Makefileが生成されるので、そのままmakeでビルドできる。

> make
Scanning dependencies of target myapp
[100%] Building CXX object CMakeFiles/myapp.dir/main.cpp.o
Linking CXX executable myapp
[100%] Built target myapp

main.cppをコンパイルするところで [100%] という表示が出ている。今はソースファイルが1つなのでいきなり100%になるが、複数ある場合は進行状況が分かるようになっている。大きなプロジェクトの場合、どのくらい時間がかかりそうか分かるのでなかなか便利だ。

何が起きているか

しかし、コンパイルの詳細(実際に実行されているコマンド)が隠蔽されているため何が起きているのか分からない。普通Makefileに書かれたコマンドは端末にエコーされるが、このMakefileではコンパイルやリンクのコマンドがエコーされない。これはMakeの.SILENTを有効にしているためだ。GNU MakeではMakefile中に.SILENTと書かれた行があるとコマンドエコーが抑制される。CMakeが作ったMakefileにも

# Suppress display of executed commands.
$(VERBOSE).SILENT:

という箇所がある。変数VERBOSEはデフォルトでは設定されていないため、.SILENTが有効になるというわけだ。

従ってVERBOSE変数に何らかの値を入れると.SILENTではなくなりコマンドがエコーされるようになる。よって、make VERBOSE=1などとすればよい。もちろん1でなくhogeでもなんでもよい。

結果は長くなるので、コンパイルとリンクの部分だけ貼る。コンパイラはc++コマンドであり、特にオプションはない。また、リンク時は-rdynamicがつくのが分かる。

(snip)
/usr/bin/c++     -o CMakeFiles/myapp.dir/main.cpp.o -c /home/wagavulin/study-cmake/test/main.cpp
(snip)
/usr/bin/c++       CMakeFiles/myapp.dir/main.cpp.o  -o myapp -rdynamic 
(snip)