2014年4月15日火曜日

【和訳】Cmakeのチュートリアル


Below is a step-by-step tutorial covering common build system issues that CMake helps to address. Many of these topics have been introduced in Mastering CMake as separate issues but seeing how they all work together in an example project can be very helpful. This tutorial can be found in the Tests/Tutorial directory of the CMake source code tree. Each step has its own subdirectory containing a complete copy of the tutorial for that step.

A Basic Starting Point (Step 1)

最も基本的なプロジェクトは、ソースファイルからの実行ファイルのビルドです。シンプルなプロジェクトでは、2行あるだけのCMakeistsファイルが必要な全てです。
いろいろと追記する前の最初期のCMakeListsは以下のとおりです。
#CMake version2.6以降をサポート
cmake_minimum_required (VERSION 2.6)

#プロジェクト名 Tutorial
project (Tutorial)

#実行ファイル"Tutorial"をソースファイル"tutorial.cxx"から作る
add_executable(Tutorial tutorial.cxx)
この例で、CMakeListsには大文字・小文字が混在しています。CMakeは混在を大文字・小文字を区別しません。
ソースコードtutorial.cxx"は、数字の平方根を計算しますが、現状では下記の通りとてもシンプルです。
// A simple program that computes the square root of a number
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main (int argc, char *argv[])
{
  if (argc < 2)
    {
    fprintf(stdout,"Usage: %s number\n",argv[0]);
    return 1;
    }
  double inputValue = atof(argv[1]);
  double outputValue = sqrt(inputValue);
  fprintf(stdout,"The square root of %g is %g\n",
          inputValue, outputValue);
  return 0;
}

Adding a Version Number and Configured Header File

最初に、実行ファイルとプロジェクトにバージョン番号を導入します。従来通りソースコードのみで管理も出来ますが、CMakeLists内で行うこと事でより高い柔軟性を得られます。バージョン番号を追加するために、CMakeListsを以下の通り修正します。
cmake_minimum_required (VERSION 2.6)
project (Tutorial)
# The version number.
set (Tutorial_VERSION_MAJOR 1)
set (Tutorial_VERSION_MINOR 0)
 
# TutorialConfig.h.inを元にTutorialConfig.hを自動生成する
configure_file (
  "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
  "${PROJECT_BINARY_DIR}/TutorialConfig.h"
  )
 
# TutorialConfig.hの出力先のディレクトリを、インクルードファイルの検索パスリストに追加
include_directories("${PROJECT_BINARY_DIR}")
 
# add the executable
add_executable(Tutorial tutorial.cxx)
"${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"を以下の通り作成
// the configured options and settings for Tutorial
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
CMakeヘッダファイルを構成する時、@Tutorial_VERSION_MAJOR@と@Tutorial_VERSION_MINOR@の値はCMakeListsで定義される値に置換されます。
次に、tutorial.cxxを、生成されたヘッダファイルをインクルードし、バージョン番号を使用するように修正します。
// A simple program that computes the square root of a number
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "TutorialConfig.h"
 
int main (int argc, char *argv[])
{
  if (argc < 2)
    {
    fprintf(stdout,"%s Version %d.%d\n",
            argv[0],
            Tutorial_VERSION_MAJOR,
            Tutorial_VERSION_MINOR);
    fprintf(stdout,"Usage: %s number\n",argv[0]);
    return 1;
    }
  double inputValue = atof(argv[1]);
  double outputValue = sqrt(inputValue);
  fprintf(stdout,"The square root of %g is %g\n",
          inputValue, outputValue);
  return 0;
}

Adding a Library (Step 2)
ライブラリをプロジェクトに追加します。. ライブラリはユーザ独自の実装を含み、実行ファイルはコンパイラが標準で提供するライブラリ代わりに、このライブラリを使用します。ここでは、ライブラリをサブディレクトリ"subdirectory"に置きます。次の1行をCMakeListsに追加します。
add_library(MathFunctions mysqrt.cxx)
"mysqrt.cxx"はライブラリに含まれる関数のソースファイルです。
ライブラリを使用するために、TOP階層のCMakeListsにadd_subdirectoryを追記します。これで、ライブラリもビルド対象になります。
また、 MathFunctions/mysqrt.hを見つけられるように、インクリード・ディレクトリに追加を行います。
最後に、ライブラリを実行ファイル"Tutorial"に追加します。
include_directories ("${PROJECT_SOURCE_DIR}/MathFunctions")
add_subdirectory (MathFunctions) 
 
