Using LuaRocks with LÖVE
Installing Lua packages in your LÖVE projects with LuaRocks will really level up your game development experience. The initial setup is a little tricky but well worth the effort to have proper package management for your project.
Windows users, you will probably need to install a terminal emulator, like GitBash or Cygwin, in order to run the commands below.
What is LuaRocks?
LuaRocks is a package manager for Lua. You can use LuaRocks to install packages/libraries (LuaRocks calls them "rocks") and use them in your project.
LuaRocks can install both Lua and C rocks. We'll be installing both in an example project, but keep in mind that the C rocks will be compiled for your platform, so they won't work on other platforms. You'll probably want to stick to Lua rocks if you're building cross-platform games.
It's certainly possible to build and distribute cross-platform LÖVE projects with compiled C dependencies, but we won't be covering that here.
Install LuaRocks
Check to see if LuaRocks is already installed by running this version check:
$ luarocks --version
[...]/lua/5.1.5/luarocks/bin/luarocks 3.9.2
LuaRocks main command-line interface
If you see output similar to mine, then you're probably ready to go. Otherwise, check out the LuaRocks Quick Start for installation instructions.
MacOS users, there is also a Homebrew formula available:
brew install luarocks
Create a New Project
We'll be building a new project in this example, but you should be able to adapt these steps to add LuaRocks to an existing project.
First, create a new directory for the project and cd
into it:
mkdir example && cd example
Run the LuaRocks init
command to initialize the project:
$ luarocks init --lua-versions 5.1
Initializing project 'example' for Lua 5.1 ...
----------------------------------------------
Checking your Lua installation ...
Wrote template at [...]/example/example-dev-1.rockspec -- you should now edit
and finish it.
Adding entries to .gitignore ...
Preparing ./.luarocks/ ...
Wrote .luarocks/config-5.1.lua
Preparing ./lua_modules/ ...
Preparing ./luarocks ...
Preparing ./lua for version 5.1...
We specified that the project is compatible with Lua 5.1, because under the covers LÖVE uses LuaJIT, which is compatible with Lua 5.1. We're assuming a standard LÖVE distribution here.
You should see the following files and directories:
.
├── .gitignore
├── .luarocks
├── lua
├── lua_modules
├── luarocks
└── example-dev-1.rockspec
.gitignore
contains patterns forgit
to ignore. LuaRocks added patterns to this file for auto-generated files and installed rocks. These files should not be checked into version control..luarocks/
contains configuration for LuaRocks itself.lua
andluarocks
are wrapper scripts that invoke the specific instances of Lua and LuaRocks that were used to initialize the project. These instances may be different from other instances of Lua and LuaRocks that may be installed on your system. It's best practice to use these scripts when running Lua and LuaRocks commands within the project.lua_modules/
contains the rocks installed by LuaRocks.example-dev-1.rockspec
is the project's rockspec - a file containing metadata about the project that's used by LuaRocks. This file is where we'll define the rocks that our project requires.
When you check out a project that uses LuaRocks like this for the first time, you need to run
luarocks init
to initialize the project and generate many of the files and directories above.
Add Some Source Code
In this example, we'll be storing our source code in a src/
directory.
Every LÖVE project needs a main.lua
file, the main entry point of the
application. We'll actually have two main.lua
files: a main.lua
file and a
src/main.lua
file.
main.lua
will configure some require paths so that we canrequire
modules installed by LuaRocks. Then, it willrequire
thesrc/main.lua
file.src/main.lua
will look more-or-less like a typical LÖVEmain.lua
file.
Create a main.lua
file with the following contents:
LUA_VERSION = "5.1"
love.filesystem.setRequirePath(table.concat({
love.filesystem.getRequirePath(),
table.concat({ ";lua_modules/share/lua/", LUA_VERSION, "/?.lua" }),
table.concat({ ";lua_modules/share/lua/", LUA_VERSION, "/?/init.lua" }),
";src/?.lua",
}))
love.filesystem.setCRequirePath(table.concat({
love.filesystem.getCRequirePath(),
table.concat({ ";lua_modules/lib/lua/", LUA_VERSION, "/??" }),
";lib/??",
}))
require("src.main")
We need to tell LÖVE how to load packages installed by LuaRocks - both Lua
packages and C packages. We do this by modifying LÖVE's require paths using the
love.filesystem.setRequirePath()
and love.filesystem.setCRequirePath()
functions. Paths are separated by semicolons ;
.
After that, we require src/main.lua
. Modules from src/main.lua
onward will
then be able to require
modules in packages we install with LuaRocks.
We added the following Lua require path patterns:
lua_modules/share/lua/5.1/?.lua
allows modules torequire
modules in packages installed by LuaRocks.lua_modules/share/lua/5.1/?/init.lua
allows modules torequire
packages installed by LuaRocks. As per Lua convention, requiring a package itself should behave as if the package'sinit.lua
module was required.src/?.lua
allows modules torequire
modules defined in thesrc/
directory without prefixing them withsrc.
. For example,require "foobar"
requiressrc/foobar.lua
unless a path pattern earlier in the list is matched. You can also fully qualify the module if there's a conflict, i.e.require "src.foobar"
.
Note:
?
in the path patterns above will be replaced with the name/path of the module. So,foo.bar
inrequire "foo.bar"
would becomefoo/bar
.
We also added the following C require path patterns:
lua_modules/lib/lua/5.1/??
allows modules torequire
C packages compiled and installed by LuaRocks.lib/??
allows modules torequire
code that we've compiled and stored in thelib/
directory. We won't be using this here, but feel free to experiment with it if you know what you're doing.
Note:
??
in the path patterns above will be replaced with both the name/path of the package as well as the platform-specific extension (usually.so
or.dll
).
Create a src/main.lua
file with the following contents:
local json = require "dkjson"
local cjson = require "cjson"
function love.draw()
love.graphics.print(json.encode({ hello = "World" }), 20, 20)
love.graphics.print(cjson.encode({ hello = "C World" }), 20, 40)
end
We require
two packages that we're about to install with LuaRocks. Both of
these packages allow you to encode tables into JSON strings and decode JSON
strings into tables. dkjson
is written in pure Lua, while lua-cjson
is
written mostly in C.
After that, we print some tables to the screen that we've encoded into JSON strings.
Install the Rocks
Open example-dev1.rockspec
and replace the dependencies
table with the
following:
-- ...
dependencies = {
"lua ~> 5.1",
"dkjson ~> 2.7",
"lua-cjson ~> 2.1"
}
-- ...
Now install the dependencies from the rockspec with LuaRocks:
$ ./luarocks install --only-deps example-dev-1.rockspec
Missing dependencies for example dev-1:
dkjson ~> 2.7 (not installed)
lua-cjson ~> 2.1 (not installed)
example dev-1 depends on lua ~> 5.1 (5.1-1 provided by VM)
example dev-1 depends on dkjson ~> 2.7 (not installed)
Installing https://luarocks.org/dkjson-2.7-1.src.rock
dkjson 2.7-1 depends on lua >= 5.1, < 5.5 (5.1-1 provided by VM)
No existing manifest. Attempting to rebuild...
dkjson 2.7-1 is now installed in [...]/example/lua_modules (license: MIT/X11)
example dev-1 depends on lua-cjson ~> 2.1 (not installed)
Installing https://luarocks.org/lua-cjson-2.1.0.10-1.src.rock
lua-cjson 2.1.0.10-1 depends on lua >= 5.1 (5.1-1 provided by VM)
env MACOSX_DEPLOYMENT_TARGET=11.0 gcc -O2 -fPIC -I[...]/lua/5.1.5/include -c lua_cjson.c -o lua_cjson.o
env MACOSX_DEPLOYMENT_TARGET=11.0 gcc -O2 -fPIC -I[...]/lua/5.1.5/include -c strbuf.c -o strbuf.o
env MACOSX_DEPLOYMENT_TARGET=11.0 gcc -O2 -fPIC -I[...]/lua/5.1.5/include -c fpconv.c -o fpconv.o
env MACOSX_DEPLOYMENT_TARGET=11.0 gcc -bundle -undefined dynamic_lookup -all_load -o cjson.so lua_cjson.o strbuf.o fpconv.o
lua-cjson 2.1.0.10-1 is now installed in [...]/example/lua_modules (license: MIT)
Stopping after installing dependencies for example dev-1
The dependencies should now be installed under the lua_modules/
directory.
Run the Project with LÖVE
You can run this project like a normal LÖVE project.
See the LÖVE wiki Getting Started page for more information.
For example, on MacOS with a
love.app
file in your home directory, execute the following command from the project directory to run the application:~/love.app/Contents/MacOS/love .
Here's what you should see:
Distribution
Distributing LÖVE projects can be complicated.
See LÖVE wiki Game Distribution page for more information.
With respect to LuaRocks, the important thing to remember is to include
main.lua
with the changes to the require paths and lua_modules/
with the
installed rocks in the distribution. For this application, we also need to
include the src/
directory with our application's source code.
Again, remember that the distribution will contain code compiled specifically for your platform, so the distribution will not work on other platforms.
We can create a simple distribution by zipping up our files and giving the zip a
.love
extension instead of a .zip
extension:
zip -r example.love main.lua src lua_modules
You should be able to run example.love
with LÖVE.
For example, on MacOS with a
love.app
file in your home directory:~/love.app/Contents/MacOS/love example.love
Conclusion
LuaRocks makes it easy to add useful libraries to your LÖVE projects. Take a
look at the love
label in LuaRocks for libraries that may already be using.
And please do contribute new libraries of your own!
LuaRocks can do a bit more than just installing rocks and managing your project's dependencies - things like build automation and executing tests. Take a look through the LuaRocks Documentation to learn more.
The complete source code for the example project that we built in this entry can be found here: complete example project.
Until next time!