diff options
author | Simon McVittie <smcv@collabora.com> | 2021-05-05 12:16:20 +0100 |
---|---|---|
committer | Simon McVittie <smcv@collabora.com> | 2022-02-18 10:42:55 +0000 |
commit | 19279812409265b2bb86edf465d47c529ed9226d (patch) | |
tree | 18c076e6f65c553e5aafe15c138de545124da0e9 | |
parent | 43c2d32d7f03ce502c248ff6575c1ce5e205ecd5 (diff) | |
download | bubblewrap-19279812409265b2bb86edf465d47c529ed9226d.tar.gz |
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>
-rw-r--r-- | .github/workflows/check.yml | 67 | ||||
-rw-r--r-- | Makefile.am | 10 | ||||
-rwxr-xr-x | ci/builddeps.sh | 2 | ||||
-rw-r--r-- | completions/bash/meson.build | 37 | ||||
-rw-r--r-- | completions/meson.build | 7 | ||||
-rw-r--r-- | completions/zsh/meson.build | 7 | ||||
-rw-r--r-- | meson.build | 147 | ||||
-rw-r--r-- | meson_options.txt | 47 | ||||
-rw-r--r-- | tests/meson.build | 59 | ||||
-rw-r--r-- | tests/use-as-subproject/.gitignore | 2 | ||||
-rw-r--r-- | tests/use-as-subproject/README | 3 | ||||
-rw-r--r-- | tests/use-as-subproject/config.h | 1 | ||||
-rw-r--r-- | tests/use-as-subproject/dummy-config.h.in | 1 | ||||
-rw-r--r-- | tests/use-as-subproject/meson.build | 19 |
14 files changed, 408 insertions, 1 deletions
diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index ce561e2..397ff3e 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -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 diff --git a/Makefile.am b/Makefile.am index 9c8145e..94ef77f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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) diff --git a/ci/builddeps.sh b/ci/builddeps.sh index 65fa8b4..4accd2e 100755 --- a/ci/builddeps.sh +++ b/ci/builddeps.sh @@ -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+} diff --git a/completions/bash/meson.build b/completions/bash/meson.build new file mode 100644 index 0000000..92ef27f --- /dev/null +++ b/completions/bash/meson.build @@ -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) diff --git a/completions/meson.build b/completions/meson.build new file mode 100644 index 0000000..958c90a --- /dev/null +++ b/completions/meson.build @@ -0,0 +1,7 @@ +if get_option('bash_completion').enabled() + subdir('bash') +endif + +if get_option('zsh_completion').enabled() + subdir('zsh') +endif diff --git a/completions/zsh/meson.build b/completions/zsh/meson.build new file mode 100644 index 0000000..7bda727 --- /dev/null +++ b/completions/zsh/meson.build @@ -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) diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..159ab2b --- /dev/null +++ b/meson.build @@ -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 diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 0000000..aa88a2f --- /dev/null +++ b/meson_options.txt @@ -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 : '', +) diff --git a/tests/meson.build b/tests/meson.build new file mode 100644 index 0000000..389658f --- /dev/null +++ b/tests/meson.build @@ -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 diff --git a/tests/use-as-subproject/.gitignore b/tests/use-as-subproject/.gitignore new file mode 100644 index 0000000..371a7d9 --- /dev/null +++ b/tests/use-as-subproject/.gitignore @@ -0,0 +1,2 @@ +/_build/ +/subprojects/ diff --git a/tests/use-as-subproject/README b/tests/use-as-subproject/README new file mode 100644 index 0000000..97d2e88 --- /dev/null +++ b/tests/use-as-subproject/README @@ -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. diff --git a/tests/use-as-subproject/config.h b/tests/use-as-subproject/config.h new file mode 100644 index 0000000..4a99af4 --- /dev/null +++ b/tests/use-as-subproject/config.h @@ -0,0 +1 @@ +#error Should not use superproject config.h to compile bubblewrap diff --git a/tests/use-as-subproject/dummy-config.h.in b/tests/use-as-subproject/dummy-config.h.in new file mode 100644 index 0000000..1d1e56a --- /dev/null +++ b/tests/use-as-subproject/dummy-config.h.in @@ -0,0 +1 @@ +#error Should not use superproject generated config.h to compile bubblewrap diff --git a/tests/use-as-subproject/meson.build b/tests/use-as-subproject/meson.build new file mode 100644 index 0000000..802fd61 --- /dev/null +++ b/tests/use-as-subproject/meson.build @@ -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-', + ], +) |