# add the executable
add_executable (Tutorial tutorial.cxx)
target_link_libraries (Tutorial MathFunctions)
MathFunctionライブラリの作成を考えるケースがあります。巨大なライブラリや、3rdパーティのコードを使いたい場合です。
トップ階層のCMakeListsにオプションを追加します。
# should we use our own math functions?
option (USE_MYMATH 
        "Use tutorial provided math implementation" ON) 
上記で記述したメッセージは、CMake時、GUIに表示されます。デフォルト値はONですが、ユーザは必要に応じて変更出来ます。設定値はキャッシュされ、CMakeするごとに設定を繰り返す必要はありません。
次の変更は、MathFunctionsライブラリのビルド・リンク条件を作成することです。トップ階層のCMakeListsの文末を変更します。
# add the MathFunctions library?
#
if (USE_MYMATH)
  include_directories ("${PROJECT_SOURCE_DIR}/MathFunctions")
  add_subdirectory (MathFunctions)
  set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
endif (USE_MYMATH)
 
# add the executable
add_executable (Tutorial tutorial.cxx)
target_link_libraries (Tutorial  ${EXTRA_LIBS})
MathFunctionsがこのプロジェクトに含まれるかどうかはオプションUSE_MYMATHの設定で決定されます。EXTRA_LIBS変数に実行ファイルにリンクされるライブラリをまとめていることに注意してください。 これは、多くのオプションコンポーネントを抱える巨大なプロジェクトを管理する時のセオリーです。
オプションUSE_MYMATHの設定をソースコードで使用するには以下のようにします。
// A simple program that computes the square root of a number
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "TutorialConfig.h"
#ifdef USE_MYMATH
#include "MathFunctions.h"
#endif
 
int main (int argc, char *argv[])
{
  if (argc < 2)
    {
    fprintf(stdout,"%s Version %d.%d\n", argv[0],
            Tutorial_VERSION_MAJOR,
            Tutorial_VERSION_MINOR);
    fprintf(stdout,"Usage: %s number\n",argv[0]);
    return 1;
    }
 
  double inputValue = atof(argv[1]);
 
#ifdef USE_MYMATH
  double outputValue = mysqrt(inputValue);
#else
  double outputValue = sqrt(inputValue);
#endif
 
  fprintf(stdout,"The square root of %g is %g\n",
          inputValue, outputValue);
  return 0;
}
マクロUSE_MYMATHは、CMakeからソースコードへTutorialConfig.h.in構成ファイルを通して提供します。
TutorialConfig.h.inに以下を記述すると、USE_MYMATHオプションがONの場合のみ、TutorialConfig.h内で#defineされます。
#cmakedefine USE_MYMATH

Installing and Testing (Step 3)
インストール・ルールとテスト機能のサポートをプロジェクトに追加します。
インストールルール:
MathFunctionsライブラリについて、インストールされるライブラリとヘッダーファイルのセットアップを行います。
これには、以下の2行をMathFunctionsのCMakeListsに追加します。 
install (TARGETS MathFunctions DESTINATION bin)
install (FILES MathFunctions.h DESTINATION include)
アプリケーションについて、トップ階層のCMakeListsへ以下を追記します。これは、実行ファイルと出力ヘッダーファイルのインストールを指示します。
# add the install targets
install (TARGETS Tutorial DESTINATION bin)
install (FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"        
         DESTINATION include)
以上です。この時点でチュートリアルのビルドが可能です。"make install"もしくは「IDEでINSTALLターゲットのビルドの実行」を行うことで、適切なヘッダーファイル、ライブラリ、実行ファイルがインストールされます。CMake変数の"CMAKE_INSTALL_PREFIX"は、インストールされるファイルのルートの決定に使用されます。 
テストの追加:
トップ階層のCMakeListsの文末に、アプリケーションの正常動作を検証する幾つかの基本テストを追加することができます。
# does the application run
add_test (TutorialRuns Tutorial 25)
 
