[備忘録]JUCEをCMakeとVSCodeで動かしたい〜CMake基礎編〜

タイトルの通り、C++のフレームワークであるJUCEをVSCodeで動かしたく奔走したのでそのまとめです。この記事はVSCodeを使ってCMakeでビルドするまでのまとめです。

用語整理

ソースファイル

  • 関数の中身を書くファイル(.cとか.cppとか)

  • 一つには必ずmain()が含まれる

ヘッダファイル

  • 関数の定義を書くファイル(.hとか.hppとか)

  • ソースファイルにincludeされて使用される

オブジェクトファイル

  • バイナリで書かれたファイル

プログラムファイル(実行ファイル)

  • ビルドされた後に出力されるファイル

コンパイル

  • ソースファイル、ヘッダファイルをオブジェクトファイルに変換する

  • この際includeされたヘッダや、使用している他のソースファイルのメモリ番地は不明なままである

リンク

  • オブジェクトファイルを1つにまとめてプログラムファイルを作成する

  • 各オブジェクトファイル中の不明なアドレス番地をメモリ番地に置き換える

  • リンクするソフトウェアをリンカと呼ぶ

ダイナミックリンク

  • 通常はコンパイル・リンクがビルドのタイミングで実行される

  • ビルド時でなく実行時にオブジェクトファイルをリンクし、実行ファイルを生成すること

  • 開発時には呼び出し側のソース・ヘッダのみのオブジェクトファイルを、実行時には呼び出されるオブジェクトファイルの番地をリンクする

  • 呼び出されるオブジェクトファイル群をライブラリと呼ぶ

  • 基本的にはOSが仲介して番地をリンカに渡す

  • VSTなどではこの手法が取られる(.dylibファイルとか)

ビルドの流れ

ソースやヘッダが複数になってくると、実行ファイルを作るまでの過程が複雑化する。複数のソースを実行ファイルに変換するツールが作られた。

make

  • コンパイル、リンクを自動で行ってくれるツール

  • Makefileにそれぞれの依存関係を書き、makeを実行することでプログラムファイルを生成する

  • 独自の文法・コマンドがあり、面倒臭い

CMake

  • Makefileを作り、makeを実行してくれるツール

IDE(統合開発環境)

  • Makefileを生成して実行ファイルを生成してくれる(ビルド)機能のあるエディタ

  • XCodeとかVSCodeとか

gccのオプション記法まとめ

  • -c:オブジェクトファイル生成。以下の記述でmain.oというオブジェクトファイルを作れる。

$ g++ -c main.cpp
  • -o:オブジェクトファイルを使用して、ライブラリや実行ファイルを生成

  • -L:ライブラリのパスの指定。libsample.soのような共有ライブラリ名だった場合、-lsampleのようにlibは省略可能。

  • -I:インクルードパスの指定

ビルドの基礎[gcc vs CMake]

1. 単一ディレクトリの場合

ソースとヘッダが以下のように配置されているディレクトリでの場合を考える。

---/
|-main.cpp
|-greetings.hpp
|-greetings.cpp

各ファイルの内容は以下の通り。

g++でのビルド

コマンドラインを使用してのビルドは以下のようになる。なおここではg++を使用してビルドを行う。

$ g++ main.cpp greetings.cpp

すると、ディレクトリ内にa.outという実行ファイルが生成される。(このビルドの仕方だとオブジェクトファイルは生成されない)

$ ./a.out
Hello, world!
Goodbye, world!

実行ファイルを実行し、正しい出力が得られているのがわかる。

CMakeでのビルド

CMakeList.txtというテキストファイルにビルドの設定を記述する。なおこのファイルはソースと同じディレクトリに入れておく。

このときgreetings.hppは指定せずともCMakeが自動的にリンクしてくれる。
以下のコマンドでビルド。

$ cmake -S . -B build
$ cmake --build build

-Sでソースの場所を指定。 一行目で、現在のディレクトリ配下のCMakeLists.txtを元に、ビルド用設定ファイルを出力。この出力先は-Bオプションで指定でき、上記の手法だとディレクトリ配下に*/buildというディレクトリを生成し、その配下に出力される。ビルド用ファイルがソースディレクトリと分けられる手法はout-of-sourceビルドと呼ばれており、元の状態に戻すのが楽。
 二行目で実行ファイルを生成する。ビルドのシステムに依存せずに実行できる。

ビルド完了後のディレクトリは以下のようになっているはず。

---/
|-CMakeLists.txt
|-main.cpp
|-greetings.cpp
|-greetings.hpp
|-bulid/
   |-a.out
   |...(省略)

2. 複数ディレクトリの場合

複数のソースファイルからライブラリを作成しており、ダイナミックリンクをして実行ファイルを生成したい場合を想定。以下のようなディレクトリ構造だとする。

