Lua Packaging Guide |
|
Code is typically distributed in the form of a "package", which is an archive (e.g. .tar or .zip file or source repository) containing one or more code modules along with documentation and other related files. It's everything the user needs to deploy the code. The contents of that archive should ideally have certain parts and qualities to it. People may differ on what these may be, but the following guidelines should be helpful. It's good to first take a look at how some existing packages do things as examples before creating your own.
require 'foo.bar'
or require 'bar'
?
test.lua
) that can be run automatically without user intervention.
\n
) line endings in all text files, not Windows style (\r\n
). Do this for git repos too [2]. On Windows, you may use a text editor that understands \n
line endings. For Unicode, use UTF-8.
do local m = require "foo" end
would have no side-effects.
-- This can be considered bad: module(math, package.seeall) -- package.seeall pollutes math table with _G fields local sqrt_ = math.sqrt function math.sqrt(x) if getmetatable(x) == Complex then return Complex.sqrt(x) else return sqrt_(x) end end -- The above overwrites a standard math function. Even if this doesn't break -- compatibility, it imposes a performance overhead and a possible security -- loophole for sandboxes. function os.sleep(n) for i=1,n do end end -- Even this has some issues. It creates a side-effect: The existence -- of os.sleep depends on the point in time in which the above code was -- first required by some module in the application, not on whether the -- code was required in the current file. Code that once ran may fail -- if the files are loaded in a different order. -- Also, two different modules will conflict if they both define their own os.sleep.
module
and luaL_register
functions violate some of these rules (see LuaModuleFunctionCritiqued) and are currently deprecated in LuaFiveTwo (alpha). It's not invalid to use them if you wish; just be warned about this controversy.
_VERSION
field in module tables so that other scripts/modules can easily check for API compatibility.
The top-level directory {foo-version} could contain a directory structure like this:
The official Lua distribution roughly follows this structure too.
For projects building with CMake, note these coding style conventions:
In particular,
One approach for including an automated test suite with your package, which is compatible in many scenarios including LuaDist, is to provide a "test.lua" script that is to be called from the command-line like "lua test.lua". The command should return a 0 exit code if-and-only-if all tests are successful. Note that the Lua interpreter will return a 0 exit code if the given script returns normally, a non-zero exit code if the script raises an uncaught error, or the exit code passed to os.exit
if os.exit
is called. For greater flexibility, such as if the script is not invoked from the command-line, you may want to avoid that latter option of calling os.exit
directly. The "test.lua" may output data to standard output (and/or standard error?) during its run, although the format is presently unspecified. The script may also accept additional command-line parameters to tell it to run different sets of tests (e.g. "lua test.lua extensive" or "lua test.lua minimal"), although if parameters are omitted then the standard automated test suite should be run. The automated test suite must be able to be run unattended, without user input, and complete in a finite time; this permits automated build+test servers to runs the tests. Ideally, test.lua should be able to be run from any directory, as may occur during out-of-source builds, and before installation (i.e. after "make" but before "make install"). If test.lua needs to read files the same source tree, it may examine arg[0] and compute paths relative to it, including prepending or appending to package.path
.
It is possible to have multiple such test scripts. If that is done, it's preferable to also create a main test script (test runner) that calls the other test scripts so that the user can easily run all tests at once via the main test script.
It's not necessary to install the test scripts with the application, although it's not wrong either. The tests could be installed as command-line scripts and/or as Lua modules (loaded via package.path/cpath
).
If using LuaDist, your CMakeLists.txt may call the macro add_lua_test
from dist.cmake
to invoke the "lua test.lua" command in the proper environment. The rockspec format in LuaRocks [3] presently does not have a field for unit tests.
At this time, there are not widely accepted conventions on the output format of the test suite (like Perl's Test Anytime Protocol), nor the implementation. See also UnitTesting for implementations of various test frameworks that may be helpful.
Here's two examples of how you might implement tests:
-- test.lua (example 1) assert(1 + 1 == 2) assert(1 + 2 == 3) print 'OK'
-- test.lua (example 2) package.path = package.path .. ';' .. arg[0]:gsub('[^\\/]+$', '') .. '?.lua' local foo = require 'foo' local pass = true local function check_equal(name, a, b) if a ~= b then io.stdout:write(name, ' ', 'FAIL\n') pass = false else io.stdout:write(name, ' ', 'PASS\n') end end check_equal('first sum', foo.sum(1, 1), 2) check_equal('second sum', foo.sum(1, 2), 3) if not pass then error 'FAILED' end