From 254b434f0a79fea54bd963e8ff2e845a5b0cd3a6 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Mon, 19 Mar 2012 20:16:46 +0000 Subject: Partially functioning communication between Ingen LV2 plugin and UI. git-svn-id: http://svn.drobilla.net/lad/trunk/ingen@4078 a436a847-0d15-0410-975c-d299462d15a1 --- src/serialisation/Parser.cpp | 87 +++-- src/serialisation/Serialiser.cpp | 92 ++--- src/serialisation/sratom/sratom.c | 731 ++++++++++++++++++++++++++++++++++++++ src/serialisation/sratom/sratom.h | 176 +++++++++ src/serialisation/wscript | 9 +- 5 files changed, 1002 insertions(+), 93 deletions(-) create mode 100644 src/serialisation/sratom/sratom.c create mode 100644 src/serialisation/sratom/sratom.h (limited to 'src/serialisation') diff --git a/src/serialisation/Parser.cpp b/src/serialisation/Parser.cpp index 9acd460c..19c1f957 100644 --- a/src/serialisation/Parser.cpp +++ b/src/serialisation/Parser.cpp @@ -15,30 +15,23 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include - #include -#include - #include #include #include #include +#include "ingen/Interface.hpp" +#include "ingen/serialisation/Parser.hpp" +#include "ingen/shared/LV2URIMap.hpp" +#include "ingen/shared/URIs.hpp" +#include "ingen/shared/World.hpp" #include "raul/Atom.hpp" -#include "raul/AtomRDF.hpp" #include "raul/log.hpp" - #include "serd/serd.h" #include "sord/sordmm.hpp" - -#include "ingen/Interface.hpp" -#include "ingen/shared/World.hpp" -#include "ingen/shared/LV2URIMap.hpp" -#include "ingen/shared/URIs.hpp" - -#include "ingen/serialisation/Parser.hpp" +#include "sratom/sratom.h" #define LOG(s) s << "[Parser] " @@ -103,18 +96,46 @@ skip_property(const Sord::Node& predicate) } static Resource::Properties -get_properties(Raul::Forge& forge, - Sord::Model& model, - const Sord::Node& subject) +get_properties(Ingen::Shared::World* world, + Sord::Model& model, + const Sord::Node& subject) { + SerdChunk out = { NULL, 0 }; + LV2_URID_Map* map = &world->lv2_uri_map()->urid_map_feature()->urid_map; + LV2_URID_Unmap* unmap = &world->lv2_uri_map()->urid_unmap_feature()->urid_unmap; + Sratom* sratom = sratom_new(map); + + LV2_Atom_Forge forge; + lv2_atom_forge_init(&forge, map); + lv2_atom_forge_set_sink(&forge, sratom_forge_sink, sratom_forge_deref, &out); + Resource::Properties props; for (Sord::Iter i = model.find(subject, nil, nil); !i.end(); ++i) { if (!skip_property(i.get_predicate())) { - props.insert( - make_pair(i.get_predicate().to_string(), - AtomRDF::node_to_atom(forge, model, i.get_object()))); + out.len = 0; + sratom_read(sratom, &forge, world->rdf_world()->c_obj(), + model.c_obj(), i.get_object().c_obj()); + LV2_Atom* atom = (LV2_Atom*)out.buf; + Atom atomm; + // FIXME: Don't bloat out all URIs + if (atom->type == forge.URID) { + atomm = world->forge().alloc_uri( + unmap->unmap(unmap->handle, *(uint32_t*)LV2_ATOM_BODY(atom))); + } else { + atomm = world->forge().alloc( + atom->size, atom->type, LV2_ATOM_BODY(atom)); + } + /* + std::cerr << "READ PROPERTY " << i.get_predicate() + << " = " << world->forge().str(atomm) + << " :: " << unmap->unmap(unmap->handle, atom->type) + << std::endl; + */ + props.insert(make_pair(i.get_predicate().to_string(), atomm)); } } + + sratom_free(sratom); return props; } @@ -130,12 +151,12 @@ get_port(Ingen::Shared::World* world, const URIs& uris = *world->uris().get(); // Get all properties - Resource::Properties props = get_properties(world->forge(), model, subject); + Resource::Properties props = get_properties(world, model, subject); // Get index Resource::Properties::const_iterator i = props.find(uris.lv2_index); if (i == props.end() - || i->second.type() != Atom::INT + || i->second.type() != world->forge().Int || i->second.get_int32() < 0) { LOG(error) << "Port " << subject << " has no valid lv2:index" << endl; return -1; @@ -254,9 +275,9 @@ parse_node(Ingen::Shared::World* world, parse_patch(world, target, model, subject, path.parent(), Raul::Symbol(path.symbol())); } else { - Resource::Properties props = get_properties(world->forge(), model, subject); + Resource::Properties props = get_properties(world, model, subject); props.insert(make_pair(uris.rdf_type, - Raul::URI(uris.ingen_Node))); + uris.forge.alloc_uri(uris.ingen_Node.str()))); target->put(path, props); } return path; @@ -275,7 +296,6 @@ parse_patch(Ingen::Shared::World* world, const Sord::URI ingen_polyphony(*world->rdf_world(), NS_INGEN "polyphony"); const Sord::URI lv2_port(*world->rdf_world(), NS_LV2 "port"); - Raul::Forge& forge = world->forge(); const URIs& uris = *world->uris().get(); const Sord::Node& patch = subject_node; @@ -284,8 +304,10 @@ parse_patch(Ingen::Shared::World* world, // Use parameter overridden polyphony, if given if (data) { GraphObject::Properties::iterator poly_param = data.get().find(uris.ingen_polyphony); - if (poly_param != data.get().end() && poly_param->second.type() == Atom::INT) + if (poly_param != data.get().end() && + poly_param->second.type() == world->forge().Int) { patch_poly = poly_param->second.get_int32(); + } } // Load polyphony from file if necessary @@ -324,7 +346,7 @@ parse_patch(Ingen::Shared::World* world, // Create patch Path patch_path(patch_path_str); - Resource::Properties props = get_properties(forge, model, subject_node); + Resource::Properties props = get_properties(world, model, subject_node); target->put(patch_path, props); // For each node in this patch @@ -438,16 +460,7 @@ parse_properties(Ingen::Shared::World* world, const Raul::URI& uri, boost::optional data) { - Resource::Properties properties; - for (Sord::Iter i = model.find(subject, nil, nil); !i.end(); ++i) { - const Sord::Node& key = i.get_predicate(); - const Sord::Node& val = i.get_object(); - if (!skip_property(key)) { - properties.insert( - make_pair(key.to_string(), - AtomRDF::node_to_atom(world->forge(), model, val))); - } - } + Resource::Properties properties = get_properties(world, model, subject); target->put(uri, properties); @@ -588,7 +601,7 @@ Parser::parse_file(Ingen::Shared::World* world, if (parsed_path) { target->set_property(*parsed_path, "http://drobilla.net/ns/ingen#document", - world->forge().alloc(Atom::URI, uri.c_str())); + world->forge().alloc_uri(uri)); } else { LOG(warn) << "Document URI lost" << endl; } diff --git a/src/serialisation/Serialiser.cpp b/src/serialisation/Serialiser.cpp index 2ea07af3..224785b6 100644 --- a/src/serialisation/Serialiser.cpp +++ b/src/serialisation/Serialiser.cpp @@ -15,19 +15,12 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include - #include #include #include #include #include -#include -#include -#include #include -#include -#include #include #include @@ -36,26 +29,23 @@ #include #include -#include "raul/Atom.hpp" -#include "raul/AtomRDF.hpp" -#include "raul/Path.hpp" -#include "raul/log.hpp" - -#include "sord/sordmm.hpp" - #include "ingen/Connection.hpp" +#include "ingen/Interface.hpp" #include "ingen/Node.hpp" #include "ingen/Patch.hpp" #include "ingen/Plugin.hpp" #include "ingen/Port.hpp" -#include "ingen/Interface.hpp" +#include "ingen/serialisation/Serialiser.hpp" #include "ingen/shared/LV2URIMap.hpp" #include "ingen/shared/ResourceImpl.hpp" #include "ingen/shared/Store.hpp" #include "ingen/shared/URIs.hpp" #include "ingen/shared/World.hpp" - -#include "ingen/serialisation/Serialiser.hpp" +#include "raul/Atom.hpp" +#include "raul/Path.hpp" +#include "raul/log.hpp" +#include "sord/sordmm.hpp" +#include "sratom/sratom.h" #define LOG(s) s << "[Serialiser] " @@ -227,21 +217,11 @@ Serialiser::Impl::write_bundle(SharedPtr patch, } string -Serialiser::to_string(SharedPtr object, - const string& base_uri, - const GraphObject::Properties& extra_rdf) +Serialiser::to_string(SharedPtr object, + const string& base_uri) { start_to_string(object->path(), base_uri); serialise(object); - - Sord::URI base_rdf_node(me->_model->world(), base_uri); - for (GraphObject::Properties::const_iterator v = extra_rdf.begin(); - v != extra_rdf.end(); ++v) { - me->_model->add_statement(base_rdf_node, - AtomRDF::atom_to_node(*me->_model, v->first), - AtomRDF::atom_to_node(*me->_model, v->second)); - } - return finish(); } @@ -252,8 +232,6 @@ Serialiser::to_string(SharedPtr object, void Serialiser::Impl::start_to_filename(const string& filename) { - setlocale(LC_NUMERIC, "C"); - assert(filename.find(":") == string::npos || filename.substr(0, 5) == "file:"); if (filename.find(":") == string::npos) { _base_uri = "file://" + filename; @@ -278,8 +256,6 @@ Serialiser::Impl::start_to_filename(const string& filename) void Serialiser::start_to_string(const Raul::Path& root, const string& base_uri) { - setlocale(LC_NUMERIC, "C"); - me->_root_path = root; me->_base_uri = base_uri; me->_model = new Sord::Model(*me->_world.rdf_world(), base_uri); @@ -328,7 +304,7 @@ void Serialiser::serialise(SharedPtr object) throw (std::logic_error) { if (!me->_model) - throw std::logic_error("serialise called without serialization in progress"); + throw std::logic_error("serialise called without serialisation in progress"); SharedPtr patch = PtrCast(object); if (patch) { @@ -372,13 +348,17 @@ Serialiser::Impl::serialise_patch(SharedPtr patch, Sord::Curie(world, "lv2:extensionData"), Sord::URI(world, "http://lv2plug.in/ns/ext/state#Interface")); + _model->add_statement(patch_id, + Sord::URI(world, "http://lv2plug.in/ns/extensions/ui#ui"), + Sord::URI(world, "http://drobilla.net/ns/ingen#ui")); + const URIs& uris = *_world.uris().get(); // Always write a symbol (required by Ingen) string symbol; GraphObject::Properties::const_iterator s = patch->properties().find(uris.lv2_symbol); if (s == patch->properties().end() - || !s->second.type() == Atom::STRING + || !s->second.type() == _world.forge().String || !Symbol::is_valid(s->second.get_string())) { symbol = Glib::path_get_basename(_model->base_uri().to_c_string()); symbol = Symbol::symbolify(symbol.substr(0, symbol.find('.'))); @@ -393,7 +373,7 @@ Serialiser::Impl::serialise_patch(SharedPtr patch, // If the patch has no doap:name (required by LV2), use the symbol if (patch->properties().find(uris.doap_name) == patch->properties().end()) _model->add_statement(patch_id, - AtomRDF::atom_to_node(*_model, uris.doap_name), + Sord::URI(world, uris.doap_name.str()), Sord::Literal(world, symbol)); serialise_properties(patch.get(), Resource::INTERNAL, patch_id); @@ -455,7 +435,7 @@ Serialiser::Impl::serialise_patch(SharedPtr patch, // Ensure lv2:name always exists so Patch is a valid LV2 plugin if (p->properties().find(NS_LV2 "name") == p->properties().end()) p->set_property(NS_LV2 "name", - _world.forge().make(p->symbol().c_str())); + _world.forge().alloc(p->symbol().c_str())); _model->add_statement(patch_id, Sord::URI(world, NS_LV2 "port"), @@ -518,13 +498,6 @@ Serialiser::Impl::serialise_port(const Port* port, Sord::Literal(world, port->path().symbol())); serialise_properties(port, context, port_id); - - if (context == Resource::INTERNAL) { - _model->add_statement( - port_id, - Sord::Curie(world, "lv2:index"), - AtomRDF::atom_to_node(*_model, _world.forge().make((int)port->index()))); - } } void @@ -542,7 +515,7 @@ Serialiser::Impl::serialise_connection(const Sord::Node& parent, { if (!_model) throw std::logic_error( - "serialise_connection called without serialization in progress"); + "serialise_connection called without serialisation in progress"); Sord::World& world = _model->world(); @@ -575,19 +548,32 @@ Serialiser::Impl::serialise_properties(const GraphObject* o, { const GraphObject::Properties props = o->properties(context); + LV2_URID_Map* map = &_world.lv2_uri_map()->urid_map_feature()->urid_map; + LV2_URID_Unmap* unmap = &_world.lv2_uri_map()->urid_unmap_feature()->urid_unmap; + Sratom* sratom = sratom_new(map); + SerdNode base = serd_node_from_string(SERD_URI, + (const uint8_t*)_base_uri.c_str()); + SerdEnv* env = serd_env_new(&base); + SordInserter* inserter = sord_inserter_new(_model->c_obj(), env); + + sratom_set_sink(sratom, _base_uri.c_str(), + (SerdStatementSink)sord_inserter_write_statement, NULL, + inserter, true); + typedef GraphObject::Properties::const_iterator iterator; for (iterator v = props.begin(); v != props.end(); ++v) { - const Sord::URI key(_model->world(), v->first.str()); - const Sord::Node value(AtomRDF::atom_to_node(*_model, v->second)); + const Sord::URI key(_model->world(), v->first.str()); if (!skip_property(key)) { - if (value.is_valid()) { - _model->add_statement(id, key, value); - } else { - LOG(warn) << "Can not serialise variable '" << v->first << "' :: " - << (int)v->second.type() << endl; - } + sratom_write(sratom, unmap, 0, + sord_node_to_serd_node(id.c_obj()), + sord_node_to_serd_node(key.c_obj()), + v->second.type(), v->second.size(), v->second.get_body()); } } + + sord_inserter_free(inserter); + serd_env_free(env); + sratom_free(sratom); } } // namespace Serialisation diff --git a/src/serialisation/sratom/sratom.c b/src/serialisation/sratom/sratom.c new file mode 100644 index 00000000..646d4623 --- /dev/null +++ b/src/serialisation/sratom/sratom.c @@ -0,0 +1,731 @@ +/* + 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 + +#include "lv2/lv2plug.in/ns/ext/atom/forge.h" +#include "lv2/lv2plug.in/ns/ext/atom/util.h" + +#include "sratom/sratom.h" + +#define NS_MIDI (const uint8_t*)"http://lv2plug.in/ns/ext/midi#" +#define NS_RDF (const uint8_t*)"http://www.w3.org/1999/02/22-rdf-syntax-ns#" +#define NS_XSD (const uint8_t*)"http://www.w3.org/2001/XMLSchema#" + +#define USTR(str) ((const uint8_t*)(str)) + +typedef enum { + MODE_NORMAL, + MODE_SEQUENCE, +} ReadMode; + +struct SratomImpl { + LV2_URID_Map* map; + LV2_Atom_Forge forge; + LV2_URID atom_Event; + LV2_URID midi_MidiEvent; + unsigned next_id; + SerdNode base_uri; + SerdStatementSink write_statement; + SerdEndSink end_anon; + void* handle; + bool pretty_numbers; + struct { + SordNode* atom_childType; + SordNode* atom_frameTime; + SordNode* rdf_first; + SordNode* rdf_rest; + SordNode* rdf_type; + SordNode* rdf_value; + SordNode* xsd_base64Binary; + } nodes; +}; + +static void +read_node(Sratom* sratom, + LV2_Atom_Forge* forge, + SordWorld* world, + SordModel* model, + const SordNode* node, + ReadMode mode); + +SRATOM_API +Sratom* +sratom_new(LV2_URID_Map* map) +{ + Sratom* sratom = (Sratom*)malloc(sizeof(Sratom)); + sratom->map = map; + sratom->atom_Event = map->map(map->handle, LV2_ATOM__Event); + sratom->midi_MidiEvent = map->map(map->handle, + (const char*)NS_MIDI "MidiEvent"); + sratom->next_id = 0; + sratom->base_uri = SERD_NODE_NULL; + sratom->pretty_numbers = false; + memset(&sratom->nodes, 0, sizeof(sratom->nodes)); + lv2_atom_forge_init(&sratom->forge, map); + return sratom; +} + +SRATOM_API +void +sratom_free(Sratom* sratom) +{ + free(sratom); +} + +SRATOM_API +void +sratom_set_sink(Sratom* sratom, + const char* base_uri, + SerdStatementSink write_statement, + SerdEndSink end_anon, + void* handle, + bool pretty_numbers) +{ + if (base_uri) { + serd_node_free(&sratom->base_uri); + sratom->base_uri = serd_node_new_uri_from_string( + USTR(base_uri), NULL, NULL); + } + sratom->write_statement = write_statement; + sratom->end_anon = end_anon; + sratom->handle = handle; + sratom->pretty_numbers = pretty_numbers; +} + +static void +gensym(SerdNode* out, char c, unsigned num) +{ + out->n_bytes = out->n_chars = snprintf( + (char*)out->buf, 10, "%c%u", c, num); +} + +static void +list_append(Sratom* sratom, + LV2_URID_Unmap* unmap, + unsigned* flags, + SerdNode* s, + SerdNode* p, + SerdNode* node, + uint32_t size, + uint32_t type, + void* body) +{ + // Generate a list node + gensym(node, 'l', sratom->next_id); + sratom->write_statement(sratom->handle, *flags, NULL, + s, p, node, NULL, NULL); + + // _:node rdf:first value + *flags = SERD_LIST_CONT; + *p = serd_node_from_string(SERD_URI, NS_RDF "first"); + sratom_write(sratom, unmap, *flags, node, p, type, size, body); + + // Set subject to node and predicate to rdf:rest for next time + gensym(node, 'l', ++sratom->next_id); + *s = *node; + *p = serd_node_from_string(SERD_URI, NS_RDF "rest"); +} + +static void +list_end(SerdStatementSink sink, + void* handle, + unsigned* flags, + SerdNode* s, + SerdNode* p) +{ + // _:node rdf:rest rdf:nil + const SerdNode nil = serd_node_from_string(SERD_URI, NS_RDF "nil"); + sink(handle, *flags, NULL, s, p, &nil, NULL, NULL); +} + +static void +start_object(Sratom* sratom, + uint32_t flags, + const SerdNode* subject, + const SerdNode* predicate, + const SerdNode* node, + const char* type) +{ + sratom->write_statement(sratom->handle, flags|SERD_ANON_O_BEGIN, NULL, + subject, predicate, node, NULL, NULL); + if (type) { + SerdNode p = serd_node_from_string(SERD_URI, NS_RDF "type"); + SerdNode o = serd_node_from_string(SERD_URI, USTR(type)); + sratom->write_statement(sratom->handle, SERD_ANON_CONT, NULL, + node, &p, &o, NULL, NULL); + } +} + +static bool +path_is_absolute(const char* path) +{ + return (path[0] == '/' + || (isalpha(path[0]) && path[1] == ':' + && (path[2] == '/' || path[2] == '\\'))); +} + +SRATOM_API +int +sratom_write(Sratom* sratom, + LV2_URID_Unmap* unmap, + uint32_t flags, + const SerdNode* subject, + const SerdNode* predicate, + uint32_t type_urid, + uint32_t size, + const void* body) +{ + const char* const type = unmap->unmap(unmap->handle, type_urid); + uint8_t idbuf[12] = "b0000000000"; + SerdNode id = serd_node_from_string(SERD_BLANK, idbuf); + uint8_t nodebuf[12] = "b0000000000"; + SerdNode node = serd_node_from_string(SERD_BLANK, nodebuf); + SerdNode object = SERD_NODE_NULL; + SerdNode datatype = SERD_NODE_NULL; + SerdNode language = SERD_NODE_NULL; + bool new_node = false; + if (type_urid == 0 && size == 0) { + object = serd_node_from_string(SERD_URI, USTR(NS_RDF "nil")); + } else if (type_urid == sratom->forge.String) { + object = serd_node_from_string(SERD_LITERAL, (const uint8_t*)body); + } else if (type_urid == sratom->forge.Literal) { + LV2_Atom_Literal_Body* lit = (LV2_Atom_Literal_Body*)body; + const uint8_t* str = USTR(lit + 1); + 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 (type_urid == sratom->forge.URID) { + const uint32_t id = *(const uint32_t*)body; + const uint8_t* str = USTR(unmap->unmap(unmap->handle, id)); + object = serd_node_from_string(SERD_URI, str); + } else if (type_urid == sratom->forge.Path) { + const uint8_t* str = USTR(body); + if (path_is_absolute((const char*)str)) { + new_node = true; + object = serd_node_new_file_uri(str, NULL, NULL, false); + } else { + SerdURI base_uri = SERD_URI_NULL; + if (!sratom->base_uri.buf || + strncmp((const char*)sratom->base_uri.buf, "file://", 7)) { + fprintf(stderr, "warning: Relative path but base is not a file URI.\n"); + fprintf(stderr, "warning: Writing ambiguous atom:Path literal.\n"); + object = serd_node_from_string(SERD_LITERAL, str); + datatype = serd_node_from_string(SERD_URI, USTR(LV2_ATOM__Path)); + } else { + if (sratom->base_uri.buf) { + serd_uri_parse(sratom->base_uri.buf, &base_uri); + } + new_node = true; + SerdNode rel = serd_node_new_file_uri(str, NULL, NULL, false); + object = serd_node_new_uri_from_node(&rel, &base_uri, NULL); + serd_node_free(&rel); + } + } + } else if (type_urid == sratom->forge.URI) { + const uint8_t* str = USTR(body); + object = serd_node_from_string(SERD_URI, str); + } else if (type_urid == sratom->forge.Int) { + new_node = true; + object = serd_node_new_integer(*(int32_t*)body); + datatype = serd_node_from_string(SERD_URI, (sratom->pretty_numbers) + ? NS_XSD "integer" : NS_XSD "int"); + } else if (type_urid == sratom->forge.Long) { + new_node = true; + object = serd_node_new_integer(*(int64_t*)body); + datatype = serd_node_from_string(SERD_URI, (sratom->pretty_numbers) + ? NS_XSD "integer" : NS_XSD "long"); + } else if (type_urid == sratom->forge.Float) { + new_node = true; + object = serd_node_new_decimal(*(float*)body, 8); + datatype = serd_node_from_string(SERD_URI, (sratom->pretty_numbers) + ? NS_XSD "decimal" : NS_XSD "float"); + } else if (type_urid == sratom->forge.Double) { + new_node = true; + object = serd_node_new_decimal(*(double*)body, 16); + datatype = serd_node_from_string(SERD_URI, (sratom->pretty_numbers) + ? NS_XSD "decimal" : NS_XSD "double"); + } else if (type_urid == sratom->forge.Bool) { + const int32_t val = *(const int32_t*)body; + datatype = serd_node_from_string(SERD_URI, NS_XSD "boolean"); + object = serd_node_from_string(SERD_LITERAL, + USTR(val ? "true" : "false")); + } else if (type_urid == sratom->midi_MidiEvent) { + new_node = true; + datatype = serd_node_from_string(SERD_URI, NS_MIDI "MidiEvent"); + uint8_t* str = calloc(size * 2 + 1, 1); + for (uint32_t i = 0; i < size; ++i) { + snprintf((char*)str + (2 * i), size * 2 + 1, "%02X", + (unsigned)(uint8_t)*((uint8_t*)body + i)); + } + object = serd_node_from_string(SERD_LITERAL, USTR(str)); + } else if (type_urid == sratom->atom_Event) { + const LV2_Atom_Event* ev = (const LV2_Atom_Event*)body; + gensym(&id, 'e', sratom->next_id++); + start_object(sratom, flags, subject, predicate, &id, NULL); + // TODO: beat time + SerdNode time = serd_node_new_integer(ev->time.frames); + SerdNode p = serd_node_from_string(SERD_URI, + USTR(LV2_ATOM__frameTime)); + datatype = serd_node_from_string(SERD_URI, NS_XSD "decimal"); + sratom->write_statement(sratom->handle, SERD_ANON_CONT, NULL, + &id, &p, &time, &datatype, &language); + serd_node_free(&time); + + p = serd_node_from_string(SERD_URI, NS_RDF "value"); + sratom_write(sratom, unmap, SERD_ANON_CONT, &id, &p, + ev->body.type, ev->body.size, LV2_ATOM_BODY(&ev->body)); + if (sratom->end_anon) { + sratom->end_anon(sratom->handle, &id); + } + } else if (type_urid == sratom->forge.Tuple) { + gensym(&id, 't', sratom->next_id++); + start_object(sratom, flags, subject, predicate, &id, type); + SerdNode p = serd_node_from_string(SERD_URI, NS_RDF "value"); + flags |= SERD_LIST_O_BEGIN; + LV2_TUPLE_BODY_FOREACH(body, size, i) { + list_append(sratom, unmap, &flags, &id, &p, &node, + i->size, i->type, LV2_ATOM_BODY(i)); + } + list_end(sratom->write_statement, sratom->handle, &flags, &id, &p); + if (sratom->end_anon) { + sratom->end_anon(sratom->handle, &id); + } + } else if (type_urid == sratom->forge.Vector) { + const LV2_Atom_Vector_Body* vec = (const LV2_Atom_Vector_Body*)body; + gensym(&id, 'v', sratom->next_id++); + start_object(sratom, flags, subject, predicate, &id, type); + SerdNode p = serd_node_from_string(SERD_URI, (const uint8_t*)LV2_ATOM__childType); + SerdNode child_type = serd_node_from_string( + SERD_URI, (const uint8_t*)unmap->unmap(unmap->handle, vec->child_type)); + sratom->write_statement(sratom->handle, flags, NULL, &id, &p, &child_type, NULL, NULL); + p = serd_node_from_string(SERD_URI, NS_RDF "value"); + flags |= SERD_LIST_O_BEGIN; + for (char* i = (char*)(vec + 1); + i < (char*)vec + size; + i += vec->child_size) { + list_append(sratom, unmap, &flags, &id, &p, &node, + vec->child_size, vec->child_type, i); + } + list_end(sratom->write_statement, sratom->handle, &flags, &id, &p); + if (sratom->end_anon) { + sratom->end_anon(sratom->handle, &id); + } + } else if (type_urid == sratom->forge.Blank) { + const LV2_Atom_Object_Body* obj = (const LV2_Atom_Object_Body*)body; + const char* otype = unmap->unmap(unmap->handle, + obj->otype); + gensym(&id, 'b', sratom->next_id++); + start_object(sratom, flags, subject, predicate, &id, otype); + LV2_OBJECT_BODY_FOREACH(obj, size, 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)); + sratom_write(sratom, unmap, flags|SERD_ANON_CONT, &id, &pred, + prop->value.type, prop->value.size, + LV2_ATOM_BODY(&prop->value)); + } + if (sratom->end_anon) { + sratom->end_anon(sratom->handle, &id); + } + } else if (type_urid == sratom->forge.Sequence) { + const LV2_Atom_Sequence_Body* seq = (const LV2_Atom_Sequence_Body*)body; + gensym(&id, 'v', sratom->next_id++); + start_object(sratom, flags, subject, predicate, &id, type); + SerdNode p = serd_node_from_string(SERD_URI, NS_RDF "value"); + flags |= SERD_LIST_O_BEGIN; + LV2_SEQUENCE_BODY_FOREACH(seq, size, i) { + LV2_Atom_Event* ev = lv2_sequence_iter_get(i); + list_append(sratom, unmap, &flags, &id, &p, &node, + sizeof(LV2_Atom_Event) + ev->body.size, + sratom->atom_Event, + ev); + } + list_end(sratom->write_statement, sratom->handle, &flags, &id, &p); + if (sratom->end_anon) { + sratom->end_anon(sratom->handle, &id); + } + } else { + gensym(&id, 'b', sratom->next_id++); + start_object(sratom, flags, subject, predicate, &id, type); + SerdNode p = serd_node_from_string(SERD_URI, NS_RDF "value"); + SerdNode o = serd_node_new_blob(body, size, true); + datatype = serd_node_from_string(SERD_URI, NS_XSD "base64Binary"); + sratom->write_statement(sratom->handle, flags, NULL, &id, &p, &o, &datatype, NULL); + if (sratom->end_anon) { + sratom->end_anon(sratom->handle, &id); + } + serd_node_free(&o); + } + + if (object.buf) { + sratom->write_statement(sratom->handle, flags, NULL, + subject, predicate, &object, &datatype, &language); + } + + if (new_node) { + serd_node_free(&object); + } + + return 0; +} + +SRATOM_API +char* +sratom_to_turtle(Sratom* sratom, + LV2_URID_Unmap* unmap, + const char* base_uri, + const SerdNode* subject, + const SerdNode* predicate, + uint32_t type, + uint32_t size, + const void* body) +{ + SerdURI buri = SERD_URI_NULL; + SerdNode base = serd_node_new_uri_from_string(USTR(base_uri), NULL, &buri); + SerdEnv* env = serd_env_new(&base); + SerdChunk str = { NULL, 0 }; + + serd_env_set_prefix_from_strings(env, USTR("midi"), NS_MIDI); + serd_env_set_prefix_from_strings(env, USTR("atom"), + USTR(LV2_ATOM_URI "#")); + serd_env_set_prefix_from_strings(env, USTR("rdf"), NS_RDF); + serd_env_set_prefix_from_strings(env, USTR("xsd"), NS_XSD); + serd_env_set_prefix_from_strings(env, USTR("eg"), + USTR("http://example.org/")); + + SerdWriter* writer = serd_writer_new( + SERD_TURTLE, + SERD_STYLE_ABBREVIATED|SERD_STYLE_RESOLVED|SERD_STYLE_CURIED, + env, &buri, serd_chunk_sink, &str); + + // Write @prefix directives + serd_env_foreach(env, + (SerdPrefixSink)serd_writer_set_prefix, + writer); + + sratom_set_sink(sratom, base_uri, + (SerdStatementSink)serd_writer_write_statement, + (SerdEndSink)serd_writer_end_anon, + writer, + false); + sratom_write(sratom, unmap, SERD_EMPTY_S, + subject, predicate, type, size, body); + serd_writer_finish(writer); + + serd_writer_free(writer); + serd_env_free(env); + return (char*)serd_chunk_sink_finish(&str); +} + +static const SordNode* +get_object(SordModel* model, + const SordNode* subject, + const SordNode* predicate) +{ + const SordNode* object = NULL; + SordQuad q = { subject, predicate, 0, 0 }; + SordIter* i = sord_find(model, q); + if (!sord_iter_end(i)) { + SordQuad quad; + sord_iter_get(i, quad); + object = quad[SORD_OBJECT]; + } + sord_iter_free(i); + return object; +} + +static void +read_list_value(Sratom* sratom, + LV2_Atom_Forge* forge, + SordWorld* world, + SordModel* model, + const SordNode* node, + ReadMode mode) +{ + const SordNode* first = get_object(model, node, sratom->nodes.rdf_first); + const SordNode* rest = get_object(model, node, sratom->nodes.rdf_rest); + if (first && rest) { + read_node(sratom, forge, world, model, first, mode); + read_list_value(sratom, forge, world, model, rest, mode); + } +} + +static uint32_t +atom_size(Sratom* sratom, uint32_t type_urid) +{ + if (type_urid == sratom->forge.Int) { + return sizeof(int32_t); + } else if (type_urid == sratom->forge.Long) { + return sizeof(int64_t); + } else if (type_urid == sratom->forge.Float) { + return sizeof(float); + } else if (type_urid == sratom->forge.Double) { + return sizeof(double); + } else if (type_urid == sratom->forge.Bool) { + return sizeof(int32_t); + } else if (type_urid == sratom->forge.URID) { + return sizeof(uint32_t); + } else { + return 0; + } +} + +static void +read_node(Sratom* sratom, + LV2_Atom_Forge* forge, + SordWorld* world, + SordModel* model, + const SordNode* node, + ReadMode mode) +{ + LV2_URID_Map* map = sratom->map; + size_t len = 0; + const char* str = (const char*)sord_node_get_string_counted(node, &len); + if (sord_node_get_type(node) == SORD_LITERAL) { + char* endptr; + SordNode* datatype = sord_node_get_datatype(node); + const char* language = sord_node_get_language(node); + if (datatype) { + const char* type_uri = (const char*)sord_node_get_string(datatype); + if (!strcmp(type_uri, (char*)NS_XSD "int") || + !strcmp(type_uri, (char*)NS_XSD "integer")) { + lv2_atom_forge_int(forge, strtol(str, &endptr, 10)); + } else if (!strcmp(type_uri, (char*)NS_XSD "long")) { + lv2_atom_forge_long(forge, strtol(str, &endptr, 10)); + } else if (!strcmp(type_uri, (char*)NS_XSD "float") || + !strcmp(type_uri, (char*)NS_XSD "decimal")) { + lv2_atom_forge_float(forge, serd_strtod(str, &endptr)); + } else if (!strcmp(type_uri, (char*)NS_XSD "double")) { + lv2_atom_forge_double(forge, serd_strtod(str, &endptr)); + } else if (!strcmp(type_uri, (char*)NS_XSD "boolean")) { + lv2_atom_forge_bool(forge, !strcmp(str, "true")); + } else if (!strcmp(type_uri, LV2_ATOM__Path)) { + lv2_atom_forge_path(forge, str, len); + } else if (!strcmp(type_uri, (char*)NS_MIDI "MidiEvent")) { + lv2_atom_forge_atom(forge, len / 2, sratom->midi_MidiEvent); + for (const char* s = str; s < str + len; s += 2) { + unsigned num; + sscanf(s, "%2X", &num); + assert(num < UINT8_MAX); + const uint8_t c = num; + lv2_atom_forge_raw(forge, &c, 1); + } + lv2_atom_forge_pad(forge, len / 2); + } else { + lv2_atom_forge_literal( + forge, str, len, + sratom->map->map(sratom->map->handle, type_uri), + 0); + } + } else if (language) { + const char* prefix = "http://lexvo.org/id/iso639-3/"; + const size_t lang_len = strlen(prefix) + strlen(language); + char* lang_uri = calloc(lang_len + 1, 1); + snprintf(lang_uri, lang_len + 1, "%s%s", prefix, language); + lv2_atom_forge_literal( + forge, str, len, 0, + sratom->map->map(sratom->map->handle, lang_uri)); + free(lang_uri); + } else { + lv2_atom_forge_string(forge, str, len); + } + } else if (sord_node_get_type(node) == SORD_URI) { + if (!strcmp(str, (const char*)NS_RDF "nil")) { + lv2_atom_forge_atom(forge, 0, 0); + } else if (!strncmp(str, "file://", 7)) { + uint8_t* path = serd_file_uri_parse((const uint8_t*)str, NULL); + lv2_atom_forge_path(forge, (const char*)path, strlen((const char*)path)); + free(path); + } else { + lv2_atom_forge_urid(forge, map->map(map->handle, str)); + } + } else { + const SordNode* type = get_object(model, node, sratom->nodes.rdf_type); + const SordNode* value = get_object(model, node, sratom->nodes.rdf_value); + + const uint8_t* type_uri = NULL; + uint32_t type_urid = 0; + if (type) { + type_uri = sord_node_get_string(type); + type_urid = map->map(map->handle, (const char*)type_uri); + } + + LV2_Atom_Forge_Frame frame = { 0, 0 }; + if (mode == MODE_SEQUENCE) { + const SordNode* frame_time = get_object( + model, node, sratom->nodes.atom_frameTime); + const char* frame_time_str = frame_time + ? (const char*)sord_node_get_string(frame_time) + : ""; + lv2_atom_forge_frame_time(forge, serd_strtod(frame_time_str, NULL)); + read_node(sratom, forge, world, model, value, MODE_NORMAL); + } else if (type_urid == sratom->forge.Tuple) { + lv2_atom_forge_tuple(forge, &frame); + read_list_value(sratom, forge, world, model, value, MODE_NORMAL); + } else if (type_urid == sratom->forge.Sequence) { + lv2_atom_forge_sequence_head(forge, &frame, 0); + read_list_value(sratom, forge, world, model, value, MODE_SEQUENCE); + } else if (type_urid == sratom->forge.Vector) { + const SordNode* child_type_node = get_object( + model, node, sratom->nodes.atom_childType); + uint32_t child_type = map->map( + map->handle, (const char*)sord_node_get_string(child_type_node)); + uint32_t child_size = atom_size(sratom, child_type); + if (child_size > 0) { + lv2_atom_forge_vector_head(forge, &frame, child_size, child_type); + read_list_value(sratom, forge, world, model, value, MODE_NORMAL); + } + } else if (value && sord_node_equals(sord_node_get_datatype(value), + sratom->nodes.xsd_base64Binary)) { + size_t vlen = 0; + const uint8_t* vstr = sord_node_get_string_counted(value, &vlen); + size_t size = 0; + void* body = serd_base64_decode(vstr, vlen, &size); + lv2_atom_forge_atom(forge, size, type_urid); + lv2_atom_forge_write(forge, body, size); + free(body); + } else { + lv2_atom_forge_blank(forge, &frame, sratom->next_id++, type_urid); + SordQuad match; + + SordQuad q2 = { node, 0, 0, 0 }; + SordIter* i = sord_find(model, q2); + for (;!sord_iter_end(i); sord_iter_next(i)) { + sord_iter_get(i, match); + const SordNode* p = match[SORD_PREDICATE]; + const char* p_uri = (const char*)sord_node_get_string(p); + uint32_t p_urid = map->map(map->handle, p_uri); + if (!sord_node_equals(p, sratom->nodes.rdf_type)) { + // TODO: This will lose multiple rdf:type properties + lv2_atom_forge_property_head(forge, p_urid, 0); + read_node(sratom, forge, world, model, + match[SORD_OBJECT], MODE_NORMAL); + } + } + sord_iter_free(i); + } + if (frame.ref) { + lv2_atom_forge_pop(forge, &frame); + } + } +} + +SRATOM_API +void +sratom_read(Sratom* sratom, + LV2_Atom_Forge* forge, + SordWorld* world, + SordModel* model, + const SordNode* node) +{ + sratom->nodes.atom_childType = sord_new_uri(world, USTR(LV2_ATOM__childType)); + sratom->nodes.atom_frameTime = sord_new_uri(world, USTR(LV2_ATOM__frameTime)); + sratom->nodes.rdf_first = sord_new_uri(world, NS_RDF "first"); + sratom->nodes.rdf_rest = sord_new_uri(world, NS_RDF "rest"); + sratom->nodes.rdf_type = sord_new_uri(world, NS_RDF "type"); + sratom->nodes.rdf_value = sord_new_uri(world, NS_RDF "value"); + sratom->nodes.xsd_base64Binary = sord_new_uri(world, NS_XSD "base64Binary"); + + sratom->next_id = 1; + read_node(sratom, forge, world, model, node, MODE_NORMAL); + + sord_node_free(world, sratom->nodes.xsd_base64Binary); + sord_node_free(world, sratom->nodes.rdf_value); + sord_node_free(world, sratom->nodes.rdf_type); + sord_node_free(world, sratom->nodes.rdf_rest); + sord_node_free(world, sratom->nodes.rdf_first); + sord_node_free(world, sratom->nodes.atom_frameTime); + sord_node_free(world, sratom->nodes.atom_childType); + memset(&sratom->nodes, 0, sizeof(sratom->nodes)); +} + +SRATOM_API +LV2_Atom_Forge_Ref +sratom_forge_sink(LV2_Atom_Forge_Sink_Handle handle, + const void* buf, + uint32_t size) +{ + SerdChunk* chunk = (SerdChunk*)handle; + const LV2_Atom_Forge_Ref ref = chunk->len + 1; + serd_chunk_sink(buf, size, chunk); + return ref; +} + +SRATOM_API +LV2_Atom* +sratom_forge_deref(LV2_Atom_Forge_Sink_Handle handle, LV2_Atom_Forge_Ref ref) +{ + SerdChunk* chunk = (SerdChunk*)handle; + return (LV2_Atom*)(chunk->buf + ref - 1); +} + +SRATOM_API +LV2_Atom* +sratom_from_turtle(Sratom* sratom, + const char* base_uri, + const SerdNode* subject, + const SerdNode* predicate, + const char* str) +{ + SerdChunk out = { NULL, 0 }; + SerdNode base = serd_node_new_uri_from_string(USTR(base_uri), NULL, NULL); + SordWorld* world = sord_world_new(); + SordModel* model = sord_new(world, SORD_SPO, false); + SerdEnv* env = serd_env_new(&base); + SerdReader* reader = sord_new_reader(model, env, SERD_TURTLE, NULL); + + if (!serd_reader_read_string(reader, (const uint8_t*)str)) { + SordNode* s = sord_node_from_serd_node(world, env, subject, 0, 0); + SordNode* p = sord_node_from_serd_node(world, env, predicate, 0, 0); + SordQuad q = { s, p, 0, 0 }; + SordIter* i = sord_find(model, q); + if (!sord_iter_end(i)) { + SordQuad result; + sord_iter_get(i, result); + lv2_atom_forge_set_sink( + &sratom->forge, sratom_forge_sink, sratom_forge_deref, &out); + sratom_read(sratom, &sratom->forge, world, model, result[SORD_OBJECT]); + } else { + fprintf(stderr, "Failed to find node\n"); + } + sord_iter_free(i); + } else { + fprintf(stderr, "Failed to read Turtle\n"); + } + + serd_reader_free(reader); + serd_env_free(env); + sord_free(model); + sord_world_free(world); + + return (LV2_Atom*)out.buf; +} diff --git a/src/serialisation/sratom/sratom.h b/src/serialisation/sratom/sratom.h new file mode 100644 index 00000000..bd4a5721 --- /dev/null +++ b/src/serialisation/sratom/sratom.h @@ -0,0 +1,176 @@ +/* + 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 sratom.h API for Sratom, an LV2 Atom RDF serialisation library. +*/ + +#ifndef SRATOM_SRATOM_H +#define SRATOM_SRATOM_H + +#include + +#include "lv2/lv2plug.in/ns/ext/urid/urid.h" +#include "lv2/lv2plug.in/ns/ext/atom/atom.h" +#include "lv2/lv2plug.in/ns/ext/atom/forge.h" +#include "serd/serd.h" +#include "sord/sord.h" + +#ifdef SRATOM_SHARED +# ifdef _WIN32 +# define SRATOM_LIB_IMPORT __declspec(dllimport) +# define SRATOM_LIB_EXPORT __declspec(dllexport) +# else +# define SRATOM_LIB_IMPORT __attribute__((visibility("default"))) +# define SRATOM_LIB_EXPORT __attribute__((visibility("default"))) +# endif +# ifdef SRATOM_INTERNAL +# define SRATOM_API SRATOM_LIB_EXPORT +# else +# define SRATOM_API SRATOM_LIB_IMPORT +# endif +#else +# define SRATOM_API +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + @defgroup sratom Sratom + An LV2 Atom RDF serialisation library. + @{ +*/ + +/** + Atom serialiser. +*/ +typedef struct SratomImpl Sratom; + +/** + Create a new Atom serialiser. +*/ +SRATOM_API +Sratom* +sratom_new(LV2_URID_Map* map); + +/** + Free an Atom serialisation. +*/ +SRATOM_API +void +sratom_free(Sratom* sratom); + +/** + Set the sink(s) where sratom will write its output. + + This must be called before calling sratom_write(). If @p pretty_numbers is + true, numbers will be written as pretty Turtle literals, rather than string + literals with precise types. The cost of this is the types might get + fudged on a round-trip to RDF and back. +*/ +SRATOM_API +void +sratom_set_sink(Sratom* sratom, + const char* base_uri, + SerdStatementSink sink, + SerdEndSink end_sink, + void* handle, + bool pretty_numbers); + +/** + Write an Atom to RDF. + The serialised atom is written to the sink set by sratom_set_sink(). + @return 0 on success, or a non-zero error code otherwise. +*/ +SRATOM_API +int +sratom_write(Sratom* sratom, + LV2_URID_Unmap* unmap, + uint32_t flags, + const SerdNode* subject, + const SerdNode* predicate, + uint32_t type, + uint32_t size, + const void* body); + +/** + Read an Atom from RDF. + The resulting atom will be written to @p forge. +*/ +SRATOM_API +void +sratom_read(Sratom* sratom, + LV2_Atom_Forge* forge, + SordWorld* world, + SordModel* model, + const SordNode* node); + +/** + Serialise an Atom to a Turtle string. + The returned string must be free()'d by the caller. +*/ +SRATOM_API +char* +sratom_to_turtle(Sratom* sratom, + LV2_URID_Unmap* unmap, + const char* base_uri, + const SerdNode* subject, + const SerdNode* predicate, + uint32_t type, + uint32_t size, + const void* body); + +/** + Read an Atom from a Turtle string. + The returned atom must be free()'d by the caller. +*/ +SRATOM_API +LV2_Atom* +sratom_from_turtle(Sratom* sratom, + const char* base_uri, + const SerdNode* subject, + const SerdNode* predicate, + const char* str); + +/** + A convenient resizing sink for LV2_Atom_Forge. + The handle must point to an initialized SerdChunk. +*/ +SRATOM_API +LV2_Atom_Forge_Ref +sratom_forge_sink(LV2_Atom_Forge_Sink_Handle handle, + const void* buf, + uint32_t size); + +/** + The corresponding deref function for sratom_forge_sink. +*/ +SRATOM_API +LV2_Atom* +sratom_forge_deref(LV2_Atom_Forge_Sink_Handle handle, + LV2_Atom_Forge_Ref ref); + +/** + @} +*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SRATOM_SRATOM_H */ diff --git a/src/serialisation/wscript b/src/serialisation/wscript index abc1dd46..58c56f60 100644 --- a/src/serialisation/wscript +++ b/src/serialisation/wscript @@ -2,10 +2,13 @@ from waflib.extras import autowaf as autowaf def build(bld): - obj = bld(features = 'cxx cxxshlib', - source = 'Parser.cpp Serialiser.cpp serialisation.cpp', + obj = bld(features = 'c cshlib cxx cxxshlib', + source = ['Parser.cpp', + 'Serialiser.cpp', + 'serialisation.cpp', + 'sratom/sratom.c'], export_includes = ['../..'], - includes = ['../..'], + includes = ['.', '../..'], name = 'libingen_serialisation', target = 'ingen_serialisation', install_path = '${LIBDIR}', -- cgit v1.2.1