# does it sqrt of 25
add_test (TutorialComp25 Tutorial 25)
 
set_tests_properties (TutorialComp25 
  PROPERTIES PASS_REGULAR_EXPRESSION "25 is 5")
 
# does it handle negative numbers
add_test (TutorialNegative Tutorial -25)
set_tests_properties (TutorialNegative
  PROPERTIES PASS_REGULAR_EXPRESSION "-25 is 0")
 
# does it handle small numbers
add_test (TutorialSmall Tutorial 0.0001)
set_tests_properties (TutorialSmall
  PROPERTIES PASS_REGULAR_EXPRESSION "0.0001 is 0.01")
 
# does the usage message work?
add_test (TutorialUsage Tutorial)
set_tests_properties (TutorialUsage
  PROPERTIES 
  PASS_REGULAR_EXPRESSION "Usage:.*number")
最初のテストは単純にアプリケーション実行を検証します。セグメンテーション違反(segfault)もしくは他のクラッシュは起こらず、0を返します。これがCTestテストの基本形です。続く幾つかのテストの全ては、テスト出力が特定の文字列を含むか検証する、PASS_REGULAR_EXPRESSIONテストプロパティを利用します(make use of)。
このケースでは、数値がふさわしいかの検証と、引数の数が不正な場合のUSAGEメッセージの出力チェックを行います。
入力値の異なる多くのテストを追加したい場合、マクロを作る事も可能です。第1引数にマクロ名、第2引数以降にマクロのパラメータを指定します。
#define a macro to simplify adding tests, then use it
macro (do_test arg result)
  add_test (TutorialComp${arg} Tutorial ${arg})
  set_tests_properties (TutorialComp${arg}
    PROPERTIES PASS_REGULAR_EXPRESSION ${result})
endmacro (do_test)
 
# do a bunch of result based tests
do_test (25 "25 is 5")
do_test (-25 "-25 is 0")
それぞれのdo_testマクロの発動すると、引数に基づいた名称・入力値・結果を使ったテストがプロジェクトに追加されます。

Adding System Introspection (Step 4)
ターゲット・プラットフォームが持っていない可能性がある機能に依存したコードをプロジェクトに追加する場合を考えます。サンプルでは、"log"や"exp"関数を必要とするコードを追加し、プラットフォームがそれらをサポートする場合はそれを使用します。最初にこれらの関数が機能するかをCheckFunctionExists.cmakeマクロでテストします。これには、トップ階層のCmakeListsに追記します。
# does this system provide the log and exp functions?
#"CheckFunctionExists.cmake"をインクルードして実行する
include (CheckFunctionExists.cmake)
check_function_exists (log HAVE_LOG)
check_function_exists (exp HAVE_EXP)
CMakeがプラットフォーム上にそれらを見つけたかを示す値を定義する為にTutorialConfig.h.inを編集します。
// does the platform provide exp and log functions?
#cmakedefine HAVE_LOG
#cmakedefine HAVE_EXP
テストが実施されるのはTutorialConfig.hのconfigure_fileコマンドより前であることは重要です。configure_fileコマンドは、Cmakeの現在の設定を使用して即座にファイルを形成します。
テストに成功した場合はマクロ(HAVE_LOG, HAVE_EXP)が定義されるのでこれを使用してコードを分岐させることが出来ます。
// if we have both log and exp then use them
#if defined (HAVE_LOG) && defined (HAVE_EXP)
  result = exp(log(x)*0.5);
#else // otherwise use an iterative approach
  . . .

Adding a Generated File and Generator (Step 5)
生成したソースファイルをビルドプロセスに追加する方法を説明します。このサンプルにおいて、ビルドプロセスでテーブルを作成し、そのテーブルをアプリケーション内にコンパイルします。まず、テーブルを作成するプログラムを用意します。
MathFunctionsサブディレクトリで、"MakeTable.cxx"を新規作成します。
// A simple program that builds a sqrt table 
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
 
