diff options
author | David Robillard <d@drobilla.net> | 2016-03-17 16:48:38 -0400 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2016-03-17 16:48:38 -0400 |
commit | 4a1e47eb6ff69c2685feca73ba19700a99736b59 (patch) | |
tree | 48dcd217ffdee6620e643e0acf93d55727e64e31 | |
parent | e1c2cf574732c52c4df791274f67381458f5e699 (diff) | |
download | lilv-4a1e47eb6ff69c2685feca73ba19700a99736b59.tar.gz lilv-4a1e47eb6ff69c2685feca73ba19700a99736b59.tar.bz2 lilv-4a1e47eb6ff69c2685feca73ba19700a99736b59.zip |
Replace bundle when newer plugin version is loaded
Based on patch from Robin Gareus.
-rw-r--r-- | NEWS | 4 | ||||
-rw-r--r-- | src/lilv_internal.h | 20 | ||||
-rw-r--r-- | src/world.c | 127 |
3 files changed, 147 insertions, 4 deletions
@@ -3,13 +3,15 @@ lilv (0.22.1) unstable; * Unload contained resources when bundle is unloaded * Do not instantiate plugin when data fails to parse * Support re-loading plugins + * Replace bundles if bundle with newer plugin version is loaded + (based on patch from Robin Gareus) * Fix loading dyn-manifest from bundles with spaces in their path * Add LILV_URI_ATOM_PORT and LILV_URI_CV_PORT defines * Fix Python bindings * Fix documentation installation * Fix outdated comment references to lilv_uri_to_path() - -- David Robillard <d@drobilla.net> Thu, 29 Oct 2015 10:32:39 -0400 + -- David Robillard <d@drobilla.net> Thu, 17 Mar 2016 16:48:08 -0400 lilv (0.22.0) stable; diff --git a/src/lilv_internal.h b/src/lilv_internal.h index 5fc37fa..ea96418 100644 --- a/src/lilv_internal.h +++ b/src/lilv_internal.h @@ -174,7 +174,9 @@ struct LilvWorldImpl { SordNode* lv2_index; SordNode* lv2_latency; SordNode* lv2_maximum; + SordNode* lv2_microVersion; SordNode* lv2_minimum; + SordNode* lv2_minorVersion; SordNode* lv2_name; SordNode* lv2_optionalFeature; SordNode* lv2_port; @@ -235,6 +237,11 @@ struct LilvUIImpl { LilvNodes* classes; }; +typedef struct LilvVersion { + int minor; + int micro; +} LilvVersion; + /* * * Functions @@ -314,6 +321,19 @@ int lilv_lib_compare(const void* a, const void* b, void* user_data); int lilv_ptr_cmp(const void* a, const void* b, void* user_data); int lilv_resource_node_cmp(const void* a, const void* b, void* user_data); +static inline int +lilv_version_cmp(const LilvVersion* a, const LilvVersion* b) +{ + if (a->minor == b->minor && a->micro == b->micro) { + return 0; + } else if ((a->minor < b->minor) + || (a->minor == b->minor && a->micro < b->micro)) { + return -1; + } else { + return 1; + } +} + struct LilvHeader* lilv_collection_get_by_uri(const ZixTree* seq, const LilvNode* uri); diff --git a/src/world.c b/src/world.c index 0710899..d171422 100644 --- a/src/world.c +++ b/src/world.c @@ -64,7 +64,9 @@ lilv_world_new(void) world->uris.lv2_index = NEW_URI(LV2_CORE__index); world->uris.lv2_latency = NEW_URI(LV2_CORE__latency); world->uris.lv2_maximum = NEW_URI(LV2_CORE__maximum); + world->uris.lv2_microVersion = NEW_URI(LV2_CORE__microVersion); world->uris.lv2_minimum = NEW_URI(LV2_CORE__minimum); + world->uris.lv2_minorVersion = NEW_URI(LV2_CORE__minorVersion); world->uris.lv2_name = NEW_URI(LV2_CORE__name); world->uris.lv2_optionalFeature = NEW_URI(LV2_CORE__optionalFeature); world->uris.lv2_port = NEW_URI(LV2_CORE__port); @@ -404,9 +406,9 @@ lilv_world_add_plugin(LilvWorld* world, plugin->loaded = false; lilv_node_free(plugin_uri); } else { - LILV_ERRORF("Duplicate plugin <%s>\n", plugin_uri_str); - LILV_ERRORF("... found in %s\n", lilv_node_as_string(last_bundle)); - LILV_ERRORF("... and %s\n", sord_node_get_string(bundle)); + LILV_WARNF("Duplicate plugin <%s>\n", plugin_uri_str); + LILV_WARNF("... found in %s\n", lilv_node_as_string(last_bundle)); + LILV_WARNF("... and %s (ignored)\n", sord_node_get_string(bundle)); lilv_node_free(plugin_uri); return; } @@ -614,6 +616,65 @@ lilv_world_get_manifest_uri(LilvWorld* world, const LilvNode* bundle_uri) return manifest; } +static SordModel* +load_plugin_model(LilvWorld* world, + const LilvNode* bundle_uri, + const LilvNode* plugin_uri) +{ + // Create model and reader for loading into it + SordNode* bundle_node = bundle_uri->node; + SordModel* model = sord_new(world->world, SORD_SPO|SORD_OPS, false); + SerdEnv* env = serd_env_new(sord_node_to_serd_node(bundle_node)); + SerdReader* reader = sord_new_reader(model, env, SERD_TURTLE, NULL); + + // Load manifest + LilvNode* manifest_uri = lilv_world_get_manifest_uri(world, bundle_uri); + serd_reader_add_blank_prefix(reader, lilv_world_blank_node_prefix(world)); + serd_reader_read_file( + reader, (const uint8_t*)lilv_node_as_string(manifest_uri)); + + // Load any seeAlso files + SordModel* files = lilv_world_filter_model( + world, model, plugin_uri->node, world->uris.rdfs_seeAlso, NULL, NULL); + + SordIter* f = sord_begin(files); + FOREACH_MATCH(f) { + const SordNode* file = sord_iter_get_node(f, SORD_OBJECT); + const uint8_t* file_str = sord_node_get_string(file); + if (sord_node_get_type(file) == SORD_URI) { + serd_reader_add_blank_prefix( + reader, lilv_world_blank_node_prefix(world)); + serd_reader_read_file(reader, file_str); + } + } + + sord_iter_free(f); + sord_free(files); + serd_reader_free(reader); + serd_env_free(env); + lilv_node_free(manifest_uri); + + return model; +} + +static LilvVersion +get_version(LilvWorld* world, SordModel* model, const LilvNode* subject) +{ + const SordNode* minor_node = sord_get( + model, subject->node, world->uris.lv2_minorVersion, NULL, NULL); + const SordNode* micro_node = sord_get( + model, subject->node, world->uris.lv2_microVersion, NULL, NULL); + + + LilvVersion version = { 0, 0 }; + if (minor_node && micro_node) { + version.minor = atoi((const char*)sord_node_get_string(minor_node)); + version.micro = atoi((const char*)sord_node_get_string(micro_node)); + } + + return version; +} + LILV_API void lilv_world_load_bundle(LilvWorld* world, const LilvNode* bundle_uri) { @@ -640,6 +701,66 @@ lilv_world_load_bundle(LilvWorld* world, const LilvNode* bundle_uri) world->uris.rdf_a, world->uris.lv2_Plugin, bundle_node); + + // Find any loaded plugins that will be replaced with a newer version + LilvNodes* unload_uris = lilv_nodes_new(); + FOREACH_MATCH(plug_results) { + const SordNode* plug = sord_iter_get_node(plug_results, SORD_SUBJECT); + + LilvNode* plugin_uri = lilv_node_new_from_node(world, plug); + const LilvPlugin* plugin = lilv_plugins_get_by_uri(world->plugins, plugin_uri); + const LilvNode* last_bundle = plugin ? lilv_plugin_get_bundle_uri(plugin) : NULL; + if (!plugin || sord_node_equals(bundle_node, last_bundle->node)) { + // No previously loaded version, or it's from the same bundle + lilv_node_free(plugin_uri); + continue; + } + + // Compare versions + SordModel* this_model = load_plugin_model(world, bundle_uri, plugin_uri); + LilvVersion this_version = get_version(world, this_model, plugin_uri); + LilvVersion last_version = get_version(world, world->model, plugin_uri); + sord_free(this_model); + if (lilv_version_cmp(&this_version, &last_version) > 0) { + zix_tree_insert((ZixTree*)unload_uris, + lilv_node_duplicate(plugin_uri), + NULL); + LILV_WARNF("Version %d.%d of <%s> in <%s> replaces %d.%d in <%s>\n", + this_version.minor, this_version.micro, + sord_node_get_string(plug), + sord_node_get_string(bundle_node), + last_version.minor, last_version.micro, + sord_node_get_string(last_bundle->node)); + } + lilv_node_free(plugin_uri); + } + + sord_iter_free(plug_results); + + // Unload any old conflicting plugins + LILV_FOREACH(nodes, i, unload_uris) { + const LilvNode* uri = lilv_nodes_get(world->plugins, i); + const LilvPlugin* plugin = lilv_plugins_get_by_uri(world->plugins, uri); + const LilvNode* bundle = lilv_plugin_get_bundle_uri(plugin); + + lilv_world_unload_resource(world, i); + lilv_world_unload_bundle(world, bundle); + + ZixTreeIter* z; + if ((z = lilv_collection_find_by_uri(world->zombies, uri))) { + lilv_plugin_free((LilvPlugin*)zix_tree_get(z)); + zix_tree_remove(world->zombies, z); + } + } + lilv_nodes_free(unload_uris); + + // Re-search for plugin results now that old plugins are gone + plug_results = sord_search(world->model, + NULL, + world->uris.rdf_a, + world->uris.lv2_Plugin, + bundle_node); + FOREACH_MATCH(plug_results) { const SordNode* plug = sord_iter_get_node(plug_results, SORD_SUBJECT); lilv_world_add_plugin(world, plug, manifest, NULL, bundle_node); |