tl;dr: 我想使用自定义目录名称模式创建 lua 包,但搜索路径有问题。
问题
我有一个应用程序,我希望允许用户为其编写插件,遵循与 Lightroom 类似的模型:
- 一组默认插件存储在
<app data>/plugins/<name>.myplugin
中 -
<name>.myplugin
是一个目录包,可能包含一组脚本、二进制文件或其他资源 - 插件可以通过不同的脚本将许多不同的功能导出到应用
- 导出函数的名称列于
info.lua
中。应用程序读取的文件
我正在解决的问题是如何最好地将插件包装为包(模块+子模块)或常规脚本。我设想插件可能包含第三方模块:
Foo.myplugin/
info.lua - returns a table with plugin name, version info, list of exported functions, etc
Foo.lua - defines the main functions exported by this plugin, which calls other scripts:
UsefulFunctions.lua - used by Foo.lua
3rdparty/3rdparty.lua - 3rd party module
如果我设置包搜索路径,package.path
包括
<appdata>/?.myplugin/?.lua
然后我可以使用 Foo=require 'Foo'
加载包。但是,我无法弄清楚如何加载子模块。如果Foo.lua
来电 UsefulFunctions=require 'UsefulFunctions'
那么这个加载失败,因为 lua 的搜索路径试图寻找 UsefulFunctions.myplugin/UsefulFunctions.lua
。我无法使用 require 'Foo.UsefulFunctions'
加载它出于类似的原因,要么。
一些选项:
- 一种解决方法是将每个插件的路径显式添加到包路径中,但如果两个插件各自包含同名的子模块,这会导致问题。
- 另一种选择是编写插件以使用常规 lua 脚本而不是提供模块,但这仍然意味着必须在每个插件内设置搜索路径。
- 后备选项可能是丢失
.myplugin
后缀可以简化包搜索路径。 - 修补 Lua 以明确支持此类搜索路径
有什么方法可以提供我需要的功能吗?
我目前使用的是 Lua 5.1。我知道 5.2 对包搜索路径有更多控制,但我认为目前无法选择更新它。我也在使用 luabind,尽管我认为它与此无关。
最佳答案
您可以使用自定义搜索器功能自定义 Lua 搜索模块的方式,使用 documentation of require
中概述的机制。和 package.loaders
.
诀窍是检测是否可以在带有 .myplugins
后缀的目录中找到该模块,并跟踪 bundle 的路径。考虑以下脚本。
-- <appdata>/plugins/foo.myplugin/foo.lua
local auxlib = require 'foo.auxlib'
local M = {}
function M.Foobnicator()
print "Called: Foobnicator!!"
auxlib.AuxFunction()
end
return M
-- <appdata>/plugins/foo.myplugin/auxlib.lua
local M = {}
function M.AuxFunction()
print "Called: AuxFunction!!"
end
return M
-- main.lua
package.path = package.path .. ";"
.. [[<appdata>/plugins/?.myplugin/?.lua]]
local bundles = {} -- holds bundle names and pathnames
local function custom_searcher( module_name )
if string.match( module_name, '%.' ) then
-- module name has a dot in it - it is a submodule,
-- let's check if it is inside a bundle
local main_module_name, subname =
string.match( module_name, '^([^.]-)%.(.+)' )
local main_path = bundles[ main_module_name ]
if main_path then -- OK, it's a submodule of a known bundle
local sub_fname = string.gsub( subname, '%.', '/' )
-- replace main module filename with that of submodule
local path = string.match( main_path, '^.*[/\\]' )
.. sub_fname .. '.lua'
return loadfile( path )
else -- not a bundle - give up the search
return
end
end
-- search for the module scanning package.path
for template in string.gmatch( package.path, '[^;]+' ) do
if string.match( template, '%.myplugin' ) then -- bundle?
local module_path =
string.gsub( template, '%?', module_name )
local fh = io.open( module_path ) -- file exists?
if fh then -- module found
fh:close()
bundles[ module_name ] = module_path
return loadfile( module_path )
end
end
end
end
-- sets the custom searcher as the first one so to take
-- precedence over default ones
table.insert( package.loaders, 1, custom_searcher )
local foo = require 'foo'
foo.Foobnicator()
运行main.lua
将产生以下输出:
Called: Foobnicator!! Called: AuxFunction!!
我希望这能让您走上正轨。也许它并没有涵盖所有可能性,并且错误处理根本不完整,但它应该为您提供良好的工作基础。
关于lua - 如何设置搜索路径允许lua插件作为包,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19664730/