Ir para o conteúdo

Última atualização:2026-10-12

Construir projetos C++ com Meson

Recentemente, ao usar alguns projetos de código aberto, percebi que todos adotaram a forma de compilação meson+ninja, abandonando o CMake e os Makefiles. Este artigo introduz o Meson, desde a instalação inicial até a escrita completa de um projeto meson.

Vamos ao conteúdo principal.

1.Instalação do Meson

https://github.com/mesonbuild/meson

O Meson está disponível no PyPi, portanto pode ser instalado com pip3 install meson. Certifique-se de usar o comando pip correto em cada sistema e use sempre o pip da versão Python 3.

Se preferir, também é possível instalar localmente com o comando Python padrão:

python3 -m pip install meson

Para compilar usando o Ninja, você pode baixá-lo diretamente da página de releases do Ninja no GitHub ou por meio do PyPi:

python3 -m pip install ninja

Para mais informações sobre a instalação do Meson build, consulte o site oficial do Meson abaixo.

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

2. Meson Vs CMake

Já que todo mundo usa CMake, por que usar Meson? Quais são as vantagens e desvantagens de cada um?

Em termos de desempenho, o Meson é geralmente conhecido pela rapidez de suas compilações. O Meson foi projetado para otimizar a velocidade de compilação e se destaca especialmente nas compilações incrementais. O Meson usa o sistema de compilação Ninja como backend padrão, e o Ninja, por si só, também é reconhecido por ser rápido e eficiente.

Em comparação, a velocidade de compilação do CMake pode ser afetada pelo tamanho do projeto, especialmente em projetos grandes. O CMake usa vários geradores (como Makefiles), e o desempenho pode variar entre os diferentes geradores.

É claro que também é possível usar CMake + Ninja para melhorar a velocidade de compilação.

Na perspectiva dos projetos de código aberto, o Meson tem muitos casos de uso, por exemplo: PostgreSQL, QEMU, GNOME Software, GTK+, entre outros

https://mesonbuild.com/Users.html

2.1 Vantagens e desvantagens do Meson

1.Vantagens

  • Baseado em Python, fácil de usar
  • Suporta compilação cruzada
  • Todos os objetos são fortemente tipados: como os objetos são entidades como 'dependency', 'include directory', entre outras, é difícil cometer erros de substituição de strings.
  • Muito conveniente para referenciar bibliotecas de terceiros
  • Muito, muito rápido, especialmente em compilações incrementais.
  • Documentação n vezes melhor que a do cmake.

http://mesonbuild.com

2.Desvantagens

2.2 Vantagens e desvantagens do CMake

1.Vantagens

  • Bom suporte de IDEs, mais amigável para o trabalho em equipe
  • Maduro, tornou-se praticamente o padrão para projetos C++

2.Desvantagens

  • Compilação cruzada é difícil
  • Velocidade de compilação relativamente lenta

3. Meson WrapDB

Como o googletest não é um projeto meson, como fazer o meson depender dele?

Da mesma forma, grpc e abseil-cpp têm uso semelhante.

Aqui é preciso primeiro introduzir o WrapDB: o Meson WrapDB é um componente do sistema de compilação Meson, usado para gerenciar e fornecer arquivos de descrição de compilação de projetos de terceiros, geralmente chamados de arquivos "wrap". Esses arquivos wrap contêm as informações necessárias para configurar e compilar projetos de terceiros, de modo que possam ser integrados facilmente a outros projetos que usam o sistema de compilação Meson.

É possível consultar bibliotecas de terceiros por meio do comando search ou do site abaixo.

https://mesonbuild.com/Wrapdb-projects.html#meson-wrapdb-packages

Tomemos o uso do gtest como exemplo:

  • Buscar o gtest
meson wrap search gtest

Se esse comando retornar o nome do projeto empacotado, você pode passar à próxima etapa:

  • Criar o diretório subprojects no seu próprio projeto e instalar o gtest via install
mkdir -p subprojects
meson wrap install gtest

Neste ponto, todas as bibliotecas de terceiros das quais o seu projeto depende já estão instaladas no diretório subprojects.

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

Isso gerará o gtest.wrap, cujo conteúdo incluirá o seguinte.

Observação: este arquivo é gerado pelo install acima; recomenda-se não modificá-lo.

[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. Como escrever um projeto meson?

A seguir, a parte prática: como escrever um projeto meson?

Usaremos como exemplo um pequeno projeto de teste de divisão a/b com testes de unidade usando o gtest.

4.1 Escrever o código

double divide(double a, double b);

Coloque o .h e o .cc no diretório src:

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

Em seguida, crie o diretório test no mesmo nível.

test
└── division_test.cc

4.2 Gerenciamento do projeto meson

Adicione o arquivo meson.build na raiz do projeto, com o seguinte conteúdo:

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)

Como se pode ver, esse conteúdo é praticamente idêntico à sintaxe do Python; para quem conhece Python, esse código é muito fácil de entender. Aqui destacamos as partes mais importantes acima.

  • Introduzir bibliotecas de terceiros

Por exemplo: suponha que o projeto da biblioteca de terceiros que você introduziu chame-se gtest, como segue:

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

Assim, é possível obter esse project por meio de subproject e, em seguida, obter as informações de dependência do gtest com gtest_proj.get_variable; por exemplo, no googletest a declaração é a seguinte:

gtest_main_dep = declare_dependency(
    include_directories : gtest_incdir,
    sources : [gtest_libsources, gtest_mainsources],
    dependencies : dependency('threads')
)
  • executable

O executable é muito semelhante ao add_executable do cmake, gerando o executável division_test.

  • test

Sintaxe dos testes de unidade:

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

Pode-se usar meson test ou ninja test; ao final da execução, o log de testes é gerado em:

build/meson-logs/testlog.txt

4.3 Compilar e executar o projeto Meson

Com as operações acima, um projeto meson está pronto. Agora, vamos compilar e executar esse projeto.

4.3.1 Configurar o diretório de compilação

Isso é semelhante ao mkdir build && cmake .. do cmake.

meson setup build

A execução é a seguinte:

➜  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

Neste ponto, podemos ver o projeto gtest em cache no subprojects.

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

4.3.2 Compilar

Podemos usar os dois comandos a seguir; o primeiro:

meson compile -C build/

Log de execução:

➜  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

O segundo:

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

Esses dois comandos são essencialmente iguais: ambos compilam com o ninja. Naturalmente, também é possível omitir o -C para especificar o diretório; nesse caso, use cd build && ninja ou cd build && meson compile.

4.3.3 Executar e testar

Neste ponto, o executável division_test é gerado; a execução é a seguinte:

➜  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.

Como o teste de unidade test foi adicionado no meson.build, podemos usar

meson test

Ou:

ninja test

Se for especificar o diretório:

meson test -C build/

or 

ninja test -C build/

Log de execução:

➜  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

Tópicos relacionados

Comentários