CMakeを使ってみた (4) ビルドオプション

今回は1つのプログラムを様々なオプションでビルドできるようにしてみる。つまりconfigureでの--enable-foo --with-barのような機能だ。

optionコマンド

optionコマンドを使うとオン・オフ指定するようなオプションが可能になる。例えば、追加機能feature-xがあり、ビルド時のオプションで切り替えられるようにしたいとしよう。ソースファイルでは#ifによる切り替えが入っている。

#include <cstdio>

int main() {
#if ENABLE_FEATURE_X
    printf("feature-x enabled\n");
#else
    printf("feature-x disabled\n");
#endif
    return 0;
}

そういうときはoptionコマンドを使うことができる。

cmake_minimum_required(VERSION 2.8)
add_executable(myapp main.cpp)
option(USE_FEATURE_X "Use feature x" ON)
if(USE_FEATURE_X)
  add_definitions(-DENABLE_FEATURE_X)
endif()

optionコマンドの引数はオプション変数名、説明、初期値だ。そして、オプション変数USE_FEATURE_XがONのときに、add_definitionsでENABLE_FEATURE_Xマクロが定義されるようにしている。

USE_FEATURE_Xのオン/オフは、以下のいずれかで指定する。

  • cmakeのコマンドライン引数で-DUSE_FEATURE_X=ON/OFFをつける
  • CMakeLists.txt中でsetコマンドを使う
  • 明示的には指定しない → 初期値またはキャッシュされた値を使う

実行すると以下のようになる(途中のメッセージは省略して、myappの結果のみ載せる)。

cmakeの初回実行で、何も指定しない -> 初期値 (ON) になる。

> cmake . && make && ./myapp
(snip)
feature-x enabled

コマンドラインオプションでOFFにする。

> cmake -DUSE_FEATURE_X=OFF . && make && ./myapp
(snip)
feature-x disabled

CMakeLists.txt中で指定する。書くにはoptionコマンドとifコマンドの間だ。

cmake_minimum_required(VERSION 2.8)
add_executable(myapp main.cpp)
option(USE_FEATURE_X "Use feature x" ON)
set(USE_FEATURE_X OFF)
if(USE_FEATURE_X)
  add_definitions(-DENABLE_FEATURE_X)
endif()

CMakeLists.txtが変わったことによりcmakeは自動で実行されるので単にmakeすればよい。

> make && ./myapp
(snip)
feature-x disabled

optionコマンドでのソースファイルの追加

前述のCMakeLists.txtではadd_definitionsによるマクロ定義の追加を行ったが、もちろんinclude_directoriesやtarget_link_librariesを使ってインクルードパスやライブラリを追加することもできる。ただし、add_executableやadd_libraryは1ターゲットにつき1回しか使えないため、そのままでは追加できない。ソースファイルのリストを適当な変数に入れ、最後にまとめてadd_executableに入れればよいだろう。例えば「デフォルトではmain.cppとsub1.cppがビルド対象で、USE_FEATURE_YがONのときはsub2.cppとsub3.cppも含める」という場合は以下のようにすればよいだろう。

cmake_minimum_required(VERSION 2.8)
set(myapp_sources main.cpp sub1.cpp)
option(USE_FEATURE_Y "Use feature y" ON)
if(USE_FEATURE_Y)
  set(myapp_sources ${myapp_sources} sub2.cpp sub3.cpp)
endif()
add_executable(myapp ${myapp_sources})

3つ以上の選択肢があるオプション

optionコマンドを使うと、選択肢はON/OFFの2択になる。しかし、オプションによっては3択以上になる場合もあるだろう。例えば何らかの通信を行うアプリケーションにおいて、SSLによる暗号化機能があるとする。そのときビルドオプションとして、OpenSSLを使う、GnuTLSを使う、暗号化機能は使わない、の3つの選択肢を設けたい、というような場合だ。ソースファイル以下のようになっているだろう。

#include <cstdio>

int main() {
#if USE_OPENSSL
    printf("use OpenSSL\n");
#elif USE_GNUTLS
    printf("use GnuTLS\n");
#else
    printf("not encrypted\n");
#endif
    return 0;
}

CMakeLists.txtは以下のようにすればよさそうだ。

cmake_minimum_required(VERSION 2.8)

# ssllibがセットされていなければopensslにする.
if(NOT DEFINED ssllib)
  set(ssllib openssl)
endif()

if(ssllib STREQUAL openssl)
  add_definitions(-DUSE_OPENSSL)
elseif(ssllib STREQUAL gnutls)
  add_definitions(-DUSE_GNUTLS)
elseif(ssllib STREQUAL none)
elseif(DEFINED ssllib)
  # openssl, gnutls, noneいずれでもないものがセットされているときはエラー
  message(FATAL_ERROR "invalid ssllib")
endif()

add_executable(myapp main.cpp)

これで以下のように使うことができる。

> cmake . && make && ./myapp                   # 初回実行で何も指定しない
(snip)                                         #
use OpenSSL                                    #   -> OpenSSLが使われる
> cmake -Dssllib=gnutls . && make && ./myapp   # gnutlsを指定する
(snip)                                         #
use GnuTLS                                     #   -> GnuTLSが使われる
> cmake . && make && ./myapp                   # 再び何も指定しない
(snip)                                         #
use GnuTLS                                     #   -> このときは前回のものになる
> cmake -Dssllib=none . && make && ./myapp     # noneを指定する
(snip)                                         #
not encrypted                                  #   -> 暗号化は使われない
> cmake -Dssllib=hoge . && make && ./myapp     # 不正な値を指定する
CMake Error at CMakeLists.txt:15 (message):    #   -> エラーになる
  invalid ssllib                               #
                                               #
                                               #
-- Configuring incomplete, errors occurred!    #