在 Lua 中通过路径引用自定义 C 模块

这是困扰我相当长时间的问题。

在我将自定义函数编写并编译为共享库后,只有当共享库直接位于我调用它的脚本所在的目录中时,require('mylib')才能正常工作。

任何试图使用/path/to/mylib或类似的绝对路径的努力都会失败。此外,“回跟踪”相对路径(即使用..)也会失败。

那么如何指定bin目录或共享库输出的位置呢?

点赞
用户3427580
用户3427580

根据 Lua 5.2 的 require 调用文档,有几种地方可以加载这些可加载模块。

看起来 require() 使用了称为 "searchers" 的机制(上方链接为文档),以确定在哪里找到这些模块。总共有四个搜索器。从文档中可以了解到:

第一个搜索器只是在 package.preload 表中查找加载器。

第二个搜索器将按照在 package.path 中存储的路径,查找一个 Lua 库加载器。搜索的方式可以参考 function package.searchpath

第三个搜索器将按照在变量 package.cpath 中给定的路径,查找一个 C 库加载器。搜索的方式与 function package.searchpath 中描述的相同。例如,如果 C 路径是字符串 "./?.so;./?.dll;/usr/local/?/init.so",那么模块 foo 的搜索器将按照这个顺序尝试打开文件 ./foo.so./foo.dll/usr/local/foo/init.so。一旦找到 C 库,这个搜索器首先使用动态链接设施将应用程序与库链接起来。然后它尝试在库内查找一个 C 函数作为加载器。这个 C 函数的名称是字符串 "luaopen_",后面跟着一份模块名称的副本,其中每个点都被替换为下划线。此外,如果模块名称有连字符,将删除其前缀,直到(包括)第一个连字符。例如,如果模块名为 a.v1-b.c,那么函数名将为 luaopen_b_c

第四个搜索器尝试使用一个全能加载器。它在 C 路径中搜索给定模块的根名称的库。例如,当需要加载 a.b.c 时,它将搜索一个名为 a 的 C 库。如果找到了,它将在其中查找子模块的打开函数;在我们的示例中,这将是 luaopen_a_b_c。使用此功能,一个包可以将多个 C 子模块打包到一个单独的库中,每个子模块都保留其原始的打开功能。

对于我们来说,有用的是第三个搜索器:它用于任何共享库(.dll 或 .so),而这通常是我们自定义的 C 模块的构建方式。

使用 template 字符串(带有问号的那个)时,搜索器将在每个指定的路径中查找,将 require() 的参数替换为问号。为了指定这个第三个搜索器的路径,必须设置(或附加到)package.cpath,然后调用 require()

因此,假设您的目录结构如下:

- ROOT
    |-lua
    |-bin

其中 lua 包含 script.lua,而 bin 包含 mylib.so

为了加载 mylib.so,您只需要在 script.lua 中添加这两行代码:

package.cpath = '/ROOT/bin/?.so;' .. package.cpath
libfuncs = require('mylib')

注:请注意分号。如果您附加路径(与上面的预设相反),请确保在您添加的路径上以分号开头。默认情况下不存在该分号。否则,您的新路径将合并到当前默认路径中,默认路径只是 ./?.so

2016-08-30 21:44:22
用户2328287
用户2328287

如果你想要配置你的系统使用某个路径来存储库,那么最好的方法就是使用 LUA_PATHLUA_CPATH 环境变量,例如 LUA_CPATH=/path/to/?.so。或者针对特定的 Lua 版本,比如 LUA_CPATH_5_3=/path/to/?.so

但是如果你已经有了某个库的完整路径,想要从这个目录加载这个特定的库,那么你可以使用 package.loadlib 函数。

local function loadlib(path, name)
  local sep = string.sub(package.config, 1, 1)
  local file = path .. sep .. name .. ((sep == '/') and '.so' or '.dll')
  local func = 'luaopen_' .. name
  local loader, msg = package.loadlib(file, func)
  assert(loader, msg)
  return assert(loader())
end

local mylib = loadlib([[/path/to]], 'mylib')
2016-08-31 08:24:30