Taking Meson Out For A Spin
I first stumbled across the Meson build system months ago on the mesa-dev mailing list. It seems that there has been some effort toward migrating that project away from Autotools. I get the feeling that there is some larger movement in the industry aiming at finally killing off Autotools, I for one would not miss it.
To make up my mind about how well Meson stacks up against other build systems, it isn’t enough to go thought the provided tutorials, I really have to try it out on a real project. So I’m going to convert an existing real world project and this blog post will be a case study of that experience.
To give you some sense of the scope of this case study, I’m not interested in
trying out any version other than what is provided by Ubuntu 16.04
on my
laptop, specifically that means Meson 0.29.0
. I’m not a Python hacker, and
I’m not interested in bypassing the package manager. If I end up needing
features that aren’t available until a more recent version of Meson, than I
have to conclude that Meson isn’t mature enough to be adopted for any project
that requires them.
Case Study: IA-Hardware-Composer
IA-Hardware-Composer (IAHWC) is an implementation of the Hardware Composer HAL used by Android’s SurfaceFlinger. In contrast to other implementations, it can actually be built and tested on non-Android platforms, with some system library configuration caveats. I have recently been doing some work with the Vulkan backend so I felt reasonably comfortable with the source tree and build system. For that reason I chose IAHWC as a non-trivial project to try converting to the Meson build system.
Convergence of build systems
For some time we have been maintaining files for two completely separate build systems, one for Android (Android NDK) and one for everything else (Autotools). It should come as no surprise that this has led to build breakages in cases where someone checks in code without modifying both systems. Obviously we can throw blame the maintainers or lack of reliable CI, personally I would rather be more proactive and adopt a system that can support the requirements of all target platforms. As such, I would consider cross platform support to be a core requirement for us when considering migrating to a new build system.
Number of lines by file for Autotools / Android NDK:
file | lines |
---|---|
Android.common.mk | 134 |
Android.mk | 54 |
Android.static.mk | 50 |
autogen.sh | 23 |
configure.ac | 110 |
Makefile.am | 82 |
Makefile.sources | 4 |
common/Android.mk | 154 |
common/Makefile.am | 68 |
common/Makefile.sources | 46 |
wsi/Android.mk | 108 |
wsi/Makefile.am | 67 |
wsi/Makefile.sources | 9 |
tests/Android.mk | 110 |
tests/Makefile.am | 68 |
Total number of lines: 1087
I knew it would be bad, but this actually blows me away… Both in terms of the number of configuration files and lines of text, this system just isn’t tractable. IAHWC isn’t that big of a project either.
Now lets contrast that with the same metric for Meson:
file | lines |
---|---|
meson.build | 82 |
common/meson.build | 48 |
wsi/meson.build | 25 |
tests/meson.build | 42 |
Total number of lines: 197
The Meson DSL
Usually the words non-Turing complete DSL
are enough to completely scare me
away from a new system, but I’m glad I gave Meson a chance. The DSL reads like
a stripped down version of Python, which to my eyes is one of the more elegant
scripting languages.
For instance, if I wanted to check for a library and populate compiler/linker flags in Autotools it would look something like:
PKG_CHECK_MODULES(DRM, [libdrm])
AM_CPPFLAGS += ${DRM_CFLAGS}
libhwcomposer_la_LIBADD += $(DRM_LIBS)
and the same in Meson:
dep_libdrm = dependency('libdrm')
libhwcomposer = shared_library(dependencies : [dep_libdrm])
I really like the concise build target definitions. Meson gives you three target types: static_library, shared_library, and executable. This may be limiting for some projects, but I have to imagine the bulk of userspace libraries and applications can be described in this way. I also really like that there is some level of type checking, not just pushing strings around.
In the Autotools build for IAHWC we had two static library archives that got linked together to create a single libhwcomposer shared library. While I was trying to convert these directly to Meson static libraries I discovered that there were some interdependencies between them, and so the build would fail with undefined references. The upstream maintainers may have some valid reasons behind keeping the build modular in this way, but it seems to me that we should be striving for independently valid build targets.
Checking for preprocessor definitions
When I was looking at adding the math library as a dependency for a Meson build target I discovered that this basically wasn’t possible in any portable way, at least not in this version. The website gives a code snippit that can be used to ask for the dependency through the compiler if it has access to the library. I’m actually a little surprised that this wasn’t handled in the initial public release.
It turns out that asking the compiler for any preprocessor definitions isn’t possible at all until a more recent version of Meson, so sad.
Python 3 and Ninja
The Meson website currently tries to make the claim that having Python 3 as an explicit dependeny means that any project using Meson can naturally use Python for tooling and scripting. That might work in practice, buy I wouldn’t recommend anyone accept that dependency as evergreen. The fact is you just don’t know what platforms will look like 10 years from now.
Also, it would be nice if there was some implementation-independent definition of the DSL, such that someone else could reasonably come along and write their own Meson interpreter. The Meson website states that the DSL is designed to make this possible, but without a spec I just don’t see how you can expect reliable behavior across implementations.
Ninja seems to have enough adoption as a generate-human-readable-file style build system, so the choice of using it as a default build generator for Meson seems fair. I have very little knowledge of Ninja itself. At the highest level, it seems to be better alternative to Make. In any case, I’m not trying to review Ninja right now.
Performance
I’ve always felt that build speed was one of the most important metrics for maintaining a healthy software project. If your developers tend to get up for a coffee while they are waiting for a fresh build, then I think to some degree you are failing as a maintainer.
Obviously this isn’t going to be an apples-to-apples comparison, as I had to make some minor changes to the build hierarchy to fit it within the Meson system, but I still think it is always worthwhile to take a look at some rough numbers, to get a sense of the impact.
system | configure | build | total |
---|---|---|---|
Autotools | 11.05s | 24.05s | 35.10s |
Meson | 0.40s | 16.84s | 17.24s |
These improvements are pretty impressive, but not surprising given how big and crufty Autotools really is. The reductions in the configure step alone are enough for serious consideration.
Conclusion: only simple projects for now
Again, I’m limiting this review to the version of Meson that you are provided today in Ubuntu 16.04. When the next version shows up in an Ubuntu LTS I’d like to do a follow up review.
Until the checking for preprocessor definitions is available, it is really difficult to recommend that everyone start moving to Meson. And again, it’s fine that Meson uses Python but I’d like to see more implementation agnostic definitions of the system.
If you are fine with requiring all your developers to upgrade their distros to a more bleeding edge version, then that’s up to you. I’ve seen some projects start to maintain a Meson build that requires a more recent version in parallel with their legacy build system, so people who are still running a LTS like me don’t wake up one morning with a broken build. I think that’s the best policy for now if you have your eyes set on Meson as your next build system.