Comparison

core/modulemanager.lua @ 12254:5b0c8e499288

modulemanager: Add plugin load filter that reads module metadata from source Metadata in modules is added using lines formatted as: --% key: value Where key is a valid identifier string, and value is also a string (leading and trailing whitespace are trimmed during parsing). The initial supported keys are: --% requires_core_features: feature1, feature2, ... --% conflicts_core_features: feature1, feature2. ... These 'features' map to features reported by the new core.features module. A benefit of this load-time metadata approach compared to e.g. something like module:requires()/module:conflicts() is that we can continue to look in module search paths for a suitable module. Aborting an already-loaded module due to a version conflict would be too late.
author Matthew Wild <mwild1@gmail.com>
date Fri, 04 Feb 2022 14:20:00 +0000
parent 12253:57d35fcde488
child 12257:7adfd5d29576
comparison
equal deleted inserted replaced
12253:57d35fcde488 12254:5b0c8e499288
4 -- 4 --
5 -- This project is MIT/X11 licensed. Please see the 5 -- This project is MIT/X11 licensed. Please see the
6 -- COPYING file in the source package for more information. 6 -- COPYING file in the source package for more information.
7 -- 7 --
8 8
9 local array = require "util.array";
9 local logger = require "util.logger"; 10 local logger = require "util.logger";
10 local log = logger.init("modulemanager"); 11 local log = logger.init("modulemanager");
11 local config = require "core.configmanager"; 12 local config = require "core.configmanager";
12 local pluginloader = require "util.pluginloader"; 13 local pluginloader = require "util.pluginloader";
13 local envload = require "util.envload"; 14 local envload = require "util.envload";
14 local set = require "util.set"; 15 local set = require "util.set";
16
17 local core_features = require "core.features".available;
15 18
16 local new_multitable = require "util.multitable".new; 19 local new_multitable = require "util.multitable".new;
17 local api = require "core.moduleapi"; -- Module API container 20 local api = require "core.moduleapi"; -- Module API container
18 21
19 local prosody = prosody; 22 local prosody = prosody;
51 54
52 local _ENV = nil; 55 local _ENV = nil;
53 -- luacheck: std none 56 -- luacheck: std none
54 57
55 local loader = pluginloader.init({ 58 local loader = pluginloader.init({
59 load_filter_cb = function (path, content)
60 local metadata = {};
61 for line in content:gmatch("([^\r\n]+)\r?\n") do
62 local key, value = line:match("^%-%-%% *([%w_]+): *(.+)$");
63 if key then
64 value = value:gsub("%s+$", "");
65 metadata[key] = value;
66 end
67 end
68
69 if metadata.conflicts_core_features then
70 local conflicts_core_features = set.new(array.collect(metadata.conflicts_core_features:gmatch("[^, ]+")));
71 local conflicted_features = set.intersection(conflicts_core_features, core_features);
72 if not conflicted_features:empty() then
73 log("warn", "Not loading module, due to conflicting features '%s': %s", conflicted_features, path);
74 return; -- Don't load this module
75 end
76 end
77 if metadata.requires_core_features then
78 local required_features = set.new(array.collect(metadata.requires_core_features:gmatch("[^, ]+")));
79 local missing_features = required_features - core_features;
80 if not missing_features:empty() then
81 log("warn", "Not loading module, due to missing features '%s': %s", missing_features, path);
82 return; -- Don't load this module
83 end
84 end
85
86 return path, content, metadata;
87 end;
56 }); 88 });
57 89
58 local load_modules_for_host, load, unload, reload, get_module, get_items; 90 local load_modules_for_host, load, unload, reload, get_module, get_items;
59 local get_modules, is_loaded, module_has_method, call_module_method; 91 local get_modules, is_loaded, module_has_method, call_module_method;
60 92