From bcdb9db57e8f68ba90077a77a4152b72ca6da7ac Mon Sep 17 00:00:00 2001 From: David Robillard Date: Mon, 16 Jan 2012 22:23:42 +0000 Subject: Support compilation as C++ under MSVC++. git-svn-id: http://svn.drobilla.net/sord/trunk@194 3d64ff67-21c5-427c-a301-fe4f08042e5a --- ChangeLog | 1 + sord/sord.h | 3 ++- src/sord.c | 64 +++++++++++++++++++++++++++---------------------- src/sord_test.c | 20 ++++++++-------- src/sordi.c | 31 ++++++------------------ src/syntax.c | 2 +- src/zix/common.h | 15 +++++++++--- src/zix/hash.c | 2 +- src/zix/tree.c | 4 ++-- src/zix/tree.h | 2 -- wscript | 73 ++++++++++++++++++++++++++++++++++---------------------- 11 files changed, 116 insertions(+), 101 deletions(-) diff --git a/ChangeLog b/ChangeLog index ad51d66..69dd838 100644 --- a/ChangeLog +++ b/ChangeLog @@ -5,6 +5,7 @@ sord (UNRELEASED) unstable; urgency=low * Tolerate serd passing NULL nodes to reader callback (serd 0.6.0) * Fix comparison of typed literals * Take advantage of interning in sord_node_equals() + * Support compilation as C++ under MSVC++. -- David Robillard (UNRELEASED) diff --git a/sord/sord.h b/sord/sord.h index b0f3017..e6f5bd8 100644 --- a/sord/sord.h +++ b/sord/sord.h @@ -21,7 +21,6 @@ #ifndef SORD_SORD_H #define SORD_SORD_H -#include #include #include #include @@ -47,6 +46,8 @@ #ifdef __cplusplus extern "C" { +#else +# include #endif /** diff --git a/src/sord.c b/src/sord.c index 0e33dd4..2895b78 100644 --- a/src/sord.c +++ b/src/sord.c @@ -17,7 +17,6 @@ // C99 #include #include -#include #include #include #include @@ -166,7 +165,7 @@ sord_literal_equal(const void* a, const void* b) SordWorld* sord_world_new(void) { - SordWorld* world = malloc(sizeof(struct SordWorldImpl)); + SordWorld* world = (SordWorld*)malloc(sizeof(SordWorld)); world->names = zix_hash_new(zix_string_hash, zix_string_equal); world->langs = zix_hash_new(zix_string_hash, zix_string_equal); world->literals = zix_hash_new(sord_literal_hash, sord_literal_equal); @@ -369,7 +368,7 @@ sord_iter_new(const SordModel* sord, ZixTreeIter* cur, const SordQuad pat, { const int* ordering = orderings[order]; - SordIter* iter = malloc(sizeof(struct SordIterImpl)); + SordIter* iter = (SordIter*)malloc(sizeof(SordIter)); iter->sord = sord; iter->cur = cur; iter->mode = mode; @@ -504,7 +503,7 @@ static inline bool sord_has_index(SordModel* sord, SordOrder* order, int* n_prefix, bool graphs) { if (graphs) { - *order += GSPO; + *order = (SordOrder)(*order + GSPO); *n_prefix += 1; } @@ -694,7 +693,7 @@ sord_free(SordModel* sord) for (; !sord_iter_end(i); sord_iter_next(i)) { sord_iter_get(i, tup); for (int i = 0; i < TUP_LEN; ++i) { - sord_drop_quad_ref(sord, (SordNode*)tup[i], i); + sord_drop_quad_ref(sord, (SordNode*)tup[i], (SordQuadIndex)i); } } sord_iter_free(i); @@ -824,13 +823,13 @@ sord_contains(SordModel* sord, const SordQuad pat) static SordNode* sord_lookup_name(SordWorld* world, const uint8_t* str) { - return zix_hash_find(world->names, str); + return (SordNode*)zix_hash_find(world->names, str); } char* sord_strndup(const char* str, size_t len) { - char* dup = malloc(len + 1); + char* dup = (char*)malloc(len + 1); memcpy(dup, str, len + 1); return dup; } @@ -838,19 +837,22 @@ sord_strndup(const char* str, size_t len) static SordNode* sord_new_node(SerdType type, const uint8_t* data, size_t n_bytes, size_t n_chars, SerdNodeFlags flags, - SordNode* datatype, const char* lang) + SordNode* datatype, const char* lang, bool copy) { - SordNode* node = malloc(sizeof(struct SordNodeImpl)); + SordNode* node = (SordNode*)malloc(sizeof(SordNode)); node->lang = lang; node->datatype = datatype; node->refs = 1; node->refs_as_obj = 0; - node->node.buf = (uint8_t*)sord_strndup((const char*)data, n_bytes); + node->node.buf = (uint8_t*)data; node->node.n_bytes = n_bytes; node->node.n_chars = n_chars; node->node.flags = flags; node->node.type = type; + if (copy) { + node->node.buf = (uint8_t*)sord_strndup((const char*)data, n_bytes); + } return node; } @@ -858,7 +860,7 @@ const char* sord_intern_lang(SordWorld* world, const char* lang) { if (lang) { - char* ilang = zix_hash_find(world->langs, lang); + char* ilang = (char*)zix_hash_find(world->langs, lang); if (!ilang) { ilang = sord_strndup(lang, strlen(lang)); zix_hash_insert(world->langs, ilang, ilang); @@ -884,7 +886,7 @@ sord_lookup_literal(SordWorld* world, SordNode* type, key.node.flags = 0; key.node.type = SERD_LITERAL; - return zix_hash_find(world->literals, &key); + return (SordNode*)zix_hash_find(world->literals, &key); } SordNodeType @@ -948,15 +950,18 @@ sord_add_node(SordWorld* world, SordNode* node) static SordNode* sord_new_uri_counted(SordWorld* world, const uint8_t* str, - size_t n_bytes, size_t n_chars) + size_t n_bytes, size_t n_chars, bool copy) { SordNode* node = sord_lookup_name(world, str); if (node) { + if (!copy) { + free((uint8_t*)str); + } ++node->refs; return node; } - node = sord_new_node(SERD_URI, str, n_bytes, n_chars, 0, 0, 0); + node = sord_new_node(SERD_URI, str, n_bytes, n_chars, 0, 0, 0, copy); assert(!zix_hash_find(world->names, node->node.buf)); zix_hash_insert(world->names, (char*)node->node.buf, node); sord_add_node(world, node); @@ -967,7 +972,7 @@ SordNode* sord_new_uri(SordWorld* world, const uint8_t* str) { const SerdNode node = serd_node_from_string(SERD_URI, str); - return sord_new_uri_counted(world, str, node.n_bytes, node.n_chars); + return sord_new_uri_counted(world, str, node.n_bytes, node.n_chars, true); } static SordNode* @@ -980,7 +985,7 @@ sord_new_blank_counted(SordWorld* world, const uint8_t* str, return node; } - node = sord_new_node(SERD_BLANK, str, n_bytes, n_chars, 0, 0, 0); + node = sord_new_node(SERD_BLANK, str, n_bytes, n_chars, 0, 0, 0, true); zix_hash_insert(world->names, (char*)node->node.buf, node); sord_add_node(world, node); return node; @@ -1012,7 +1017,7 @@ sord_new_literal_counted(SordWorld* world, node = sord_new_node(SERD_LITERAL, str, n_bytes, n_chars, flags, - sord_node_copy(datatype), lang); + sord_node_copy(datatype), lang, true); zix_hash_insert(world->literals, node, node); // FIXME: correct? sord_add_node(world, node); assert(node->refs == 1); @@ -1062,8 +1067,8 @@ sord_node_from_serd_node(SordWorld* world, return ret; case SERD_URI: if (serd_uri_string_has_scheme(sn->buf)) { - return sord_new_uri_counted(world, - sn->buf, sn->n_bytes, sn->n_chars); + return sord_new_uri_counted( + world, sn->buf, sn->n_bytes, sn->n_chars, true); } else { SerdURI base_uri; serd_env_get_base_uri(env, &base_uri); @@ -1073,7 +1078,8 @@ sord_node_from_serd_node(SordWorld* world, SordNode* ret = sord_new_uri_counted(world, abs_uri_node.buf, abs_uri_node.n_bytes, - abs_uri_node.n_chars); + abs_uri_node.n_chars, + true); serd_node_free(&abs_uri_node); return ret; } @@ -1085,13 +1091,13 @@ sord_node_from_serd_node(SordWorld* world, return NULL; } const size_t uri_len = uri_prefix.len + uri_suffix.len; - uint8_t buf[uri_len + 1]; + uint8_t* buf = (uint8_t*)malloc(uri_len + 1); memcpy(buf, uri_prefix.buf, uri_prefix.len); memcpy(buf + uri_prefix.len, uri_suffix.buf, uri_suffix.len); buf[uri_len] = '\0'; SordNode* ret = sord_new_uri_counted( world, buf, uri_prefix.len + uri_suffix.len, - uri_prefix.len + uri_suffix.len); // FIXME: UTF-8 + uri_prefix.len + uri_suffix.len, false); // FIXME: UTF-8 return ret; } case SERD_BLANK: @@ -1144,12 +1150,12 @@ sord_add(SordModel* sord, const SordQuad tup) return false; } - const SordNode** quad = malloc(sizeof(SordQuad)); + const SordNode** quad = (const SordNode**)malloc(sizeof(SordQuad)); memcpy(quad, tup, sizeof(SordQuad)); for (unsigned i = 0; i < NUM_ORDERS; ++i) { if (sord->indices[i]) { - if (!sord_add_to_index(sord, quad, i)) { + if (!sord_add_to_index(sord, quad, (SordOrder)i)) { assert(i == 0); // Assuming index coherency free(quad); return false; // Quad already stored, do nothing @@ -1157,8 +1163,8 @@ sord_add(SordModel* sord, const SordQuad tup) } } - for (SordQuadIndex i = 0; i < TUP_LEN; ++i) - sord_add_quad_ref(sord, tup[i], i); + for (int i = 0; i < TUP_LEN; ++i) + sord_add_quad_ref(sord, tup[i], (SordQuadIndex)i); ++sord->n_quads; return true; @@ -1175,7 +1181,7 @@ sord_remove(SordModel* sord, const SordQuad tup) ZixTreeIter* const cur = index_search(sord->indices[i], tup); if (!zix_tree_iter_is_end(cur)) { if (!quad) { - quad = zix_tree_get(cur); + quad = (SordNode**)zix_tree_get(cur); } zix_tree_remove(sord->indices[i], cur); } else { @@ -1187,8 +1193,8 @@ sord_remove(SordModel* sord, const SordQuad tup) free(quad); - for (SordQuadIndex i = 0; i < TUP_LEN; ++i) - sord_drop_quad_ref(sord, tup[i], i); + for (int i = 0; i < TUP_LEN; ++i) + sord_drop_quad_ref(sord, tup[i], (SordQuadIndex)i); --sord->n_quads; } diff --git a/src/sord_test.c b/src/sord_test.c index 93fa735..fa2e9ff 100644 --- a/src/sord_test.c +++ b/src/sord_test.c @@ -23,6 +23,8 @@ static const int DIGITS = 3; static const int MAX_NUM = 999; +static const int n_objects_per = 2; + typedef struct { SordQuad query; @@ -58,7 +60,6 @@ int generate(SordWorld* world, SordModel* sord, size_t n_quads, - size_t n_objects_per, SordNode* graph) { fprintf(stderr, "Generating %zu (S P *) quads with %zu objects each\n", @@ -177,7 +178,7 @@ generate(SordWorld* world, int test_read(SordWorld* world, SordModel* sord, SordNode* g, - const size_t n_quads, const int n_objects_per) + const size_t n_quads) { int ret = EXIT_SUCCESS; @@ -330,8 +331,7 @@ test_read(SordWorld* world, SordModel* sord, SordNode* g, int main(int argc, char** argv) { - static const size_t n_quads = 300; - static const int n_objects_per = 2; + static const size_t n_quads = 300; sord_free(NULL); // Shouldn't crash @@ -339,9 +339,9 @@ main(int argc, char** argv) // Create with minimal indexing SordModel* sord = sord_new(world, SORD_SPO, false); - generate(world, sord, n_quads, n_objects_per, NULL); + generate(world, sord, n_quads, NULL); - if (test_read(world, sord, NULL, n_quads, n_objects_per)) { + if (test_read(world, sord, NULL, n_quads)) { sord_free(sord); sord_world_free(world); return EXIT_FAILURE; @@ -454,8 +454,8 @@ main(int argc, char** argv) for (int i = 0; i < 6; ++i) { sord = sord_new(world, (1 << i), false); printf("Testing Index `%s'\n", index_names[i]); - generate(world, sord, n_quads, n_objects_per, 0); - if (test_read(world, sord, 0, n_quads, n_objects_per)) + generate(world, sord, n_quads, 0); + if (test_read(world, sord, 0, n_quads)) goto fail; sord_free(sord); } @@ -468,8 +468,8 @@ main(int argc, char** argv) sord = sord_new(world, (1 << i), true); printf("Testing Index `%s'\n", graph_index_names[i]); SordNode* graph = uri(world, 42); - generate(world, sord, n_quads, n_objects_per, graph); - if (test_read(world, sord, graph, n_quads, n_objects_per)) + generate(world, sord, n_quads, graph); + if (test_read(world, sord, graph, n_quads)) goto fail; sord_free(sord); } diff --git a/src/sordi.c b/src/sordi.c index 0cbf241..03ba916 100644 --- a/src/sordi.c +++ b/src/sordi.c @@ -1,5 +1,5 @@ /* - Copyright 2011 David Robillard + Copyright 2011-2012 David Robillard Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -34,8 +34,8 @@ int print_version() { printf("sordi " SORD_VERSION " \n"); - printf("Copyright 2011 David Robillard .\n" - "License: \n" + printf("Copyright 2011-2012 David Robillard .\n" + "License: \n" "This is free software; you are free to change and redistribute it." "\nThere is NO WARRANTY, to the extent permitted by law.\n"); return 0; @@ -128,25 +128,8 @@ main(int argc, char** argv) if (from_file) { in_name = in_name ? in_name : input; if (!in_fd) { - if (serd_uri_string_has_scheme(input)) { - // INPUT is an absolute URI, ensure it a file and chop scheme - if (strncmp((const char*)input, "file:", 5)) { - fprintf(stderr, "Unsupported URI scheme `%s'\n", input); - return 1; -#ifdef _WIN32 - } else if (!strncmp((const char*)input, "file:///", 8)) { - input += 8; -#else - } else if (!strncmp((const char*)input, "file://", 7)) { - input += 7; -#endif - } else { - input += 5; - } - } - in_fd = fopen((const char*)input, "r"); - if (!in_fd) { - fprintf(stderr, "Failed to open file %s\n", input); + input = serd_uri_to_path(in_name); + if (!input || !(in_fd = fopen((const char*)input, "rb"))) { return 1; } } @@ -188,7 +171,7 @@ main(int argc, char** argv) SerdEnv* write_env = serd_env_new(&base_uri_node); - SerdStyle output_style = SERD_STYLE_RESOLVED; + int output_style = SERD_STYLE_RESOLVED; if (output_syntax == SERD_NTRIPLES) { output_style |= SERD_STYLE_ASCII; } else { @@ -197,7 +180,7 @@ main(int argc, char** argv) SerdWriter* writer = serd_writer_new( output_syntax, - output_style, + (SerdStyle)output_style, write_env, &base_uri, serd_file_sink, stdout); // Write @prefix directives diff --git a/src/syntax.c b/src/syntax.c index e69607f..67fd31e 100644 --- a/src/syntax.c +++ b/src/syntax.c @@ -96,7 +96,7 @@ sord_new_reader(SordModel* model, SerdSyntax syntax, SordNode* graph) { - ReadState* state = malloc(sizeof(ReadState)); + ReadState* state = (ReadState*)malloc(sizeof(ReadState)); state->env = env; state->graph_uri_node = graph; state->world = sord_get_world(model); diff --git a/src/zix/common.h b/src/zix/common.h index 2f63fca..e7d35e1 100644 --- a/src/zix/common.h +++ b/src/zix/common.h @@ -17,8 +17,6 @@ #ifndef ZIX_COMMON_H #define ZIX_COMMON_H -#include - /** @addtogroup zix @{ @@ -43,6 +41,12 @@ #endif /** @endcond */ +#ifdef __cplusplus +extern "C" { +#else +# include +#endif + typedef enum { ZIX_STATUS_SUCCESS, ZIX_STATUS_ERROR, @@ -61,7 +65,12 @@ typedef int (*ZixComparator)(const void* a, const void* b, void* user_data); */ typedef bool (*ZixEqualFunc)(const void* a, const void* b); -/**@} +/** + @} */ +#ifdef __cplusplus +} /* extern "C" */ +#endif + #endif /* ZIX_COMMON_H */ diff --git a/src/zix/hash.c b/src/zix/hash.c index c267757..f654e91 100644 --- a/src/zix/hash.c +++ b/src/zix/hash.c @@ -55,7 +55,7 @@ zix_hash_new(ZixHashFunc hash_func, hash->key_equal_func = key_equal_func; hash->count = 0; hash->n_buckets = &sizes[0]; - hash->buckets = malloc(*hash->n_buckets * sizeof(Entry*)); + hash->buckets = (Entry**)malloc(*hash->n_buckets * sizeof(Entry*)); memset(hash->buckets, 0, *hash->n_buckets * sizeof(Entry*)); return hash; diff --git a/src/zix/tree.c b/src/zix/tree.c index 066b038..64e6e7c 100644 --- a/src/zix/tree.c +++ b/src/zix/tree.c @@ -47,7 +47,7 @@ ZIX_API ZixTree* zix_tree_new(bool allow_duplicates, ZixComparator cmp, void* cmp_data) { - ZixTree* t = malloc(sizeof(ZixTree)); + ZixTree* t = (ZixTree*)malloc(sizeof(ZixTree)); t->root = NULL; t->cmp = cmp; t->cmp_data = cmp_data; @@ -287,7 +287,7 @@ zix_tree_insert(ZixTree* t, void* e, ZixTreeIter** ti) } // Allocate a new node n - if (!(n = malloc(sizeof(ZixTreeNode)))) { + if (!(n = (ZixTreeNode*)malloc(sizeof(ZixTreeNode)))) { return ZIX_STATUS_NO_MEM; } memset(n, '\0', sizeof(ZixTreeNode)); diff --git a/src/zix/tree.h b/src/zix/tree.h index 670c437..bcbde26 100644 --- a/src/zix/tree.h +++ b/src/zix/tree.h @@ -17,8 +17,6 @@ #ifndef ZIX_TREE_H #define ZIX_TREE_H -#include - #include "zix/common.h" #ifdef __cplusplus diff --git a/wscript b/wscript index 9ab2ec1..5cc4afb 100644 --- a/wscript +++ b/wscript @@ -43,7 +43,11 @@ def configure(conf): autowaf.configure(conf) autowaf.display_header('Sord configuration') - conf.env.append_unique('CFLAGS', '-std=c99') + if conf.env['MSVC_COMPILER']: + conf.env.append_unique('CFLAGS', ['-TP', '-MD']) + conf.env.append_unique('CXXFLAGS', ['-TP', '-MD']) + else: + conf.env.append_unique('CFLAGS', '-std=c99') autowaf.check_pkg(conf, 'serd-0', uselib_store='SERD', atleast_version='0.8.0', mandatory=True) @@ -104,8 +108,12 @@ def build(bld): source = 'src/sord.c src/syntax.c src/zix/hash.c src/zix/tree.c' libflags = [ '-fvisibility=hidden' ] - if sys.platform == 'win32': + libs = [ 'm' ] + defines = [] + if bld.env['MSVC_COMPILER']: libflags = [] + libs = [] + defines = ['snprintf=_snprintf'] # Shared Library obj = bld(features = 'c cshlib', @@ -116,7 +124,8 @@ def build(bld): target = 'sord-%s' % SORD_MAJOR_VERSION, vnum = SORD_LIB_VERSION, install_path = '${LIBDIR}', - libs = [ 'm' ], + libs = libs, + defines = defines, cflags = libflags + [ '-DSORD_SHARED', '-DSORD_INTERNAL' ]) autowaf.use_lib(bld, obj, 'SERD') @@ -131,12 +140,12 @@ def build(bld): target = 'sord-%s' % SORD_MAJOR_VERSION, vnum = SORD_LIB_VERSION, install_path = '${LIBDIR}', - libs = [ 'm' ], + libs = libs, cflags = [ '-DSORD_INTERNAL' ]) autowaf.use_lib(bld, obj, 'SERD') if bld.env['BUILD_TESTS']: - test_libs = ['m'] + test_libs = libs test_cflags = [''] if bld.is_defined('HAVE_GCOV'): test_libs += ['gcov'] @@ -149,6 +158,7 @@ def build(bld): name = 'libsord_profiled', target = 'sord_profiled', install_path = '', + defines = defines, cflags = test_cflags, lib = test_libs) autowaf.use_lib(bld, obj, 'SERD') @@ -161,6 +171,7 @@ def build(bld): lib = test_libs, target = 'sord_test', install_path = '', + defines = defines, cflags = test_cflags) autowaf.use_lib(bld, obj, 'SERD') @@ -172,6 +183,7 @@ def build(bld): lib = test_libs, target = 'sordi_static', install_path = '', + defines = defines, cflags = test_cflags) autowaf.use_lib(bld, obj, 'SERD') @@ -183,7 +195,7 @@ def build(bld): lib = test_libs, target = 'sordmm_test', install_path = '', - cflags = test_cflags) + defines = defines) autowaf.use_lib(bld, obj, 'SERD') # Command line utility @@ -193,7 +205,8 @@ def build(bld): includes = ['.', './src'], use = 'libsord', target = 'sordi', - install_path = '${BINDIR}') + install_path = '${BINDIR}', + defines = defines) autowaf.use_lib(bld, obj, 'SERD') # Documentation @@ -256,36 +269,40 @@ def test(ctx): autowaf.pre_test(ctx, APPNAME) + os.environ['PATH'] = '.' + os.pathsep + os.getenv('PATH') + + nul = os.devnull + autowaf.run_tests(ctx, APPNAME, [ - './sordi_static file:%s/tests/manifest.ttl > /dev/null' % srcdir, - './sordi_static file://%s/tests/manifest.ttl > /dev/null' % srcdir, - './sordi_static %s/tests/UTF-8.ttl > /dev/null' % srcdir, - './sordi_static -v > /dev/null', - './sordi_static -h > /dev/null', - './sordi_static -s " a <#Thingie> ." > /dev/null', - './sordi_static /dev/null > /dev/null'], + 'sordi_static file://%s/tests/manifest.ttl > %s' % (srcdir, nul), + 'sordi_static %s/tests/UTF-8.ttl > %s' % (srcdir, nul), + 'sordi_static -v > %s' % nul, + 'sordi_static -h > %s' % nul, + 'sordi_static -s " a <#Thingie> ." > %s' % nul, + 'sordi_static %s > %s' % (nul, nul)], 0, name='sordi-cmd-good') autowaf.run_tests(ctx, APPNAME, [ - './sordi_static > /dev/null', - './sordi_static ftp://example.org/unsupported.ttl > /dev/null', - './sordi_static -i > /dev/null', - './sordi_static -o > /dev/null', - './sordi_static -z > /dev/null', - './sordi_static -p > /dev/null', - './sordi_static -c > /dev/null', - './sordi_static -i illegal > /dev/null', - './sordi_static -o illegal > /dev/null', - './sordi_static -i turtle > /dev/null', - './sordi_static /no/such/file > /dev/null'], + 'sordi_static > %s' % nul, + 'sordi_static ftp://example.org/unsupported.ttl > %s' % nul, + 'sordi_static -i > %s' % nul, + 'sordi_static -o > %s' % nul, + 'sordi_static -z > %s' % nul, + 'sordi_static -p > %s' % nul, + 'sordi_static -c > %s' % nul, + 'sordi_static -i illegal > %s' % nul, + 'sordi_static -o illegal > %s' % nul, + 'sordi_static -i turtle > %s' % nul, + 'sordi_static /no/such/file > %s' % nul], 1, name='sordi-cmd-bad') - autowaf.run_tests(ctx, APPNAME, ['./sord_test']) + autowaf.run_tests(ctx, APPNAME, ['sord_test']) commands = [] for test in good_tests: - base_uri = 'http://www.w3.org/2001/sw/DataAccess/df1/' + test - commands += [ './sordi_static %s/%s \'%s\' > %s.out' % (srcdir, test, base_uri, test) ] + base_uri = 'http://www.w3.org/2001/sw/DataAccess/df1/' + test.replace('\\', '/') + commands += [ 'sordi_static "%s" "%s" > %s.out' % ( + os.path.join(srcdir, test), base_uri, test) ] autowaf.run_tests(ctx, APPNAME, commands, 0, name='good') -- cgit v1.2.1