Перейти к содержанию

Последнее обновление:2026-10-12

Сборка проектов C++ с помощью Meson

В последнее время, работая с рядом открытых проектов, я обнаружил, что все они используют сборку meson+ninja, отказавшись от CMake и Makefile; в этой статье мы познакомимся с Meson — от установки до написания полного meson-проекта.

Итак, переходим к сути.

1. Установка Meson

https://github.com/mesonbuild/meson

Meson можно получить через PyPi, поэтому для установки подойдёт pip3 install meson. В разных системах используйте правильную команду pip — обязательно pip версии Python 3.

При желании можно установить локально стандартной 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.Недостатки

  • Возможно, менее зрелый, чем 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

meson wrap search gtest
Если команда вернула имя упакованного проекта, можно переходить к следующему шагу:

  • Создаём в своём проекте каталог subprojects и устанавливаем gtest через install

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 очень похож на add_executable в cmake — генерирует исполняемый файл 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 Настройка каталога сборки

Это аналогично mkdir build && cmake .. в 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 Компиляция

Можно использовать две команды ниже, первый вариант:

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
Второй вариант:

➜  gtest_div ninja -C build
ninja: Entering directory `build'
[5/5] Linking target division_test
Эти две команды по сути одинаковы — обе компилируют через 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

Похожие темы

Комментарии