---/
|-include/
|  |-greetings.hpp
|  |-number.hpp
|
|-src/
|  |-greetings.cpp
|  |-number.cpp
|
|-main/
   |-main.cpp

number.hpp、number.cppの中身は以下の通り。

g++でのビルド

$ cd src
$ g++ -c greetings.cpp number.cpp -I../include
$ g++ -shared -install_name @rpath/libsample.dylib *.o -o libsample.dylib

srcディレクトリに移動し、greetings.cppとnumber.cppからオブジェクトファイルを生成。この際に-Iのオプションでincludeのパスを指定。三行目でオブジェクトファイルをまとめて共有ライブラリ(-sharedで指定)libsample.dylibを生成。この際に-install_nameオプションでライブラリのinstall nameを指定しないと、プログラムファイル実行時にライブラリのダイナミックリンクができずリンクエラーとなるので注意。

$ cd ../main
$ g++ main.cpp -I../include -L../src -lsample -Wl,-rpath,../src

mainディレクトリに移動し、ライブラリとmain.cppをリンクして実行ファイルを生成。-Iでのインクルードパスの指定に加えて、-Lでの共有ライブラリのパスの指定も必要。なおライブラリ自体はlibsample.dylibというファイル名だが、-lsampleと省略可能。「-Wl,-rpath,パスの名前」でrpathを指定。実行時にライブラリリンクのために参照されるパスを記述する。

これで/main/a.outが生成され、ディレクトリ構造は以下のようになるはずである。

---/
|-include/
|  |-greetings.hpp
|  |-number.hpp
|
|-src/
|  |-greetings.cpp
|  |-greetings.o
|  |-number.cpp
|  |-number.o
|  |-libsample.dylib
|
|-main/
   |-main.cpp
   |-a.out

ここまでわかるようにオプションの指定が面倒臭く、ライブラリの数が増えると大変なことになる。

CMakeでのビルド

各ディレクトリにCMakeLists.txtを追加する。

---/
|-include/
|  |-greetings.hpp
|  |-number.hpp
|
|-src/
|  |-greetings.cpp
|  |-number.cpp
|  |-CMakeLists.txt
|
|-main/
|  |-main.cpp
|  |-CMakeLists.txt
|
|-CMakeLists.txt

---/CMakeLists.txtには以下のように記述し、サブディレクトリの指定をする。

---/src/CMakeLists.txtでは以下のように記述し、ライブラリの設定を行う。SHAREDで共有ライブラリ(ダイナミックリンク)となり、STATICで静的ライブラリとなる。

この際に、以下のようにすることでビルドのコマンド入力時にライブラリを共有にするか選択できるようになる。

「target_include_directories」ではライブラリのインクルードパスを指定する。ここではPUBLICとなっているが「PUBLIC・PRIVATE・INTERFACE」の3種類がある。ターゲット(最初の変数、ここではlibsample)に対してどの程度の依存度でインクルードが必要かに応じて使い分ける。

  • PUBLIC:ターゲット(libsample)とターゲットに依存するターゲット(a.out)で必要。

  • PRIVATE:ターゲットのみで必要。

  • INTERFACE:ターゲットに依存するターゲットのみで必要。

---/main/CMakeLists.txtは以下のようになる。ここでは実行時にリンクするライブラリを指定する。

以上のようにCMakeLists.txtを作成したら。cmakeを実行してビルドをする。g++と比べると圧倒的にコマンドが短くて楽なのがわかる。

$ cmake -S . -B build
$ cmake --build build

ビルド後のディレクトリは以下のようになる。

---/
|-build/
|  |-main/
|  |  |-a.out
|  |  |...(省略)
|  |
|  |-src/
|  |  |-libsample.dylib
|  |  |...(省略)
|  |
|  |...(省略)
|
|-include/
|  |-greetings.hpp
|  |-number.hpp
|
|-src/
|  |-greetings.cpp
|  |-number.cpp
|  |-CMakeLists.txt
|
|-main/
|  |-main.cpp
|  |-CMakeLists.txt
|
|-CMakeLists.txt

ディレクトリ構造もg++と比べると非常にすっきりしており、CMakeのメリットがよくわかる。

VSCodeでのCMake

 さてここまでは、CMakeの基本をおさらいした形になる。VSCodeにはCMake Toolsという拡張機能が用意されており、強力な開発環境として使用できる。

拡張機能を入れたら、コマンドパレットから「CMake Quick Start」を選択。プロジェクト名とコンパイラを決め、ライブラリを作るか実行ファイルを作るかを選択。するとmain.cppとCMakeLists.txtが生成される。

この記事が気に入ったらサポートをしてみませんか?