An opinionated Skeleton for meson projects
I admit it. I fell in love with meson at first sight. It is exactly the thing that I always wanted CMake to be. I have used it for a while, so I wanted to share a couple of tricks and best practices that I've picked up.
I start out with a pretty standard "preamble":
project('myproj', ['c'], version : '0.1', license : 'GPL-3.0-or-later', default_options: [ 'warning_level=2', 'c_std=gnu99', 'werror=true', ], ) add_project_arguments([ '-D_GNU_SOURCE', ], language: 'c', )
I like my GCC extensions, so naturally I set some friendly options for that.
Next up is something I think should be somehow baked into meson at some point. meson builds everything inside a dedicated build directory so when if we use stuff like __FILE__ in our debugging code we end up with paths like ../src. Let's get rid of that:
cc = meson.get_compiler('c') # strip relative path prefixes from the code if possible if cc.has_argument('-fmacro-prefix-map=prefix=') add_project_arguments([ '-fmacro-prefix-map=../=', ], language: 'c', ) endif
I picked up this trick from sway, though they complicate it a bit.
Moving on, my projects usually have dependencies and more often than I would like, I vendor them. But I do like to be able to also pick them up as system libraries as well. I use meson_options.txt for this. Say I depend on libfoo. In meson_options.txt add:
option('libfoo', type: 'combo', value: 'auto', choices: ['auto', 'system', 'internal'], description: 'how to find the foo library')
This gives us a -Dlibfoo= switch that we can set to either auto, system or internal.
To actually take action on this, add the following in meson.build:
libfoo_opt = get_option('libfoo') if libfoo_opt in ['auto', 'system'] libfoo = dependency('libfoo', # if auto, allow fallback to internal version required: libfoo_opt == 'system', ) if libfoo.found() libfoo_opt = 'system' else if libfoo_opt == 'auto' # fall back to internal version libfoo_opt = 'internal' endif endif if libfoo_opt == 'internal' libfoo_proj = subproject('libfoo', default_options: [ 'warning_level=1', # always choose a static version for embedded builds 'default_library=static', ], required: true, ) libfoo = libfoo_proj.get_variable('libfoo_dep') endif
This utilizes the meson subproject functionality (it's pretty awesome).
At this point I typically start adding subdir()'s. It depends on your project if you want to split it, but I almost always split it up from the beginning.
Following any subdir()'s I add any run_target()'s, like generating ctags:
ctags = find_program('ctags', required: false) if ctags.found() run_target('ctags', command: [ find_program('scripts/ctags.sh') ]) endif
scripts/ctags.sh is a small wrapper around invoking ctags for several directories.