From 61d33dd36446c1a6d801c1d3781c4051641af615 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 4 Feb 2022 14:20:00 +0000 Subject: 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. --- core/modulemanager.lua | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/core/modulemanager.lua b/core/modulemanager.lua index 436ee378..669df440 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -6,6 +6,7 @@ -- COPYING file in the source package for more information. -- +local array = require "util.array"; local logger = require "util.logger"; local log = logger.init("modulemanager"); local config = require "core.configmanager"; @@ -13,6 +14,8 @@ local pluginloader = require "util.pluginloader"; local envload = require "util.envload"; local set = require "util.set"; +local core_features = require "core.features".available; + local new_multitable = require "util.multitable".new; local api = require "core.moduleapi"; -- Module API container @@ -53,6 +56,35 @@ local _ENV = nil; -- luacheck: std none local loader = pluginloader.init({ + load_filter_cb = function (path, content) + local metadata = {}; + for line in content:gmatch("([^\r\n]+)\r?\n") do + local key, value = line:match("^%-%-%% *([%w_]+): *(.+)$"); + if key then + value = value:gsub("%s+$", ""); + metadata[key] = value; + end + end + + if metadata.conflicts_core_features then + local conflicts_core_features = set.new(array.collect(metadata.conflicts_core_features:gmatch("[^, ]+"))); + local conflicted_features = set.intersection(conflicts_core_features, core_features); + if not conflicted_features:empty() then + log("warn", "Not loading module, due to conflicting features '%s': %s", conflicted_features, path); + return; -- Don't load this module + end + end + if metadata.requires_core_features then + local required_features = set.new(array.collect(metadata.requires_core_features:gmatch("[^, ]+"))); + local missing_features = required_features - core_features; + if not missing_features:empty() then + log("warn", "Not loading module, due to missing features '%s': %s", missing_features, path); + return; -- Don't load this module + end + end + + return path, content, metadata; + end; }); local load_modules_for_host, load, unload, reload, get_module, get_items; -- cgit v1.2.3