1. ホーム
  2. Other

CMakeLists.txt 構文の紹介と例のウォークスルー

2022-02-12 13:03:36

I. Cmake入門

cmakeは、クロスプラットフォームのオープンソースビルドシステムです。ソフトウェアのビルド、テスト、パッケージングをオールインワンで行うことができます。プラットフォームやコンパイラに依存しない設定ファイルを使用し、ソフトウェアのビルドプロセスを制御することができます。

II. 共通コマンド

1. cmakeの最小バージョンを指定する

cmake_minimum_required(VERSION 3.4.1)

この行はオプションで省略可能ですが、CMakeLists.txt ファイルで cmake の上位バージョンに特有のコマンドを使用している場合、cmake を実行する前にそのバージョンにアップグレードするようユーザーに注意を促すためにこの行を追加する必要がある場合があります。

2. プロジェクト名の設定

project(demo)

このコマンドは必須ではありませんが、すべて追加しておいたほうがよいでしょう。これは demo_BINARY_DIR と demo_SOURCE_DIR という二つの変数を導入し、cmake は自動的に PROJECT_BINARY_DIR と PROJECT_SOURCE_DIR という二つの同等の変数を定義しています。

3. コンパイルの種類を設定する

add_executable(demo demo.cpp) # Generate executable file
add_library(COMMON STATIC util.cpp) # Generate static library
add_library(COMMON SHARED util.cpp) # Generate dynamic or shared libraries

add_library はデフォルトでスタティックライブラリとして生成され、上記のコマンドでファイル名が生成されます。

  • Linuxではそうなっています。
    デモ
    libcommon.a
    libcommon.so
  • Windowsではそうなっています。
    デモ.exe
    共通.lib
    common.dll

4. コンパイルに含めるソースファイルを指定する

4.1 インクルードするソースファイルの明示的な指定

add_library(demo demo.cpp test.cpp util.cpp)

4.2 すべての cpp ファイルを検索する

aux_source_directory(dir VAR) は、ディレクトリ内のすべてのソースファイルを見つけ、そのリストを変数に格納します。

aux_source_directory(. SRC_LIST) # Search for all .cpp files in the current directory
add_library(demo ${SRC_LIST})

4.3 検索ルールのカスタマイズ

file(GLOB SRC_LIST "*.cpp" "protocol/*.cpp")
add_library(demo ${SRC_LIST})
# or
file(GLOB SRC_LIST "*.cpp")
file(GLOB SRC_PROTOCOL_LIST "protocol/*.cpp")
add_library(demo ${SRC_LIST} ${SRC_PROTOCOL_LIST})
# or
file(GLOB_RECURSE SRC_LIST "*.cpp") # recursive search
FILE(GLOB SRC_PROTOCOL RELATIVE "protocol" "*.cpp") # Search relative to the protocol directory
add_library(demo ${SRC_LIST} ${SRC_PROTOCOL_LIST})
# or
aux_source_directory(. SRC_LIST)
aux_source_directory(protocol SRC_PROTOCOL_LIST)
add_library(demo ${SRC_LIST} ${SRC_PROTOCOL_LIST})

5. 指定されたライブラリファイルを検索する

find_library(VAR name path) は、指定されたプリコンパイルされたライブラリを見つけ、そのパスを変数に格納します。
デフォルトの検索パスはcmakeに含まれるシステムライブラリなので、NDK用のパブリックライブラリであればライブラリ名を指定すればOKです。

find_library( # Sets the name of the path variable.
              log-lib
 
              # Specifies the name of the NDK library that
              # you want CMake to locate.
              log )

類似のコマンドとして、find_file()、find_path()、find_program()、find_package()があります。

6. インクルードディレクトリの設定

include_directories(
    ${CMAKE_CURRENT_SOURCE_DIR}
    ${CMAKE_CURRENT_BINARY_DIR}
    ${CMAKE_CURRENT_SOURCE_DIR}/include
)

Linuxでは、includeディレクトリを以下のように設定することもできます。

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I${CMAKE_CURRENT_SOURCE_DIR}")

7. リンクライブラリの検索ディレクトリを設定する

link_directories(
    ${CMAKE_CURRENT_SOURCE_DIR}/libs
)

Linuxでは、以下のようにincludeディレクトリを設定することもできます。

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_CURRENT_SOURCE_DIR}/libs")

