CMakeを使ってみた (5) 設定ファイルを作る

今度は設定ファイルを作ってみる。設定ファイルと言ってもCMakeに対する設定ではなくプログラムに対するものだ。

条件付コンパイルと指定方法

ある程度の規模のプログラムの場合、#ifなどによる条件付コンパイルがあるだろう。

#include <cstdio>

int main(){
#if USE_FEATURE_X
    printf("use feature-x\n");
#else
    printf("do not use feature-x\n");
#endif
    return 0;
}

このコードはUSE_FEATURE_Xの値によってコンパイル結果が変わるわけだが、その値を決める方法は2種類ある。

  • コンパイラオプションで指定する(-DUSE_FEATURE_X=1)
  • ソースコード中の別の場所で定義する(#define USE_FEATURE_X 1を追加)

コンパイル結果は同じになるので、いちいちファイルを編集する必要がないコンパイラオプションで指定するのが楽だ。しかし、場合によってはそれが望ましくないこともある。

例えば、実行ファイルではなくライブラリ(libHoge.so)を作っているとしよう。以下のような構成だ。

ソースファイル
Hoge.cpp
ヘッダファイル
Hoge.h これはライブラリのユーザに対する公開ヘッダでもある

そして、Hoge.hに以下のような条件分岐がある。

class Hoge {
public:
    Hoge();
    ~Hoge();
private:
    int n;
#if USE_FEATURE_X
    int x;
#endif
};

このような場合、当然libHoge.soのビルド時と、それを使うアプリケーションのビルド時でUSE_FEATURE_Xの値を揃えておかなくてはならない。しかし、libHoge.soビルド時にコンパイラオプションで-DUSE_FEATURE_X=1にしていた場合、アプリケーションのビルド時にも同じオプションをつけなくてはならない。こうなるとアプリケーションの開発者は自分が使うライブラリすべてに対してマクロ設定を把握しておかなくてはならないわけで、当然把握しきれず破綻することになるだろう。こういう場合はUSE_FEATURE_Xをヘッダファイル中に定義しておかなくてはならない。

そこで、環境依存の部分があるヘッダファイルはCMakeに自動生成させるようにする。ヘッダファイルの元となる雛形を用意しておき、libHoge.soビルド時に指定したオプションによって実際に使われるヘッダファイルを生成するのだ。

もちろん、HogeConfig.hを自動生成させても良いが、こういうことは専用の別ファイルにしておくと見通しがよくなる。そこで、環境依存の設定を記述するHogeConfig.hを用意することにしよう。これをHoge.hからインクルードすればよい。

CMakeでの設定ファイルの作り方

まずはHogeConfig.hの雛形となるHogeConfig.h.inを用意する。

#ifndef HOGECONFIG_H
#define HOGECONFIG_H

#define USE_FEATURE_X @USE_FEATURE_X@

#endif // HOGECONFIG_H

@で囲まれた部分は、CMakeによって置き換えられる部分だ。また、CMakeLists.txtは以下のようにする。

cmake_minimum_required(VERSION 2.8)
configure_file("HogeConfig.h.in" "HogeConfig.h")
add_executable(myapp main.cpp Hoge.cpp)

configure_fileコマンドで、雛形となるファイルと自動生成するファイルの名前を指定する。ファイル名は何でも良いが、GNU Autotoolsの慣習に従って、{元のファイル名}.inとするのが良いだろう。これでHogeConfig.hはUSE_FEATURE_Xの値が反映されたものになる。

> cmake -DUSE_FEATURE_X=1
(snip)
> cat HogeConfig.h
#ifndef HOGECONFIG_H
#define HOGECONFIG_H

#define USE_FEATURE_X 1

#endif // HOGECONFIG_H
> cmake -DUSE_FEATURE_X=0
(snip)
> cat HogeConfig.h
#ifndef HOGECONFIG_H
#define HOGECONFIG_H

#define USE_FEATURE_X 0

#endif // HOGECONFIG_H

パスの設定

ここではconfigure_fileコマンドの引数に単純にファイル名だけを記述したが、実際にはPROJECT_SOURCE_DIR以下であることを記述した方が良い。

configure_file(
  "${PROJECT_SOURCE_DIR}/HogeConfig.h.in"
  "${PROJECT_SOURCE_DIR}/HogeConfig.h"
)

これはソース外ビルドを行ったときに重要になる。ソース外ビルドについては次回書こうと思う。