Build source with meson (1)

Date: 2021/08/06 (initial publish), 2021/09/01 (last update)

Source: en/note-00024.md

Previous Post Top Next Post

TOC

Since around 2017, GNOME has pushed to use Meson to build its associated programs. I now have no choice but to learn Meson.

Here is my learning process note.

Build infrastructure history

GNU Autotools on top of Make has been the de facto standard for the portable build infrastructure since 1990s.

Configure script generation utility, GNU Autoconf has been a core part of GNU Autotools but it was extremely slow to execute and was not intuitive to customize. Root cause of problem were requirements for:

CMake initially release in 2000, addressed the first 3 issues listed in the above.

Ninja initially release in 2012, addressed the 4th issue by offering the alternative smaller and faster low level build tool of Make.

Meson initially release in 2013, is a higher level build tool in line with the functionalities of GNU Autotools and CMake. It used Ninja as its low level build infrastructure and supports string, list and dictionary data types.

(Some of the configuration syntax of Meson seems to resemble SCons initially release in 2000.)

(Since around 2016, CMake also supports the use of Ninja as its low level build infrastructure but still limited to the string data type.)

Meson using Ninja as its backend seems to me the winner these days with its modular and declarative simple configuration file syntax and its execution speed.

See:

Learning Meson

Command line options and the environment variables of Meson are very similar to what we use in more traditional GNU Autotools and CMake, including DESTDIR and prefix.

I read the following basic documents first:

Meson has a well documented native support of most compiler languages. But for building documentation, HotDoc documentation is the only documentation platform with the native support on Meson by the Hotdoc module.

Many document building activities usually involve processing files with sed/perl/python-scripts, or XSLT-transformation-scripts. The answer to “How do I build documents with Meson?” involves running external commands, creating custom targets, and creating generator objects. The followings seem to be useful.

As for i18n, I18n module provides internationalization and localization functionality. An implementation example can be found at Localization.

Note on recent feature enhancements

Meson is still in pre-1.0 stage as of July 2021 and is evolving quickly.

Since the Meson version of Debian Bullseye/11 (stable) is 0.56.2, some features may not work on Debian Bullseye/11 (stable). Notable ones are:

The current Debian Bookworm/12 in testing has 0.59.1, so the above issues are solved.

Learning Ninja

In order to grok Meson generated build.ninja, I browsed The Ninja build system.

Note on Meson

Basics

Meson is for out-of-tree building. Meson uses single quotations for the string.

For the cross platform customization, tutorial documents tend to use:

Functionalities

Please note setup is configure. (confusing…)

Example for run_command() and custom_target()

Here is a text conversion example to generate a shell script:

project('foo', version : '0.1')
# run in setup
date = find_program('date')
sed = find_program('sed')
sh = find_program('sh')
cat = find_program('cat')

datex = 's/@timestamp@/' + run_command(date, '--iso=sec', '-u').stdout().strip() + '/'
versionx = 's/@version@/' + meson.project_version() + '/'
hellox = 's/@msg@/date=@timestamp@ version=@version@ -- Hello World!/'

# run in compile
date1 = custom_target('date1',
  capture : true,
  output : 'date1',
  command : [ date, '--iso=sec', '-u' ])

date2 = custom_target('date2',
  input : date1,
  capture : true,
  output : 'date2',
  command : [ sed, '-e', 's/^/echo "date=/', '-e', 's/$/"/', '@INPUT@' ])

foo1 = custom_target('foo1',
  input : 'foo0',
  capture : true,
  output : 'foo1',
  command : [ sed, '-e', hellox , '-e', datex, '-e', versionx, '@INPUT@'])

sh1 = custom_target('sh1',
  input : '/etc/motd',
  capture : true,
  output : 'sh1',
  command : [ sh, '-c',
      'cat /etc/motd | grep -e GNU | sed -e \'s/^/echo "motd=/\' -e \'s/$/"/\' '
    ])

foo = custom_target('default',
  input : [foo1, date2, sh1],
  build_by_default: true,
  capture : true,
  install : true,
  install_dir : 'bin',
  install_mode : 'rwxr-xr-x',
  output : 'foo',
  command : [ cat, '@INPUT@'])

(Please note, new feed feature is not used to be compatible with Meson version 0.56.2)

This starts with this template file:

#!/bin/sh
echo "@msg@"

Let’s configure and build script foo:

$ meson setup build       # configure
$ cd build
$ meson configure --prefix=~
$ meson compile           # build
$ meson install

This generates foo with meson compile and install it to ~/bin/foo with meson install:

#!/bin/sh
echo "date=2021-08-22T07:24:15+00:00 version=0.1 -- Hello World!"
echo "date=2021-08-22T07:25:13+00:00"
echo "motd=The programs included with the Debian GNU/Linux system are free software;"
echo "motd=Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent"

Most compilers are well supported by Meson and there are many examples.

Abusing meson with shell codes

Please note that if we don’t worry about Windows, we can embed shell codes almost as in the case of Makefile.

Notable things are:

Here is an example of handling of long lines and quoted lines.

project('foo', version : '0.1')
# run in setup
date = find_program('date')
sed = find_program('sed')
sh = find_program('sh')
cat = find_program('cat')

datex = 's/@timestamp@/' + run_command(date, '--iso=sec', '-u').stdout().strip() + '/'
versionx = 's/@version@/' + meson.project_version() + '/'
hellox = 's/@msg@/date=@timestamp@ version=@version@ -- Hello World!/'

# run in compile
date1 = custom_target('date1',
  capture : true,
  output : 'date1',
  command : [ date, '--iso=sec', '-u' ])

date2 = custom_target('date2',
  input : date1,
  capture : true,
  output : 'date2',
  command : [ sed, '-e', 's/^/echo "date=/', '-e', 's/$/"/', '@INPUT@' ])

# Some NLs are OK and act as just whitespaces
foo1 = custom_target('foo1',
  input : 'foo0',
  capture : true,
  output : 'foo1',
  command : [ sh, '-c',
      'sed -e \'' + hellox + '\' -e \'' + datex + '\' -e \''
	  + versionx + '\' @INPUT@'
    ])

# Triple-single-quote disables escaping
# NLs in triple-single-quotes become just whitespaces
sh1 = custom_target('sh1',
  input : '/etc/motd',
  capture : true,
  output : 'sh1',
  command : [ sh, '-c',
      '''cat /etc/motd |
      grep -e GNU |
      sed -e 's/^/echo "motd=/' -e 's/$/"/' '''
    ])

foo = custom_target('default',
  input : [foo1, date2, sh1],
  build_by_default: true,
  capture : true,
  install : true,
  install_dir : 'bin',
  install_mode : 'rwxr-xr-x',
  output : 'foo',
  command : [ cat, '@INPUT@'])
Previous Post Top Next Post