8. リンクさせるライブラリのターゲットを設定する

target_link_libraries( # target_libraries
                       demo
 
                       # The libraries to be linked to the target library
                       # log-lib is the variable name specified by find_library above
                       ${log-lib} )

Windowsではリンク先のライブラリディレクトリを基準にxxx.libファイルを、Linuxではxxx.soまたはxxx.aファイルを検索し、両方が存在する場合はダイナミックライブラリ(拡張子がso)を優先してリンクします。

8.1 リンクする動的ライブラリまたは静的ライブラリの指定

target_link_libraries(demo libface.a) # link libface.a
target_link_libraries(demo libface.so) # link libface.so

8.2 フルパスの指定

target_link_libraries(demo ${CMAKE_CURRENT_SOURCE_DIR}/libs/libface.a)
target_link_libraries(demo ${CMAKE_CURRENT_SOURCE_DIR}/libs/libface.so)

8.3 複数ライブラリのリンク指定

target_link_libraries(demo
    ${CMAKE_CURRENT_SOURCE_DIR}/libs/libface.a
    boost_system.a
    boost_thread
    pthread)

9. 変数の設定

9.1 set 変数の値を直接設定する

set(SRC_LIST main.cpp test.cpp)
add_executable(demo ${SRC_LIST})

9.2 set 変数の値の追加

set(SRC_LIST main.cpp)
set(SRC_LIST ${SRC_LIST} test.cpp)
add_executable(demo ${SRC_LIST})

9.3 list 変数の値を追加・削除する

set(SRC_LIST main.cpp)
list(APPEND SRC_LIST test.cpp)
list(REMOVE_ITEM SRC_LIST main.cpp)
add_executable(demo ${SRC_LIST})


10. 条件付きコントロール

10.1 if...elseif...endifの場合

論理的な判断と比較。
if (式) : expression が空でなければ真(0,N,NO,OFF,FALSE,NOTFOUND)
if (not exp) : 上記の逆
if (var1 AND var2)
if (var1 OR var2)
if (COMMAND cmd) : cmd が本当にコマンドであり、起動可能な場合、true を返します。
if (EXISTS dir) if (EXISTS file) : ディレクトリまたはファイルが存在すれば真
if (file1 IS_NEWER_THAN file2) : file1 が file2 よりも新しい場合、または file1/file2 のいずれかが存在しない場合に真、ファイル名にはフルパスが必要です。
if (IS_DIRECTORY dir) : dir がディレクトリであるとき、true を返します。
if (DEFINED var) : 変数がtrueと定義されている場合
if (var MATCHES regex) : 与えられた変数や文字列が正規表現 regex にマッチする場合に真となります。
if (string MATCHES regex)

数値の比較。
if (変数 LESS 数) : LESS は以下である。
if (文字列 LESS 数)
if (変数 GREATER 数) : GREATER は以下より大きい。
if (文字列 GREATER 数字)
if (変数 EQUAL 数) : EQUALは以下と同じです。
if (文字列 EQUAL 数字)

アルファベット順の比較。
if (変数 STRLESS 文字列)
if (文字列 STRLESS 文字列)
if (変数 STRGREATER 文字列)
if (文字列 STRGREATER 文字列)
if (変数 STREQUAL 文字列)
if (文字列 STREQUAL 文字列)

if(MSVC)
    set(LINK_LIBS common)
else()
    set(boost_thread boost_log.a boost_system.a)
endif()
target_link_libraries(demo ${LINK_LIBS})
# or
if(UNIX)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fpermissive -g")
else()
    add_definitions(-D_SCL_SECURE_NO_WARNINGS
    D_CRT_SECURE_NO_WARNINGS
    -D_WIN32_WINNT=0x601
    -D_WINSOCK_DEPRECATED_NO_WARNINGS)
endif()
 
if(${CMAKE_BUILD_TYPE} MATCHES "debug")
    ...
else()
    ...
endif()

10.2 while...endwhile

while(condition)
    ...
endwhile()

10.3 foreach...endforeach

foreach(loop_var RANGE start stop [step])
    ...
endforeach(loop_var)

startは開始番号、stopは停止番号、stepはステップの長さ、例。

foreach(i RANGE 1 9 2)
    message(${i})
endforeach(i)
# Output: 13579

