diff options
author | David Robillard <d@drobilla.net> | 2022-06-27 12:59:34 -0400 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2022-07-10 13:39:53 -0400 |
commit | 5e78edf6e09373938a796cf44fb38d2309d04b4d (patch) | |
tree | e80618e5e092dcb3d9501a2568cf67489fe3d1bd | |
parent | bcc1c936b15782d8fa59e2ebf471cf686527135c (diff) | |
download | serd-5e78edf6e09373938a796cf44fb38d2309d04b4d.tar.gz serd-5e78edf6e09373938a796cf44fb38d2309d04b4d.tar.bz2 serd-5e78edf6e09373938a796cf44fb38d2309d04b4d.zip |
Switch to meson build system
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | .gitlab-ci.yml | 125 | ||||
-rw-r--r-- | .gitmodules | 3 | ||||
-rw-r--r-- | INSTALL.md | 92 | ||||
-rw-r--r-- | NEWS | 3 | ||||
-rw-r--r-- | doc/_static/serd.svg (renamed from resources/serd.svg) | 0 | ||||
-rw-r--r-- | doc/c/Doxyfile.in (renamed from doc/c/Doxyfile) | 9 | ||||
-rw-r--r-- | doc/c/api/meson.build | 6 | ||||
-rw-r--r-- | doc/c/meson.build | 52 | ||||
-rw-r--r-- | doc/c/wscript | 41 | ||||
-rw-r--r-- | doc/c/xml/meson.build | 18 | ||||
-rw-r--r-- | doc/conf.py.in | 7 | ||||
-rw-r--r-- | doc/meson.build | 38 | ||||
-rw-r--r-- | meson.build | 151 | ||||
-rw-r--r-- | meson/library/meson.build | 31 | ||||
-rw-r--r-- | meson/suppressions/meson.build | 68 | ||||
-rw-r--r-- | meson/warnings/meson.build | 175 | ||||
-rw-r--r-- | meson_options.txt | 17 | ||||
-rw-r--r-- | serd.pc.in | 11 | ||||
-rw-r--r-- | test/meson.build | 198 | ||||
-rwxr-xr-x | test/test_quiet.py | 23 | ||||
-rwxr-xr-x | test/test_stdin.py | 38 | ||||
-rwxr-xr-x | test/test_write_error.py | 31 | ||||
-rwxr-xr-x | waf | 27 | ||||
m--------- | waflib | 0 | ||||
-rw-r--r-- | wscript | 504 |
26 files changed, 962 insertions, 708 deletions
@@ -1,4 +1,2 @@ build/** -.waf-* -.lock-waf* __pycache__ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 41438bd3..764055b4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -5,174 +5,161 @@ variables: .build_template: &build_definition stage: build - artifacts: - paths: ["build/", ".lock-waf*"] - arm32_dbg: <<: *build_definition image: lv2plugin/debian-arm32 script: - - python3 ./waf configure build test -dST --no-coverage --werror --wrapper=qemu-arm-static - variables: - AR: "arm-linux-gnueabihf-ar" - CC: "arm-linux-gnueabihf-gcc" - CFLAGS: "-O0 -g -D_FILE_OFFSET_BITS=64" - CXX: "arm-linux-gnueabihf-g++" - CXXFLAGS: "-O0 -g -D_FILE_OFFSET_BITS=64" + - meson setup build --cross-file=/usr/share/meson/cross/arm-linux-gnueabihf.ini -Dbuildtype=debug -Ddocs=disabled -Dstrict=true -Dwerror=true + - ninja -C build test arm32_rel: <<: *build_definition image: lv2plugin/debian-arm32 script: - - python3 ./waf configure build -ST --werror --no-coverage --wrapper=qemu-arm-static - variables: - AR: "arm-linux-gnueabihf-ar" - CC: "arm-linux-gnueabihf-gcc" - CFLAGS: "-O2 -DNDEBUG -D_FILE_OFFSET_BITS=64" - CXX: "arm-linux-gnueabihf-g++" - CXXFLAGS: "-O2 -DNDEBUG -D_FILE_OFFSET_BITS=64" + - meson setup build --cross-file=/usr/share/meson/cross/arm-linux-gnueabihf.ini -Dbuildtype=release -Ddocs=disabled -Dstrict=true -Dwerror=true + - ninja -C build test arm64_dbg: <<: *build_definition image: lv2plugin/debian-arm64 script: - - python3 ./waf configure build test -dST --werror --no-coverage --wrapper=qemu-aarch64-static - variables: - AR: "aarch64-linux-gnu-ar" - CC: "aarch64-linux-gnu-gcc" - CXX: "aarch64-linux-gnu-g++" + - meson setup build --cross-file=/usr/share/meson/cross/aarch64-linux-gnu.ini -Dbuildtype=debug -Ddocs=disabled -Dstrict=true -Dwerror=true + - ninja -C build test arm64_rel: <<: *build_definition image: lv2plugin/debian-arm64 script: - - python3 ./waf configure build test -ST --werror --no-coverage --wrapper=qemu-aarch64-static - variables: - AR: "aarch64-linux-gnu-ar" - CC: "aarch64-linux-gnu-gcc" - CXX: "aarch64-linux-gnu-g++" + - meson setup build --cross-file=/usr/share/meson/cross/aarch64-linux-gnu.ini -Dbuildtype=release -Ddocs=disabled -Dstrict=true -Dwerror=true + - ninja -C build test x64_dbg: <<: *build_definition image: lv2plugin/debian-x64 script: - - python3 ./waf configure build test -dST --werror --docs + - meson setup build -Dbuildtype=debug -Ddocs=enabled -Dstrict=true -Dwerror=true -Db_coverage=true + - ninja -C build test + - ninja -C build coverage-html - mkdir -p build/doc/ - cp doc/*.svg build/doc/ - - cp doc/mandoc.css build/doc/ - - mandoc -Thtml -Werror -O style=mandoc.css doc/serdi.1 > build/doc/serdi.html artifacts: paths: - build/doc - - build/coverage + - build/meson-logs/coveragereport x64_rel: <<: *build_definition image: lv2plugin/debian-x64 script: - - python3 ./waf configure build test -ST --werror + - meson setup build -Dbuildtype=release -Ddocs=disabled -Dstrict=true -Dwerror=true + - ninja -C build test + x64_static: <<: *build_definition image: lv2plugin/debian-x64 script: - - python3 ./waf configure build test -ST --werror --no-posix --static-progs + - meson setup build -Ddocs=disabled -Dstrict=true -Dwerror=true -Dstatic=true -Ddefault_library=static + - ninja -C build test + x64_sanitize: <<: *build_definition image: lv2plugin/debian-x64-clang script: - - python3 ./waf configure build test -ST --werror --no-coverage + - meson setup build -Db_lundef=false -Dbuildtype=plain -Ddocs=disabled -Dstrict=true -Dwerror=true + - ninja -C build test variables: CC: "clang" - CFLAGS: "-fno-sanitize-recover=all -fsanitize=address -fsanitize=undefined -fsanitize=float-divide-by-zero -fsanitize=unsigned-integer-overflow -fsanitize=implicit-conversion -fsanitize=local-bounds -fsanitize=nullability" CXX: "clang++" + CFLAGS: "-fno-sanitize-recover=all -fsanitize=address -fsanitize=undefined -fsanitize=float-divide-by-zero -fsanitize=unsigned-integer-overflow -fsanitize=implicit-conversion -fsanitize=local-bounds -fsanitize=nullability" CXXFLAGS: "-fno-sanitize-recover=all -fsanitize=address -fsanitize=undefined -fsanitize=float-divide-by-zero -fsanitize=unsigned-integer-overflow -fsanitize=implicit-conversion -fsanitize=local-bounds -fsanitize=nullability" - LINKFLAGS: "-fno-sanitize-recover=all -fsanitize=address -fsanitize=undefined -fsanitize=float-divide-by-zero -fsanitize=unsigned-integer-overflow -fsanitize=implicit-conversion -fsanitize=local-bounds -fsanitize=nullability" + LDFLAGS: "-fno-sanitize-recover=all -fsanitize=address -fsanitize=undefined -fsanitize=float-divide-by-zero -fsanitize=unsigned-integer-overflow -fsanitize=implicit-conversion -fsanitize=local-bounds -fsanitize=nullability" mingw32_dbg: <<: *build_definition image: lv2plugin/debian-mingw32 script: - - python3 ./waf configure build -dST --werror --no-coverage --wrapper=wine + - meson setup build --cross-file=/usr/share/meson/cross/i686-w64-mingw32.ini -Dbuildtype=debug -Ddocs=disabled -Dstrict=true -Dwerror=true + - ninja -C build variables: - AR: "i686-w64-mingw32-ar" - CC: "i686-w64-mingw32-gcc" - CXX: "i686-w64-mingw32-g++" + WINEPATH: "Z:\\usr\\lib\\gcc\\i686-w64-mingw32\\10-win32" mingw32_rel: <<: *build_definition image: lv2plugin/debian-mingw32 script: - - python3 ./waf configure build -ST --werror --no-coverage --wrapper=wine + - meson setup build --cross-file=/usr/share/meson/cross/i686-w64-mingw32.ini -Dbuildtype=release -Ddocs=disabled -Dstrict=true -Dwerror=true + - ninja -C build variables: - AR: "i686-w64-mingw32-ar" - CC: "i686-w64-mingw32-gcc" - CXX: "i686-w64-mingw32-g++" + WINEPATH: "Z:\\usr\\lib\\gcc\\i686-w64-mingw32\\10-win32" mingw64_dbg: <<: *build_definition image: lv2plugin/debian-mingw64 script: - - python3 ./waf configure build -dST --werror --no-coverage --wrapper=wine + - meson setup build --cross-file=/usr/share/meson/cross/x86_64-w64-mingw32.ini -Dbuildtype=debug -Ddocs=disabled -Dstrict=true -Dwerror=true + - ninja -C build test variables: - AR: "x86_64-w64-mingw32-ar" - CC: "x86_64-w64-mingw32-gcc" - CXX: "x86_64-w64-mingw32-g++" + WINEPATH: "Z:\\usr\\lib\\gcc\\x86_64-w64-mingw32\\8.3-win32" mingw64_rel: <<: *build_definition image: lv2plugin/debian-mingw64 script: - - python3 ./waf configure build -ST --werror --no-coverage --wrapper=wine + - meson setup build --cross-file=/usr/share/meson/cross/x86_64-w64-mingw32.ini -Dbuildtype=release -Ddocs=disabled -Dstrict=true -Dwerror=true + - ninja -C build test variables: - AR: "x86_64-w64-mingw32-ar" - CC: "x86_64-w64-mingw32-gcc" - CXX: "x86_64-w64-mingw32-g++" + WINEPATH: "Z:\\usr\\lib\\gcc\\x86_64-w64-mingw32\\8.3-win32" mac_dbg: <<: *build_definition - script: - - python ./waf configure build test -dST --werror --no-coverage tags: [macos] + script: + - meson setup build -Dbuildtype=debug -Ddocs=disabled -Dstrict=true -Dwerror=true + - ninja -C build test mac_rel: <<: *build_definition - script: - - python ./waf configure build test -ST --werror --no-coverage tags: [macos] + script: + - meson setup build -Dbuildtype=release -Ddocs=disabled -Dstrict=true -Dwerror=true + - ninja -C build test win_dbg: <<: *build_definition + tags: [windows,meson] script: - - python ./waf configure build test -dST --werror --no-coverage - tags: [windows,msvc,python] + - meson setup build -Dbuildtype=debug -Ddocs=disabled -Dstrict=true -Dwerror=true + - ninja -C build test win_rel: <<: *build_definition + tags: [windows,meson] script: - - python ./waf configure build test -ST --werror --no-coverage - tags: [windows,msvc,python] - + - meson setup build -Dbuildtype=release -Ddocs=disabled -Dstrict=true -Dwerror=true + - ninja -C build test pages: stage: deploy script: - - mkdir -p .public/doc - - mkdir -p .public/man - - mkdir -p .public/c - - mv build/doc/c/singlehtml .public/c/singlehtml - - mv build/doc/c/html .public/c/html - - mv build/doc/serdi.html .public/man/serdi.html - - mv build/doc/mandoc.css .public/man/mandoc.css - - mv .public public + - mkdir public + - mkdir public/c + - mkdir public/man + - mv build/meson-logs/coveragereport/ public/coverage + - mv build/doc/c/html/ public/c/html/ + - mv build/doc/c/singlehtml/ public/c/singlehtml/ + - mv build/doc/serdi.html public/man/serdi.html + - mv build/doc/mandoc.css public/man/mandoc.css + needs: + - x64_dbg artifacts: paths: - public diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index b2babe71..00000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "waflib"] - path = waflib - url = ../autowaf.git @@ -1,66 +1,70 @@ Installation Instructions ========================= -Basic Installation ------------------- +Prerequisites +------------- -Building this software requires only Python. To install with default options: +To build from source, you will need: - ./waf configure - ./waf - ./waf install # or sudo ./waf install + * A relatively modern C compiler (GCC, Clang, and MSVC are known to work). -Configuration Options ---------------------- + * [Meson](http://mesonbuild.com/), which depends on + [Python](http://python.org/). -All supported options can be viewed using the command: +This is a brief overview of building this project with meson. See the meson +documentation for more detailed information. - ./waf --help +Configuration +------------- -Most options only need to be passed during the configure stage, for example: +The build is configured with the `setup` command, which creates a new build +directory with the given name: - ./waf configure --prefix=/usr - ./waf - ./waf install + meson setup build -Compiler Configuration ----------------------- +Some environment variables are read during `setup` and stored with the +configuration: -Several standard environment variables can be used to control how compilers are -invoked: + * `CC`: Path to C compiler. + * `CFLAGS`: C compiler options. + * `LDFLAGS`: Linker options. - * CC: Path to C compiler - * CFLAGS: C compiler options - * CXX: Path to C++ compiler - * CXXFLAGS: C++ compiler options - * CPPFLAGS: C preprocessor options - * LINKFLAGS: Linker options +However, it is better to use meson options for configuration. All options can +be inspected with the `configure` command from within the build directory: -Library Versioning ------------------- + cd build + meson configure -This library uses semantic versioning <http://semver.org/>. +Options can be set by passing C-style "define" options to `configure`: -Several major versions can be installed in parallel. The shared library name, -include directory, and pkg-config file are suffixed with the major version -number. For example, a library named "foo" at version 1.x.y might install: + meson configure -Dc_args="-march=native" -Dprefix="/opt/mypackage/" - /usr/include/foo-1/foo/foo.h - /usr/lib/foo-1.so.1.x.y - /usr/lib/pkgconfig/foo-1.pc +Building +-------- -Dependencies can check for the package "foo-1" with pkg-config. +From within a configured build directory, everything can be built with the +`compile` command: -Packaging ---------- + meson compile -Everything can be installed to a specific root directory by passing a --destdir -option to the install stage (or setting the DESTDIR environment variable), -which adds a prefix to all install paths. For example: +Similarly, tests can be run with the `test` command: - ./waf configure --prefix=/usr - ./waf - ./waf install --destdir=/tmp/package + meson test -Packages should allow parallel installation of several major versions. For -example, the above would be packaged as "foo-1".
\ No newline at end of file +Meson can also generate a project for several popular IDEs, see the `backend` +option for details. + +Installation +------------ + +A compiled project can be installed with the `install` command: + + meson install + +You may need to acquire root permissions to install to a system-wide prefix. +For packaging, the installation may be staged to a directory using the +`DESTDIR` environment variable or the `--destdir` option: + + DESTDIR=/tmp/mypackage/ meson install + + meson install --destdir=/tmp/mypackage/ @@ -1,9 +1,10 @@ serd (0.30.13) unstable; * Fix memory consumption when reading documents + * Switch to Meson build system * Update README and project metadata - -- David Robillard <d@drobilla.net> Sun, 12 Jun 2022 23:37:20 +0000 + -- David Robillard <d@drobilla.net> Sun, 12 Jun 2022 23:37:46 +0000 serd (0.30.12) stable; diff --git a/resources/serd.svg b/doc/_static/serd.svg index 570a4f98..570a4f98 100644 --- a/resources/serd.svg +++ b/doc/_static/serd.svg diff --git a/doc/c/Doxyfile b/doc/c/Doxyfile.in index 600e0e94..12d0440b 100644 --- a/doc/c/Doxyfile +++ b/doc/c/Doxyfile.in @@ -2,12 +2,13 @@ PROJECT_NAME = Serd PROJECT_BRIEF = "A lightweight C library for working with RDF data" QUIET = YES -WARN_AS_ERROR = NO +WARN_AS_ERROR = YES WARN_IF_UNDOCUMENTED = NO WARN_NO_PARAMDOC = NO JAVADOC_AUTOBRIEF = YES +FULL_PATH_NAMES = NO CASE_SENSE_NAMES = YES HIDE_IN_BODY_DOCS = YES REFERENCES_LINK_SOURCE = NO @@ -28,6 +29,8 @@ PREDEFINED = SERD_ALLOCATED \ SERD_NULLABLE= \ SERD_PURE_FUNC= -INPUT = ../../include/serd/serd.h +RECURSIVE = YES +STRIP_FROM_PATH = @SERD_SRCDIR@ +INPUT = @SERD_SRCDIR@/include -OUTPUT_DIRECTORY = . +OUTPUT_DIRECTORY = @DOX_OUTPUT@ diff --git a/doc/c/api/meson.build b/doc/c/api/meson.build new file mode 100644 index 00000000..bc75345a --- /dev/null +++ b/doc/c/api/meson.build @@ -0,0 +1,6 @@ +c_serd_rst = custom_target( + 'serd.rst', + command: [dox_to_sphinx, '-f', '@INPUT0@', '@OUTDIR@'], + input: [c_index_xml] + c_rst_files, + output: 'serd.rst', +) diff --git a/doc/c/meson.build b/doc/c/meson.build new file mode 100644 index 00000000..ee104b13 --- /dev/null +++ b/doc/c/meson.build @@ -0,0 +1,52 @@ +config = configuration_data() +config.set('SERD_VERSION', meson.project_version()) +config.set('SERD_SRCDIR', serd_src_root) +config.set('SERD_TITLE', get_option('title')) + +conf_py = configure_file( + configuration: config, + input: files('../conf.py.in'), + output: 'conf.py', +) + +configure_file( + copy: true, + input: files('../summary.rst'), + output: 'summary.rst', +) + +c_rst_files = files( + 'index.rst', + 'overview.rst', +) + +foreach f : c_rst_files + configure_file(copy: true, input: f, output: '@PLAINNAME@') +endforeach + +subdir('xml') +subdir('api') + +# TODO: Add install_tag: 'doc' after requiring meson 0.60.0 + +custom_target( + 'singlehtml', + build_by_default: true, + command: [sphinx_build, '-M', 'singlehtml', '@OUTDIR@', '@OUTDIR@', + '-E', '-q', '-t', 'singlehtml'], + input: [c_rst_files, c_serd_rst, c_index_xml], + install: true, + install_dir: docdir / versioned_name, + output: 'singlehtml', +) + +custom_target( + 'html', + build_by_default: true, + command: [sphinx_build, '-M', 'html', '@OUTDIR@', '@OUTDIR@', + '-E', '-q', '-t', 'html'], + input: [c_rst_files, c_serd_rst, c_index_xml], + install: true, + install_dir: docdir / versioned_name, + output: 'html', +) diff --git a/doc/c/wscript b/doc/c/wscript deleted file mode 100644 index 9a524e3a..00000000 --- a/doc/c/wscript +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python - -def build(bld): - dox_to_sphinx = bld.path.find_node("../../scripts/dox_to_sphinx.py") - index_xml = bld.path.get_bld().make_node("xml/index.xml") - - files = [ - ("../../resources/serd.svg", "sphinx/_static/serd.svg"), - ("../summary.rst", "sphinx/summary.rst"), - ("index.rst", "sphinx/index.rst"), - ("overview.rst", "sphinx/overview.rst"), - ] - - # Run Doxygen to generate XML documentation - bld(features="doxygen", doxyfile="Doxyfile") - - # Substitute variables to make Sphinx configuration file - bld(features="subst", - source="../conf.py.in", - target="sphinx/conf.py", - SERD_VERSION=bld.env.SERD_VERSION) - - # Copy static documentation files to Sphinx build directory - for f in files: - bld(features="subst", is_copy=True, source=f[0], target=f[1]) - - # Generate Sphinx markup from Doxygen XML - bld.add_group() - bld(rule="${PYTHON} " + dox_to_sphinx.abspath() + " -f ${SRC} ${TGT}", - source=index_xml, - target="sphinx/api/") - - doc_dir = bld.env.DOCDIR + "/serd-%s/" % bld.env.SERD_MAJOR_VERSION - - # Run Sphinx to generate HTML documentation - for builder in ["html", "singlehtml"]: - bld(features="sphinx", - sphinx_source=bld.path.get_bld().make_node("sphinx"), - sphinx_output_format=builder, - sphinx_options=["-E", "-q", "-t", builder], - install_path=doc_dir + "c/%s/" % builder) diff --git a/doc/c/xml/meson.build b/doc/c/xml/meson.build new file mode 100644 index 00000000..a49e2754 --- /dev/null +++ b/doc/c/xml/meson.build @@ -0,0 +1,18 @@ +doxygen = find_program('doxygen') + +config = configuration_data() +config.set('SERD_SRCDIR', serd_src_root) +config.set('DOX_OUTPUT', meson.current_build_dir() / '..') + +c_doxyfile = configure_file( + configuration: config, + input: files('../Doxyfile.in'), + output: 'Doxyfile', +) + +c_index_xml = custom_target( + 'index.xml', + command: [doxygen, '@INPUT0@'], + input: [c_doxyfile] + c_headers, + output: 'index.xml', +) diff --git a/doc/conf.py.in b/doc/conf.py.in index de3f183c..af0ea25a 100644 --- a/doc/conf.py.in +++ b/doc/conf.py.in @@ -1,6 +1,6 @@ # Project information -project = "Serd" +project = "@SERD_TITLE@" copyright = "2022, David Robillard" author = "David Robillard" release = "@SERD_VERSION@" @@ -47,9 +47,10 @@ nitpick_ignore = list(map(lambda x: ("c:identifier", x), _opaque)) # HTML output -html_static_path = ["_static"] html_copy_source = False -html_short_title = "Serd" +html_short_title = "@SERD_TITLE@" +html_static_path = ["@SERD_SRCDIR@/doc/_static"] +html_theme = "sphinx_lv2_theme" if have_lv2_theme: html_theme = "sphinx_lv2_theme" diff --git a/doc/meson.build b/doc/meson.build new file mode 100644 index 00000000..13ce7f1d --- /dev/null +++ b/doc/meson.build @@ -0,0 +1,38 @@ +docdir = get_option('datadir') / 'doc' + +doxygen = find_program('doxygen', required: get_option('docs')) +dox_to_sphinx = files('../scripts/dox_to_sphinx.py') +sphinx_build = find_program('sphinx-build', required: get_option('docs')) +mandoc = find_program('mandoc', required: false) + +build_docs = doxygen.found() and sphinx_build.found() + +if build_docs + subdir('c') +endif + +mandoc_css = files('mandoc.css') + +if not get_option('tests').disabled() + stylelint = find_program('stylelint', required: false) + if stylelint.found() + test('stylelint', stylelint, args: [mandoc_css], suite: 'data') + endif +endif + +if mandoc.found() + configure_file(input: mandoc_css, output: '@PLAINNAME@', copy: true) + + serdi_html = custom_target( + 'serdi.html', + build_by_default: true, + capture: true, + command: [mandoc, '-Thtml', '-Werror', '-O', 'style=mandoc.css', '@INPUT@'], + input: files('serdi.1'), + output: 'serdi.html', + ) +endif + +if not meson.is_subproject() + summary('API Documentation', build_docs, bool_yn: true) +endif diff --git a/meson.build b/meson.build new file mode 100644 index 00000000..13aec982 --- /dev/null +++ b/meson.build @@ -0,0 +1,151 @@ +# Copyright 2021-2022 David Robillard <d@drobilla.net> +# SPDX-License-Identifier: CC0-1.0 OR ISC + +project('serd', ['c'], + version: '0.30.13', + license: 'ISC', + meson_version: '>= 0.56.0', + default_options: [ + 'b_ndebug=if-release', + 'buildtype=release', + 'c_std=c99', + ]) + +serd_src_root = meson.current_source_dir() +major_version = meson.project_version().split('.')[0] +version_suffix = '-@0@'.format(major_version) +versioned_name = 'serd' + version_suffix + +####################### +# Compilers and Flags # +####################### + +# Required tools +pkg = import('pkgconfig') +cc = meson.get_compiler('c') + +# Set global warning flags +if get_option('strict') and not meson.is_subproject() + subdir('meson/warnings') +endif +subdir('meson/suppressions') + +################ +# Dependencies # +################ + +m_dep = cc.find_library('m', required: false) + +########### +# Library # +########### + +include_dirs = include_directories('include') + +c_headers = files( + 'include/serd/serd.h', +) + +sources = files( + 'src/base64.c', + 'src/byte_source.c', + 'src/env.c', + 'src/n3.c', + 'src/node.c', + 'src/reader.c', + 'src/string.c', + 'src/system.c', + 'src/uri.c', + 'src/writer.c', +) + +# Set appropriate arguments for building against the library type +subdir('meson/library') +if get_option('default_library') == 'static' + add_project_arguments(['-DSERD_STATIC'], language: ['c']) +endif + +# Build shared and/or static library +libserd = library( + meson.project_name() + library_suffix, + sources, + c_args: c_suppressions + [ + '-DSERD_INTERNAL', + '-DSERD_VERSION="@0@"'.format(meson.project_version()), + ], + dependencies: m_dep, + gnu_symbol_visibility: 'hidden', + include_directories: include_dirs, + install: true, + version: meson.project_version()) + +# Declare dependency for internal meson dependants +serd_dep = declare_dependency( + include_directories: include_dirs, + link_with: libserd, +) + +# Generage pkg-config file for external dependants +pkg.generate( + libserd, + description: 'A lightweight library for working with RDF', + filebase: versioned_name, + name: get_option('title'), + subdirs: [versioned_name], + version: meson.project_version(), +) + +# Install header to a versioned include directory +install_headers(c_headers, subdir: versioned_name / 'serd') + +######### +# Tools # +######### + +# Build serdi command line utility +if not get_option('tools').disabled() + tool_link_args = [] + if get_option('static') + tool_link_args += ['-static'] + endif + + serdi = executable( + 'serdi', + files('src/serdi.c'), + c_args: c_suppressions, + dependencies: serd_dep, + install: true, + link_args: tool_link_args, + ) + + if not get_option('docs').disabled() + install_man(files('doc/serdi.1')) + endif +endif + +########### +# Support # +########### + +if not get_option('tests').disabled() + subdir('test') +endif + +if not get_option('docs').disabled() + subdir('doc') +endif + +if not meson.is_subproject() + summary('Tests', not get_option('tests').disabled(), bool_yn: true) + summary('Tools', not get_option('tools').disabled(), bool_yn: true) + + summary('Install prefix', get_option('prefix')) + + summary('Headers', get_option('prefix') / get_option('includedir')) + summary('Libraries', get_option('prefix') / get_option('libdir')) + + if not get_option('tools').disabled() + summary('Executables', get_option('prefix') / get_option('bindir')) + summary('Man pages', get_option('prefix') / get_option('mandir')) + endif +endif diff --git a/meson/library/meson.build b/meson/library/meson.build new file mode 100644 index 00000000..fffc8310 --- /dev/null +++ b/meson/library/meson.build @@ -0,0 +1,31 @@ +# Copyright 2020-2022 David Robillard <d@drobilla.net> +# SPDX-License-Identifier: CC0-1.0 OR ISC + +# General definitions for building libraries. +# +# These are essentially workarounds for Meson/Windows/MSVC. Unfortunately, +# Meson's default_library option doesn't support shared and static builds very +# well. In particular, it's often necessary to define different symbols for +# static and shared builds of libraries so that symbols can be exported. To +# work around this, default_library=both isn't supported on Windows. On other +# platforms with GCC-like compilers, we can support both because symbols can +# safely be exported in the same way (giving them default visibility) in both +# static and shared builds. + +default_library = get_option('default_library') +host_system = host_machine.system() + +# Abort on Windows with default_library=both +if host_system == 'windows' and default_library == 'both' + error('default_library=both is not supported on Windows') +endif + +# Set library_suffix to the suffix for libraries +if host_system == 'windows' and default_library == 'shared' + # Meson appends a version to the name only for DLLs, which leads to + # inconsistent library names, like `mylib-1-1`. So, provide no suffix to + # ultimately get the same name as on other platforms, like `mylib-1`. + library_suffix = '' +else + library_suffix = '-@0@'.format(meson.project_version().split('.')[0]) +endif diff --git a/meson/suppressions/meson.build b/meson/suppressions/meson.build new file mode 100644 index 00000000..a7aabecf --- /dev/null +++ b/meson/suppressions/meson.build @@ -0,0 +1,68 @@ +# Copyright 2020-2022 David Robillard <d@drobilla.net> +# SPDX-License-Identifier: CC0-1.0 OR ISC + +# Project-specific warning suppressions. +# +# This should be used in conjunction with the generic "warnings" sibling that +# enables all reasonable warnings for the compiler. It lives here just to keep +# the top-level meson.build more readable. + +##### +# C # +##### + +if is_variable('cc') + c_suppressions = [] + + if get_option('strict') + if cc.get_id() == 'clang' + c_suppressions += [ + '-Wno-cast-align', + '-Wno-cast-qual', + '-Wno-conversion', + '-Wno-double-promotion', + '-Wno-format-nonliteral', + '-Wno-nullability-extension', + '-Wno-nullable-to-nonnull-conversion', + '-Wno-padded', + '-Wno-reserved-id-macro', + '-Wno-sign-conversion', + ] + + elif cc.get_id() == 'gcc' + c_suppressions += [ + '-Wno-cast-align', + '-Wno-cast-qual', + '-Wno-format-nonliteral', + '-Wno-inline', + '-Wno-padded', + '-Wno-sign-conversion', + '-Wno-switch-default', + '-Wno-unsuffixed-float-constants', + '-Wno-unused-const-variable', + ] + + if host_machine.system() == 'windows' + c_suppressions += [ + '-Wno-float-conversion', + ] + endif + + elif cc.get_id() == 'msvc' + c_suppressions += [ + '/wd4061', # enumerator in switch is not explicitly handled + '/wd4365', # signed/unsigned mismatch + '/wd4514', # unreferenced inline function has been removed + '/wd4706', # assignment within conditional expression + '/wd4710', # function not inlined + '/wd4711', # function selected for automatic inline expansion + '/wd4800', # implicit conversion from int to bool + '/wd4820', # padding added after construct + '/wd4996', # POSIX name for this item is deprecated + '/wd5045', # will insert Spectre mitigation for memory load + ] + endif + endif + + c_suppressions = cc.get_supported_arguments(c_suppressions) +endif diff --git a/meson/warnings/meson.build b/meson/warnings/meson.build new file mode 100644 index 00000000..6780caed --- /dev/null +++ b/meson/warnings/meson.build @@ -0,0 +1,175 @@ +# Copyright 2020-2022 David Robillard <d@drobilla.net> +# SPDX-License-Identifier: CC0-1.0 OR ISC + +# General code to enable approximately all warnings in GCC 12, clang, and MSVC. +# +# This is trivial for clang and MSVC, but GCC doesn't have an "everything" +# option, so we need to enable everything we want explicitly. Wall is assumed, +# but Wextra is not, for stability. +# +# These are collected from common.opt and c.opt in the GCC source, and manually +# curated with the help of the GCC documentation. Warnings that are +# application-specific, historical, or about compatibility between specific +# language revisions are omitted. The intent here is to have roughly the same +# meaning as clang's Weverything: extremely strict, but general. Specifically +# omitted are: +# +# General: +# +# Wabi= +# Waggregate-return +# Walloc-size-larger-than=BYTES +# Walloca-larger-than=BYTES +# Wframe-larger-than=BYTES +# Wlarger-than=BYTES +# Wstack-usage=BYTES +# Wsystem-headers +# Wtraditional +# Wtraditional-conversion +# Wtrampolines +# Wvla-larger-than=BYTES +# +# Build specific: +# +# Wpoison-system-directories +# +# C Specific: +# +# Wc11-c2x-compat +# Wc90-c99-compat +# Wc99-c11-compat +# Wdeclaration-after-statement +# Wtraditional +# Wtraditional-conversion +# +# C++ Specific: +# +# Wc++0x-compat +# Wc++1z-compat +# Wc++2a-compat +# Wctad-maybe-unsupported +# Wnamespaces +# Wtemplates + +# GCC warnings that apply to all C-family languages +gcc_common_warnings = [ + '-Walloc-zero', + '-Walloca', + '-Wanalyzer-too-complex', + '-Warith-conversion', + '-Warray-bounds=2', + '-Wattribute-alias=2', + '-Wbidi-chars=ucn', + '-Wcast-align=strict', + '-Wcast-function-type', + '-Wcast-qual', + '-Wclobbered', + '-Wconversion', + '-Wdate-time', + '-Wdisabled-optimization', + '-Wdouble-promotion', + '-Wduplicated-branches', + '-Wduplicated-cond', + '-Wempty-body', + '-Wendif-labels', + '-Wfloat-equal', + '-Wformat-overflow=2', + '-Wformat-signedness', + '-Wformat-truncation=2', + '-Wformat=2', + '-Wignored-qualifiers', + '-Wimplicit-fallthrough=3', + '-Winit-self', + '-Winline', + '-Winvalid-pch', + '-Wlogical-op', + '-Wmissing-declarations', + '-Wmissing-field-initializers', + '-Wmissing-include-dirs', + '-Wmultichar', + '-Wnormalized=nfc', + '-Wnull-dereference', + '-Wopenacc-parallelism', + '-Woverlength-strings', + '-Wpacked', + '-Wpacked-bitfield-compat', + '-Wpadded', + '-Wpointer-arith', + '-Wredundant-decls', + '-Wshadow', + '-Wshift-negative-value', + '-Wshift-overflow=2', + '-Wstack-protector', + '-Wstrict-aliasing=3', + '-Wstrict-overflow=5', + '-Wstring-compare', + '-Wstringop-overflow=3', + '-Wsuggest-attribute=cold', + '-Wsuggest-attribute=const', + '-Wsuggest-attribute=format', + '-Wsuggest-attribute=malloc', + '-Wsuggest-attribute=noreturn', + '-Wsuggest-attribute=pure', + '-Wswitch-default', + '-Wswitch-enum', + '-Wtrampolines', + '-Wtrivial-auto-var-init', + '-Wtype-limits', + '-Wundef', + '-Wuninitialized', + '-Wunsafe-loop-optimizations', + '-Wunused', + '-Wunused-const-variable=2', + '-Wunused-macros', + '-Wvector-operation-performance', + '-Wvla', + '-Wwrite-strings', +] + +##### +# C # +##### + +if is_variable('cc') + # Set all_c_warnings for the current C compiler + all_c_warnings = [] + + if cc.get_id() == 'clang' + all_c_warnings += ['-Weverything'] + + if not meson.is_cross_build() + all_c_warnings += [ + '-Wno-poison-system-directories', + ] + endif + + elif cc.get_id() == 'gcc' + all_c_warnings += gcc_common_warnings + [ + '-Wabsolute-value', + '-Wbad-function-cast', + '-Wc++-compat', + '-Wenum-conversion', + '-Wjump-misses-init', + '-Wmissing-parameter-type', + '-Wmissing-prototypes', + '-Wnested-externs', + '-Wold-style-declaration', + '-Wold-style-definition', + '-Woverride-init', + '-Wsign-compare', + '-Wstrict-prototypes', + '-Wunsuffixed-float-constants', + ] + + elif cc.get_id() == 'msvc' + all_c_warnings += [ + '/Wall', + '/experimental:external', + '/external:W0', + '/external:anglebrackets', + ] + endif + + all_c_warnings = cc.get_supported_arguments(all_c_warnings) + add_project_arguments(all_c_warnings, language: ['c']) +endif diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 00000000..9dc7218d --- /dev/null +++ b/meson_options.txt @@ -0,0 +1,17 @@ +option('docs', type: 'feature', value: 'auto', yield: true, + description: 'Build documentation') + +option('static', type: 'boolean', value: false, yield: true, + description: 'Statically link executables') + +option('strict', type: 'boolean', value: false, yield: true, + description: 'Enable ultra-strict warnings') + +option('tests', type: 'feature', value: 'auto', yield: true, + description: 'Build tests') + +option('title', type: 'string', value: 'Serd', + description: 'Project title') + +option('tools', type: 'feature', value: 'auto', yield: true, + description: 'Build command line utilities') diff --git a/serd.pc.in b/serd.pc.in deleted file mode 100644 index 13a50d93..00000000 --- a/serd.pc.in +++ /dev/null @@ -1,11 +0,0 @@ -prefix=@PREFIX@ -exec_prefix=@EXEC_PREFIX@ -libdir=@LIBDIR@ -includedir=@INCLUDEDIR@ - -Name: Serd -Description: A lightweight library for working with RDF -Version: @SERD_VERSION@ -Libs: -L${libdir} -l@LIB_SERD@ -Libs.private: -lm -Cflags: -I${includedir}/serd-@SERD_MAJOR_VERSION@ diff --git a/test/meson.build b/test/meson.build new file mode 100644 index 00000000..b38cee42 --- /dev/null +++ b/test/meson.build @@ -0,0 +1,198 @@ +# Copyright 2021-2022 David Robillard <d@drobilla.net> +# SPDX-License-Identifier: CC0-1.0 OR ISC + +autoship = find_program('autoship', required: false) +run_test_suite = files('run_test_suite.py') + +wrapper = meson.get_external_property('exe_wrapper', '') + +#################### +# Project Metadata # +#################### + +if autoship.found() + test('autoship', autoship, args: ['test', serd_src_root], suite: 'data') +endif + +############## +# Unit Tests # +############## + +unit_tests = [ + 'env', + 'free_null', + 'node', + 'read_chunk', + 'reader_writer', + 'string', + 'uri', + 'writer', +] + +foreach unit : unit_tests + test( + unit, + executable( + 'test_@0@'.format(unit), + files('test_@0@.c'.format(unit)), + c_args: c_suppressions, + dependencies: serd_dep, + ), + suite: 'unit', + ) +endforeach + +################ +# System Tests # +################ + +if not get_option('tools').disabled() + if wrapper != '' + script_args = ['--wrapper', wrapper, '--serdi', serdi.full_path()] + else + script_args = ['--serdi', serdi.full_path()] + endif + + serd_ttl = files('../serd.ttl')[0] + + test('serd.ttl', serdi, args: [serd_ttl], suite: 'data') + + # Command line options + + good_args = [ + ['-v'], + ['-h'], + ['-s', '<urn:eg:s> a <urn:eg:T> .'], + ] + + foreach args : good_args + test(args[0], serdi, args: args, suite: ['serdi', 'options']) + endforeach + + bad_args = [ + ['/no/such/file'], + ['ftp://unsupported.org'], + ['-c'], + ['-i', 'unknown'], + ['-i', 'turtle'], + ['-i'], + ['-fi'], + ['-o', 'unknown'], + ['-o'], + ['-p'], + ['-r'], + ['-z'], + ['-s', '<foo> a <Bar> .'], + ] + + foreach args : bad_args + name = ' '.join(args).underscorify() + test(name, serdi, + args: args, + should_fail: true, + suite: ['serdi', 'options']) + endforeach + + test('none', serdi, should_fail: true, suite: ['serdi', 'options']) + + test('quiet', files('test_quiet.py'), + args: script_args + files('bad/bad-base.ttl'), + suite: ['serdi', 'options']) + + # Inputs + + test('stdin', files('test_stdin.py'), + args: script_args, + suite: ['serdi', 'input']) + + test('string', serdi, + args: ['-s', '<foo> a <Bar> .'], + should_fail: true, + suite: ['serdi', 'input']) + + test('missing', serdi, + args: ['-i', 'turtle'], + should_fail: true, + suite: ['serdi', 'input']) + + test('no_such_file', serdi, + args: ['no_such_file'], + should_fail: true, + suite: ['serdi', 'input']) + + test('remote', serdi, + args: ['ftp://example.org/unsupported.ttl'], + should_fail: true, + suite: ['serdi', 'input']) + + # IO errors + + test('read_dir', serdi, + args: ['-e', 'file://@0@/'.format(serd_src_root)], + should_fail: true, + suite: 'io_errors') + + test('bulk_read_dir', serdi, + args: ['file://@0@/'.format(serd_src_root)], + should_fail: true, + suite: 'io_errors') + + test('write_error', files('test_write_error.py'), + args: script_args + [serd_ttl], + suite: 'io_errors') + + # RDF test suites + + ## Serd-specific test suites + + serd_suites = ['good', 'bad'] + serd_base = 'http://drobilla.net/sw/serd/test/' + + ### Run all suites with no special arguments + foreach name : serd_suites + manifest = files(name / 'manifest.ttl') + base_uri = serd_base + name + '/' + test(name, run_test_suite, + args: script_args + [manifest, base_uri], + suite: ['rdf', 'serd'], + timeout: 240) + endforeach + + ### The lax suite is special because it is run twice... + lax_manifest = files('lax/manifest.ttl') + lax_base_uri = serd_base + name + '/' + + ### ... once with strict parsing to test the hard errors + test('lax.strict', run_test_suite, + args: script_args + [lax_manifest, lax_base_uri], + is_parallel: false, + suite: ['rdf', 'serd'], + timeout: 240) + + ### ... and once with lax parsing to tolerate them + test('lax.lax', run_test_suite, + args: script_args + [lax_manifest, lax_base_uri, '--', '-l'], + is_parallel: false, + suite: ['rdf', 'serd'], + timeout: 240) + + ## Standard W3C test suites + + w3c_suites = ['Turtle', 'NTriples', 'NQuads', 'TriG'] + w3c_base = 'http://www.w3.org/2013/' + + foreach syntax : w3c_suites + manifest = files(syntax + 'Tests' / 'manifest.ttl') + base_uri = w3c_base + syntax + 'Tests/' + + args = ['--syntax', syntax, manifest, base_uri] + if syntax == 'TriG' + args += ['--', '-a'] + endif + + test(syntax, run_test_suite, + args: script_args + args, + suite: ['rdf', 'w3c'], + timeout: 240) + endforeach +endif diff --git a/test/test_quiet.py b/test/test_quiet.py new file mode 100755 index 00000000..f1d4e739 --- /dev/null +++ b/test/test_quiet.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 + +"""Test serdi quiet option.""" + +import argparse +import sys +import shlex +import subprocess + +parser = argparse.ArgumentParser(description=__doc__) + +parser.add_argument("--serdi", default="./serdi", help="path to serdi") +parser.add_argument("--wrapper", default="", help="executable wrapper") +parser.add_argument("input", help="invalid input file") + +args = parser.parse_args(sys.argv[1:]) +command = shlex.split(args.wrapper) + [args.serdi, "-q", args.input] +proc = subprocess.run( + command, check=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE +) + +assert proc.returncode != 0 +assert args.wrapper or len(proc.stderr) == 0 diff --git a/test/test_stdin.py b/test/test_stdin.py new file mode 100755 index 00000000..84b6a8b2 --- /dev/null +++ b/test/test_stdin.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 + +"""Test reading from stdin with serdi.""" + +import argparse +import sys +import shlex +import subprocess +import tempfile + +parser = argparse.ArgumentParser(description=__doc__) + +parser.add_argument("--serdi", default="./serdi", help="path to serdi") +parser.add_argument("--wrapper", default="", help="executable wrapper") + +args = parser.parse_args(sys.argv[1:]) +command = shlex.split(args.wrapper) + [args.serdi, "-"] + +DOCUMENT = "<{0}s> <{0}p> <{0}o> .".format("http://example.org/") + +with tempfile.TemporaryFile() as out: + proc = subprocess.run( + command, + check=False, + encoding="utf-8", + input=DOCUMENT, + stdout=out, + stderr=subprocess.PIPE, + ) + + assert proc.returncode == 0 + assert args.wrapper or len(proc.stderr) == 0 + + out.seek(0) + lines = out.readlines() + + assert len(lines) == 1 + assert lines[0].decode("utf-8").strip() == DOCUMENT diff --git a/test/test_write_error.py b/test/test_write_error.py new file mode 100755 index 00000000..35b4693b --- /dev/null +++ b/test/test_write_error.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 + +"""Test errors writing to a file.""" + +import argparse +import sys +import shlex +import subprocess +import os + +parser = argparse.ArgumentParser(description=__doc__) + +parser.add_argument("--serdi", default="./serdi", help="path to serdi") +parser.add_argument("--wrapper", default="", help="executable wrapper") +parser.add_argument("input", help="valid input file") + +args = parser.parse_args(sys.argv[1:]) +command = shlex.split(args.wrapper) + [args.serdi, args.input] + +if os.path.exists("/dev/full"): + + with open("/dev/full", "w") as out: + proc = subprocess.run( + command, check=False, stdout=out, stderr=subprocess.PIPE + ) + + assert proc.returncode != 0 + assert "error" in proc.stderr.decode("utf-8") + +else: + sys.stderr.write("warning: /dev/full not present, skipping test") @@ -1,27 +0,0 @@ -#!/usr/bin/env python - -# Minimal waf script for projects that include waflib directly - -import sys -import inspect -import os - -try: - from waflib import Context, Scripting -except Exception as e: - sys.stderr.write('error: Failed to import waf (%s)\n' % e) - if os.path.exists('.git'): - sys.stderr.write("Are submodules up to date? " - "Try 'git submodule update --init --recursive'\n") - - sys.exit(1) - - -def main(): - script_path = os.path.abspath(inspect.getfile(inspect.getmodule(main))) - project_path = os.path.dirname(script_path) - Scripting.waf_entry_point(os.getcwd(), Context.WAFVERSION, project_path) - - -if __name__ == '__main__': - main() diff --git a/waflib b/waflib deleted file mode 160000 -Subproject aeef9f5fdf416d9b68c61c75de7dae409f1ac6a diff --git a/wscript b/wscript deleted file mode 100644 index d5dc31c8..00000000 --- a/wscript +++ /dev/null @@ -1,504 +0,0 @@ -#!/usr/bin/env python - -import glob -import os -import sys - -from waflib import Build, Logs, Options -from waflib.extras import autowaf - -# Library and package version (UNIX style major, minor, micro) -# major increment <=> incompatible changes -# minor increment <=> compatible changes (additions) -# micro increment <=> no interface changes -SERD_VERSION = '0.30.13' -SERD_MAJOR_VERSION = '0' - -# Mandatory waf variables -APPNAME = 'serd' # Package name for waf dist -VERSION = SERD_VERSION # Package version for waf dist -top = '.' # Source directory -out = 'build' # Build directory - -# Release variables -uri = 'http://drobilla.net/sw/serd' -dist_pattern = 'http://download.drobilla.net/serd-%d.%d.%d.tar.bz2' -post_tags = ['Hacking', 'RDF', 'Serd'] - - -def options(ctx): - ctx.load('compiler_c') - ctx.add_flags( - ctx.configuration_options(), - {'no-utils': 'do not build command line utilities', - 'stack-check': 'include runtime stack sanity checks', - 'static': 'build static library', - 'no-shared': 'do not build shared library', - 'static-progs': 'build programs as static binaries', - 'largefile': 'build with large file support on 32-bit systems', - 'no-posix': 'do not use POSIX functions, even if present'}) - - -def configure(conf): - conf.load('compiler_c', cache=True) - conf.load('autowaf', cache=True) - - if conf.env.DOCS: - conf.load('sphinx') - - if not autowaf.set_c_lang(conf, 'c11', mandatory=False): - autowaf.set_c_lang(conf, 'c99') - - if Options.options.strict: - # Check for programs used by lint target - conf.find_program("flake8", var="FLAKE8", mandatory=False) - conf.find_program("clang-tidy", var="CLANG_TIDY", mandatory=False) - conf.find_program("iwyu_tool", var="IWYU_TOOL", mandatory=False) - - if Options.options.ultra_strict: - autowaf.add_compiler_flags(conf.env, '*', { - 'clang': [ - '-Wno-cast-align', - '-Wno-cast-qual', - '-Wno-conversion', - '-Wno-disabled-macro-expansion', - '-Wno-double-promotion', - '-Wno-format-nonliteral', - '-Wno-nullability-extension', - '-Wno-nullable-to-nonnull-conversion', - '-Wno-padded', - '-Wno-reserved-id-macro', - '-Wno-sign-conversion', - ], - 'gcc': [ - '-Wno-cast-align', - '-Wno-cast-qual', - '-Wno-float-conversion', - '-Wno-inline', - '-Wno-padded', - '-Wno-sign-conversion', - ], - 'msvc': [ - '/wd4061', # enumerator in switch is not explicitly handled - '/wd4365', # signed/unsigned mismatch - '/wd4514', # unreferenced inline function has been removed - '/wd4710', # function not inlined - '/wd4711', # function selected for automatic inline expansion - '/wd4800', # implicit conversion from int to bool - '/wd4820', # padding added after construct - '/wd4996', # POSIX name for this item is deprecated - ], - }) - - autowaf.add_compiler_flags(conf.env, 'c', { - 'clang': [ - '-Wno-bad-function-cast', - ], - 'gcc': [ - '-Wno-bad-function-cast', - ], - 'msvc': [ - '/wd4706', # assignment within conditional expression - '/wd5045', # will insert Spectre mitigation for memory load - ], - }) - - if 'mingw' in conf.env.CC[0]: - conf.env.append_value('CFLAGS', '-Wno-unused-macros') - - conf.env.update({ - 'BUILD_UTILS': not Options.options.no_utils, - 'BUILD_SHARED': not Options.options.no_shared, - 'STATIC_PROGS': Options.options.static_progs, - 'BUILD_STATIC': (Options.options.static or - Options.options.static_progs)}) - - if not conf.env.BUILD_SHARED and not conf.env.BUILD_STATIC: - conf.fatal('Neither a shared nor a static build requested') - - if Options.options.stack_check: - conf.define('SERD_STACK_CHECK', SERD_VERSION) - - if Options.options.largefile: - conf.env.append_unique('DEFINES', ['_FILE_OFFSET_BITS=64']) - - if not Options.options.no_posix: - funcs = {'posix_memalign': ('stdlib.h', 'int', 'void**,size_t,size_t'), - 'posix_fadvise': ('fcntl.h', 'int', 'int,off_t,off_t,int'), - 'fileno': ('stdio.h', 'int', 'FILE*')} - - for name, (header, ret, args) in funcs.items(): - conf.check_function('c', name, - header_name = header, - return_type = ret, - arg_types = args, - define_name = 'HAVE_' + name.upper(), - defines = ['_POSIX_C_SOURCE=200809L'], - mandatory = False) - - # Set up environment for building/using as a subproject - autowaf.set_lib_env(conf, 'serd', SERD_VERSION, - include_path=str(conf.path.find_node('include'))) - - if conf.env.BUILD_TESTS: - serdi_node = conf.path.get_bld().make_node('serdi_static') - else: - serdi_node = conf.path.get_bld().make_node('serdi') - - conf.env.SERDI = [serdi_node.abspath()] - - conf.define('SERD_NO_DEFAULT_CONFIG', 1) - - autowaf.display_summary( - conf, - {'Build static library': bool(conf.env['BUILD_STATIC']), - 'Build shared library': bool(conf.env['BUILD_SHARED']), - 'Build utilities': bool(conf.env['BUILD_UTILS']), - 'Build unit tests': bool(conf.env['BUILD_TESTS'])}) - - -lib_headers = ['src/reader.h'] - -lib_source = ['src/base64.c', - 'src/byte_source.c', - 'src/env.c', - 'src/n3.c', - 'src/node.c', - 'src/reader.c', - 'src/string.c', - 'src/system.c', - 'src/uri.c', - 'src/writer.c'] - - -def build(bld): - # C Headers - includedir = '${INCLUDEDIR}/serd-%s/serd' % SERD_MAJOR_VERSION - bld.install_files(includedir, bld.path.ant_glob('include/serd/*.h')) - - # Pkgconfig file - autowaf.build_pc(bld, 'SERD', SERD_VERSION, SERD_MAJOR_VERSION, [], - {'SERD_MAJOR_VERSION': SERD_MAJOR_VERSION}) - - defines = [] - lib_args = {'export_includes': ['include'], - 'includes': ['include'], - 'cflags': ['-fvisibility=hidden'], - 'lib': ['m'], - 'vnum': SERD_VERSION, - 'install_path': '${LIBDIR}'} - if bld.env.MSVC_COMPILER: - lib_args['cflags'] = [] - lib_args['lib'] = [] - defines = [] - - # Shared Library - if bld.env.BUILD_SHARED: - bld(features = 'c cshlib', - source = lib_source, - name = 'libserd', - target = 'serd-%s' % SERD_MAJOR_VERSION, - defines = defines + ['SERD_INTERNAL'], - **lib_args) - - # Static library - if bld.env.BUILD_STATIC: - bld(features = 'c cstlib', - source = lib_source, - name = 'libserd_static', - target = 'serd-%s' % SERD_MAJOR_VERSION, - defines = defines + ['SERD_STATIC', 'SERD_INTERNAL'], - **lib_args) - - if bld.env.BUILD_TESTS: - coverage_flags = [''] if bld.env.NO_COVERAGE else ['--coverage'] - test_args = {'includes': ['include'], - 'cflags': coverage_flags, - 'linkflags': coverage_flags, - 'lib': lib_args['lib'], - 'install_path': ''} - - # Profiled static library for test coverage - bld(features = 'c cstlib', - source = lib_source, - name = 'libserd_profiled', - target = 'serd_profiled', - defines = defines + ['SERD_STATIC', 'SERD_INTERNAL'], - **test_args) - - # Test programs - for prog in [('serdi_static', 'src/serdi.c'), - ('test_env', 'test/test_env.c'), - ('test_free_null', 'test/test_free_null.c'), - ('test_node', 'test/test_node.c'), - ('test_read_chunk', 'test/test_read_chunk.c'), - ('test_reader_writer', 'test/test_reader_writer.c'), - ('test_string', 'test/test_string.c'), - ('test_uri', 'test/test_uri.c'), - ('test_writer', 'test/test_writer.c')]: - bld(features = 'c cprogram', - source = prog[1], - use = 'libserd_profiled', - target = prog[0], - defines = defines + ['SERD_STATIC'], - **test_args) - - # Utilities - if bld.env.BUILD_UTILS: - obj = bld(features = 'c cprogram', - source = 'src/serdi.c', - target = 'serdi', - includes = ['include'], - use = 'libserd', - lib = lib_args['lib'], - install_path = '${BINDIR}') - if not bld.env.BUILD_SHARED or bld.env.STATIC_PROGS: - obj.use = 'libserd_static' - if bld.env.STATIC_PROGS: - obj.env.SHLIB_MARKER = obj.env.STLIB_MARKER - obj.linkflags = ['-static'] - - # Documentation - if bld.env.DOCS: - bld.env.SERD_MAJOR_VERSION = SERD_MAJOR_VERSION - bld.recurse('doc/c') - - # Man page - bld.install_files('${MANDIR}/man1', 'doc/serdi.1') - - bld.add_post_fun(autowaf.run_ldconfig) - - -class LintContext(Build.BuildContext): - fun = cmd = 'lint' - - -def lint(ctx): - "checks code for style issues" - import subprocess - - st = 0 - - if "FLAKE8" in ctx.env: - Logs.info("Running flake8") - st = subprocess.call([ctx.env.FLAKE8[0], - "wscript", - "--ignore", - "E101,E129,W191,E221,W504,E251,E241,E741"]) - st += subprocess.call([ctx.env.FLAKE8[0], - "scripts/serd_bench.py", - "--ignore", - "E203"]) - else: - Logs.warn("Not running flake8") - - if "IWYU_TOOL" in ctx.env: - Logs.info("Running include-what-you-use") - cmd = [ctx.env.IWYU_TOOL[0], "-o", "clang", "-p", "build"] - output = subprocess.check_output(cmd).decode('utf-8') - if 'error: ' in output: - sys.stdout.write(output) - st += 1 - else: - Logs.warn("Not running include-what-you-use") - - if "CLANG_TIDY" in ctx.env and "clang" in ctx.env.CC[0]: - Logs.info("Running clang-tidy") - sources = glob.glob('include/serd/*.h*') - sources += glob.glob('src/*.c') - sources += glob.glob('test/*.c') - sources = list(map(os.path.abspath, sources)) - procs = [] - for source in sources: - cmd = [ctx.env.CLANG_TIDY[0], "--quiet", "-p=.", source] - procs += [subprocess.Popen(cmd, cwd="build")] - - for proc in procs: - stdout, stderr = proc.communicate() - st += proc.returncode - else: - Logs.warn("Not running clang-tidy") - - if st != 0: - sys.exit(st) - - -def amalgamate(ctx): - "builds single-file amalgamated source" - import shutil - import re - shutil.copy('serd/serd.h', 'build/serd.h') - - def include_line(line): - return (not re.match(r'#include "[^/]*\.h"', line) and - not re.match('#include "serd/serd.h"', line)) - - with open('build/serd.c', 'w') as amalgamation: - amalgamation.write('/* This is amalgamated code, do not edit! */\n') - amalgamation.write('#include "serd.h"\n\n') - - for header_path in ['src/serd_internal.h', - 'src/system.h', - 'src/byte_sink.h', - 'src/byte_source.h', - 'src/stack.h', - 'src/string_utils.h', - 'src/uri_utils.h', - 'src/reader.h']: - with open(header_path) as header: - for l in header: - if include_line(l): - amalgamation.write(l) - - for f in lib_headers + lib_source: - with open(f) as fd: - amalgamation.write('\n/**\n @file %s\n*/' % f) - for l in fd: - if include_line(l): - amalgamation.write(l) - - for i in ['c', 'h']: - Logs.info('Wrote build/serd.%s' % i) - - -def test(tst): - import tempfile - - # Create test output directories - for i in ['bad', 'good', 'lax', - 'TurtleTests', 'NTriplesTests', 'NQuadsTests', 'TriGTests']: - try: - test_dir = os.path.join('test', i) - os.makedirs(test_dir) - for i in glob.glob(test_dir + '/*.*'): - os.remove(i) - except Exception: - pass - - serdi = './serdi_static' - srcdir = tst.path.abspath() - - with tst.group('Unit') as check: - check(['./test_env']) - check(['./test_free_null']) - check(['./test_node']) - check(['./test_read_chunk']) - check(['./test_reader_writer']) - check(['./test_string']) - check(['./test_uri']) - check(['./test_writer']) - - def test_syntax_io(check, in_name, check_name, lang): - in_path = 'test/good/%s' % in_name - out_path = in_path + '.io' - check_path = '%s/test/good/%s' % (srcdir, check_name) - - check([serdi, '-o', lang, '%s/%s' % (srcdir, in_path), in_path], - stdout=out_path, name=in_name) - - check.file_equals(check_path, out_path) - - with tst.group('ThroughSyntax') as check: - test_syntax_io(check, 'base.ttl', 'base.ttl', 'turtle') - test_syntax_io(check, 'qualify-in.ttl', 'qualify-out.ttl', 'turtle') - - with tst.group('GoodCommands') as check: - check([serdi, '%s/serd.ttl' % srcdir], stdout=os.devnull) - check([serdi, '-li', 'turtle', '%s/test/lax/test-bad-string.ttl' % srcdir], - stdout=os.devnull) - check([serdi, '-v']) - check([serdi, '-h']) - check([serdi, '-s', '<urn:eg:s> a <urn:eg:T> .']) - check([serdi, os.devnull]) - with tempfile.TemporaryFile(mode='r') as stdin: - check([serdi, '-'], stdin=stdin) - - with tst.group('BadCommands', - expected=1, - stderr=autowaf.NONEMPTY) as check: - check([serdi]) - check([serdi, '/no/such/file']) - check([serdi, 'ftp://example.org/unsupported.ttl']) - check([serdi, '-c']) - check([serdi, '-i', 'illegal']) - check([serdi, '-i', 'turtle']) - check([serdi, '-i']) - check([serdi, '-fi']) - check([serdi, '-o', 'illegal']) - check([serdi, '-o']) - check([serdi, '-p']) - check([serdi, '-q', '%s/test/bad/bad-base.ttl' % srcdir], stderr=None) - check([serdi, '-r']) - check([serdi, '-z']) - check([serdi, '-s', '<foo> a <Bar> .']) - - with tst.group('IoErrors', expected=1) as check: - check([serdi, '-e', 'file://%s/' % srcdir], name='Read directory') - check([serdi, 'file://%s/' % srcdir], name='Bulk read directory') - if os.path.exists('/dev/full'): - check([serdi, 'file://%s/test/good/manifest.ttl' % srcdir], - stdout='/dev/full', name='Write error') - - if sys.version_info.major >= 3: - from waflib.extras import autoship - try: - with tst.group('NEWS') as check: - news_path = os.path.join(srcdir, 'NEWS') - entries = autoship.read_news(top=srcdir) - autoship.write_news(entries, 'NEWS.norm') - check.file_equals(news_path, 'NEWS.norm') - - meta_path = os.path.join(srcdir, 'serd.ttl') - autoship.write_news(entries, 'NEWS.ttl', - format='turtle', template=meta_path) - - ttl_entries = autoship.read_news('NEWS.ttl', - top=srcdir, format='turtle') - - autoship.write_news(ttl_entries, 'NEWS.round') - check.file_equals(news_path, 'NEWS.round') - except ImportError: - Logs.warn('Failed to import rdflib, not running NEWS tests') - - run_test_suite = ['../test/run_test_suite.py', '--serdi', './serdi_static'] - - with tst.group('TestSuites') as check: - # Run serd-specific test suites - serd_base = 'http://drobilla.net/sw/serd/test/' - check(run_test_suite + ['../test/good/manifest.ttl', serd_base + 'good/']) - check(run_test_suite + ['../test/bad/manifest.ttl', serd_base + 'bad/']) - check(run_test_suite + ['../test/lax/manifest.ttl', serd_base + 'lax/', '--', '-l']) - check(run_test_suite + ['../test/lax/manifest.ttl', serd_base + 'lax/']) - - # Start test report for standard test suites - report_filename = 'earl.ttl' - with open(report_filename, 'w') as report: - report.write('@prefix earl: <http://www.w3.org/ns/earl#> .\n' - '@prefix dc: <http://purl.org/dc/elements/1.1/> .\n') - - with open(os.path.join(srcdir, 'serd.ttl')) as serd_ttl: - report.writelines(serd_ttl) - - # Run standard test suites - w3c_base = 'http://www.w3.org/2013/' - - check(run_test_suite + [ - '--syntax', 'Turtle', - '--report', report_filename, - '../test/TurtleTests/manifest.ttl', w3c_base + 'TurtleTests/']) - - check(run_test_suite + [ - '--syntax', 'NTriples', - '--report', report_filename, - '../test/NTriplesTests/manifest.ttl', w3c_base + 'NTriplesTests/']) - - check(run_test_suite + [ - '--syntax', 'NQuads', - '--report', report_filename, - '../test/NQuadsTests/manifest.ttl', w3c_base + 'NQuadsTests/']) - - check(run_test_suite + [ - '--syntax', 'TriG', - '--report', report_filename, - '../test/TriGTests/manifest.ttl', w3c_base + 'TriGTests/', - '--', '-a']) |