こんにちは。田原です。
C++は大変多くのプラットフォームで使える高生産性な開発ツールです。しかし、マルチ・プラットフォーム対応のビルド・ツールは多くはないため、C++を使っても意外にマルチ・プラットフォームなプログラムの開発には苦労します。その苦労をかなり軽減してくれるツールがCMakeです。そこで、今回からCMake編としてCMakeの使い方を解説していきたいと思います。
なお、今回は、説明の都合上、以前解説したCMakeの基礎とTheolizerの組み込み方から抽出したので前半はほとんど重複してます。ごめんなさい。
1.CMakeの基本
CMake編のインデックス・ページで基本的な説明をしていますので、もしまだご覧になっていない方は是非ざっと目を通して下さい。
ビルドについて
ビルドの基本についてCMakeの基礎とTheolizerの組み込み方の「1.予備知識」にて、私なりに説明しています。興味のある方は参考にされて下さい。
1-1.CMakeのダウンロードとインストール方法
当技術ブログの「実践C++入門講座」にて解説していますので、以下を参照下さい。
1-2.一番簡単なCMakeLists.txt
CMakeはCMakeLists.txtを解釈してビルド・システム(いわゆる Makefile のことです)を自動生成します。そして、最も簡単なCMakeLists.txtは本当に簡単です。
add_executable()文にて、コンパイルするソース・ファイルの名前と生成する実行ファイルの名前を指定するだけです。
add_executable(main main.cpp)
add_executable()は実行ファイルを生成するよう指示します。
最初のmainが生成する実行ファイル名です。拡張子はOSによって異なるのでここには記述しません。
その次のmain.cppがコンパイルするソース・ファイル名です。複数記述できます。
早速hello worldを作ってみましょう。
#include <iostream> int main() { std::cout << "Hello, World!!\n"; }
main.cppとCMakeLists.txtを同じフォルダに保存して下さい。
次にMakefileやプロジェクト・ファイルを生成しますが、ソースと入り交じるとソース・ファイルの管理が面倒な事になりますので、ビルド専用のフォルダを用意することを強くお薦めします。
ここでは、お手軽にmain.cppとCMakeLists.txtを保存しているフォルダの下にビルド専用フォルダを作ってみます。
main.cppのあるフォルダがカレント・フォルダの時、次のコマンドでgcc用のMakefileやVisual C++用のプロジェクト・ファイルを生成できます。
mkdir build cd build cmake ..
linuxでもWindowsのVisual C++でも同じコマンドでできてしまいます。ちょっとびっくりです。
さて、実はcmake ..
は手抜きコマンドです。本来はどのコンパイラ向けに生成するのかを-G
オプション(ジェネレータ)を指定します。
cmake -G
で使用可能なジェネレータのリストが出力されます。
例えば、Visual Studio 2015で64ビット・プログラム用のプロジェクトを生成する時は、次のコマンドです。
cmake -G "Visual Studio 15 2015 Win64" ..
1-3.project文
プロジェクト名を決定するproject文があります。
Visual Studioではソリューション名になります。Visual Studioの場合は次のように対応するようです。
CMake | Visual C++ |
---|---|
project | ソリューション |
add_executable add_library add_custom_target |
プロジェクト |
例えば、以下のように「プロジェクト名(Visual Studioのソリューション名になります)」を指定します。
project(example) add_executable(main main.cpp)
project文の必要性は実は良く分かっていません。使用するプログラミング言語やアプリのバージョンを記述できます。私自身は主に Visual Studio のソリューション名を指定するために使っています。
なお、上記のMakefile生成処理の際に「お試しコンパイル」が走っているのにお気づきかと思いますが、project文があるとproject文のところで「お試しコンパイル」されています。project文がない時は CMakeLists.txt 処理の先頭で「お試しコンパイル」が行われるようです。
また、project文で各種のCMAKE変数が設定されます。project文がなかったらCMakeLists.txtの先頭でそれらが行われるようです。(ただそれだけの話ですが。)
1-4.コンパイル・オプションを指定する
デフォルトでは警告はあまりでません。ですが、できるだけ警告は有効にした方が無駄なデバッグに苦労しなくて済みます。
例えば、次の6行目はinと20を比較しようとして代入してます。結構やりがちなバグです。
警告オプションを指定しない場合、警告はでません。
#include <iostream> int main() { int in; std::cin >> in; if (in = 20) { std::cout << "OK\n"; } }
できるだけ警告を出すようにコンパイル・オプションを指定してみます。
project(example) add_executable(main main.cpp) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
project(example) add_executable(main main.cpp) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
Visual C++もgccも警告が出るようになりました。
下手に悩まずとも警告を潰しているだけでそこそこデバッグできます。
もちろん警告を潰すだけではデバッグは完了しませんが、警告があるとかなりデバッグ作業が捗ります。
1-5.マルチ・プラットフォームに対応する
前節ではVisual Studio用とgcc用のCMakeLists.txtを分けていました。
しかし、大半は同じものですので、両方に対応するCMakeLists.txtを1つ提供できれば便利です。
前節のようなケースでは、Visual Studioかgccかを判別してCMAKE_CXX_FLAGSの設定を切り替えることができるとうまくいきます。
まず、CMakeはC++の#if/#else/#endifのような構文があります。次のように書きます。
if(条件) elseif(条件) else() endif()
elseやendifにも()が必要なことと、#が不要な点が異なります。
CMakeの#
はコメント行です。私は時々#ifや#endifと書いてしまってCMakeエラーに見舞われます。皆さんもご注意下さい。
なお、()を忘れるとエラーになります。
また、elseif()とelse()はオプションです。不要な時は省略して良いです。
そして、ここに条件に指定できる変数のリストがあります。
例えば、Visual C++(MSVC)とその他でCMAKE_CXX_FLAGSの設定を切り替える場合、次のように書きます。
project(example) add_executable(main main.cpp) if (MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") else() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") endif()
2.スクリプト・モード
いきなり第一回目にスクリプト・モードを説明する CMake の解説は珍しいと思います。しかし、先に説明した方が理解しやすいのではないかと考えています。(ちょっとドキドキですが。)また、スクリプト・モードは要するにインタプリタのようなものですから、走らせて結果が分かるのが早いです。お手軽にお試しできるので学習効率が上がります。なので、最初から解説してみます。
さて、CMakeは実は広い意味でのプログラミング言語です。Windowsのコマンド・プロンプトやlinuxのbashなどとよく似た機能を持っています。ビルド・システム生成に特化しているので一般的な処理はコマンド・プロンプトやbash程は書きやすくは無いですが、機能的にはこれらとほぼ同等です。
更にconfigure_fileのようなテキスト・ファイル生成機能も内蔵しているため、バージョン番号の反映等でも威力を発揮します。
ビルド・システムを生成する専用の一部のコマンド(project文やadd_excute文など)は、スクリプト・モードでは使えません。逆に、ビルド・システムを生成する際には全てのコマンドを使うことができます。
つまり、コマンドは大きく2種類に分かれています。(短縮のため、ビルド・システム=Makefileと記載しています。)
コマンドの種別 | Makefile生成 | スクリプト・モード | 効果タイミング |
---|---|---|---|
①Makefile生成専用コマンド | 使用可 | 使用不可 | Makefile生成時~ビルド時 |
②スクリプト・コマンド | 使用可 | 使用可 | Makefile生成時と スクリプト・モード時 |
①は主に-G
オプション(もしくは-P
オプション無し)で起動します。②は-P
オプションで起動します。
さて、CMakeの理解を難しくしている要因の1つに、①のコマンドと②のコマンドに一見よく似た物があることです。(特に、add_custom_commandとexecute_process)
しかし、上記のように効果を及ぼすタイミングが決定的に異なりますので、両者は互いに相手と交換できません。
ですので、それぞれについて常に意識するよう務めると理解が早くなると思います。
2-1.スクリプト・ファイル
ビルド・システム生成は、CMakeLists.txtというファイルに設定します。(1つのフォルダに1つしか存在する必要がないため、このファイル名は変更できません。)
スクリプト・モード用のスクリプト・ファイルは1つのフォルダの配下に複数用意することがあるので名前を自由に付けることができます。拡張子は.cmake
に決っているようです。(他の拡張子を付けても呼び出せますが、慣習に従って.cmakeにしておいたほうが良いと思います。)
2-2.スクリプト・モードの起動方法
基本は以下の通りです。(スクリプト・ファイルがあるフォルダをカレント・フォルダとします。)
$ cmake -P スクリプト・ファイル名
パラメータを渡すことができます。CMake変数をコマンドラインで定義するイメージになります。
$ cmake -D変数名=変数値 -D変数名=変数値 ・・・ -P スクリプト・ファイル名
-Pオプションより後ろに書いてもスクリプト内に渡りません。エラーにもならないのでご注意下さい。
つまり、以下のように書いても各変数は定義されません。
$ cmake -P スクリプト・ファイル名 -D変数名=変数値 -D変数名=変数値 ・・・
2-3.コマンドの基本
C++のif文のような文は、CMakeではコマンドと呼ばれています。
上記の公式のリファレンス・ページでコマンドは下記の4つに分類されています。
- Scripting Commands
上述の「②スクリプト・コマンド」です。ビルド・システム生成時とスクリプト・モード時に使えます。 - Project Commands
上述の「①Makefile生成専用コマンド」です。ビルド・システム生成時のみ使えます。 - CTest Commands
CMakeにはCTestというテスト・ツールがあります。そのテスト・モード時のみ使えるコマンドです。
CTest自体は使っているのですが、これらの専用コマンドは使ったことがありません。よく判ってないので当面当講座では解説しません。ごめんなさい。 - Deprecated Commands
いつかなくなるものですので使わない方が良いコマンド群です。当講座でも解説しない予定です。
2-3-1.messageコマンド
スクリプト・コマンドの1つにmessageコマンドがあります。
かなり柔軟な書き方ができるのですが、まずは以下のような使い方で覚えるのが良いと思います。
message("メッセージ")
他に、次のような書き方もよく使います。
message(STATUS "メッセージ") message(SEND_ERROR "エラー・メッセージ")
後者は、エラー発生場所とエラー・メッセージを表示します。
なお、文字列は、原則として””で括ることをお勧めします。必ずしも括らない方が良い時もありますが、変数の様々な振る舞いを学んでから必要に応じて使った方が良いと思います。最初の頃は文字列は括っておくと思わぬ振る舞い(間にスペースが入った時など)が減るので安全なのです。
2-3-2.コマンド名
コマンドは大文字小文字を区別しません。例えば、以下は全く同じ動作をします。
message("メッセージ") MESSAGE("メッセージ")
以下のように記述してもOKでした。
Message("メッセージ") meSSage("メッセージ")
後者は言うまでもなくやめておくべきですね。
前者もあまり見かけない記述ですので避けておいた方が良いかもしれません。全部小文字、もしくは、全部大文字のどちらかをよく見かけます。
2-3-3.パラメータ
CMakeのコマンドはmessageコマンドのように複数の可変個のパラメータを受け取るものが多いです。
パラメータは空白で区切ります。””で括られたパラメータは””の文字列の間に空白が有っても空白を含めて1つのパラメータとして解釈されます。
message("文字列1" "文字列2") message("文字列1 文字列2")
2-3-4.複数行に渡るコマンド
1つのコマンドは1行で書くことも多いですが、コマンドによってはとても収まらないので複数行に分けて書くこともあります。
コマンドは、コマンド名(パラメータ群)
で1つですから、最後の)
までを複数行に分けることができます。
なお、コマンド名内の改行は当然NGですが、コマンド名と(
の間の改行もNGです。(空白はOK)
message ( "1行目" "2行目" "3行目" )
>cmake -P sample.cmake 1行目2行目3行目
2-4.変数の設定と参照方法の基本
CMakeはユーザ定義変数、事前定義定数、生成時自動定義変数など、さまざまな「変数」があります。
定数は設定や変更ができないだけで参照方法は変数と同じです。
設定はsetコマンドやCMake -Pで起動する際の-Dオプションを使います。${変数名}
で変数の中身を取り出(参照)します。
set(WORLD "World!") message(${HELLO} " " ${WORLD}) message("${HELLO} ${WORLD}")
>cmake -DHELLO=Hello -P sample.cmake Hello World! Hello World!
C++に慣れていると前者のmessageコマンドのような書き方をついしてしまうと思いますが、後者のような書き方もできます。C++のような言語との大きな違いは””の中にある変数参照も展開されることです。
この振る舞いはコマンド・プロンプトやbashと同様です。CMakeはスクリプト言語ということですね。
3.まとめ
今回はCMakeの触りの部分を解説しました。CMakeは一種のプログラミング言語ですが、モードが2つあります。1つはビルド・システム(=Makefile)生成モード、もう一つはスクリプト・モードです。
前者はCMakeの最大の機能であり様々なプラットフォームでメジャーなビルド・ツール用の設定ファイル(Makefile等)を自動生成します。
後者はWindowsのコマンド・プロンプトやlinuxのbashのようなものです。
これら2つを組み合わせることで非常に強力、かつ、マルチ・プラットフォームなビルド・システムを提供することが可能になります。
その分これらの相違を意識して理解しないと結構混乱しますので、ドキドキしつつ最初の解説に含めてみました。
さて、次回はよく使うコマンドについて解説する方向で考えています。少し間があくと思いますが、よろしくご期待のほどお願いします!
Pingback: CMake – Site-Builder.wiki