Using LuaRocks to install packages in the current directory
How to configure where LuaRocks installs modules
LuaRocks is the package manager for Lua. When you decide to install a package (or module1) there are a few places it can be installed.
The directory that LuaRocks installs to is called a tree. It’s a specially structured directory that contains all the installed module files, along with metadata about those modules.
A common request I've heard is “I wish LuaRocks worked like npm
and let me
install modules in the current directory.” I'm glad to report it already can!
I've written this guide to clear up any confusion about how and where LuaRocks
installs modules.
There are three ways of choosing where packages get installed:
- Global — the default, system level (eg.
/usr/share/lua/5.1
) - Local — your home directory, with
--local
- Custom — any directory you specify with
--tree some/directory
For general purpose modules, ones that will be available for any Lua script you
execute, the local tree should be used. This tree is located in your home
directory (~/.luarocks
). A global install is not recommended because it
requires root access.
If you're building a project in Lua you'll probably want to more closely control the dependencies and their versions. For this scenario I recommend using a tree that is located in the project’s directory. This is the approach used by other package mangers such as npm.
How Lua finds packages
This section is not specific to LuaRocks, but LuaRocks utilizes this system to provide Lua a way to load the modules it installs.
When executing require("hello.world")
Lua must figure out where the file for
this module is in order to load it. A module can resolve to either Lua source
code, a .lua
file, or a native code library, a .so
on Linux or .dll
on
Windows.
Similar to your operating system’s PATH
environment variable for finding
executables, Lua has a path to find modules. As mentioned above, there are
two types of modules, so Lua has two paths.
You can view (and edit) the paths using package.path
and package.cpath
within a Lua script:
print(package.path) -- where .lua files are searched for
print(package.cpath) -- where native modules are searched for
-- add a new directory to the path
package.path = package.path .. ";/opt/custom/?.lua"
require("hello.zone") -- might load /opt/custom/hello/zone.lua
The initial value of package.path
and package.cpath
comes from the defaults
compiled into your Lua executable, and the special environment variables
LUA_PATH
and LUA_CPATH
.
Setting the path with LuaRocks
LuaRocks has a built in command for setting the Lua path environment variables.
It’s called luarocks path
Running it the command might produce:
$ luarocks path
export LUA_PATH='/home/leafo/.luarocks/share/lua/5.1/?.lua;/home/leafo/.luarocks/share/lua/5.1/?/init.lua;/usr/share/lua/5.1/?.lua;/usr/share/lua/5.1/?/init.lua;;./?.lua;/usr/lib/lua/5.1/?.lua;/usr/lib/lua/5.1/?/init.lua'
export LUA_CPATH='/home/leafo/.luarocks/lib/lua/5.1/?.so;/usr/lib/lua/5.1/?.so;;./?.so;/usr/lib/lua/5.1/loadall.so'
The special syntax
;;
can be used in the environment variable’s value to represent the default value provided by the Lua runtime.
This is a shell script that will set the Lua path environment variables to correctly load modules from the global and local install locations of LuaRocks.
You might want to append this to your .bashrc
like this:
$ luarocks path >> ~/.bashrc
Now you can require()
any modules that you've installed locally or globally
without any additional steps. (After restarting your shell or sourcing the rc
file)
Modules installed globally will work without adjusting the Lua path since they are installed in the default Lua system path.
The install locations
Using a custom directory
Don’t care about the explanation, go to the quick guide.
Installing packages to the current directory is as simple as
luarocks install --tree lua_modules lpeg
This will install the package lpeg
(and any dependencies if necessary) to the
directory lua_modules
in the current directory.
Loading those modules is a bit more complicated.
The structure of the tree, at lua_modules/
, after installing lpeg
looks
like this:
lua_modules/
├── lib
│ ├── lua
│ │ └── 5.1
│ │ └── lpeg.so
│ └── luarocks
│ └── rocks-5.1
│ ├── lpeg
│ │ └── 1.0.0-1
│ │ ├── lpeg-1.0.0-1.rockspec
│ │ └── rock_manifest
│ └── manifest
└── share
└── lua
└── 5.1
└── re.lua
I happen to be using a version of LuaRocks compiled for Lua 5.1 in this example, but this technique will work for any version. Just make sure the paths you create have the correct version
For this example I chose lpeg
because it contains both a .lua
module: re
, and a native .so
module:
lpeg
. From this we can already see how we might structure our Lua path
and
cpath
.
- The
path
would be:lua_modules/share/lua/5.1/?.lua
- The
cpath
would be:lua_modules/lib/lua/5.1/?.so
There’s one more entry we'd like to add to the path. A common idiom is to use
an init.lua
file as the entry point for a package, located in the directory
of that package. We'll amend the path:
- The
path
would be:lua_modules/share/lua/5.1/?.lua;lua_modules/share/lua/5.1/?/init.lua
- The
cpath
would belua_modules/lib/lua/5.1/?.so
;
is used to provide two places to look in the path
As discussed above there are a few ways to enable these paths. From command line you could prepend the environment variables in front of your command:
LUA_PATH='lua_modules/share/lua/5.1/?.lua;lua_modules/share/lua/5.1/?/init.lua;;' LUA_CPATH='lua_modules/lib/lua/5.1/?.so' lua my_script.lua
That’s a mouthful though, so lets use an alternate approach. The lua
command
line executable can take an -l
flag to specify a module to load before
executing the script.
Lets create a new file in the current directory, set_paths.lua
:
-- set_paths.lua
local version = _VERSION:match("%d+%.%d+")
package.path = 'lua_modules/share/lua/' .. version .. '/?.lua;lua_modules/share/lua/' .. version .. '/?/init.lua;' .. package.path
package.cpath = 'lua_modules/lib/lua/' .. version .. '/?.so;' .. package.cpath
Now to run a script with the modules installed in that directory:
lua -l set_paths my_script.lua
This requires that the script be run from the same directory that
set_paths.lua
andlua_modules/
is located
You might be tempted to put require("set_paths")
in your code’s entry point
to avoid the additional argument, but it’s a bad idea.
A module should run on any machine it is installed to regardless of the package
path configuration. The set_paths
script is only to facilitate development
and execution in the current directory.
By configuring paths on script execution, you avoid embedding any assumptions
about the package path in your code. If this additional step is too much to
type, consider a Makefile
that includes some of your common commands.
Quick guide
Want the full explanation? Scroll up.
Installing a package
luarocks install --tree lua_modules lpeg
Running scripts with packages
Create file set_paths.lua
:
-- set_paths.lua
local version = _VERSION:match("%d+%.%d+")
package.path = 'lua_modules/share/lua/' .. version .. '/?.lua;lua_modules/share/lua/' .. version .. '/?/init.lua;' .. package.path
package.cpath = 'lua_modules/lib/lua/' .. version .. '/?.so;' .. package.cpath
Execute scripts:
lua -l set_paths my_script.lua
Running tests with busted:
busted --helper=set_paths
Do not include require("set_paths")
in your code, scroll up to see why.
Local install
Installing packages to the local tree, or the home directory, requires the
--local
flag to be passed to the luarocks install
install command. This is
the recommended way to install packages that are generally available because it
does not require root access to perform the installation.
$ luarocks install --local moonscript
The LUA_PATH
and LUA_CPATH
should be configured. This can be done with the
luarocks path
command as discussed above.
print(require("moonscript"))
Global install
Installing packages in the system tree requires root access. Prefixing the
luarocks
command with sudo
is a common way to install global packages:
$ sudo luarocks install moonscript
Installing globally is not recommended since it requires root access. Use the local tree to install packages and make them available for all your scripts.
1: The distinction isn’t always clear, but a package is synonymous
with a library, and a module is an individual thing (.lua
file, shred
library) you can include from that package. I tend to use them interchangeably.
Related projects
A community LuaRocks hosting repository