CMake入門

警告

免責事項:このドキュメントは、CMakeプロジェクトに関係のないLLVMプロジェクトのコントリビューターによって作成されています。このドキュメントには、不正確な用語、言い回し、または技術的な詳細が含まれている場合があります。このドキュメントは、善意に基づいて提供されています。

はじめに

LLVMプロジェクトとLLVM上に構築された多くのコアプロジェクトは、CMakeを使用してビルドされています。このドキュメントは、LLVMプロジェクトを修正したり、LLVM上に独自のプロジェクトを構築したりする開発者向けに、CMakeの簡単な概要を提供することを目的としています。

公式のCMake言語リファレンスは、cmake-language manページとcmake-languageオンラインドキュメントで入手できます。

概要

CMakeは、ソフトウェアプロジェクトのビルド方法を記述する独自の言語のスクリプトファイルを読み取るツールです。CMakeがスクリプトを評価すると、ソフトウェアプロジェクトの内部表現が構築されます。スクリプトが完全に処理され、エラーがない場合、CMakeはプロジェクトを実際にビルドするためのビルドファイルを生成します。CMakeは、さまざまなコマンドラインビルドツールや一般的なIDE用のビルドファイルの生成をサポートしています。

ユーザーがCMakeを実行すると、autoconfが歴史的に機能した方法と同様に、さまざまなチェックが実行されます。チェックとビルド記述スクリプトの評価中に、CMakeは値をCMakeCacheにキャッシュします。これは、インクリメンタル開発中にビルドシステムが長時間実行されるチェックをスキップできるため便利です。CMakeのキャッシュにはいくつかの欠点もありますが、それについては後で説明します。

スクリプトの概要

CMakeのスクリプト言語は、非常にシンプルな文法を持っています。すべての言語構成要素は、_name_(_args_)というパターンに一致するコマンドです。コマンドには、主に3つのタイプがあります。言語定義(CMakeのC++で実装されたコマンド)、定義された関数、および定義されたマクロです。CMakeディストリビューションには、便利な機能の定義を含むCMakeモジュールのスイートも含まれています。

以下の例は、C++の「Hello World」プログラムをビルドするための完全なCMakeビルドです。この例では、CMake言語定義の関数のみを使用しています。

cmake_minimum_required(VERSION 3.20.0)
project(HelloWorld)
add_executable(HelloWorld HelloWorld.cpp)

CMake言語は、foreachループとifブロックの形式で制御フローの構成要素を提供します。上記の例をより複雑にするために、Appleプラットフォームをターゲットにしている場合に「APPLE」を定義するifブロックを追加できます。

cmake_minimum_required(VERSION 3.20.0)
project(HelloWorld)
add_executable(HelloWorld HelloWorld.cpp)
if(APPLE)
  target_compile_definitions(HelloWorld PUBLIC APPLE)
endif()

変数、型、スコープ

間接参照

CMakeでは、変数は「文字列」型です。すべての変数は、評価を通じて文字列として表されます。変数を${}で囲むと、その変数が間接参照され、名前が値にリテラル置換されます。CMakeは、これをドキュメントで「変数評価」と呼んでいます。間接参照は、呼び出されるコマンドが引数を受け取るに実行されます。つまり、リストを間接参照すると、複数の別個の引数がコマンドに渡されます。

変数の間接参照はネストでき、複雑なデータをモデル化するために使用できます。例えば

set(var_name var1)
set(${var_name} foo) # same as "set(var1 foo)"
set(${${var_name}}_var bar) # same as "set(foo_var bar)"

設定されていない変数を間接参照すると、空の展開になります。CMakeでは、変数が設定されていないコードパスで使用されることを認識して、変数を条件付きで設定する一般的なパターンです。LLVM CMakeビルドシステム全体に、この例があります。

変数の空の展開の例は次のとおりです。

if(APPLE)
  set(extra_sources Apple.cpp)
endif()
add_executable(HelloWorld HelloWorld.cpp ${extra_sources})

この例では、extra_sources変数は、Appleプラットフォームをターゲットにしている場合にのみ定義されます。その他のすべてのターゲットの場合、add_executableに引数が渡される前に、extra_sourcesは空として評価されます。

リスト

CMakeでは、リストはセミコロンで区切られた文字列であり、リストでセミコロンを使用することは避けることを強くお勧めします。うまくいきません。リストを定義するいくつかの例

# Creates a list with members a, b, c, and d
set(my_list a b c d)
set(my_list "a;b;c;d")

# Creates a string "a b c d"
set(my_string "a b c d")

リストのリスト

CMakeのより複雑なパターンの1つは、リストのリストです。リストにはセミコロンを含む要素を含めることができないため、リストのリストを作成するには、他のリストを参照する変数名のリストを作成します。例えば

