当使用LuaJIT 2.x时,静态链接一个纯净的Lua字节码模块到可运行的Nginx中是可行的。
首先你要使用luajit
,把.lua
的Lua模块编译成.o
的目标文件(包含导出的字节码数据),然后链接这些.o
文件到Nginx的构造环境。
用下面这个小例子来印证一下。这里我们的.lua
文件使用foo.lua
名字:
-- foo.lua
local _M = {}
function _M.go()
print("Hello from foo")
end
return _M
我们把.lua
文件编译成foo.o
文件:
/path/to/luajit/bin/luajit -bg foo.lua foo.o
这里重要的是.lua
文件名,它决定了这个模块在业务Lua中是如何使用的。文件名foo.o
除了扩展名是.o
以外其他都不重要(只是用来告诉luajit
什么输出格式)。如果你想对字节码输出去掉Lua调试信息,你可以指明-b
选项替代原有的-bg
。
然后你构建Nginx或者OpenResty,对./configure
脚本添加--with-ld-opt="foo.o"
选项:
./configure --with-ld-opt="/path/to/foo.o" ...
最后,你就可以按照下面的示例,在Lua代码中调用了:
local foo = require "foo"
foo.go()
并且,这段代码不在依赖外部的foo.lua
文件,因为它已经被编译到了nginx
程序中。
通过require
调用Lua模块,如果你想使用点方法,如下:
local foo = require "resty.foo"
在你使用luajit
命令行工具把他编译成.o
文件之前,你需要把foo.lua
文件重命名为resty_foo.lua
。
.lua
文件编译成.o
文件,构建nginx + ngx_lua,这两者使用相同版本的LuaJIT是至关重要的。这是因为LuaJIT字节码格式在不同版本之间可能是不兼容的。当字节码文件出现了不兼容情况,你将看到一行Lua运行时错误信息:没找到Lua模块。
当你拥有多个.lua
文件需要链接,你可以一次指明所有的.o
文件,并赋给--with-ld-opt
选项,参考:
./configure --with-ld-opt="/path/to/foo.o /path/to/bar.o" ...
如果你有非常多的.o
文件,把这些文件的名字写到命令行中都不太可行,这种情况下,对你的.o
文件可以构建一个静态库(或者归档),参考:
ar rcus libmyluafiles.a *.o
然后你就可以把myluafiles
链接到你的nginx可执行程序中:
./configure \
--with-ld-opt="-L/path/to/lib -Wl,--whole-archive -lmyluafiles -Wl,--no-whole-archive"
/path/to/lib
目录中应包含libmyluafiles.a
文件。 应当指出的是,这里要添加链接选项--whole-archive
,否则我们的归档将被跳过,因为在我们的归档没有导出nginx执行需要的函数符号。
where /path/to/lib
is the path of the directory containing the libmyluafiles.a
file. It should be noted that the linker option --whole-archive
is required here because otherwise our archive will be skipped because no symbols in our archive are mentioned in the main parts of the nginx executable.
English source:
When LuaJIT 2.x is used, it is possible to statically link the bytecode of pure Lua modules into the Nginx executable.
Basically you use the luajit
executable to compile .lua
Lua module files to .o
object files containing the exported bytecode data, and then link the .o
files directly in your Nginx build.
Below is a trivial example to demonstrate this. Consider that we have the following .lua
file named foo.lua
:
-- foo.lua
local _M = {}
function _M.go()
print("Hello from foo")
end
return _M
And then we compile this .lua
file to foo.o
file:
/path/to/luajit/bin/luajit -bg foo.lua foo.o
What matters here is the name of the .lua
file, which determines how you use this module later on the Lua land. The file name foo.o
does not matter at all except the .o
file extension (which tells luajit
what output format is used). If you want to strip the Lua debug information from the resulting bytecode, you can just specify the -b
option above instead of -bg
.
Then when building Nginx or OpenResty, pass the --with-ld-opt="foo.o"
option to the ./configure
script:
./configure --with-ld-opt="/path/to/foo.o" ...
Finally, you can just do the following in any Lua code run by ngx_lua:
local foo = require "foo"
foo.go()
And this piece of code no longer depends on the external foo.lua
file any more because it has already been compiled into the nginx
executable.
If you want to use dot in the Lua module name when calling require
, as in
local foo = require "resty.foo"
then you need to rename the foo.lua
file to resty_foo.lua
before compiling it down to a .o
file with the luajit
command-line utility.
It is important to use exactly the same version of LuaJIT when compiling .lua
files to .o
files as building nginx + ngx_lua. This is because the LuaJIT bytecode format may be incompatible between different LuaJIT versions. When the bytecode format is incompatible, you will see a Lua runtime error saying that the Lua module is not found.
When you have multiple .lua
files to compile and link, then just specify their .o
files at the same time in the value of the --with-ld-opt
option. For instance,
./configure --with-ld-opt="/path/to/foo.o /path/to/bar.o" ...
If you have just too many .o
files, then it might not be feasible to name them all in a single command. In this case, you can build a static library (or archive) for your .o
files, as in
ar rcus libmyluafiles.a *.o
then you can link the myluafiles
archive as a whole to your nginx executable:
./configure \
--with-ld-opt="-L/path/to/lib -Wl,--whole-archive -lmyluafiles -Wl,--no-whole-archive"
where /path/to/lib
is the path of the directory containing the libmyluafiles.a
file. It should be noted that the linker option --whole-archive
is required here because otherwise our archive will be skipped because no symbols in our archive are mentioned in the main parts of the nginx executable.