11. メッセージの印刷

message(${PROJECT_SOURCE_DIR})
message("build with debug mode")
message(WARNING "this is warnning message")
message(FATAL_ERROR "this build has many error") # FATAL_ERROR will cause the build to fail

12. 他の cmake ファイルをインクルードする

include(. /common.cmake) # Specify the full path to the include file
include(def) # Search for the def.cmake file in the search path
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) # Set the search path for include

III. 共通変数

1. 定義済み変数

プロジェクト・ソース・ディレクトリー : プロジェクトのルートディレクトリ
プロジェクト_バイナリ_ディレクトリ : cmakeコマンドが実行されるディレクトリで、通常は ${PROJECT_SOURCE_DIR}/build です。
プロジェクト名(PROJECT_NAME : project コマンドで定義されたプロジェクトの名称を返します。
cmake_current_source_dir 現在処理中の CMakeLists.txt へのパス
cmake_current_binary_dir : ターゲットコンパイルディレクトリ
cmake_current_list_dir CMakeLists.txt へのフルパス
cmake_current_list_line : 現在の行
cmake_module_path : SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) と定義すると、INCLUDEコマンドで自分のcmakeモジュールを呼び出すことができるようになります。
実行可能出力パス : ターゲットバイナリの実行ファイルの場所を再定義する
ライブラリ_出力_パス : リンク先のライブラリファイルの場所を再定義する

2. 環境変数

環境変数の使用

 $ENV{Name}

環境変数への書き込み

 set(ENV{Name} value) # No "$" sign here

3. システム情報

cmake_major_version cmakeのメジャーバージョン番号、例:3.4.1なら3
cmake_minor_version cmakeのサブバージョン番号、例えば3.4.1なら4
cmake_patch_version cmakeのパッチレベル、例:3.4.1では1
CMAKE_SYSTEM : システム名、例:Linux-2.6.22
cmake_system_name : システム名(バージョンなし)、例:Linux
cmake_system_version : システムのバージョン、例:2.6.22
cmake_system_processor : プロセッサー名、例:i686
ユニックス : この値は、OS X や cygwin を含むすべての UNIX 系プラットフォームで TRUE となります。
WIN32 : この値は、cygwin を含むすべての win32 プラットフォームで TRUE になります。

4. 主なスイッチオプション

ビルド_共有_ライブラリ : このスイッチは、デフォルトのライブラリのコンパイル方法を制御するために使用されます。このスイッチを設定せず、ライブラリーの種類を指定せずに add_library を使用した場合、生成されるデフォルトのライブラリーはスタティック・ライブラリーとなります。set(BUILD_SHARED_LIBS ON)すると、デフォルトでダイナミック・ライブラリーが生成されます。
CMAKE_C_FLAGS : C コンパイルのオプションを設定します。add_definitions() ディレクティブで追加することもできます。
cmake_cxx_flags : C++ コンパイルオプションを設定します。add_definitions() ディレクティブで追加することも可能です。

add_definitions(-DENABLE_DEBUG -DABC) # Separate arguments with spaces

IV. プロジェクト例

1. シンプルなプロジェクト(ソースファイル1つ)

古典的なCプログラムは、どのようにcmakeでコンパイルできますか?

1.1 新しいファイル、main.c を作成する

#include <stdio.h>
int main() {
    printf("Hello World!\n");
    return 0;
}

1.2 新しいファイル CMakeLists.txt を作成します(名前は CMakeLists.txt で、大文字と小文字を区別し、文字が欠けないようにしてください)。

project(HELLO)
add_executable(hello main.c)

1.3 コンパイルと実行

以下のディレクトリで新しいビルドフォルダを作成します。

<スパン なぜ新しいビルドフォルダーが必要なのですか?
一般的に、cmakeのアウトオブソース方式でビルドする(生成された中間生成物をソースコードから分離する)ことで、生成ファイルとソースファイルが混ざらないようにし、ディレクトリ構造もスッキリした印象になります。このフォルダの命名には特に制限はなく、buildという名前を使っています。

以下のコマンドを順番に実行します。

cd build
cmake ...

まずビルドディレクトリに移動し、次に cmake ... コマンドを使用しますが、cmake を直接使用する前に環境変数に設定する必要があり、そうでない場合は絶対パス ... が前のディレクトリを示すと、cmake は前のディレクトリにある CMakeLists.txt ファイルを見つけてコンパイルし、以下のような中間ファイルをいくつか生成します。

