コンテンツにスキップ

最終更新:2026-10-12

Meson で C++ プロジェクトをビルド

最近いくつかのオープンソースプロジェクトを利用する中で、CMake や Makefile の方式を捨てて meson+ninja のビルド方式を採用しているものが多いことに気づきました。本稿では Meson を紹介し、入門インストールから完全な meson プロジェクトの作成までを説明します。

それでは、本題に入りましょう。

1.Meson のインストール

https://github.com/mesonbuild/meson

Meson は PyPi から入手できるため、pip3 install meson でインストールできます。異なるシステムでは正しい pip コマンドを使用し、必ず Python 3 版の pip を使ってください。

必要であれば、標準の Python コマンドでローカルにインストールすることもできます:

python3 -m pip install meson

Ninja を使ってビルドする場合は、Ninja を Ninja の GitHub リリースページから直接、または PyPi 経由でダウンロードできます:

python3 -m pip install ninja

Meson build のインストールについての詳細は、以下の Meson 公式サイトをご覧ください。

https://mesonbuild.com/Getting-meson.html

2.Meson Vs Cmake

皆が CMake を使っているのに、なぜ Meson を使う必要があるのでしょうか?それぞれの優劣は何でしょうか?

パフォーマンスの面では、Meson は一般的に高速なビルド速度で知られています。Meson の設計はビルド速度の最適化を目的としており、特にインクリメンタルビルドで優れた性能を発揮します。Meson は Ninja ビルドシステムをデフォルトのバックエンドとして使用しており、Ninja 自体も高速かつ効率的なことで知られています。

一方、CMake のビルド速度は、特に大きなプロジェクトではプロジェクト規模の影響を受ける可能性があります。CMake はさまざまなジェネレータ(Makefile など)を使用しますが、ジェネレータごとに性能も異なる場合があります。

もちろん、CMake + Ninja を使ってコンパイル速度を上げることもできます。

オープンソースプロジェクトの観点から Meson を見ると、その使用シーンも非常に多く、例えば:PostgreSQL、QEMU、GNOME Software、GTK+、などなど

https://mesonbuild.com/Users.html

2.1 Meson の優位性と劣勢

1.優位点

  • Python ベースで、使い方が簡単
  • クロスコンパイルをサポート
  • すべてのオブジェクトが強く型付けされている:オブジェクトは 'dependency'、'include directory' などの実体であるため、文字列置換のエラーが起きにくい
  • サードパーティライブラリの参照が非常に便利
  • 非常識なほど速い、特にインクリメンタルビルド時には。
  • ドキュメントは cmake より n 倍良いです。

http://mesonbuild.com

2.劣勢

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 を検索
meson wrap search gtest

このコマンドがラップされたプロジェクト名を返したら、次のステップに進めます:

  • 自分のプロジェクトに subprojects ディレクトリを作成し、install で gtest をインストールします
mkdir -p subprojects
meson wrap install gtest

このとき、プロジェクトが依存するサードパーティライブラリはすべて subprojects ディレクトリにインストール済みになっています。

➜  gtest_div tree -L 1 subprojects
subprojects
├── gtest.wrap

このとき 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 コードを書く

double divide(double a, double b);

.h と .cc は src ディレクトリに置きます:

src
├── division.cc
└── division.h

その後、同階層に test ディレクトリを作成します。

test
└── division_test.cc

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 という名前であれば、次のようになります:

project('gtest', 'cpp',
  version : '1.14.0',
  default_options : ['cpp_std=c++14'],
  license : 'bsd')

すると、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

ユニットテストの構文:

e = executable('prog', 'testprog.c')
test('name of test', e)

meson test または ninja test が使えます。実行が終わるとテストログが生成され、次の場所にあります:

build/meson-logs/testlog.txt

4.3 Meson プロジェクトのビルドと実行

上の操作で、meson プロジェクトの準備が整いました。次に、このプロジェクトをビルドして実行してみましょう。

4.3.1 ビルドディレクトリの設定

これは cmake の mkdir build && cmake .. に相当します。

meson setup build

実行結果は次の通りです:

➜  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 プロジェクトが見られます。

subprojects
├── googletest-1.14.0  # here
├── gtest.wrap
└── packagecache # here

4.3.2 コンパイル

次の2つのコマンドが使えます。1つ目:

meson compile -C build/

実行ログ:

➜  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つ目:

➜  gtest_div ninja -C build
ninja: Entering directory `build'
[5/5] Linking target division_test

この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 を追加したので、次のコマンドが使えます

meson test

または:

ninja test

ディレクトリを指定する場合:

meson test -C build/

or 

ninja test -C build/

実行ログ:

➜  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

関連トピック

コメント