Meson
为什么要放着好的 CMake 不用 而来用 Meson
因为 Meson 足够简单 比起冗长的 CMakeLists.txt
meson.build 要更加易读
许多的 Linux 项目都是使用 Meson
特别是 Gnome 和 GTK 的项目
https://github.com/mesonbuild/meson/releases/download/1.12.1/meson-1.12.1.tar.gz
使用 pip 安装 Meson
Meson 可在 Python 包索引 中获得
用
pip3 install --user meson
安装。这不需要任何特殊权限
~/.local/
中安装软件包,因此你必须将
~/.local/bin
添加到你的 PATH 中
如果你仅将 Meson 用于本地开发 这应该是你需要的全部操作
入门示例 编译一个单文件项目,其目录结构如下
.
├── main.cpp
└── meson.build
其中 meson.build 内容如下
project('meson tutorial', 'cpp')
executable('tutorial', 'main.cpp')
初始化
要使用 Meson 来构建项目
首先要使用 Meson 来初始化
meson setup --prefix="$HOME/.local/sdk/user" --backend="ninja" --buildtype="debug" "./build/debug" "./"
其中
--prefix 指定了软件安装位置
--backend 指定了底层编译工具
--buildtype 指定了编译类型,是调试类型还是发行类型
而如果要优化编译,可以使用发行类型
meson setup --prefix="$HOME/.local/sdk/user" --backend="ninja" --buildtype="release" --debug "./build/release" "./"
这些选项都可以忽略
默认情况的安装位置是 /local/usr
而编译类型则默认是调试类型 所以一般来说可以简化为如下指令
meson setup "./build/debug" "./"
后面两个是位置参数
分别代表编译到的位置以及源代码所在目录
更为直接的,可以使用
meson build
来初始化项目, 默认编译内容会放到
./build
目录下 但是为了方便区分,一般还是会放到类似 ./build/debug 这种目录下
编译
初始化项目后就是要编译项目
meson compile -C "./build/debug"
其中
-C 表示执行编译指令前先切换到对应目录
这是因为 meson compile 只有在 Meson Build 目录下才能编译
也可与手动使用对应的编译后端来编译,例如 ninja
ninja -C "./build/debug"
正常项目的结构
.
├── include
│ ├── meson.build
│ └── tut
│ └── tut.hpp
├── meson.build
├── src
│ ├── main.cpp
│ ├── meson.build
│ └── tut
│ ├── meson.build
│ └── tut.cpp
└── subprojects
└── libtutorial
├── include
│ ├── meson.build
│ └── tutorial
│ └── tutorial.hpp
├── meson.build
└── tutorial.cpp
其中 tut 是项目内部库
而 libtutorial 则是子项目,可以是用源码管理的外部项目
自顶向下来看看每个 meson.build 文件里写了什么,以及各自的作用
项目配置
# ./meson.build
project(
'example',
'cpp',
version: '0.1',
default_options: ['cpp_std=c++17'],
)
# headers
subdir('include')
incs = include_directories('include')
# subprojects
libtutorial_dep = dependency('libtutorial', fallback: ['libtutorial', 'libtutorial_dep'])
# sources
subdir('src')
executable(
'example',
sources,
include_directories: incs,
dependencies: [libtutorial_dep, libtut_dep],
install: true,
)
在 project() 中,
可以设置非常多的东西,例如项目的版本,使用的语言,以及标准等级
通过 subdir() 将 include 目录加入项目,
这是一个推荐做法,也就是将头文件专门用一个目录存放,
而不是和源码放在一起,
方便 pkgconfig 之类的工具安装库和设置对应变量
include_directories() 则是为了生成 compile_commands.json 文件,方便 IDE 提示
对于外部依赖,如果是通过源码管理的,
可以使用 subproject() 来加入依赖,
但是由于需求非常常见,所以我们可以直接使用 dependency() 的 fallback 选项快捷地添加依赖
而后就是将源文件目录加入项目,通过 executable() 从而编译生成一个可执行文件
头文件管理
在 include 目录中,文件内容如下:
# ./include/meson.build
install_subdir('tut', install_dir: get_option('includedir'))
而在 subprojects/libtutorial/include 文件夹中的内容大同小异,只是将目录名改成了 tutorial
install_subdir() 指定了要安装的头文件,方便之后寻找头文件
源码管理
这里源码分成了两部分,一个是可执行文件对应的 main.cpp,另一个就是项目自己的库 tut
# ./src/meson.build
# all source file for executable
sources = files('main.cpp')
# local libs
subdir('tut')
内容非常简单,通过 files 指定有哪些文件,然后在 ./meson.build 中就可以通过 sources 变量来使用这些文件,然后将 tut 加入项目
# ./src/tut/meson.build
# sources
tut_sources = files('tut.cpp')
# math dependency
cxx = meson.get_compiler('cpp')
m_dep = cxx.find_library('m', required: true)
# declare a shared library
# can use static_library() for static libraries
# or just use library() and declare by options
libtut = shared_library(
'tut',
tut_sources,
include_directories: incs,
dependencies: [m_dep],
install: true,
)
# declare as a dependency
libtut_dep = declare_dependency(include_directories: incs, link_with: libtut)
# generate pkgconfig files
pkg_config = import('pkgconfig')
pkg_config.generate(
name: 'Tut',
filebase: 'tut',
description: 'a meson tutorial dependcies',
libraries: libtut,
)
在 tut 中
定义了一个库,库的定义一般使用 library()
但是如果要指定库的形式,那么也可以使用 static_library() 和 shared_library() 来指定
编译器有时候并不会自动链接一些库,例如数学库 m 和线程库,
需要通过编译器来链接对应的库依赖,即 cxx.find_library(), 其中 required 选项如果是 true,那么在找不到库的情况下编译过程就会停止,而 false 则不会
为了方便别人链接我们的库,
可以自己暴露一些变量,例如 libtut_dep,
而其他人就可以通过 get_variable() 来获取这些内容
如果要更进一步使用 pkgconfig 管理,
使用 import 加载 pkgconfig 的相关内容, 然后通过 generate 为我们的库生成对应的 .pc 文件
子项目
子项目一定要放在 ./subprojects 文件夹下,而 ./subprojects/libtutorial/meson.build 文件则是项目文件,也就是需要有 project()
# ./subprojects/libtutorial/meson.build
# library project declare
project('libtutorial', 'cpp', default_options: ['cpp_std=c++17'])
# headers
subdir('include')
headers = include_directories('include')
# sources
sources = files('tutorial.cpp')
# declare a static library
# can use shared_library() for shared libraries
# or just use library() and declare by options
libtutorial = static_library(
'tutorial',
sources,
include_directories: headers,
install: true,
)
# declare as a dependency
libtutorial_dep = declare_dependency(include_directories: headers, link_with: libtutorial)
# generate pkgconfig files
pkg_config = import('pkgconfig')
pkg_config.generate(
name: 'Tutorial',
filebase: 'tutorial',
description: 'a meson tutorial subproject',
libraries: libtutorial,
)
编译和安装
准备好这些文件后,我们就可以开始编译了,初始化过程和普通项目一样,编译可以使用 meson compile -C "./build/debug" 来编译
meson setup --prefix="$HOME/.local/sdk/user" --backend="ninja" --buildtype="debug" "./build/debug" "./"
meson compile -C "./build/debug"
meson install -C "./build/debug"
后记
Meson 看起来比较繁琐,但是功能上比较直观,并且非常方便配合 pkgconfig 工具使用,所以还是非常值得一学的