最終更新:2026-10-12
Meson で C++ プロジェクトをビルド¶
最近いくつかのオープンソースプロジェクトを利用する中で、CMake や Makefile の方式を捨てて meson+ninja のビルド方式を採用しているものが多いことに気づきました。本稿では Meson を紹介し、入門インストールから完全な meson プロジェクトの作成までを説明します。
それでは、本題に入りましょう。
1.Meson のインストール¶
Meson は PyPi から入手できるため、pip3 install meson でインストールできます。異なるシステムでは正しい pip コマンドを使用し、必ず Python 3 版の pip を使ってください。
必要であれば、標準の Python コマンドでローカルにインストールすることもできます:
Ninja を使ってビルドする場合は、Ninja を Ninja の GitHub リリースページから直接、または PyPi 経由でダウンロードできます:
Meson build のインストールについての詳細は、以下の Meson 公式サイトをご覧ください。
2.Meson Vs Cmake¶
皆が CMake を使っているのに、なぜ Meson を使う必要があるのでしょうか?それぞれの優劣は何でしょうか?
パフォーマンスの面では、Meson は一般的に高速なビルド速度で知られています。Meson の設計はビルド速度の最適化を目的としており、特にインクリメンタルビルドで優れた性能を発揮します。Meson は Ninja ビルドシステムをデフォルトのバックエンドとして使用しており、Ninja 自体も高速かつ効率的なことで知られています。
一方、CMake のビルド速度は、特に大きなプロジェクトではプロジェクト規模の影響を受ける可能性があります。CMake はさまざまなジェネレータ(Makefile など)を使用しますが、ジェネレータごとに性能も異なる場合があります。
もちろん、CMake + Ninja を使ってコンパイル速度を上げることもできます。
オープンソースプロジェクトの観点から Meson を見ると、その使用シーンも非常に多く、例えば:PostgreSQL、QEMU、GNOME Software、GTK+、などなど
2.1 Meson の優位性と劣勢¶
1.優位点
- Python ベースで、使い方が簡単
- クロスコンパイルをサポート
- すべてのオブジェクトが強く型付けされている:オブジェクトは 'dependency'、'include directory' などの実体であるため、文字列置換のエラーが起きにくい
- サードパーティライブラリの参照が非常に便利
- 非常識なほど速い、特にインクリメンタルビルド時には。
- ドキュメントは cmake より n 倍良いです。
2.劣勢
- CMake ほど成熟していない可能性がある
- Python3 + Ninja への依存がある
- プロジェクトジェネレータでは、Meson への IDE サポートが CMake に劣る
- vscode サポート
- https://github.com/mesonbuild/vscode-meson
- 利用可能なモジュールが限られている
- 非 meson プロジェクトは自分で wrapdb に提出する必要がある
- https://mesonbuild.com/Wrapdb-projects.html
2.2 CMake の優位性と劣勢¶
1.優位点
- IDE サポートが充実しており、チームでの共同作業が比較的行いやすい
- 成熟しており、ほぼ C++ プロジェクトの標準となっている
2.欠点
- クロスコンパイルが難しい
- コンパイル速度が比較的遅い
3.Meson WrapDB¶
googletest は meson プロジェクトではないため、meson でそれに依存するにはどうすればよいのでしょうか?
同様に、grpc、abseil-cpp の使用方法も似ています。
ここでまず WrapDB を導入する必要があります。Meson WrapDB は Meson ビルドシステムのコンポーネントで、サードパーティプロジェクトのビルド記述ファイル、いわゆる "wrap" ファイルを管理・提供します。これらの wrap ファイルには、サードパーティプロジェクトを設定・ビルドするために必要な情報が含まれており、Meson を使う他のプロジェクトに簡単に統合できるようになります。
search コマンドまたは以下のサイトでサードパーティライブラリを検索できます。
https://mesonbuild.com/Wrapdb-projects.html#meson-wrapdb-packages
ここでは gtest の使用を例にとります:
- gtest を検索
このコマンドがラップされたプロジェクト名を返したら、次のステップに進めます:
- 自分のプロジェクトに subprojects ディレクトリを作成し、install で gtest をインストールします
このとき、プロジェクトが依存するサードパーティライブラリはすべて subprojects ディレクトリにインストール済みになっています。
このとき gtest.wrap が生成され、その中には次の内容が含まれます。
注意:このファイルは上記の install 実行後に生成されるファイルなので、変更しないことをお勧めします。
[wrap-file]
directory = googletest-1.14.0
source_url = https://github.com/google/googletest/archive/refs/tags/v1.14.0.tar.gz
source_filename = gtest-1.14.0.tar.gz
source_hash = 8ad598c73ad796e0d8280b082cebd82a630d73e73cd3c70057938a6501bba5d7
patch_filename = gtest_1.14.0-1_patch.zip
patch_url = https://wrapdb.mesonbuild.com/v2/gtest_1.14.0-1/get_patch
patch_hash = 2e693c7d3f9370a7aa6dac802bada0874d3198ad4cfdf75647b818f691182b50
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/gtest_1.14.0-1/gtest-1.14.0.tar.gz
wrapdb_version = 1.14.0-1
[provide]
gtest = gtest_dep
gtest_main = gtest_main_dep
gmock = gmock_dep
gmock_main = gmock_main_dep
4.meson プロジェクトの書き方?¶
次は実践のセクションです。meson プロジェクトはどのように書けばよいのでしょうか?
以下では a/b の除算テストを例に、gtest を使ってユニットテストを行う小さなプロジェクトを紹介します。
4.1 コードを書く¶
.h と .cc は src ディレクトリに置きます:
その後、同階層に test ディレクトリを作成します。
4.2 meson プロジェクトの管理¶
プロジェクトのルートパスに meson.build ファイルを追加します。内容は次の通りです:
project('gtest_div', 'cpp')
gtest_proj = subproject('gtest')
gtest_dep = gtest_proj.get_variable('gtest_main_dep')
div_src = files('src/division.cc')
tests_src = files('test/division_test.cc')
e = executable(
'division_test',
tests_src + div_src,
dependencies : [ gtest_dep],
override_options : ['cpp_std=c++14']
)
# https://mesonbuild.com/Unit-tests.html#testing-tool
test('this is a division test', e)
この内容は python の構文とほぼ同じであることがわかります。python に慣れていれば、このコードは非常に読みやすいものです。ここでは、上の重要な部分について重点的に説明します。
- サードパーティライブラリの導入
例えば:導入するサードパーティライブラリのプロジェクトが gtest という名前であれば、次のようになります:
すると、subproject を通じてこの project を取得でき、さらに gtest_proj.get_variable によって gtest の依存情報を取得できます。例えば、googletest の中では次のように宣言されています:
gtest_main_dep = declare_dependency(
include_directories : gtest_incdir,
sources : [gtest_libsources, gtest_mainsources],
dependencies : dependency('threads')
)
- executable
executable は cmake の add_executable と非常によく似ており、実行ファイル division_test を生成します。
- test
ユニットテストの構文:
meson test または ninja test が使えます。実行が終わるとテストログが生成され、次の場所にあります:
4.3 Meson プロジェクトのビルドと実行¶
上の操作で、meson プロジェクトの準備が整いました。次に、このプロジェクトをビルドして実行してみましょう。
4.3.1 ビルドディレクトリの設定¶
これは cmake の mkdir build && cmake .. に相当します。
実行結果は次の通りです:
➜ gtest_div meson setup build
The Meson build system
Version: 1.2.3
Source dir: /Users/light/work/practice/gtest_div
Build dir: /Users/light/work/practice/gtest_div/build
Build type: native build
Project name: gtest_div
Project version: undefined
C++ compiler for the host machine: c++ (clang 12.0.5 "Apple clang version 12.0.5 (clang-1205.0.22.11)")
C++ linker for the host machine: c++ ld64 650.9
Host machine cpu family: x86_64
Host machine cpu: x86_64
Downloading gtest source from https://github.com/google/googletest/archive/refs/tags/v1.14.0.tar.gz
Download size: 867764
Downloading: ..........
Downloading gtest patch from https://wrapdb.mesonbuild.com/v2/gtest_1.14.0-1/get_patch
Download size: 2440
Downloading: ..........
Executing subproject gtest
gtest| Project name: gtest
gtest| Project version: 1.14.0
gtest| C++ compiler for the host machine: c++ (clang 12.0.5 "Apple clang version 12.0.5 (clang-1205.0.22.11)")
gtest| C++ linker for the host machine: c++ ld64 650.9
gtest| Run-time dependency threads found: YES
gtest| Dependency threads found: YES unknown (cached)
gtest| Dependency threads found: YES unknown (cached)
gtest| Dependency threads found: YES unknown (cached)
gtest| Build targets in project: 0
gtest| Subproject gtest finished.
Build targets in project: 1
gtest_div undefined
Subprojects
gtest: YES
Found ninja-1.11.1 at /usr/local/bin/ninja
このとき、subprojects の中にキャッシュされた gtest プロジェクトが見られます。
4.3.2 コンパイル¶
次の2つのコマンドが使えます。1つ目:
実行ログ:
➜ gtest_div meson compile -C build/
INFO: autodetecting backend as ninja
INFO: calculating backend command to run: /usr/local/bin/ninja -C /Users/light/work/practice/gtest_div/build
ninja: Entering directory `/Users/light/work/practice/gtest_div/build'
[5/5] Linking target division_test
2つ目:
この2つのコマンドは本質的に同じで、どちらも ninja でコンパイルします。もちろん -C でディレクトリを指定しなくても構いません。その場合は cd build && ninja または cd build && meson compile が必要です。
4.3.3 実行とテスト¶
このとき division_test 実行プログラムが生成されます。実行結果は次の通りです:
➜ gtest_div ./build/division_test
Running main() from ../subprojects/googletest-1.14.0/googletest/src/gtest_main.cc
[==========] Running 2 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 2 tests from DivisionTest
[ RUN ] DivisionTest.Divide
[ OK ] DivisionTest.Divide (0 ms)
[ RUN ] DivisionTest.DivideByZero
[ OK ] DivisionTest.DivideByZero (0 ms)
[----------] 2 tests from DivisionTest (0 ms total)
[----------] Global test environment tear-down
[==========] 2 tests from 1 test suite ran. (0 ms total)
[ PASSED ] 2 tests.
meson.build の中にユニットテスト test を追加したので、次のコマンドが使えます
または:
ディレクトリを指定する場合:
実行ログ:
➜ gtest_div meson test -C build
ninja: Entering directory `/Users/light/work/practice/gtest_div/build'
ninja: no work to do.
1/1 this is a division test OK 0.01s
Ok: 1
Expected Fail: 0
Fail: 0
Unexpected Pass: 0
Skipped: 0
Timeout: 0
Full log written to /Users/light/work/practice/gtest_div/build/meson-logs/testlog.txt
関連トピック¶
- Bazel で C++ プロジェクトをビルド | C++那些事
- Bazel と LLDB の連携デバッグ | C++那些事
- CMake 入門と実践 | C++那些事
- Conan C++ パッケージ管理入門 | C++那些事
- C++ Insights によるコンパイル期観察 | C++那些事
- C++ Jupyter Notebook インタラクティブ | C++那些事
- C++ string から int への変換 | C++那些事
- C++ dbg-macro デバッグマクロ | C++那些事
- GCC コンパイルオプション詳解 | C++那些事
- GDB で C++ プログラムをデバッグ | C++那些事