2

tl;dr: I want to create lua packages with a custom directory name pattern, having problem with search paths.

The problem

I've got an application that I'm wanted to allow the user to write plugins for, following a similar model to Lightroom:

  • A default set of plugins are stored in <app data>/plugins/<name>.myplugin
  • <name>.myplugin is a directory bundle which may contain a set of scripts, binaries or other resources
  • A plugin may export many different functions to the app, via different scripts
  • The names of the exported functions are listed in an info.lua file that is read by the app

The problem I'm grappling with is how best to wrap the plugins as packages (modules + submodules) or regular scripts. I envisage that a plugin might include 3rd party modules:

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

If I set the package search path, package.path to include

<appdata>/?.myplugin/?.lua

then I can load the package with Foo=require 'Foo'. However, I can't work out how to get submodules loaded. If Foo.lua calls UsefulFunctions=require 'UsefulFunctions' then this load fails because lua's search path tries to look for UsefulFunctions.myplugin/UsefulFunctions.lua. I can't load it with require 'Foo.UsefulFunctions' either, for similar reasons.

Some options:

  • One workaround would be to explicitly add each plugin's path to the package path, but this would cause problems if two plugins each contained a submodule of the same name.
  • Another option is to write the plugins to use regular lua scripts rather than providing modules, but this still means that the search paths have to be set up inside each plugin.
  • A fallback option could be to lose the .myplugin suffix which would simplify the package search path.
  • Patch Lua to explicitly support this type of search path

Is there any way of providing the functionality I need?

I'm currently on Lua 5.1. I know 5.2 has more control over package search paths, but I don't think I have the option of updating to it at the moment. I'm also using luabind, though I don't think it is relevant to this.

4

1 回答 1

4

您可以使用自定义搜索器函数自定义 Lua 搜索模块的方式,使用 和文档requirepackage.loaders概述的机制。

诀窍是检测模块是否可以在带有.myplugins后缀的目录中找到并跟踪捆绑包的路径。考虑以下脚本。

-- <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将产生以下输出:

被称为:Foobnicator!!
调用:辅助功能!!

我希望这会让你走上正确的轨道。可能它并没有涵盖所有可能性,并且错误处理根本不完整,但它应该为您提供良好的工作基础。

于 2013-10-29T19:54:51.080 回答