set(list_of_lists a b c)
set(a 1 2 3)
set(b 4 5 6)
set(c 7 8 9)

このレイアウトを使用すると、次のコードで各値を出力するリストのリストを反復処理できます。

foreach(list_name IN LISTS list_of_lists)
  foreach(value IN LISTS ${list_name})
    message(${value})
  endforeach()
endforeach()

内側のforeachループのリストが二重に間接参照されていることに気づくでしょう。これは、最初の間接参照でlist_nameがサブリストの名前(例ではa、b、またはc)に変換され、次に2番目の間接参照でリストの値を取得するためです。

このパターンはCMake全体で使用されており、最も一般的な例はコンパイラフラグオプションです。CMakeでは、CMAKE_${LANGUAGE}_FLAGSおよびCMAKE_${LANGUAGE}_FLAGS_${CMAKE_BUILD_TYPE}という変数展開を使用して参照しています。

その他の型

キャッシュされた変数またはコマンドラインで指定された変数には、関連付けられた型を持つことができます。変数の型は、CMakeのUIツールが正しい入力フィールドを表示するために使用されます。変数の型は一般的に評価に影響を与えませんが、CMakeにはPATHなどの一部の変数に対する特別な処理があります。特別な処理の詳細については、CMakeのsetドキュメントを参照してください。

スコープ

CMakeには、本質的にディレクトリベースのスコープがあります。CMakeListsファイルで変数を設定すると、そのファイルとすべてのサブディレクトリの変数が設定されます。CMakeListsファイルに含まれるCMakeモジュールで設定された変数は、含まれているスコープとすべてのサブディレクトリで設定されます。

すでに設定されている変数がサブディレクトリで再度設定されると、そのスコープおよびより深いサブディレクトリの値が上書きされます。

CMakeのsetコマンドには、スコープに関連する2つのオプションがあります。PARENT_SCOPEは、現在のスコープではなく、親スコープに変数を設定します。CACHEオプションは、CMakeCacheに変数を設定し、すべてのスコープで設定されるようにします。CACHEオプションは、FORCEオプションが指定されていない限り、CACHEにすでに存在する変数を設定しません。

ディレクトリベースのスコープに加えて、CMake関数にも独自のスコープがあります。つまり、関数内で設定された変数は、親スコープに漏れません。これはマクロには当てはまりません。このため、LLVMでは、妥当な場合は常にマクロよりも関数を優先しています。

注意

Cベースの言語とは異なり、CMakeのループと制御フローブロックには独自のスコープがありません。

制御フロー

CMakeには、スクリプト言語に期待されるのと同じ基本的な制御フロー構成要素がありますが、CMakeのすべてのものと同様に、制御フロー構成要素はコマンドであるため、いくつかの癖があります。

If、ElseIf、Else

注意

CMakeのifコマンドの詳細については、こちらを参照してください。そのリソースの方がはるかに完全です。

一般的に、CMakeのifブロックは期待どおりに機能します。

if(<condition>)
  message("do stuff")
elseif(<condition>)
  message("do other stuff")
else()
  message("do other other stuff")
endif()

Cのバックグラウンドから来たCMakeのifブロックについて知っておくべき最も重要なことは、ifブロックには独自のスコープがないということです。条件ブロック内で設定された変数は、endif()の後も残ります。

ループ

CMake foreachブロックの最も一般的な形式は次のとおりです。

foreach(var ...)
  message("do stuff")
endforeach()

foreachブロックの変数引数部分には、間接参照されたリスト、反復処理する値、または両方の組み合わせを含めることができます。

foreach(var foo bar baz)
  message(${var})
endforeach()
# prints:
#  foo
#  bar
#  baz

set(my_list 1 2 3)
foreach(var ${my_list})
  message(${var})
endforeach()
# prints:
#  1
#  2
#  3

foreach(var ${my_list} out_of_bounds)
  message(${var})
endforeach()
# prints:
#  1
#  2
#  3
#  out_of_bounds

よりモダンなCMake foreach構文もあります。以下のコードは上記のコードと同等です。

foreach(var IN ITEMS foo bar baz)
  message(${var})
endforeach()
# prints:
#  foo
#  bar
#  baz

set(my_list 1 2 3)
foreach(var IN LISTS my_list)
  message(${var})
endforeach()
# prints:
#  1
#  2
#  3

foreach(var IN LISTS my_list ITEMS out_of_bounds)
  message(${var})
endforeach()
# prints:
#  1
#  2
#  3
#  out_of_bounds

条件ステートメントと同様に、これらは一般的に期待どおりに動作し、独自のスコープはありません。

CMakeはwhileループもサポートしていますが、LLVMではあまり使用されていません。

モジュール、関数、マクロ

モジュール

モジュールは、コードの再利用を可能にするためのCMakeの手段です。CMakeモジュールは、単なるCMakeスクリプトファイルです。これらには、インクルード時に実行するコードと、コマンドの定義を含めることができます。