の直接実行 make コマンドを使用して、以下のように実行プログラムを生成します。

次のように、プログラムを実行します。以下のように、プログラムを実行します。

2. 複雑なプロジェクト (複数のディレクトリ、複数のソースファイル、複数のプロジェクト)

ディレクトリ構成は以下の通りです。

デモのルートにあるCMakeLists.txtは以下のようなファイルです。

cmake_minimum_required (VERSION 2.8)
project(demo)
aux_source_directory(. DIR_SRCS)
# Add math subdirectory
add_subdirectory(math)
# Specify the target for generation
add_executable(demo ${DIR_SRCS})
# Add linked libraries
target_link_libraries(demo MathFunctions)

mathディレクトリにあるCMakeLists.txtは以下のようなファイルです。

aux_source_directory(. DIR_LIB_SRCS)
# Generate link libraries
add_library(MathFunctions ${DIR_LIB_SRCS})

3. コンパイルオプションのカスタマイズ

cmake では、コンパイルオプションをプロジェクトに追加することで、ユーザーの環境とニーズに応じて最適なコンパイル方式を選択することができます。
例えば、MathFunctionsライブラリをオプションライブラリとして設定し、オプションがONの場合はそのライブラリで定義された数学関数を用いて演算を行い、それ以外の場合は標準ライブラリにある数学関数ライブラリを呼び出すことができる。
ルートディレクトリのCMakeLists.txtファイルを以下のように修正します。

# CMake minimum version number requirement
cmake_minimum_required (VERSION 2.8)
# Project information
project (Demo)
# Add a configuration header file that handles CMake's settings for the source code
configure_file (
    "${PROJECT_SOURCE_DIR}/config.h.in"
    "${PROJECT_BINARY_DIR}/config.h"
    )
# Whether to use your own MathFunctions library
option (USE_MYMATH
        "Use provided math implementation" ON)
# Whether to add the MathFunctions library
if (USE_MYMATH)
    include_directories ("${PROJECT_SOURCE_DIR}/math")
    add_subdirectory (math)
    set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
endif (USE_MYMATH)
# Find all source files in the current directory
# and save the names to the DIR_SRCS variable
aux_source_directory(. DIR_SRCS)
# Specify the target for generation
add_executable(Demo ${DIR_SRCS})
target_link_libraries (Demo ${EXTRA_LIBS})

  • configure_fileコマンドは、cmakeがconfig.h.inから生成する設定用ヘッダーファイルconfig.hを追加するために使用します。この仕組みにより、パラメータや変数をあらかじめ定義しておくことで、コードの生成を制御することができるようになります。
  • は、その
  • optionコマンドは、USE_MYMATHオプションを追加し、デフォルト値はONです。変数USE_MYMATHの値によって、独自のMathFunctionsライブラリを使用するかどうかが決定される。

あらかじめ定義された USE_MYMATH の値に基づいて、標準ライブラリと MathFunctions ライブラリのどちらを呼び出すかを決定するように main.cc ファイルを修正します。

#include "config.h"
#ifdef USE_MYMATH
    #include "math/MathFunctions.h"
#else
    #include <math.h>
#endif
 
int main(int argc, char *argv[])
{
    if (argc < 3){
        printf("Usage: %s base exponent \n", argv[0]);
        return 1;
    }
    double base = atof(argv[1]);
    int exponent = atoi(argv[2]);
 
#ifdef USE_MYMATH
    printf("Now we use our own Math library. \n");
    double result = power(base, exponent);
#else
    printf("Now we use the standard library. \n");
    double result = pow(base, exponent);
#endif
    printf("%g ^ %d is %g\n", base, exponent, result);
    return 0;
}


config.h.inファイルの書き方
main.ccの最初の行は、USE_MYMATHの値を事前に定義しているconfig.hファイルを参照していることに注意してください。CMakeLists.txt から設定をインポートしやすくするために、 config.h.in ファイルに次のように記述しています。

#cmakedefine USE_MYMATH

こうすることで、cmake は設定ファイル CMakeLists.txt の設定に基づいて config.h ファイルを自動生成します。

その他

NDKラーニングシリーズ。 Android NDKのビギナーからマスターまで(まとめ)