Because Fennel can be used in so many different contexts, it can be difficult to know how to prepare your code so it can be used by others. Here is a selection of different methods and tips for how to work with them:
Running from source
For certain kinds of programs, you don't need any fancy distribution
mechanism beyond simply running from a source checkout. In this case
it's best to simply include a copy of the standalone fennel Lua
script (from running make fennel in the Fennel codebase) in your
repository.
For instance, the pengbot IRC bot can be run just by
executing make run in a source checkout:
run:
./fennel pengbot.fnl $(SERVER) $(PORT) $(NICK) $(CHANNEL)
Simple Scripts
If you have a standalone Fennel script that doesn't use any C
libraries, the easiest way to distribute it for others to use is to
compile it to Lua and put a #!/usr/bin/env lua shebang at the top of
the file output. This will run on any system that already has Lua
installed.
$ echo "#!/usr/bin/env lua" > myprogram
$ fennel --require-as-include --compile myprogram.fnl >> myprogram
$ chmod 755 myprogram
The --require-as-include flag will make it so your compiled program
includes any files required directly. It will not include transitive
requires from Lua, so you may need to directly require some of those
modules which you wouldn't otherwise need.
Binary executable
If you want to distribute something that works on systems that don't
already have Lua, you can compile a binary that includes Lua along
with your program using the --compile-binary flag. You will have to
compile a separate binary for every OS+architecture you want to
support, but this can be completely standalone.
You can include a Makefile rule which fetches and compiles the version
of Lua you want to use, and cross-compiles it by setting CC:
LUA_VERSION=5.4.2
LUA_DIR=lua-$(LUA_VERSION)
$(LUA_DIR): ; curl http://www.lua.org/ftp/lua-$(LUA_VERSION).tar.gz | tar xzf /dev/stdin
$(LUA_DIR)/src/liblua.a: $(LUA_DIR) ; make -C $^
myprogram: myprogram.fnl $(LUA_DIR)/src/liblua.a
./fennel --compile-binary $< $@ lua-$(LUA_VERSION)/src/liblua.a $(LUA_DIR)/src
myprogram.exe: myprogram.fnl $(LUA_DIR)/src/liblua-mingw.a
CC=i686-w64-mingw32-gcc ./fennel --compile-binary $< myprogram \
$(LUA_DIR)/src/liblua-mingw.a $(LUA_DIR)/src
$(LUA_DIR)/src/liblua-mingw.a: $(LUA_DIR)
$(MAKE) -C $(LUA_DIR)/src CC=i686-w64-mingw32-gcc \
"AR=i686-w64-mingw32-gcc -shared -o" \
"RANLIB=i686-w64-mingw32-strip --strip-unneeded" clean liblua.a
mv $(LUA_DIR)/src/liblua.a $@
On a Debian-based system, the gcc-mingw-w64-i686 package must be
installed to cross-compile Windows binaries; other OSes may provide it
under a different name.
The --compile-binary method can also be used to distribute standalone
programs that use C libraries, but it's more complicated and doesn't
always work with all libraries; see the HTTP page for details.
LÖVE (love2d)
One of the most popular uses of Fennel is to create games with the LÖVE framework. The min-love2d-fennel project skeleton provides everything you need to get started.
The included Makefile can create executables for Windows, Mac OS, and
Linux-based systems as well as .love files which are portable to any
system if you install the LÖVE framework manually. It also supports
automating publishing to itch.io, the fabulous game hosting
web site.
In this case, all the Fennel code is loaded from .fnl files during
development, but then precompiled into .lua code for
distribution. This allows a nice tight feedback loop during
development but doesn't involve the compiler unnecessarily later on.
Debian / apt
Making a command-line tool you can distribute using an unofficial .deb file is fairly easy, and making a service isn't much more difficult. The first step is ensuring you have a Makefile with an install task that checks DESTDIR, PREFIX, and BIN_DIR:
DESTDIR ?=
PREFIX ?= /usr/local
BIN_DIR ?= $(PREFIX)/bin
install: myscript
mkdir -p $(DESTDIR)$(BIN_DIR) && cp $< $(DESTDIR)$(BIN_DIR)/
From there, for a command-line tool, take a look at the debian/ directory of fnlfmt to see the handful of packaging scripts needed. Making a debian-policy-compliant package is difficult, but making an unofficial one is pretty easy!
Once those scripts are in place, install gbp and run gbp buildpackage. Note that this will create a bunch of packaging files in the parent of the current directory so you probably don't want to do this if your checkout is in ~/src/myscript or something; move it to a packaging subdirectory.
Packaging a service isn't much harder, but you'll need a systemd service unit; see the one from yotosocial for an example. Add install -m 644 myservice.service $(DESTDIR)/etc/systemd/system/ to the install target of your makefile, and it will get set up when your package is installed; you just have to run sudo systemctl enable --now myservice.service to get it running.
If you have a static web server, it's also pretty easy to turn your .deb file into a source that apt can read from using reprepro.
Luarocks
TODO: someone should write this who uses luarocks!