Add a Meson build system

This allows bwrap to be built as a subproject in larger Meson projects.
When built as a subproject, we install into the --libexecdir and
require a program prefix to be specified: for example, Flatpak would use
program_prefix=flatpak- to get /usr/libexec/flatpak-bwrap. Verified to
be backwards-compatible as far as Meson 0.49.0 (Debian 9 backports).

Loosely based on previous work by Jussi Pakkanen (see #133).

Differences between the Autotools and Meson builds:

The Meson build requires a version of libcap that has pkg-config
metadata (introduced in libcap 2.23, in 2013).

The Meson build has no equivalent of --with-priv-mode=setuid. On
distributions like Debian <= 10 and RHEL <= 7 that require a setuid bwrap
executable, the sysadmin or distribution packaging will need to set the
correct permissions on the bwrap executable; Debian already did this via
packaging rather than the upstream build system.

The Meson build supports being used as a subproject, and there is CI
for this. It automatically disables shell completions and man pages,
moves the bubblewrap executable to ${libexecdir}, and renames the
bubblewrap executable according to a program_prefix option that the
caller must specify (for example, Flatpak would use
-Dprogram_prefix=flatpak- to get /usr/libexec/flatpak-bwrap). See the
tests/use-as-subproject/ directory for an example.

Signed-off-by: Simon McVittie <smcv@collabora.com>
This commit is contained in:
Simon McVittie 2021-05-05 12:16:20 +01:00
parent 43c2d32d7f
commit 1927981240
14 changed files with 408 additions and 1 deletions

View File

@ -10,7 +10,7 @@ on:
jobs:
check:
name: Build with gcc and test
name: Build with Autotools and gcc, and test
runs-on: ubuntu-latest
steps:
- name: Check out
@ -69,6 +69,71 @@ jobs:
run: |
make -C _build -j $(getconf _NPROCESSORS_ONLN) distcheck VERBOSE=1 BWRAP_MUST_WORK=1
meson:
name: Build with Meson and gcc, and test
runs-on: ubuntu-latest
steps:
- name: Check out
uses: actions/checkout@v1
- name: Install build-dependencies
run: sudo ./ci/builddeps.sh
- name: Create logs dir
run: mkdir test-logs
- name: setup
run: |
meson _build
env:
CFLAGS: >-
-O2
-Wp,-D_FORTIFY_SOURCE=2
-fsanitize=address
-fsanitize=undefined
- name: compile
run: ninja -C _build -v
- name: smoke-test
run: |
set -x
./_build/bwrap --bind / / --tmpfs /tmp true
env:
ASAN_OPTIONS: detect_leaks=0
- name: test
run: |
BWRAP_MUST_WORK=1 meson test -C _build
env:
ASAN_OPTIONS: detect_leaks=0
- name: Collect overall test logs on failure
if: failure()
run: mv _build/meson-logs/testlog.txt test-logs/ || true
- name: install
run: |
DESTDIR="$(pwd)/DESTDIR" meson install -C _build
( cd DESTDIR && find -ls )
- name: dist
run: |
BWRAP_MUST_WORK=1 meson dist -C _build
- name: Collect dist test logs on failure
if: failure()
run: mv _build/meson-private/dist-build/meson-logs/testlog.txt test-logs/disttestlog.txt || true
- name: use as subproject
run: |
mkdir tests/use-as-subproject/subprojects
tar -C tests/use-as-subproject/subprojects -xf _build/meson-dist/bubblewrap-*.tar.xz
mv tests/use-as-subproject/subprojects/bubblewrap-* tests/use-as-subproject/subprojects/bubblewrap
( cd tests/use-as-subproject && meson _build )
ninja -C tests/use-as-subproject/_build -v
meson test -C tests/use-as-subproject/_build
DESTDIR="$(pwd)/DESTDIR-as-subproject" meson install -C tests/use-as-subproject/_build
( cd DESTDIR-as-subproject && find -ls )
test -x DESTDIR-as-subproject/usr/local/libexec/not-flatpak-bwrap
test ! -e DESTDIR-as-subproject/usr/local/bin/bwrap
test ! -e DESTDIR-as-subproject/usr/local/libexec/bwrap
- name: Upload test logs
uses: actions/upload-artifact@v1
if: failure() || cancelled()
with:
name: test logs
path: test-logs
clang:
name: Build with clang and analyze
runs-on: ubuntu-latest

View File

@ -5,11 +5,21 @@ EXTRA_DIST = \
.editorconfig \
README.md \
autogen.sh \
completions/bash/meson.build \
completions/meson.build \
completions/zsh/meson.build \
demos/bubblewrap-shell.sh \
demos/flatpak-run.sh \
demos/flatpak.bpf \
demos/userns-block-fd.py \
meson.build \
meson_options.txt \
packaging/bubblewrap.spec \
tests/meson.build \
tests/use-as-subproject/README \
tests/use-as-subproject/config.h \
tests/use-as-subproject/dummy-config.h.in \
tests/use-as-subproject/meson.build \
uncrustify.cfg \
uncrustify.sh \
$(NULL)

View File

@ -64,6 +64,7 @@ if dpkg-vendor --derives-from Debian; then
libcap-dev \
libselinux1-dev \
libtool \
meson \
pkg-config \
python3 \
xsltproc \
@ -92,6 +93,7 @@ if command -v yum; then
libubsan \
libxslt \
make \
meson \
redhat-rpm-config \
rsync \
${NULL+}

View File

@ -0,0 +1,37 @@
bash_completion_dir = get_option('bash_completion_dir')
if bash_completion_dir == ''
bash_completion = dependency(
'bash-completion',
version : '>=2.0',
required : false,
)
if bash_completion.found()
if meson.version().version_compare('>=0.51.0')
bash_completion_dir = bash_completion.get_variable(
default_value: '',
pkgconfig: 'completionsdir',
pkgconfig_define: [
'prefix', get_option('prefix'),
'datadir', get_option('prefix') / get_option('datadir'),
],
)
else
bash_completion_dir = bash_completion.get_pkgconfig_variable(
'completionsdir',
default: '',
define_variable: [
'prefix', get_option('prefix'),
'datadir', get_option('prefix') / get_option('datadir'),
],
)
endif
endif
endif
if bash_completion_dir == ''
bash_completion_dir = get_option('datadir') / 'bash-completion' / 'completions'
endif
install_data('bwrap', install_dir : bash_completion_dir)

7
completions/meson.build Normal file
View File

@ -0,0 +1,7 @@
if get_option('bash_completion').enabled()
subdir('bash')
endif
if get_option('zsh_completion').enabled()
subdir('zsh')
endif

View File

@ -0,0 +1,7 @@
zsh_completion_dir = get_option('zsh_completion_dir')
if zsh_completion_dir == ''
zsh_completion_dir = get_option('datadir') / 'zsh' / 'site-functions'
endif
install_data('_bwrap', install_dir : zsh_completion_dir)

147
meson.build Normal file
View File

@ -0,0 +1,147 @@
project(
'bubblewrap',
'c',
version : '0.5.0',
meson_version : '>=0.49.0',
default_options : [
'warning_level=2',
],
)
cc = meson.get_compiler('c')
add_project_arguments('-D_GNU_SOURCE', language : 'c')
# Keep this in sync with ostree, except remove -Wall (part of Meson
# warning_level 2) and -Werror=declaration-after-statement
add_project_arguments(
cc.get_supported_arguments([
'-Werror=shadow',
'-Werror=empty-body',
'-Werror=strict-prototypes',
'-Werror=missing-prototypes',
'-Werror=implicit-function-declaration',
'-Werror=pointer-arith',
'-Werror=init-self',
'-Werror=missing-declarations',
'-Werror=return-type',
'-Werror=overflow',
'-Werror=int-conversion',
'-Werror=parenthesis',
'-Werror=incompatible-pointer-types',
'-Werror=misleading-indentation',
'-Werror=missing-include-dirs',
'-Werror=aggregate-return',
# Extra warnings specific to bubblewrap
'-Werror=switch-default',
'-Wswitch-enum',
# Meson warning_level=2 would do this, but we are not fully
# signedness-safe yet
'-Wno-sign-compare',
'-Wno-error=sign-compare',
# Deliberately not warning about these, ability to zero-initialize
# a struct is a feature
'-Wno-missing-field-initializers',
'-Wno-error=missing-field-initializers',
]),
language : 'c',
)
if (
cc.has_argument('-Werror=format=2')
and cc.has_argument('-Werror=format-security')
and cc.has_argument('-Werror=format-nonliteral')
)
add_project_arguments([
'-Werror=format=2',
'-Werror=format-security',
'-Werror=format-nonliteral',
], language : 'c')
endif
sh = find_program('sh', required : true)
bash = find_program('bash', required : false)
libcap_dep = dependency('libcap', required : true)
selinux_dep = dependency(
'libselinux',
version : '>=2.1.9',
# if disabled, Meson will behave as though libselinux was not found
required : get_option('selinux'),
)
cdata = configuration_data()
cdata.set_quoted(
'PACKAGE_STRING',
'@0@ @1@'.format(meson.project_name(), meson.project_version()),
)
if selinux_dep.found()
cdata.set('HAVE_SELINUX', 1)
if selinux_dep.version().version_compare('>=2.3')
cdata.set('HAVE_SELINUX_2_3', 1)
endif
endif
if get_option('require_userns')
cdata.set('ENABLE_REQUIRE_USERNS', 1)
endif
configure_file(
output : 'config.h',
configuration : cdata,
)
if meson.is_subproject()
bwrapdir = get_option('libexecdir')
if get_option('program_prefix') == ''
error('program_prefix option must be set when bwrap is a subproject')
endif
else
bwrapdir = get_option('bindir')
endif
bwrap = executable(
get_option('program_prefix') + 'bwrap',
[
'bubblewrap.c',
'bind-mount.c',
'network.c',
'utils.c',
],
install : true,
install_dir : bwrapdir,
dependencies : [selinux_dep, libcap_dep],
)
xsltproc = find_program('xsltproc', required : get_option('man'))
if xsltproc.found() and not meson.is_subproject()
custom_target(
'bwrap.1',
output : 'bwrap.1',
input : 'bwrap.xml',
command : [
xsltproc,
'--nonet',
'--stringparam', 'man.output.quietly', '1',
'--stringparam', 'funcsynopsis.style', 'ansi',
'--stringparam', 'man.th.extra1.suppress', '1',
'--stringparam', 'man.authors.section.enabled', '0',
'--stringparam', 'man.copyright.section.enabled', '0',
'-o', '@OUTPUT@',
'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl',
'@INPUT@',
],
install : true,
install_dir : get_option('mandir') / 'man1',
)
endif
if not meson.is_subproject()
subdir('completions')
endif

47
meson_options.txt Normal file
View File

@ -0,0 +1,47 @@
option(
'bash_completion',
type : 'feature',
description : 'install bash completion script',
value : 'enabled',
)
option(
'bash_completion_dir',
type : 'string',
description : 'install bash completion script in this directory',
value : '',
)
option(
'man',
type : 'feature',
description : 'generate man pages',
value : 'auto',
)
option(
'program_prefix',
type : 'string',
description : 'Prepend string to bwrap executable name, for use with subprojects',
)
option(
'require_userns',
type : 'boolean',
description : 'require user namespaces by default when installed setuid',
value : 'false',
)
option(
'selinux',
type : 'feature',
description : 'enable optional SELINUX support',
value : 'auto',
)
option(
'zsh_completion',
type : 'feature',
description : 'install zsh completion script',
value : 'enabled',
)
option(
'zsh_completion_dir',
type : 'string',
description : 'install zsh completion script in this directory',
value : '',
)

59
tests/meson.build Normal file
View File

@ -0,0 +1,59 @@
test_programs = [
['test-utils', executable(
'test-utils',
'test-utils.c',
'../utils.c',
'../utils.h',
dependencies : [selinux_dep],
)],
]
test_scripts = [
'test-run.sh',
'test-seccomp.py',
'test-specifying-pidns.sh',
'test-specifying-userns.sh',
]
test_env = environment()
test_env.set('BWRAP', bwrap.full_path())
test_env.set('G_TEST_BUILDDIR', meson.current_build_dir())
test_env.set('G_TEST_SRCDIR', meson.current_source_dir())
foreach pair : test_programs
name = pair[0]
test_program = pair[1]
if meson.version().version_compare('>=0.50.0')
test(
name,
test_program,
env : test_env,
protocol : 'tap',
)
else
test(
name,
test_program,
env : test_env,
)
endif
endforeach
foreach test_script : test_scripts
if meson.version().version_compare('>=0.50.0')
test(
test_script,
bash,
args : [test_script],
env : test_env,
protocol : 'tap',
)
else
test(
test_script,
bash,
args : [test_script],
env : test_env,
)
endif
endforeach

2
tests/use-as-subproject/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/_build/
/subprojects/

View File

@ -0,0 +1,3 @@
This is a simple example of a project that uses bubblewrap as a
subproject. The intention is that if this project can successfully build
bubblewrap as a subproject, then so could Flatpak.

View File

@ -0,0 +1 @@
#error Should not use superproject config.h to compile bubblewrap

View File

@ -0,0 +1 @@
#error Should not use superproject generated config.h to compile bubblewrap

View File

@ -0,0 +1,19 @@
project(
'use-bubblewrap-as-subproject',
'c',
version : '0',
meson_version : '>=0.49.0',
)
configure_file(
output : 'config.h',
input : 'dummy-config.h.in',
configuration : configuration_data(),
)
subproject(
'bubblewrap',
default_options : [
'program_prefix=not-flatpak-',
],
)