CMakeでは、マクロと関数は普遍的にコマンドと呼ばれており、複数回呼び出すことができるコードを定義する主な方法です。

LLVMでは、ソースからプロジェクトをビルドしない開発者向けに、ディストリビューションの一部として含まれているいくつかのCMakeモジュールがあります。これらのモジュールは、CMakeを使用してLLVMベースのプロジェクトをビルドするために必要な基本的な部分です。また、LLVMプロジェクト内で保守性と再利用のためにビルドシステムの機能を整理する方法としてもモジュールを使用しています。

引数の処理

CMakeコマンドを定義する場合、引数を処理すると非常に役立ちます。このセクションの例はすべて、CMake functionブロックを使用しますが、これはmacroブロックにもすべて適用されます。

CMakeのコマンドは、すべての呼び出しサイトで必須となる名前付き引数を持つことができます。さらに、すべてのコマンドは暗黙的に可変個の追加引数を受け付けます(Cの用語で言うと、すべてのコマンドは可変引数関数です)。コマンドが(名前付き引数以外に)追加引数付きで呼び出されると、CMakeは引数の完全なリスト(名前付きと名前なしの両方)をARGVという名前のリストに、名前なし引数のサブリストをARGNに格納します。以下に、CMakeの組み込み関数add_dependenciesのラッパー関数を提供する簡単な例を示します。

function(add_deps target)
  add_dependencies(${target} ${ARGN})
endfunction()

この例では、必須の最初の引数を取り、最初の引数と後続のすべての引数を別の関数に渡すだけのadd_depsという名前の新しいマクロを定義しています。

CMakeには、高度な引数解析の実装を提供するモジュールCMakeParseArgumentsがあります。これはLLVM全体で使用されており、複雑な引数ベースの動作やオプションの引数を持つすべての関数に推奨されます。このモジュールのCMake公式ドキュメントはcmake-modulesのマニュアルページにあり、cmake-modulesのオンラインドキュメントでも入手できます。

注意

CMake 3.5以降、cmake_parse_argumentsコマンドはネイティブコマンドになり、CMakeParseArgumentsモジュールは空になり、互換性のためだけに残されています。

関数とマクロ

関数とマクロは、どのように使用されるかという点では非常に似ていますが、2つの間には根本的な違いが1つあります。関数には独自のスコープがありますが、マクロにはありません。これは、マクロで設定された変数は呼び出し元のスコープに漏れることを意味します。そのため、マクロは非常に小さな機能だけを定義するのに適しています。

CMakeの関数とマクロのもう1つの違いは、引数の渡し方です。マクロへの引数は変数として設定されません。代わりに、パラメータへの逆参照は、マクロを実行する前にマクロ全体で解決されます。これにより、未参照の変数を使用すると、予期しない動作が発生する可能性があります。たとえば

macro(print_list my_list)
  foreach(var IN LISTS my_list)
    message("${var}")
  endforeach()
endmacro()

set(my_list a b c d)
set(my_list_of_numbers 1 2 3 4)
print_list(my_list_of_numbers)
# prints:
# a
# b
# c
# d

一般的に言えば、この問題は親スコープで重複する名前を持つ非参照変数を使用する必要があるため、まれですが、微妙なバグにつながる可能性があるため、注意が必要です。

LLVMプロジェクトのラッパー

LLVMプロジェクトは、重要なCMake組み込みコマンドの周りに多くのラッパーを提供しています。これらのラッパーを使用して、LLVMコンポーネント全体で一貫した動作を提供し、コードの重複を減らしています。

一般的に(常にではありませんが)、llvm_で始まるコマンドは、他のコマンドの構成要素としてのみ使用することを意図しているという慣習に従っています。直接使用を意図したラッパーコマンドは、通常、コマンド名の中央にプロジェクト名が続くように命名されています(つまり、add_llvm_executableadd_executableのラッパーです)。LLVMのadd_*ラッパー関数はすべて、LLVMディストリビューションの一部としてインストールされるAddLLVM.cmakeで定義されています。これは、LLVMを必要とする任意のLLVMサブプロジェクトによって含められ、使用できます。

注意

すべてのLLVMプロジェクトがすべてのユースケースでLLVMを必要とするわけではありません。たとえば、コンパイラrtはLLVMなしでビルドでき、コンパイラrtサニタイザーライブラリはGCCで使用されます。

便利な組み込みコマンド

CMakeには便利な組み込みコマンドがたくさんあります。CMakeプロジェクトには優れたドキュメントがあるため、このドキュメントでは詳細には触れません。いくつかの便利な関数を強調するために、以下を参照してください。

CMakeコマンドの完全なドキュメントは、cmake-commandsのマニュアルページにあり、CMakeのWebサイトで入手できます。