int main (int argc, char *argv[])
{
  int i;
  double result;
 
  // make sure we have enough arguments
  if (argc < 2)
    {
    return 1;
    }
  
  // open the output file
  FILE *fout = fopen(argv[1],"w");
  if (!fout)
    {
    return 1;
    }
  
  // create a source file with a table of square roots
  fprintf(fout,"double sqrtTable[] = {\n");
  for (i = 0; i < 10; ++i)
    {
    result = sqrt(static_cast<double>(i));
    fprintf(fout,"%g,\n",result);
    }
 
  // close the table with a zero
  fprintf(fout,"0};\n");
  fclose(fout);
  return 0;
}
テーブルはC++のコードで生成され、出力ファイルは実行時の引数で指定します。
MathFunctionsのCMakeListsに、実行ファイルMakeTableをビルドするコマンドを追加し、ビルドプロセスの一部としてMakeTableを実行します。
# テーブルを生成する実行ファイル"MakeTable"を追加
add_executable(MakeTable MakeTable.cxx)
 
# ソースコードを生成するコマンドを追加
add_custom_command (
  OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
  COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
  DEPENDS MakeTable
  )
 
# 生成したソースコードのパスをインクルードファイルの検索パスに追加 
include_directories( ${CMAKE_CURRENT_BINARY_DIR} )
 
# メインライブラリ"MathFunctions"に追加
add_library(MathFunctions mysqrt.cxx ${CMAKE_CURRENT_BINARY_DIR}/Table.h  )
最初に、MakeTableの実行ファイルを、他の実行ファイルの場合と同様に、追加します。
それから、MakeTableの実行からTable.hを作る方法を詳述するカスタム・コマンドを追加します。
次に、mysqrt.cxxが、生成されるファイル"Table.h"に依存することをCMakeに知らせる必要があります。Table.hをライブラリMathFunctionsのソースリストに追加します。mysqrt.cxxがTable.hを見つけてインクルード出来るように、Table.hのパスをインクルード・ディレクトリのリストに追加する必要もあります。
このプロジェクトのビルド時、MakeTable実行ファイルを最初にビルドします。次に、Table.hを生成するためにMakeTableを実行します。最後に、mysqrt.cxxをコンパイルします。このmysqrt.cxxはMathFunctionsライブラリを生成するTable.hを含んでいます。
以上の修正を盛り込んだトップ階層のCMakeListsは下記のとおりです。
cmake_minimum_required (VERSION 2.6)
project (Tutorial)
 
# The version number.
set (Tutorial_VERSION_MAJOR 1)
set (Tutorial_VERSION_MINOR 0)
 
# does this system provide the log and exp functions?
include (${CMAKE_ROOT}/Modules/CheckFunctionExists.cmake)
 
check_function_exists (log HAVE_LOG)
check_function_exists (exp HAVE_EXP)
 
# should we use our own math functions
option(USE_MYMATH 
  "Use tutorial provided math implementation" ON)
 
# configure a header file to pass some of the CMake settings
# to the source code
configure_file (
  "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
  "${PROJECT_BINARY_DIR}/TutorialConfig.h"
  )
 
# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
include_directories ("${PROJECT_BINARY_DIR}")
 
# add the MathFunctions library?
if (USE_MYMATH)
  include_directories ("${PROJECT_SOURCE_DIR}/MathFunctions")
  add_subdirectory (MathFunctions)
  set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
endif (USE_MYMATH)
 
# add the executable
add_executable (Tutorial tutorial.cxx)
target_link_libraries (Tutorial  ${EXTRA_LIBS})
 
