お仕事ではVisualStudio(*.sln)でMSVC(C/C++)の開発環境を構築しているのだけれども、他言語の開発環境はぜんぶVSCodeに統一してるからMSVCのビルド関係も揃えたいのよねー。しかしVSCodeを使ってVisualStudio同等の事をやるのって、それはそれで大変そう。要は、いままでGUIで設定してた某を事細かにMakefile的な奴へゴリゴリ書かなきゃいけないんでしょ?おじさん、ゆとりすぎてMakeよく分からないんよね。
そんなこんなでGoogle先生で色々調べてたら、CMakeとかいうツールが割とオススメらしい。「このコンパイラとかリンカ使いたいぜ」とか「ビルド対象はこのファイルだぜ」とか「実行ファイルとかライブラリとか作りたんだぜ」等のフワッとした願いを設定ファイルに書き込んでおけば、CMakeがそれを読み込んでステキなビルド環境を作ってくれるらしい。(フワッとした理解力)
まあ、百聞は実践に如かずってことで、実際にCMake使って構築してみることにした。CMakeのダウンロードはこちら。
CMakeLists.txtをつくる
root └src ├fuga │└unyora.cpp/h ├hoge │└toppiroky.cpp/h └main.cpp
unyora.cppとかtoppiroky.cppは、文字列を返すだけの処理を持っていて、それをmain.cppで呼び出す。ハロワのちょっとめんどい版みたいな感じ。全体をexeでビルドすると芸が無いので、「fuga」フォルダ以下を「fuga.lib」、「hoge」フォルダ以下を「hoge.lib」てな静的ライブラリで作って、そいつらをmain.cppにリンクして「nyapon.exe」を作ってみたい。
まず、CMakeのルールとして、ビルド対象のフォルダ以下に「CMakeLists.txt」とかいう設定ファイルを置けばいいらしい。
root ├CMakeLists.txt └src ├fuga │├CMakeLists.txt │└unyora.cpp/h ├hoge │├CMakeLists.txt │└toppiroky.cpp/h ├CMakeLists.txt └main.cpp
なので、こんな置き方になる。まず、各フォルダで何を生成するのかって話は後回しにして、ルート直下にあるCMakeLists.txtはこんな感じで書いておいた。
# CMakeバージョン指定 cmake_minimum_required(VERSION 3.16) # プロジェクトの名前と種別 project(cmake_test CXX) # ビルド対象のファイルが置かれてるディレクトリを追加 add_subdirectory(src/fuga) # fuga.lib を生成 add_subdirectory(src/hoge) # hoge.lib を生成 add_subdirectory(src) # nyapon.exe を生成
cmake_minimum_required() は、自分のダウンロードしてきたCMakeのバージョンに合わせて設定するっぽい。俺が落としてきたときのバージョンは3.16だったので、そのとおりに書いておいた。
んで、project()でプロジェクト名とC言語なりC++なりの種別を設定してやる。仮に「*.c」が混ざってるようなプロジェクトだったら、下記のような書き方すればいいみたい。
project(cmake_test C CXX)
そいであとは、add_subdirectory()を記述しておいてやれば、各サブディレクトリに置いてあるCMakeLists.txtにぶん投げられる。「他のフォルダにCMakeLists.txt置きたくないよ!」ってな場合は頑張ってひとつのCMakeLists.txtに記述を集約できなくもないのだけれど、結果として見通しが悪くなったのでサブディレクトリに分けたほうが素直だと感じた。
# fuga.lib を作りたい add_library(fuga STATIC unyora.cpp)
fugaフォルダ直下のCMakeLists.txt はこれだけ書けばおわり。add_library(ライブラリ名 STATIC ビルド対象のソースコード)で静的ライブラリが生成できるのだが、STATICの部分をSHAREDにすればdllができたりするみたい(試してないけど)。hogeフォルダ以下のCMakeListsもだいたい同じ感じの指定をして、hoge.libを作るようにしておく。
# nyapon.exe を作りたい add_executable(nyapon main.cpp) # include対象のファイルが置いてあるフォルダ include_directories(fuga hoge) # hoge.lib と fuga.lib をリンクしたい target_link_libraries(nyapon hoge) target_link_libraries(nyapon fuga)
そして、main.cppと同階層のCMakeLists.txtはこう書いた。add_executable()で出力するexe名を指定したあと、ビルドターゲットのソースコードを指定。今回はmain.cppだけだが、複数ある場合は半角スペースで区切って羅列していくみたい。
include_directories()は、プロジェクトの設定でよくあるincludeパスの設定的なやつ。今回は自作ライブラリのヘッダを読み込みたいので、各ライブラリのディレクトリを参照先に指定してる。そして最後に、target_link_libraries()を使ってnyaponに対して各ライブラリをリンク。以上だ!
#include#include "unyora.h" #include "toppiroky.h" using namespace std; void main(){ unyora a; toppiroky b; cout << a.disp() << " " << b.disp() << endl; }
main.cppの中身はこんなの。include_directories指定のおかげで「fuga/unyora.h」と「hoge/toppiroky.h」は問題なく読み込むことができる。
ソリューションを吐き出す
で、MSVCのビルド環境というかslnを吐き出すためにはどうすればいいのん?といったところで調べると、どこのサイトを眺めてもだいたい下記の流れでやってるみたい。
mkdir build cd build cmake ..
「cmake (CMakeLists.txtが置いてあるフォルダ)」と打ち込めばビルド環境の生成が始まるのだけれども、愚直に「cmake .」ってやっちゃうと、root以下に雑多なファイルができちゃうのでgitで管理するとき大変。なので、gitignoreでビルド用フォルダを管理対象外にしといた上で、その中に生成物をぶちこんでやればソース管理と分離できて良きって話。それをアウトオブプレースだとか、アウトオブソースとか言うらしいぞ。よくわからんけどかっこいい!
root ├CMakeLists.txt ├build ←このフォルダ内にslnができる └src
なので、ソースツリー的にはこんな形になる。
ちなみに複数バージョンのVisualStudio積んでたらどのバージョンが選択されるかよく分からんので、cmakeコマンド打つときにジェネレータ(-Gオプション)で対象のVIsualStudioバージョンを指定しておけば安心できるかも。コマンドラインで「cmake --help」すれば、ジェネレータの一覧が出てくる。今回はVS2019を使いたいので、下記のように指定してやった。
cmake .. -G"Visual Studio 16 2019"
ビルドをかます
これでslnは出来上がったから、あとはこれをビルドすればええんやね!でもVisualStudio立ち上げてGUIでビルドするのは本末転倒だし、msbuildにsln流し込むのもめんどくさいなぁ。そこらへんも自動でやってくんねーかなー。と思ってたら、どうやらCMakeには「ビルドツールモード」なるやつがあるらしく、「--build」オプションを付けるとビルドまでやってくれる。すっごーい!
cmake --build . --config Debug --clean-first --target ALL_BUILD
クリーンビルドしたいときは--clean-firstつけると良さげ。もちろん差分ビルドだったらナシで。cmakeが吐き出したALL_BUILD.slnをビルドしたら良さそうなので、--targetにそいつを指定してやるとうまくいった。あとついでに「--config release」とかつけてやると、ビルドモードの指定までできちゃう。msbuildのコマンド直打ちするよりも覚えやすいわねぇ。
>nyapon.exe unyora toppiroky
できあがったものをコマンドライン上で実行したら、ちゃんと動作してる模様。めでたしめでたし。
とりあえず最低限やりたい事はできたっぽいので、今回ためした某はGitHubにうpしといた。tasks.jsonにVSCodeのタスクをぶちこんであるので、そいつを叩けばビルド/リビルドが走る。
まだVSCodeの拡張まだあんまり深く触れてないからアレだけど、「CMake」拡張を入れるとCMakeLists.txtを弄る時にインテリセンスが効くので超オヌヌメ。
会社の開発環境に適用してみてもひとまず動いたのだけれど、まだ肝心のユニットテストが動かせてないので、次回はそこらへんにチャレンジかねー。
おまけ
# こっから下の全ファイルをビルド対象に加える file(GLOB_RECURSE SOURCES "*.c" "*.cpp") # fuga.lib を作りたい add_library(fuga STATIC ${SOURCES})
add_libraryとかadd_executableするとき、わざわざビルド対象のソースコード羅列するのめんどいしやってらんないっすわーと思ってたのだが、file()を使えば任意の変数にソースファイルの一覧ぶちこめる素敵仕様になってた。うーん、CMakeしゅっごい。