diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index d84fb793..ceb3b82d 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 9c8145e0..94ef77f7 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 65fa8b42..4accd2e3 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 00000000..92ef27fc --- /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 00000000..958c90a2 --- /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 00000000..7bda727c --- /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 00000000..159ab2b3 --- /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 00000000..aa88a2fc --- /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 00000000..389658f6 --- /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 00000000..371a7d97 --- /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 00000000..97d2e88e --- /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 00000000..4a99af4d --- /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 00000000..1d1e56a8 --- /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 00000000..802fd61c --- /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-', + ], +)