# add the install targets
# TARGETSに続く"Tutorial"を名前とする実行ファイルを、binディレクトリ以下に作成する
install (TARGETS Tutorial DESTINATION bin)
# "Tutorial"を名前とする実行ファイルを、binディレクトリ以下に作成する
# FILESに続くファイルのパス(複数のコピー)を、DESTINATIONに続く"include"ディレクトリにコピーする
install (FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h" DESTINATION include) # does the application run add_test (TutorialRuns Tutorial 25) # does the usage message work? add_test (TutorialUsage Tutorial) set_tests_properties (TutorialUsage PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number" ) #define a macro to simplify adding tests macro (do_test arg result) add_test (TutorialComp${arg} Tutorial ${arg}) set_tests_properties (TutorialComp${arg} PROPERTIES PASS_REGULAR_EXPRESSION ${result} ) endmacro (do_test) # do a bunch of result based tests do_test (4 "4 is 2") do_test (9 "9 is 3") do_test (5 "5 is 2.236") do_test (7 "7 is 2.645") do_test (25 "25 is 5") do_test (-25 "-25 is 0") do_test (0.0001 "0.0001 is 0.01")
TutorialConfig.h looks like:
// the configured options and settings for Tutorial
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
#cmakedefine USE_MYMATH
 
// does the platform provide exp and log functions?
#cmakedefine HAVE_LOG
#cmakedefine HAVE_EXP
And the CMakeLists file for MathFunctions looks like:
# first we add the executable that generates the table
add_executable(MakeTable MakeTable.cxx)
# add the command to generate the source code
add_custom_command (
  OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
  DEPENDS MakeTable
  COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
  )
# add the binary tree directory to the search path 
# for include files
include_directories( ${CMAKE_CURRENT_BINARY_DIR} )
 
# add the main library
add_library(MathFunctions mysqrt.cxx ${CMAKE_CURRENT_BINARY_DIR}/Table.h)
 
install (TARGETS MathFunctions DESTINATION bin)
install (FILES MathFunctions.h DESTINATION include)

Building an Installer (Step 6)
プロジェクトを一般に配布する事を考えます。様々なプラットフォームに対して、バイナリとソースの双方を配布するには、前述のStep3「Installing and Testing」で行ったインストール(ソースコードからビルドしてインストール)とは若干の違いが存在します。
このサンプルでは、インストールパッケージをビルドしますが、バイナリインストールと、cygwin, debian, RPMs等で見られるようなパッケージ管理システム機能をサポートします。
プラットフォーム固有のインストーラを作るために、"Packing with CPack"チャプターで記述されている通り、CPackを使用します。
トップ階層のCMakeLists.txtに追記します。
# build a CPack driven installer package
include (InstallRequiredSystemLibraries)
set (CPACK_RESOURCE_FILE_LICENSE  
     "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
set (CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
set (CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
include (CPack)
以上で達成出来ます。.
"InstallRequiredSystemLibraries"のインストールから開始します。このモジュールはランタイムライブラリをインクルードします。ランタイムライブラリは、現在のプラットフォームに対してプロジェクトが必要とします。
次に、ライセンスを置いた場所と、プロジェクトのバージョン情報を示すCPack変数をセットします。
最後に、CPackモジュールをインクルードします。CPackモジュールは、定義した変数や、他のインストーラの対象となるシステムのプロパティを使用します。
通常の手順でプロジェクトをビルドを行い、その上で絵CPackを実行します。バイナリで配布する場合は以下となります。
cpack -C CPackConfig.cmake
ソースファイルで配布する場合は以下となります。
cpack -C CPackSourceConfig.cmake

Adding Support for a Dashboard (Step 7)
ダッシュボードへテスト結果の投稿をサポートする機能は、容易に追加出来ます。このチュートリアルの以前に、既に幾つかのテストをプロジェクトに定義しています。これらのテストを実行し、結果をダッシュボードに投稿します。ダッシュボードのサポートをインクルードするために、TOP階層のCMakeListsにCTestモジュールをインクルードします。
# enable dashboard scripting
include (CTest)
また、CTestConfig.cmakeファイルを作成します。これに、ダッシュボードに対してプロジェクト名を記述します。
set (CTEST_PROJECT_NAME "Tutorial")
CTestは実行時、このファイルを読み取ります。シンプルなダッシュボードを作成するためには、プロジェクトでCMakeを実行し、ディレクトリを生成ファイルの出力先に変更し、"ctest –D Experimental"を実行します。ダッシュボードの結果は、Kitwareの公式ダッシュボードにアップロードされます。

0 件のコメント:

コメントを投稿