Learning meson

Last Friday at Red Hat the fourth Day of Learning happened. This time I picked the meson build system. More and more projects have switched to it, like systemd more than 3 years ago, or most of GNOME. Back then I was really impressed by how much faster a systemd build became with meson – but now I actually want to learn it, peek behind the curtain, be able to contribute to projects that use it, and to know if a conversion makes sense.

Also, I have been sponsoring Jussi’s Debian meson package uploads for years now, it’s really time to understand what exactly I am uploading there! 😉

First steps

At the beginning there was of course the Hello World tutorial, learning about the basic concepts, and getting an initial overview about the documentation. As a warm-up, I tried to use meson in fatrace, a rather small project of mine which just uses a simple Makefile:

CFLAGS ?= -O2 -g -Wall -Wextra -Werror
PREFIX ?= /usr/local

fatrace: fatrace.o
	$(CC) $(LDFLAGS) -o $@ $<

clean:
	rm -f *.o fatrace

distclean: clean

install: fatrace
	install -m 755 -D fatrace $(DESTDIR)$(PREFIX)/sbin/fatrace
	install -m 755 power-usage-report $(DESTDIR)$(PREFIX)/sbin/
	install -d $(DESTDIR)$(PREFIX)/share/man/man8/
	install -m 644 *.8 $(DESTDIR)$(PREFIX)/share/man/man8/

.PHONY: clean distclean install

After some half hour I had a functionally equivalent meson.build file, and learned how to set default compiler arguments, using non-compiled scripts in install and test, and to use meson setup --buildtype release|debug for controlling the debug symbol/optimization flags.

project('fatrace', 'c', license: 'GPLv3+')
add_global_arguments('-Werror', language: 'c')

executable('fatrace', 'fatrace.c', install: true)
install_man('fatrace.8')
install_data('power-usage-report', install_dir: get_option('bindir'))

test('integration tests', files('tests/run'))

I don’t actually want to commit that, I still want to keep the Makefile in the official project for the time being. But it was a nice playground!

Converting umockdev

Levelling up, my actual goal for the day was to convert umockdev. Build system wise this is already rather complex, with source code in Vala and C, building a shared library with gobject-introspection bindings, some Python tests, building documentation with gtk-doc, and several unit test suites.

The meson branch has some preparatory commits and 37e95656 is how far I got during that day. Of course the first steps always take very long, especially when starting with a shared library written in Vala; but the library and command line utilities build, the DESTDIR=/tmp/u meson install tree looks reasonable and useful, and the unit tests and helpers build and run.

However, I did encounter several non-trivial snags along the way:

  • My first build attemps failed on some Error: symbol 'stat64' is already defined. It turns out that meson always sets -D_FILE_OFFSET_BITS=64, which umockdev’s preload library did not expect. This took me some time to understand, but in the end was easy enough to fix, and may also help distributions/users that use the autotools build system.

  • meson does not use libtool, so there is no direct equivalent for blah_LDFLAGS = -export-symbols-regex '^...'. Instead, with direct ld you have to define a map file with exported symbol patterns, and tell ld via a custom -Wl,--version-script,src/umockdev.map link_args option.

  • For chaining build steps, meson does not work (any more) with file name based dependencies like make, but file “objects”, i.e. you can assign the result of executable(), shared_library(), etc to a variable and use that in the next step of a build chain with depends: requisite_var. This works fine with custom_target(), but there is some impedance mismatch with shared_library(), which does not have useful auxiliary outputs. In particular, I want to post-process the generated library .gir first through sed and then through g-ir-compiler to build a .typelib.

    I’m by far not the first person to run into this, there are at least four reports about this. I left some thoughts to one of it, and then went on with an obvious and simple, but a bit unelegant workaround.

  • The gtk-doc documentation build was very non-obvious, and the gnome.gtkdoc() command is documented only sparsely. After spying on various GNOME projects I got it working, with bit-by-bit identical HTML result, but only when I previously do a build with autotools (in separate build tree, which I delete afterwards). Otherwise it fails to generate the .types file, which is a long-standing open bug. This is all very mysterious still, and I need some more time to debug what magical thing the autotools build does to the tree to unbreak it.

  • Two of the 7 unit test suites fail in the middle with getting killed by SIGHUP if I run them through meson test, but work fine if I call them directly – even if I call them in the exact same way as meson seems to do. I had to get the command from strace, as meson test -v does not show the command. This also requires more debugging.

To-Do

The last two snags are hard blockers before this has a chance of landing. After these are solved, there are still a few more things to add to reach feature parity with automake:

  • Running tests through valgrind - this should be easy with another add_test_setup()).
  • Unit test code coverage. meson directly supports this, I just didn’t try it at all yet.
  • Fix builds without gudev, python, and/or gobject-introspection.
  • Make the build work on all the OSes that umockdev has CI for; in particular, CentOS 7 and Alpine Linux may have some extra surprises.

But these all seem to be rather straightforward.

Conclusion

I really like the concept of meson: It is very declarative, clean, composite, modular, featureful, and reasonably well documented. Automatic aggressive parallelization and caching makes building lightning fast. I also like that you have to use a separate build tree, thus never mess up your source tree, don’t need carefully tuned .gitignore files, and thanks to its pragmatic dist approach never have to meticoulously enumerate EXTRA_DIST and such.

I have a feeling that I’ve been particularly unlucky with the above snags with umockdev, though. Investigating these was really what took the bulk of the day, it was not because meson itself is hard to learn. But then again I still remember the countless hours that I spent in total on the automake build system.

So, a bit shout-out to Jussi for coming up with meson’s concepts and first implementation, and of course to all the contributors! Build systems are often enough the messiest part of a software project, and this effort of giving us developers better tools is highly appreciated!