Lua Packaging Guide

lua-users home
wiki

So, you've written some Lua code that you want to share with the world, but will the world actually use your code and contribute back to it? Or will they run away in frustration, shrug in indifference, or tell you a few years from now that they wish they were aware your code even existed before they had reinvented it themselves. Below are some suggestions for improving the quality of code you distribute to make a positive outcome more likely.

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.

-- 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.

Note: The 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.

File structure

The top-level directory {foo-version} could contain a directory structure like this:

The official Lua distribution roughly follows this structure too.

CMake Conventions

For projects building with CMake, note these coding style conventions:

In particular,

Testing Conventions

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

RecentChanges · preferences
edit · history
Last edited March 19, 2011 6:22 pm GMT (diff)