From ee1e682e0a3787a4b293e55a09d2394bd1d47252 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sun, 19 Feb 2012 06:42:02 +0000 Subject: Add 'seriatom', an LV2 Atom RDF serialisation library. git-svn-id: http://svn.drobilla.net/lad/trunk/seriatom@3989 a436a847-0d15-0410-975c-d299462d15a1 --- seriatom.pc.in | 11 ++ seriatom/seriatom.h | 88 ++++++++++++++++ src/atom_to_rdf.c | 173 +++++++++++++++++++++++++++++++ tests/seriatom_test.c | 274 ++++++++++++++++++++++++++++++++++++++++++++++++++ waf | Bin 0 -> 91475 bytes wscript | 181 +++++++++++++++++++++++++++++++++ 6 files changed, 727 insertions(+) create mode 100644 seriatom.pc.in create mode 100644 seriatom/seriatom.h create mode 100644 src/atom_to_rdf.c create mode 100644 tests/seriatom_test.c create mode 100755 waf create mode 100644 wscript diff --git a/seriatom.pc.in b/seriatom.pc.in new file mode 100644 index 0000000..1e3af45 --- /dev/null +++ b/seriatom.pc.in @@ -0,0 +1,11 @@ +prefix=@PREFIX@ +exec_prefix=@EXEC_PREFIX@ +libdir=@LIBDIR@ +includedir=@INCLUDEDIR@ + +Name: Seriatom +Version: @SERIATOM_VERSION@ +Description: LV2 Atom RDF serialisation library +Requires: serd-0 +Libs: -L${libdir} -l@LIB_SERIATOM@ +Cflags: -I${includedir}/seriatom-@SERIATOM_MAJOR_VERSION@ diff --git a/seriatom/seriatom.h b/seriatom/seriatom.h new file mode 100644 index 0000000..8381778 --- /dev/null +++ b/seriatom/seriatom.h @@ -0,0 +1,88 @@ +/* + Copyright 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 + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file seriatom.h API for Seriatom, an LV2 Atom RDF serialisation library. +*/ + +#ifndef SERIATOM_SERIATOM_H +#define SERIATOM_SERIATOM_H + +#include + +#include "lv2/lv2plug.in/ns/ext/atom/atom.h" +#include "lv2/lv2plug.in/ns/ext/urid/urid.h" +#include "serd/serd.h" + +#ifdef SERIATOM_SHARED +# ifdef _WIN32 +# define SERIATOM_LIB_IMPORT __declspec(dllimport) +# define SERIATOM_LIB_EXPORT __declspec(dllexport) +# else +# define SERIATOM_LIB_IMPORT __attribute__((visibility("default"))) +# define SERIATOM_LIB_EXPORT __attribute__((visibility("default"))) +# endif +# ifdef SERIATOM_INTERNAL +# define SERIATOM_API SERIATOM_LIB_EXPORT +# else +# define SERIATOM_API SERIATOM_LIB_IMPORT +# endif +#else +# define SERIATOM_API +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + @defgroup seriatom Seriatom + An LV2 Atom RDF serialisation library. + @{ +*/ + +/** + Serialise an Atom to a SerdWriter. +*/ +SERIATOM_API +void +atom_to_rdf(SerdWriter* writer, + LV2_URID_Unmap* unmap, + const SerdNode* subject, + const SerdNode* predicate, + const LV2_Atom* atom, + uint32_t flags); + +/** + Serialise an Atom to a Turtle string. + The returned string must be free()'d by the caller. +*/ +SERIATOM_API +char* +atom_to_turtle(LV2_URID_Unmap* unmap, + const SerdNode* subject, + const SerdNode* predicate, + const LV2_Atom* atom); + +/** + @} +*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SERIATOM_SERIATOM_H */ diff --git a/src/atom_to_rdf.c b/src/atom_to_rdf.c new file mode 100644 index 0000000..f9e3895 --- /dev/null +++ b/src/atom_to_rdf.c @@ -0,0 +1,173 @@ +/* + Copyright 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 + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include +#include + +#include "lv2/lv2plug.in/ns/ext/atom/util.h" +#include "seriatom/seriatom.h" + +#define NS_RDF "http://www.w3.org/1999/02/22-rdf-syntax-ns#" +#define NS_XSD "http://www.w3.org/2001/XMLSchema#" + +#define USTR(str) ((const uint8_t*)(str)) + +typedef struct { + char* buf; + size_t len; +} String; + +static size_t +string_sink(const void* buf, size_t len, void* stream) +{ + String* str = (String*)stream; + str->buf = realloc(str->buf, str->len + len); + memcpy(str->buf + str->len, buf, len); + str->len += len; + return len; +} + +SERIATOM_API +void +atom_to_rdf(SerdWriter* writer, + LV2_URID_Unmap* unmap, + const SerdNode* subject, + const SerdNode* predicate, + const LV2_Atom* atom, + uint32_t flags) +{ + const char* const type = unmap->unmap(unmap->handle, atom->type); + SerdNode object = SERD_NODE_NULL; + SerdNode datatype = SERD_NODE_NULL; + SerdNode language = SERD_NODE_NULL; + bool new_node = false; + if (atom->type == 0 && atom->size == 0) { + object = serd_node_from_string(SERD_BLANK, USTR("null")); + } else if (!strcmp(type, LV2_ATOM__String)) { + const uint8_t* str = USTR(LV2_ATOM_BODY(atom)); + object = serd_node_from_string(SERD_LITERAL, str); + } else if (!strcmp(type, LV2_ATOM__Literal)) { + LV2_Atom_Literal* lit = (LV2_Atom_Literal*)atom; + const uint8_t* str = USTR(LV2_ATOM_CONTENTS(LV2_Atom_Literal, lit)); + object = serd_node_from_string(SERD_LITERAL, str); + if (lit->datatype) { + datatype = serd_node_from_string( + SERD_URI, USTR(unmap->unmap(unmap->handle, lit->datatype))); + } else if (lit->lang) { + const char* lang = unmap->unmap(unmap->handle, lit->lang); + const char* prefix = "http://lexvo.org/id/iso639-3/"; + const size_t prefix_len = strlen(prefix); + if (lang && !strncmp(lang, prefix, prefix_len)) { + language = serd_node_from_string( + SERD_LITERAL, USTR(lang + prefix_len)); + } else { + fprintf(stderr, "Unknown language URI <%s>\n", lang); + } + } + } else if (!strcmp(type, LV2_ATOM__URID)) { + const uint32_t id = *(const uint32_t*)LV2_ATOM_BODY(atom); + const uint8_t* str = USTR(unmap->unmap(unmap->handle, id)); + object = serd_node_from_string(SERD_URI, str); + } else if (!strcmp(type, LV2_ATOM__Path)) { + const uint8_t* str = USTR(LV2_ATOM_BODY(atom)); + object = serd_node_from_string(SERD_LITERAL, str); + datatype = serd_node_from_string(SERD_URI, USTR(LV2_ATOM__Path)); + } else if (!strcmp(type, LV2_ATOM__URI)) { + const uint8_t* str = USTR(LV2_ATOM_BODY(atom)); + object = serd_node_from_string(SERD_URI, str); + } else if (!strcmp(type, LV2_ATOM__Int32)) { + new_node = true; + object = serd_node_new_integer(*(int32_t*)LV2_ATOM_BODY(atom)); + datatype = serd_node_from_string(SERD_URI, USTR(NS_XSD "int")); + } else if (!strcmp(type, LV2_ATOM__Int64)) { + new_node = true; + object = serd_node_new_integer(*(int64_t*)LV2_ATOM_BODY(atom)); + datatype = serd_node_from_string(SERD_URI, USTR(NS_XSD "long")); + } else if (!strcmp(type, LV2_ATOM__Float)) { + new_node = true; + object = serd_node_new_decimal(*(float*)LV2_ATOM_BODY(atom), 8); + datatype = serd_node_from_string(SERD_URI, USTR(NS_XSD "float")); + } else if (!strcmp(type, LV2_ATOM__Double)) { + new_node = true; + object = serd_node_new_decimal(*(double*)LV2_ATOM_BODY(atom), 16); + datatype = serd_node_from_string(SERD_URI, USTR(NS_XSD "double")); + } else if (!strcmp(type, LV2_ATOM__Bool)) { + const int32_t val = *(const int32_t*)LV2_ATOM_BODY(atom); + datatype = serd_node_from_string(SERD_URI, USTR(NS_XSD "boolean")); + object = serd_node_from_string(SERD_LITERAL, + USTR(val ? "true" : "false")); + } else if (!strcmp(type, LV2_ATOM__Blank)) { + const LV2_Atom_Object* obj = (const LV2_Atom_Object*)atom; + const char* otype = unmap->unmap(unmap->handle, obj->otype); + SerdNode idnum = serd_node_new_integer(obj->id); + SerdNode id = serd_node_from_string(SERD_BLANK, idnum.buf); + serd_writer_write_statement( + writer, flags|SERD_ANON_O_BEGIN, NULL, + subject, predicate, &id, NULL, NULL); + if (otype) { + SerdNode p = serd_node_from_string(SERD_URI, USTR(NS_RDF "type")); + SerdNode o = serd_node_from_string(SERD_URI, USTR(otype)); + serd_writer_write_statement(writer, SERD_ANON_CONT, NULL, + &id, &p, &o, NULL, NULL); + } + LV2_OBJECT_FOREACH(obj, i) { + const LV2_Atom_Property_Body* prop = lv2_object_iter_get(i); + const char* const key = unmap->unmap(unmap->handle, prop->key); + SerdNode pred = serd_node_from_string(SERD_URI, USTR(key)); + atom_to_rdf(writer, unmap, &id, &pred, &prop->value, SERD_ANON_CONT); + } + serd_writer_end_anon(writer, &id); + serd_node_free(&idnum); + } else { + object = serd_node_from_string(SERD_LITERAL, USTR("(unknown)")); + } + + if (object.buf) { + serd_writer_write_statement(writer, flags, NULL, + subject, predicate, &object, + &datatype, &language); + } + + if (new_node) { + serd_node_free(&object); + } +} + +SERIATOM_API +char* +atom_to_turtle(LV2_URID_Unmap* unmap, + const SerdNode* subject, + const SerdNode* predicate, + const LV2_Atom* atom) +{ + SerdURI base_uri = SERD_URI_NULL; + SerdEnv* env = serd_env_new(NULL); + String str = { NULL, 0 }; + SerdWriter* writer = serd_writer_new( + SERD_TURTLE, + SERD_STYLE_ABBREVIATED|SERD_STYLE_RESOLVED|SERD_STYLE_CURIED, + env, &base_uri, string_sink, &str); + + serd_env_set_prefix_from_strings(env, USTR("rdf"), USTR(NS_RDF)); + serd_env_set_prefix_from_strings(env, USTR("xsd"), USTR(NS_XSD)); + atom_to_rdf(writer, unmap, subject, predicate, atom, 0); + serd_writer_finish(writer); + string_sink("", 1, &str); + + serd_writer_free(writer); + serd_env_free(env); + return str.buf; +} diff --git a/tests/seriatom_test.c b/tests/seriatom_test.c new file mode 100644 index 0000000..799c498 --- /dev/null +++ b/tests/seriatom_test.c @@ -0,0 +1,274 @@ +/* + Copyright 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 + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include +#include +#include + +#include "lv2/lv2plug.in/ns/ext/atom/forge.h" +#include "lv2/lv2plug.in/ns/ext/atom/util.h" +#include "seriatom/seriatom.h" + +#define NS_RDF "http://www.w3.org/1999/02/22-rdf-syntax-ns#" + +#define USTR(s) ((const uint8_t*)(s)) + +char** uris = NULL; +size_t n_uris = 0; + +char* +copy_string(const char* str) +{ + const size_t len = strlen(str); + char* dup = (char*)malloc(len + 1); + memcpy(dup, str, len + 1); + return dup; +} + +LV2_URID +urid_map(LV2_URID_Map_Handle handle, const char* uri) +{ + for (size_t i = 0; i < n_uris; ++i) { + if (!strcmp(uris[i], uri)) { + return i + 1; + } + } + + uris = (char**)realloc(uris, ++n_uris * sizeof(char*)); + uris[n_uris - 1] = copy_string(uri); + return n_uris; +} + +const char* +urid_unmap(LV2_URID_Unmap_Handle handle, + LV2_URID urid) +{ + if (urid <= n_uris) { + return uris[urid - 1]; + } + return NULL; +} + +int +test_fail(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + fprintf(stderr, "error: "); + vfprintf(stderr, fmt, args); + va_end(args); + return 1; +} + +int +main() +{ + LV2_URID_Map map = { NULL, urid_map }; + LV2_URID_Unmap unmap = { NULL, urid_unmap }; + LV2_Atom_Forge forge; + lv2_atom_forge_init(&forge, &map); + + LV2_URID eg_Object = urid_map(NULL, "http://example.org/Object"); + LV2_URID eg_one = urid_map(NULL, "http://example.org/one"); + LV2_URID eg_two = urid_map(NULL, "http://example.org/two"); + LV2_URID eg_three = urid_map(NULL, "http://example.org/three"); + LV2_URID eg_four = urid_map(NULL, "http://example.org/four"); + LV2_URID eg_true = urid_map(NULL, "http://example.org/true"); + LV2_URID eg_false = urid_map(NULL, "http://example.org/false"); + LV2_URID eg_path = urid_map(NULL, "http://example.org/path"); + LV2_URID eg_uri = urid_map(NULL, "http://example.org/uri"); + LV2_URID eg_urid = urid_map(NULL, "http://example.org/urid"); + LV2_URID eg_string = urid_map(NULL, "http://example.org/string"); + LV2_URID eg_langlit = urid_map(NULL, "http://example.org/langlit"); + LV2_URID eg_typelit = urid_map(NULL, "http://example.org/typelit"); + LV2_URID eg_blank = urid_map(NULL, "http://example.org/blank"); + LV2_URID eg_tuple = urid_map(NULL, "http://example.org/tuple"); + LV2_URID eg_vector = urid_map(NULL, "http://example.org/vector"); + LV2_URID eg_seq = urid_map(NULL, "http://example.org/seq"); + + uint8_t buf[1024]; + lv2_atom_forge_set_buffer(&forge, buf, sizeof(buf)); + + LV2_Atom_Forge_Frame obj_frame; + LV2_Atom* obj = (LV2_Atom*)lv2_atom_forge_blank( + &forge, &obj_frame, 1, eg_Object); + + // eg_one = (Int32)1 + lv2_atom_forge_property_head(&forge, eg_one, 0); + LV2_Atom_Int32* one = lv2_atom_forge_int32(&forge, 1); + if (one->value != 1) { + return test_fail("%d != 1\n", one->value); + } + + // eg_two = (Int64)2 + lv2_atom_forge_property_head(&forge, eg_two, 0); + LV2_Atom_Int64* two = lv2_atom_forge_int64(&forge, 2); + if (two->value != 2) { + return test_fail("%ld != 2\n", two->value); + } + + // eg_three = (Float)3.0 + lv2_atom_forge_property_head(&forge, eg_three, 0); + LV2_Atom_Float* three = lv2_atom_forge_float(&forge, 3.0f); + if (three->value != 3) { + return test_fail("%f != 3\n", three->value); + } + + // eg_four = (Double)4.0 + lv2_atom_forge_property_head(&forge, eg_four, 0); + LV2_Atom_Double* four = lv2_atom_forge_double(&forge, 4.0); + if (four->value != 4) { + return test_fail("%ld != 4\n", four->value); + } + + // eg_true = (Bool)1 + lv2_atom_forge_property_head(&forge, eg_true, 0); + LV2_Atom_Bool* t = lv2_atom_forge_bool(&forge, true); + if (t->value != 1) { + return test_fail("%ld != 1 (true)\n", t->value); + } + + // eg_false = (Bool)0 + lv2_atom_forge_property_head(&forge, eg_false, 0); + LV2_Atom_Bool* f = lv2_atom_forge_bool(&forge, false); + if (f->value != 0) { + return test_fail("%ld != 0 (false)\n", f->value); + } + + // eg_path = (Path)"/foo/bar" + const uint8_t* pstr = (const uint8_t*)"/foo/bar"; + const size_t pstr_len = strlen((const char*)pstr); + lv2_atom_forge_property_head(&forge, eg_path, 0); + LV2_Atom_String* path = lv2_atom_forge_uri(&forge, pstr, pstr_len); + uint8_t* pbody = (uint8_t*)LV2_ATOM_BODY(path); + if (strcmp((const char*)pbody, (const char*)pstr)) { + return test_fail("%s != \"%s\"\n", + (const char*)pbody, (const char*)pstr); + } + + // eg_uri = (URI)"http://example.org/value" + const uint8_t* ustr = (const uint8_t*)"http://example.org/value"; + const size_t ustr_len = strlen((const char*)ustr); + lv2_atom_forge_property_head(&forge, eg_uri, 0); + LV2_Atom_String* uri = lv2_atom_forge_uri(&forge, ustr, ustr_len); + uint8_t* ubody = (uint8_t*)LV2_ATOM_BODY(uri); + if (strcmp((const char*)ubody, (const char*)ustr)) { + return test_fail("%s != \"%s\"\n", + (const char*)ubody, (const char*)ustr); + } + + // eg_urid = (URID)"http://example.org/value" + LV2_URID eg_value = urid_map(NULL, "http://example.org/value"); + lv2_atom_forge_property_head(&forge, eg_urid, 0); + LV2_Atom_URID* urid = lv2_atom_forge_urid(&forge, eg_value); + if (urid->id != eg_value) { + return test_fail("%u != %u\n", urid->id, eg_value); + } + + // eg_string = (String)"hello" + lv2_atom_forge_property_head(&forge, eg_string, 0); + LV2_Atom_String* string = lv2_atom_forge_string( + &forge, (const uint8_t*)"hello", strlen("hello")); + uint8_t* sbody = (uint8_t*)LV2_ATOM_BODY(string); + if (strcmp((const char*)sbody, "hello")) { + return test_fail("%s != \"hello\"\n", (const char*)sbody); + } + + // eg_langlit = (Literal)"bonjour"@fr + lv2_atom_forge_property_head(&forge, eg_langlit, 0); + LV2_Atom_Literal* langlit = lv2_atom_forge_literal( + &forge, (const uint8_t*)"bonjour", strlen("bonjour"), + 0, urid_map(NULL, "http://lexvo.org/id/iso639-3/fra")); + uint8_t* llbody = (uint8_t*)LV2_ATOM_CONTENTS(LV2_Atom_Literal, langlit); + if (strcmp((const char*)llbody, "bonjour")) { + return test_fail("%s != \"bonjour\"\n", (const char*)llbody); + } + + // eg_typelit = (Literal)"bonjour"@fr + lv2_atom_forge_property_head(&forge, eg_typelit, 0); + LV2_Atom_Literal* typelit = lv2_atom_forge_literal( + &forge, (const uint8_t*)"value", strlen("value"), + urid_map(NULL, "http://example.org/Type"), 0); + uint8_t* tlbody = (uint8_t*)LV2_ATOM_CONTENTS(LV2_Atom_Literal, typelit); + if (strcmp((const char*)tlbody, "value")) { + return test_fail("%s != \"value\"\n", (const char*)tlbody); + } + + // eg_blank = [ a ] + lv2_atom_forge_property_head(&forge, eg_blank, 0); + LV2_Atom_Forge_Frame blank_frame; + lv2_atom_forge_blank(&forge, &blank_frame, 2, eg_Object); + lv2_atom_forge_pop(&forge, &blank_frame); + + // eg_tuple = "foo",true + lv2_atom_forge_property_head(&forge, eg_tuple, 0); + LV2_Atom_Forge_Frame tuple_frame; + LV2_Atom_Tuple* tuple = (LV2_Atom_Tuple*)lv2_atom_forge_tuple( + &forge, &tuple_frame); + LV2_Atom_String* tup0 = lv2_atom_forge_string( + &forge, (const uint8_t*)"foo", strlen("foo")); + LV2_Atom_Bool* tup1 = lv2_atom_forge_bool(&forge, true); + lv2_atom_forge_pop(&forge, &tuple_frame); + LV2_Atom_Tuple_Iter i = lv2_tuple_begin(tuple); + if (lv2_tuple_is_end(tuple, i)) { + return test_fail("Tuple iterator is empty\n"); + } + LV2_Atom* tup0i = (LV2_Atom*)lv2_tuple_iter_get(i); + if (!lv2_atom_equals((LV2_Atom*)tup0, tup0i)) { + return test_fail("Corrupt tuple element 0\n"); + } + i = lv2_tuple_iter_next(i); + if (lv2_tuple_is_end(tuple, i)) { + return test_fail("Premature end of tuple iterator\n"); + } + LV2_Atom* tup1i = lv2_tuple_iter_get(i); + if (!lv2_atom_equals((LV2_Atom*)tup1, tup1i)) { + return test_fail("Corrupt tuple element 1\n"); + } + i = lv2_tuple_iter_next(i); + if (!lv2_tuple_is_end(tuple, i)) { + return test_fail("Tuple iter is not at end\n"); + } + + // eg_vector = (Vector)1,2,3,4 + lv2_atom_forge_property_head(&forge, eg_vector, 0); + int32_t elems[] = { 1, 2, 3, 4 }; + LV2_Atom_Vector* vector = lv2_atom_forge_vector( + &forge, 4, forge.Int32, sizeof(int32_t), elems); + void* vec_body = LV2_ATOM_CONTENTS(LV2_Atom_Vector, vector); + if (memcmp(elems, vec_body, sizeof(elems))) { + return test_fail("Corrupt vector\n"); + } + + // eg_seq = (Sequence)1, 2 + lv2_atom_forge_property_head(&forge, eg_seq, 0); + LV2_Atom_Forge_Frame seq_frame; + lv2_atom_forge_sequence_head(&forge, &seq_frame, 0); + lv2_atom_forge_audio_time(&forge, 0, 0); + lv2_atom_forge_int32(&forge, 1); + lv2_atom_forge_audio_time(&forge, 1, 0); + lv2_atom_forge_int32(&forge, 2); + lv2_atom_forge_pop(&forge, &seq_frame); + + lv2_atom_forge_pop(&forge, &obj_frame); + + SerdNode s = serd_node_from_string(SERD_BLANK, USTR("obj")); + SerdNode p = serd_node_from_string(SERD_URI, USTR(NS_RDF "value")); + printf("%s", atom_to_turtle(&unmap, &s, &p, obj)); + + printf("All tests passed.\n"); + return 0; +} diff --git a/waf b/waf new file mode 100755 index 0000000..907b984 Binary files /dev/null and b/waf differ diff --git a/wscript b/wscript new file mode 100644 index 0000000..aa3ad48 --- /dev/null +++ b/wscript @@ -0,0 +1,181 @@ +#!/usr/bin/env python +import glob +import os +import shutil +import subprocess +import sys + +from waflib.extras import autowaf as autowaf +import waflib.Logs as Logs, waflib.Options as Options + +# Version of this package (even if built as a child) +SERIATOM_VERSION = '0.0.0' +SERIATOM_MAJOR_VERSION = '0' + +# Library version (UNIX style major, minor, micro) +# major increment <=> incompatible changes +# minor increment <=> compatible changes (additions) +# micro increment <=> no interface changes +# Seriatom uses the same version number for both library and package +SERIATOM_LIB_VERSION = SERIATOM_VERSION + +# Variables for 'waf dist' +APPNAME = 'seriatom' +VERSION = SERIATOM_VERSION + +# Mandatory variables +top = '.' +out = 'build' + +def options(opt): + opt.load('compiler_c') + autowaf.set_options(opt) + opt.add_option('--test', action='store_true', default=False, dest='build_tests', + help="Build unit tests") + opt.add_option('--static', action='store_true', default=False, dest='static', + help="Build static library") + +def configure(conf): + conf.load('compiler_c') + autowaf.configure(conf) + autowaf.display_header('Seriatom Configuration') + + if conf.env['MSVC_COMPILER']: + conf.env.append_unique('CFLAGS', ['-TP', '-MD']) + else: + conf.env.append_unique('CFLAGS', '-std=c99') + + conf.env['BUILD_TESTS'] = Options.options.build_tests + conf.env['BUILD_STATIC'] = Options.options.static + + # Check for gcov library (for test coverage) + if conf.env['BUILD_TESTS']: + conf.check_cc(lib='gcov', + define_name='HAVE_GCOV', + mandatory=False) + + autowaf.check_pkg(conf, 'serd-0', uselib_store='SERD', + atleast_version='0.8.0', mandatory=True) + + autowaf.define(conf, 'SERIATOM_VERSION', SERIATOM_VERSION) + conf.write_config_header('seriatom_config.h', remove=False) + + autowaf.display_msg(conf, "Unit tests", str(conf.env['BUILD_TESTS'])) + print('') + +lib_source = [ + 'src/atom_to_rdf.c' +] + +def build(bld): + # C Headers + includedir = '${INCLUDEDIR}/seriatom-%s/seriatom' % SERIATOM_MAJOR_VERSION + bld.install_files(includedir, bld.path.ant_glob('seriatom/*.h')) + + # Pkgconfig file + autowaf.build_pc(bld, 'SERIATOM', SERIATOM_VERSION, SERIATOM_MAJOR_VERSION, + 'SERD', + {'SERIATOM_MAJOR_VERSION' : SERIATOM_MAJOR_VERSION}) + + libflags = [ '-fvisibility=hidden' ] + libs = [ 'm' ] + if bld.env['MSVC_COMPILER']: + libflags = [] + libs = [] + + # Shared Library + obj = bld(features = 'c cshlib', + export_includes = ['.'], + source = lib_source, + includes = ['.', './src'], + lib = libs, + name = 'libseriatom', + target = 'seriatom-%s' % SERIATOM_MAJOR_VERSION, + vnum = SERIATOM_LIB_VERSION, + install_path = '${LIBDIR}', + cflags = libflags + [ '-DSERIATOM_SHARED', + '-DSERIATOM_INTERNAL' ]) + autowaf.use_lib(bld, obj, 'SERD') + + # Static library + if bld.env['BUILD_STATIC']: + obj = bld(features = 'c cstlib', + export_includes = ['.'], + source = lib_source, + includes = ['.', './src'], + lib = libs, + name = 'libseriatom_static', + target = 'seriatom-%s' % SERIATOM_MAJOR_VERSION, + vnum = SERIATOM_LIB_VERSION, + install_path = '${LIBDIR}', + cflags = ['-DSERIATOM_INTERNAL']) + autowaf.use_lib(bld, obj, 'SERD') + + if bld.env['BUILD_TESTS']: + test_libs = libs + test_cflags = [''] + if bld.is_defined('HAVE_GCOV'): + test_libs += ['gcov'] + test_cflags += ['-fprofile-arcs', '-ftest-coverage'] + + # Static library (for unit test code coverage) + obj = bld(features = 'c cstlib', + source = lib_source, + includes = ['.', './src'], + lib = test_libs, + name = 'libseriatom_profiled', + target = 'seriatom_profiled', + install_path = '', + cflags = test_cflags + ['-DSERIATOM_INTERNAL']) + autowaf.use_lib(bld, obj, 'SERD') + + # Unit test program + obj = bld(features = 'c cprogram', + source = 'tests/seriatom_test.c', + includes = ['.', './src'], + use = 'libseriatom_profiled', + lib = test_libs, + target = 'seriatom_test', + install_path = '', + cflags = test_cflags) + + # Documentation + autowaf.build_dox(bld, 'SERIATOM', SERIATOM_VERSION, top, out) + + bld.add_post_fun(autowaf.run_ldconfig) + if bld.env['DOCS']: + bld.add_post_fun(fix_docs) + +def test(ctx): + autowaf.pre_test(ctx, APPNAME) + os.environ['PATH'] = '.' + os.pathsep + os.getenv('PATH') + autowaf.run_tests(ctx, APPNAME, ['seriatom_test'], dirs=['./src','./tests']) + autowaf.post_test(ctx, APPNAME) + +def lint(ctx): + subprocess.call('cpplint.py --filter=+whitespace/comments,-whitespace/tab,-whitespace/braces,-whitespace/labels,-build/header_guard,-readability/casting,-readability/todo,-build/include src/* seriatom/*', shell=True) + +def build_dir(ctx, subdir): + if autowaf.is_child(): + return os.path.join('build', APPNAME, subdir) + else: + return os.path.join('build', subdir) + +def fix_docs(ctx): + try: + top = os.getcwd() + os.chdir(build_dir(ctx, 'doc/html')) + os.system("sed -i 's/SERIATOM_API //' group__seriatom.html") + os.system("sed -i 's/SERIATOM_DEPRECATED //' group__seriatom.html") + os.remove('index.html') + os.symlink('group__seriatom.html', + 'index.html') + os.chdir(top) + os.chdir(build_dir(ctx, 'doc/man/man3')) + os.system("sed -i 's/SERIATOM_API //' seriatom.3") + os.chdir(top) + except: + Logs.error("Failed to fix up %s documentation" % APPNAME) + +def upload_docs(ctx): + os.system("rsync -ravz --delete -e ssh build/doc/html/ drobilla@drobilla.net:~/drobilla.net/docs/seriatom/") -- cgit v1.2.1