From 7d2183d13b298f33733e67184dd86a34a71723bf Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sat, 9 Jan 2021 19:17:31 +0100 Subject: Switch to Meson --- .gitignore | 2 - .gitlab-ci.yml | 207 +++++++------------ .gitmodules | 3 - doc/_static/meson.build | 1 + doc/c/Doxyfile | 33 ---- doc/c/Doxyfile.in | 36 ++++ doc/c/api/meson.build | 5 + doc/c/meson.build | 44 +++++ doc/c/wscript | 41 ---- doc/c/xml/meson.build | 19 ++ doc/conf.py.in | 5 +- doc/meson.build | 17 ++ meson.build | 200 +++++++++++++++++++ meson/meson.build | 203 +++++++++++++++++++ meson_options.txt | 14 ++ serd.pc.in | 11 -- test/meson.build | 184 +++++++++++++++++ test/test_quiet.py | 21 ++ test/test_stdin.py | 38 ++++ test/test_write_error.py | 31 +++ waf | 27 --- waflib | 1 - wscript | 504 ----------------------------------------------- 23 files changed, 889 insertions(+), 758 deletions(-) delete mode 100644 .gitmodules create mode 100644 doc/_static/meson.build delete mode 100644 doc/c/Doxyfile create mode 100644 doc/c/Doxyfile.in create mode 100644 doc/c/api/meson.build create mode 100644 doc/c/meson.build delete mode 100644 doc/c/wscript create mode 100644 doc/c/xml/meson.build create mode 100644 doc/meson.build create mode 100644 meson.build create mode 100644 meson/meson.build create mode 100644 meson_options.txt delete mode 100644 serd.pc.in create mode 100644 test/meson.build create mode 100755 test/test_quiet.py create mode 100755 test/test_stdin.py create mode 100755 test/test_write_error.py delete mode 100755 waf delete mode 160000 waflib delete mode 100644 wscript diff --git a/.gitignore b/.gitignore index 204c242d..467d5b52 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,2 @@ build/** -.waf-* -.lock-waf* __pycache__ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c9e3db17..a3998eea 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,231 +1,170 @@ -stages: [build, test, deploy] - -variables: - GIT_SUBMODULE_STRATEGY: normal +stages: + - build + - deploy .build_template: &build_definition stage: build - artifacts: - paths: ["build/", ".lock-waf*"] - -.test_template: &test_definition - stage: test - artifacts: - paths: [build/coverage] - arm32_dbg: <<: *build_definition image: lv2plugin/debian-arm32 - script: python ./waf configure build -dST --werror - variables: - CC: "arm-linux-gnueabihf-gcc" - CXX: "arm-linux-gnueabihf-g++" - -test:arm32_dbg: - <<: *test_definition - image: lv2plugin/debian-arm32 - script: python ./waf test --wrapper=qemu-arm - needs: ["arm32_dbg"] - + script: + - 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: python ./waf configure build -ST --werror - variables: - CC: "arm-linux-gnueabihf-gcc" - CXX: "arm-linux-gnueabihf-g++" - -test:arm32_rel: - <<: *test_definition - image: lv2plugin/debian-arm32 - script: python ./waf test --wrapper=qemu-arm - needs: ["arm32_rel"] + script: + - 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: python ./waf configure build -dST --werror - variables: - CC: "aarch64-linux-gnu-gcc" - CXX: "aarch64-linux-gnu-g++" - -test:arm64_dbg: - <<: *test_definition - image: lv2plugin/debian-arm64 - script: python ./waf test --wrapper=qemu-aarch64 - needs: ["arm64_dbg"] - + script: + - 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: python ./waf configure build -ST --werror - variables: - CC: "aarch64-linux-gnu-gcc" - CXX: "aarch64-linux-gnu-g++" - -test:arm64_rel: - <<: *test_definition - image: lv2plugin/debian-arm64 - script: python ./waf test --wrapper=qemu-aarch64 - needs: ["arm64_rel"] + script: + - 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: python ./waf configure build -dST --werror --docs - -test:x64_dbg: - <<: *test_definition - image: lv2plugin/debian-x64 script: - - python ./waf test + - 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 - needs: ["x64_dbg"] artifacts: paths: - build/doc - - build/coverage - + - build/meson-logs/coveragereport x64_rel: <<: *build_definition image: lv2plugin/debian-x64 - script: python ./waf configure build -ST --werror - -test:x64_rel: - <<: *test_definition - image: lv2plugin/debian-x64 - script: python ./waf test - needs: ["x64_rel"] + script: + - meson setup build -Dbuildtype=release -Ddocs=disabled -Dstrict=true -Dwerror=true + - ninja -C build test x64_static: <<: *build_definition image: lv2plugin/debian-x64 - script: python ./waf configure build -ST --werror --no-posix --static-progs - -test:x64_static: - <<: *test_definition - image: lv2plugin/debian-x64 - script: python ./waf test - needs: ["x64_static"] + script: + - meson setup build -Dstatic=true -Ddefault_library=static -Ddocs=disabled -Dstrict=true -Dwerror=true + - ninja -C build test x64_sanitize: <<: *build_definition image: lv2plugin/debian-x64-clang script: - - python ./waf configure build -ST --werror --no-coverage - - python ./waf test + - 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: python ./waf configure build -dST --werror --no-coverage + script: + - meson setup build --cross-file=/usr/share/meson/cross/i686-w64-mingw32.ini -Dbuildtype=debug -Ddocs=disabled -Dstrict=true -Dwerror=true + - ninja -C build test variables: - CC: "i686-w64-mingw32-gcc" - CXX: "i686-w64-mingw32-g++" + MESON_TESTTHREADS: "1" + WINEPATH: "Z:\\usr\\lib\\gcc\\i686-w64-mingw32\\8.3-win32" mingw32_rel: <<: *build_definition image: lv2plugin/debian-mingw32 - script: python ./waf configure build -ST --werror --no-coverage + script: + - meson setup build --cross-file=/usr/share/meson/cross/i686-w64-mingw32.ini -Dbuildtype=release -Ddocs=disabled -Dstrict=true -Dwerror=true + - ninja -C build test variables: - CC: "i686-w64-mingw32-gcc" - CXX: "i686-w64-mingw32-g++" + MESON_TESTTHREADS: "1" + WINEPATH: "Z:\\usr\\lib\\gcc\\i686-w64-mingw32\\8.3-win32" mingw64_dbg: <<: *build_definition image: lv2plugin/debian-mingw64 - script: python ./waf configure build -dST --werror --no-coverage + script: + - 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: - CC: "x86_64-w64-mingw32-gcc" - CXX: "x86_64-w64-mingw32-g++" + MESON_TESTTHREADS: "1" + WINEPATH: "Z:\\usr\\lib\\gcc\\x86_64-w64-mingw32\\8.3-win32" mingw64_rel: <<: *build_definition image: lv2plugin/debian-mingw64 - script: python ./waf configure build -ST --werror --no-coverage + script: + - 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: - CC: "x86_64-w64-mingw32-gcc" - CXX: "x86_64-w64-mingw32-g++" + MESON_TESTTHREADS: "1" + WINEPATH: "Z:\\usr\\lib\\gcc\\x86_64-w64-mingw32\\8.3-win32" mac_dbg: <<: *build_definition - script: python ./waf configure build -dST --werror --no-coverage tags: [macos] - -test:mac_dbg: - <<: *test_definition - script: python ./waf test - needs: ["mac_dbg"] - 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 -ST --werror --no-coverage - tags: [macos] - -test:mac_rel: - <<: *test_definition - script: python ./waf test - needs: ["mac_rel"] tags: [macos] + script: + - meson setup build -Dbuildtype=release -Ddocs=disabled -Dstrict=true -Dwerror=true + - ninja -C build test win_dbg: <<: *build_definition - script: python ./waf configure build -dST --werror --no-coverage - tags: [windows,msvc,python] - -test:win_dbg: - <<: *test_definition - script: python ./waf test - needs: ["win_dbg"] - tags: [windows,msvc,python] - + tags: [windows,meson] + script: + - meson setup build -Dbuildtype=debug -Ddocs=disabled -Dstrict=true -Dwerror=true + - ninja -C build test win_rel: <<: *build_definition - script: python ./waf configure build -ST --werror --no-coverage - tags: [windows,msvc,python] - -test:win_rel: - <<: *test_definition - script: python ./waf test - needs: ["win_rel"] - tags: [windows,msvc,python] - + tags: [windows,meson] + script: + - 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 + dependencies: + - 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 diff --git a/doc/_static/meson.build b/doc/_static/meson.build new file mode 100644 index 00000000..7d30dbac --- /dev/null +++ b/doc/_static/meson.build @@ -0,0 +1 @@ +configure_file(copy: true, input: '../../resources/serd.svg', output: 'serd.svg') diff --git a/doc/c/Doxyfile b/doc/c/Doxyfile deleted file mode 100644 index a88f8ce8..00000000 --- a/doc/c/Doxyfile +++ /dev/null @@ -1,33 +0,0 @@ -PROJECT_NAME = Serd -PROJECT_BRIEF = "A lightweight library for RDF storage and serialisation" - -QUIET = YES -WARN_AS_ERROR = NO -WARN_IF_UNDOCUMENTED = NO -WARN_NO_PARAMDOC = NO - -JAVADOC_AUTOBRIEF = YES - -CASE_SENSE_NAMES = YES -HIDE_IN_BODY_DOCS = YES -REFERENCES_LINK_SOURCE = NO - -GENERATE_HTML = NO -GENERATE_LATEX = NO -GENERATE_XML = YES -XML_PROGRAMLISTING = NO -SHOW_FILES = NO - -MACRO_EXPANSION = YES -PREDEFINED = SERD_ALLOCATED \ - SERD_API \ - SERD_CONST_FUNC= \ - SERD_DEPRECATED_BY(x)= \ - SERD_DISABLE_DEPRECATED \ - SERD_NONNULL= \ - SERD_NULLABLE= \ - SERD_PURE_FUNC= - -INPUT = ../../include/serd/serd.h - -OUTPUT_DIRECTORY = . diff --git a/doc/c/Doxyfile.in b/doc/c/Doxyfile.in new file mode 100644 index 00000000..007f2be2 --- /dev/null +++ b/doc/c/Doxyfile.in @@ -0,0 +1,36 @@ +PROJECT_NAME = Serd +PROJECT_BRIEF = "A lightweight library for RDF storage and serialisation" + +QUIET = YES +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 + +GENERATE_HTML = NO +GENERATE_LATEX = NO +GENERATE_XML = YES +XML_PROGRAMLISTING = NO +SHOW_FILES = NO + +MACRO_EXPANSION = YES +PREDEFINED = SERD_ALLOCATED \ + SERD_API \ + SERD_CONST_FUNC= \ + SERD_DEPRECATED_BY(x)= \ + SERD_DISABLE_DEPRECATED \ + SERD_NONNULL= \ + SERD_NULLABLE= \ + SERD_PURE_FUNC= + +RECURSIVE = YES +STRIP_FROM_PATH = @SERD_SRCDIR@ +INPUT = @SERD_SRCDIR@/include + +OUTPUT_DIRECTORY = @DOX_OUTPUT@ diff --git a/doc/c/api/meson.build b/doc/c/api/meson.build new file mode 100644 index 00000000..1c0cbfee --- /dev/null +++ b/doc/c/api/meson.build @@ -0,0 +1,5 @@ +c_serd_rst = custom_target( + 'Serd C API ReST Documentation', + command: [dox_to_sphinx, '-f', '@INPUT0@', meson.current_build_dir()], + 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..4e044f97 --- /dev/null +++ b/doc/c/meson.build @@ -0,0 +1,44 @@ +config = configuration_data() +config.set('SERD_VERSION', meson.project_version()) + +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') + +docs = custom_target( + 'singlehtml documentation for serd', + command: [sphinx_build, '-M', 'singlehtml', + meson.current_build_dir(), meson.current_build_dir(), + '-E', '-q', '-t', 'singlehtml'], + input: [c_rst_files, c_serd_rst, c_index_xml], + output: 'singlehtml', + build_by_default: true, + install: true, + install_dir: docdir / versioned_name) + +docs = custom_target( + 'html documentation for serd', + command: [sphinx_build, '-M', 'html', + meson.current_build_dir(), meson.current_build_dir(), + '-E', '-q', '-t', 'html'], + input: [c_rst_files, c_serd_rst, c_index_xml], + output: 'html', + build_by_default: true, + install: true, + install_dir: docdir / versioned_name) 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..b9087439 --- /dev/null +++ b/doc/c/xml/meson.build @@ -0,0 +1,19 @@ +doxygen = find_program('doxygen') + +c_doxygen_input = [] +foreach h : c_headers + c_doxygen_input += ['..' / h] +endforeach + +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('serd-c-index.xml', + command: [doxygen, '@INPUT0@'], + input: [c_doxyfile] + c_header_files, + output: 'index.xml') diff --git a/doc/conf.py.in b/doc/conf.py.in index 9375f0dc..4d5bf6b8 100644 --- a/doc/conf.py.in +++ b/doc/conf.py.in @@ -1,7 +1,7 @@ # Project information project = "Serd" -copyright = "2020, David Robillard" +copyright = "2021, 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_static_path = ["../_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..5555cfbb --- /dev/null +++ b/doc/meson.build @@ -0,0 +1,17 @@ +docdir = get_option('datadir') / 'doc' + +doxygen = find_program('doxygen', required: get_option('docs')) +dox_to_sphinx = find_program('../scripts/dox_to_sphinx.py') +sphinx_build = find_program('sphinx-build', required: get_option('docs')) + +build_docs = doxygen.found() and sphinx_build.found() + +if build_docs + subdir('_static') + subdir('c') +endif + +if not meson.is_subproject() and meson.version().version_compare('>=0.53.0') + summary('Documentation', build_docs, bool_yn: true) +endif + diff --git a/meson.build b/meson.build new file mode 100644 index 00000000..bf29d82c --- /dev/null +++ b/meson.build @@ -0,0 +1,200 @@ +project('serd', ['c'], + version: '0.30.11', + license: 'ISC', + meson_version: '>= 0.49.2', + default_options: [ + 'b_ndebug=if-release', + 'buildtype=release', + 'c_std=c99', + 'default_library=shared', + 'warning_level=2', + ]) + +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 + +# Load build tools +pkg = import('pkgconfig') +cc = meson.get_compiler('c') + +# Set ultra strict warnings for developers, if requested +c_warnings = [] +c_suppressions = [] +if get_option('strict') + subdir('meson') + + c_warnings = all_c_warnings + + 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-float-conversion', # MinGW + '-Wno-format-nonliteral', + '-Wno-inline', + '-Wno-padded', + '-Wno-sign-conversion', + '-Wno-switch-default', + '-Wno-unsuffixed-float-constants', + '-Wno-unused-const-variable', + ] + 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_warnings += cc.get_supported_arguments(c_suppressions) + +# Add special arguments for MSVC +if cc.get_id() == 'msvc' + msvc_args = [ + '/D_CRT_SECURE_NO_WARNINGS', + '/TP', + '/experimental:external', + '/external:W0', + '/external:anglebrackets', + ] + + add_project_arguments(msvc_args, language: ['c']) +endif + +c_headers = ['include/serd/serd.h'] +c_header_files = files(c_headers) +c_header = files('include/serd/serd.h') + +sources = [ + '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', +] + +# System libraries +m_dep = cc.find_library('m', required: false) + +# Determine library type and the flags needed to build it +library_name = versioned_name +if get_option('default_library') == 'both' + if host_machine.system() == 'windows' + error('default_library=both is not supported on Windows') + endif + + prog_args = [] + library_type = 'both_libraries' + library_args = ['-DSERD_INTERNAL'] +elif get_option('default_library') == 'shared' + if host_machine.system() == 'windows' + # Meson annoyingly adds its own suffix, so remove this one + library_name = 'serd' + endif + + prog_args = [] + library_type = 'shared_library' + library_args = ['-DSERD_INTERNAL'] +else + prog_args = ['-DSERD_STATIC'] + library_type = 'static_library' + library_args = ['-DSERD_INTERNAL', '-DSERD_STATIC'] +endif + +# Build shared and/or static library/libraries +libserd = build_target( + library_name, + sources, + version: meson.project_version(), + include_directories: include_directories(['include']), + c_args: c_warnings + library_args, + dependencies: m_dep, + gnu_symbol_visibility: 'hidden', + install: true, + target_type: library_type) + +serd_dep = declare_dependency( + include_directories: include_directories(['include']), + link_with: libserd) + +pkg.generate( + libserd, + name: 'Serd', + filebase: versioned_name, + subdirs: [versioned_name], + version: meson.project_version(), + description: 'A lightweight library for working with RDF') + +# Build serdi command line utility +if get_option('utils') + + tool_link_args = [] + if get_option('static') + tool_link_args += ['-static'] + endif + + serdi = executable('serdi', 'src/serdi.c', + c_args: c_warnings + prog_args, + link_args: tool_link_args, + install: true, + dependencies: serd_dep) + + if not get_option('docs').disabled() + install_man('doc/serdi.1') + endif +endif + +# Install header to a versioned include directory +install_headers(c_headers, subdir: versioned_name / 'serd') + +if not get_option('docs').disabled() + subdir('doc') +endif + +if get_option('tests') + subdir('test') +endif + +if not meson.is_subproject() and meson.version().version_compare('>=0.53.0') + summary('Tests', get_option('tests'), bool_yn: true) + summary('Utilities', get_option('utils'), 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 get_option('utils') + summary('Executables', get_option('prefix') / get_option('bindir')) + summary('Man pages', get_option('prefix') / get_option('mandir')) + endif +endif diff --git a/meson/meson.build b/meson/meson.build new file mode 100644 index 00000000..a91fa2b5 --- /dev/null +++ b/meson/meson.build @@ -0,0 +1,203 @@ +# General code to enable approximately all warnings. +# +# This is trivial for clang and MSVC, but GCC does not have such an option, and +# has several esoteric warnings, so we need to enable everything we want +# explicitly. We enable everything that does not require a value argument, +# except for warnings that are only relevant for very old languages (earlier +# than C99 or C++11) or non-standard extensions. +# +# Omitted common warnings: +# +# 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 +# +# Omitted C warnings: +# +# Wc90-c99-compat +# Wdeclaration-after-statement +# Wtraditional +# Wtraditional-conversion +# +# Omitted C++ warnings: +# +# Wnamespaces +# Wtemplates + +gcc_common_warnings = [ + '-Walloc-zero', + '-Walloca', + '-Wanalyzer-too-complex', + '-Warith-conversion', + '-Warray-bounds=2', + '-Wattribute-alias=2', + '-Wcast-align=strict', + '-Wcast-qual', + '-Wconversion', + '-Wdate-time', + '-Wdisabled-optimization', + '-Wdouble-promotion', + '-Wduplicated-branches', + '-Wduplicated-cond', + '-Wfloat-equal', + '-Wformat-overflow=2', + '-Wformat-signedness', + '-Wformat-truncation=2', + '-Wformat=2', + '-Wimplicit-fallthrough=2', + '-Winit-self', + '-Winline', + '-Winvalid-pch', + '-Wlogical-op', + '-Wmissing-declarations', + '-Wmissing-include-dirs', + '-Wmultichar', + '-Wnormalized=nfc', + '-Wnull-dereference', + '-Wpacked', + '-Wpadded', + '-Wredundant-decls', + '-Wscalar-storage-order', + '-Wshadow', + '-Wshift-overflow=2', + '-Wsizeof-array-argument', + '-Wstack-protector', + '-Wstrict-aliasing=3', + '-Wstrict-overflow=5', + '-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', + '-Wsync-nand', + '-Wundef', + '-Wunused-const-variable=2', + '-Wunused-macros', + '-Wvarargs', + '-Wvector-operation-performance', + '-Wvla', + '-Wwrite-strings', +] + +gcc_c_warnings = [ + '-Wbad-function-cast', + '-Wc++-compat', + '-Wc99-c11-compat', + '-Wdesignated-init', + '-Wdiscarded-array-qualifiers', + '-Wdiscarded-qualifiers', + '-Wincompatible-pointer-types', + '-Wjump-misses-init', + '-Wmissing-prototypes', + '-Wnested-externs', + '-Wold-style-definition', + '-Wstrict-prototypes', + '-Wunsuffixed-float-constants', +] + +# Set all_c_warnings for the current C compiler +if is_variable('cc') and not is_variable('all_c_warnings') + if cc.get_id() == 'clang' + all_c_warnings = ['-Weverything'] + elif cc.get_id() == 'gcc' + all_c_warnings = gcc_common_warnings + [ + '-Wbad-function-cast', + '-Wc++-compat', + '-Wc99-c11-compat', + '-Wdesignated-init', + '-Wdiscarded-array-qualifiers', + '-Wdiscarded-qualifiers', + '-Wincompatible-pointer-types', + '-Wjump-misses-init', + '-Wmissing-prototypes', + '-Wnested-externs', + '-Wold-style-definition', + '-Wstrict-prototypes', + '-Wunsuffixed-float-constants', + ] + elif cc.get_id() == 'msvc' + all_c_warnings = ['/Wall'] + else + all_c_warnings = [] + endif + + all_c_warnings = cc.get_supported_arguments(all_c_warnings) + +endif + +# Set all_cpp_warnings for the current C++ compiler +if is_variable('cpp') and not is_variable('all_cpp_warnings') + if cpp.get_id() == 'clang' + all_cpp_warnings = [ + '-Weverything', + '-Wno-c++98-compat', + '-Wno-c++98-compat-pedantic' + ] + elif cpp.get_id() == 'gcc' + all_cpp_warnings = gcc_common_warnings + [ + '-Wabi-tag', + '-Waligned-new=all', + '-Wcatch-value=3', + '-Wcomma-subscript', + '-Wconditionally-supported', + '-Wctor-dtor-privacy', + '-Wdeprecated-copy-dtor', + '-Weffc++', + '-Wextra-semi', + '-Wmismatched-tags', + '-Wmultiple-inheritance', + '-Wnoexcept', + '-Wnoexcept-type', + '-Wnon-virtual-dtor', + '-Wold-style-cast', + '-Woverloaded-virtual', + '-Wplacement-new=2', + '-Wredundant-tags', + '-Wregister', + '-Wsign-promo', + '-Wstrict-null-sentinel', + '-Wsuggest-final-methods', + '-Wsuggest-final-types', + '-Wsuggest-override', + '-Wvirtual-inheritance', + '-Wvolatile', + '-Wzero-as-null-pointer-constant', + ] + elif cpp.get_id() == 'msvc' + all_cpp_warnings = ['/Wall'] + else + all_cpp_warnings = [] + endif + + all_cpp_warnings = cpp.get_supported_arguments(all_cpp_warnings) + +endif + +# Set all_objc_warnings for the current Objective C compiler +if is_variable('objcc') and not is_variable('all_objc_warnings') + all_objc_warnings = [] + if objcc.get_id() == 'clang' + all_objc_warnings = ['-Weverything'] + elif objc.get_id() == 'gcc' + all_objc_warnings = gcc_common_warnings + [ + '-Wno-direct-ivar-access', + ] + else + all_objc_warnings = [] + endif + + all_objc_warnings = objcc.get_supported_arguments(all_objc_warnings) +endif diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 00000000..5d55e09c --- /dev/null +++ b/meson_options.txt @@ -0,0 +1,14 @@ +option('docs', type: 'feature', value: 'auto', yield: true, + description: 'Build documentation') + +option('strict', type: 'boolean', value: false, yield: true, + description: 'Enable ultra-strict warnings') + +option('tests', type: 'boolean', value: true, yield: true, + description: 'Build tests') + +option('utils', type: 'boolean', value: true, yield: true, + description: 'Build command line utilities') + +option('static', type: 'boolean', value: false, yield: true, + description: 'Statically link executables') diff --git a/serd.pc.in b/serd.pc.in deleted file mode 100644 index c3604c8a..00000000 --- a/serd.pc.in +++ /dev/null @@ -1,11 +0,0 @@ -prefix=@PREFIX@ -exec_prefix=@EXEC_PREFIX@ -libdir=@LIBDIR@ -includedir=@INCLUDEDIR@ - -Name: Serd -Version: @SERD_VERSION@ -Description: Lightweight RDF syntax library -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..61ed6d6f --- /dev/null +++ b/test/meson.build @@ -0,0 +1,184 @@ +autoship = find_program('autoship', required: false) + +run_test_suite = find_program('run_test_suite.py') + +wrapper = meson.get_cross_property('exe_wrapper', '') + +unit_tests = [ + 'env', + 'free_null', + 'node', + 'read_chunk', + 'reader_writer', + 'string', + 'uri', + 'writer', +] + +foreach unit : unit_tests + test(unit, + executable('test_@0@'.format(unit), + 'test_@0@.c'.format(unit), + c_args: c_warnings + prog_args, + dependencies: serd_dep), + suite: 'unit') +endforeach + +if autoship.found() + test('autoship', autoship, args: ['test', serd_src_root], suite: 'data') +endif + +if get_option('utils') + + 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', ' a .'], + ] + + 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', ' a .'], + ] + + 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', ' a .'], + 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(meson.source_root())], + should_fail: true, + suite: 'io_errors') + + test('bulk_read_dir', serdi, + args: ['file://@0@/'.format(meson.source_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..7f141943 --- /dev/null +++ b/test/test_quiet.py @@ -0,0 +1,21 @@ +#!/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, capture_output=True) + +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") diff --git a/waf b/waf deleted file mode 100755 index 887215c7..00000000 --- a/waf +++ /dev/null @@ -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 index b600c928..00000000 --- a/waflib +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b600c928b221a001faeab7bd92786d0b25714bc8 diff --git a/wscript b/wscript deleted file mode 100644 index 121cd5da..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.11' -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', ' a .']) - 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', ' a .']) - - 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: .\n' - '@prefix dc: .\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']) -- cgit v1.2.1