A project in-between luv and luvit.
The goal of this is to make building luvit and derivatives much easier.
Luvi has a somewhat unique, but very easy workflow for creating self-contained binaries on systems that don't have a compiler.
# Make a folder
git init myapp
# Write the app
vim myapp/main.lua
# Run the app
LUVI_APP=myapp luvi
# Build the binary when done
LUVI_APP=myapp LUVI_TARGET=mybinary luvi
# Run the new self-contained binary
./mybinary
# Deploy / Publish / Profit!
Your main.lua
is run in a mostly stock luajit environment with a few extra
things added. This means you can use the luajit extensions including
DLUAJIT_ENABLE_LUA52COMPAT
features which we turn on.
The "uv" module contains bindings to libuv as defined in the luv
project. Simply require("uv")
to access it.
Use this for file I/O, network I/O, timers, or various interfaces with the operating system. This lets you write fast non-blocking network servers or frameworks. The APIs in luv mirror what's in libuv allowing you to add whatever API sugar you want on top be it callbacks, coroutines, or whatever.
Just be sure to call uv.run()
and the end of your script to start the
event loop if you want to actually wait for any events to happen.
local uv = require('uv')
local function setTimeout(timeout, callback)
local timer = uv.new_timer()
local function ontimeout()
print("ontimeout", self)
uv.timer_stop(timer)
uv.close(timer)
callback(self)
end
uv.timer_start(timer, timeout, 0, ontimeout)
return timer
end
setTimeout(1000, function ()
print("This happens later")
end)
print("This happens first")
-- This blocks till the timer is done
uv.run()
The raw argc
and argv
from C side is exposed as a zero indexed lua table
of strings at args
.
print("Your arguments were", args)
The "env" module provides read/write access to your local environment variables
via env.keys
, env.get
, env.put
, env.set
, and env.unset
.
local env = require('env')
-- Convert the module to a mutable magic table.
local environment = setmetatable({}, {
__pairs = function (table)
local keys = env.keys()
local index = 0
return function (...)
index = index + 1
local name = keys[index]
if name then
return name, table[name]
end
end
end,
__index = function (table, name)
return env.get(name)
end,
__newindex = function (table, name, value)
if value then
env.set(name, value, 1)
else
env.unset(name)
end
end
}))
If you return an integer from main.lua
it will be your program's exit code.
If you're running from a unzipped folder on disk or a zipped bundle appended to
the binary, the I/O to read from this is the same. This is exposed as the
bundle
property in the "luvi" module.
local bundle = require("luvi").bundle
local files = bundle.readdir("")
Load metadata about a file in the bundle. This includes type
("file" or
"tree"), mtime
(in ms since epoch), and size
(in bytes).
If the file doesn't exist, it returns nil
.
Read a directory. Returns a list of filenames in the directory.
If the directory doesn't exist, it return nil
.
Read the contents of a file. Returns a string if the file exists and nil
if
it doesn't.
There is also a "utils" module that has some useful debugging stuff like a colorized pretty printer.
local uv = require('uv')
local dump = require('utils').dump
-- Create a global p() function that pretty prints any values
-- to stdout using libuv's APIs
_G.p = function (...)
local n = select('#', ...)
local arguments = { ... }
for i = 1, n do
arguments[i] = dump(arguments[i])
end
local toWrite = table.concat(arguments, "\t") .. "\n"
uv.write(stdout, toWrite);
end
We maintain several binary releases of luvi to ease bootstrapping of lit and luvit apps.
The following platforms are supported:
- Windows (amd64)
- FreeBSD 10.1 (amd64)
- Raspberry PI Raspbian (armv6)
- BeagleBone Black Debian (armv7)
- Ubuntu 14.04 (x86_64)
- OSX Yosemite (x86_64)
If you want to not wait for pre-built binaries and dive right in, building is based on CMake and is pretty simple.
First clone this repo recursively.
git clone --recursive [email protected]:luvit/luvi.git
Then run the makefile inside it. (Note this assumes you have cmake in your path.)
If you're on windows, there is a make.bat
file that works mostly like the unix
Makefile
.
cd luvi
make
When that's done you should have a shiny little binary in build/luvi
.
$ ls -lh build/luvi
-rwxr-xr-x 1 tim tim 948K Nov 20 16:39 build/luvi
Run it to see usage information:
$ ./build/luvi
Luvi Usage Instructions:
Bare Luvi uses environment variables to configure its runtime parameters.
LUVI_APP is a colon separated list of paths to folders and/or zip files to
be used as the bundle virtual file system. Items are searched in
the paths from left to right.
LUVI_TARGET is set when you wish to build a new binary instead of running
directly out of the raw folders. Set this in addition to
LUVI_APP and luvi will build a new binary with the vfs embedded
inside as a single zip file at the end of the executable.
Examples:
# Run luvit directly from the filesystem (like a git checkout)
LUVI_APP=luvit/app ./build/luvi
# Run an app that layers on top of luvit
LUVI_APP=myapp:luvit/app ./build/luvi
# Build the luvit binary
LUVI_APP=luvit/app LUVI_TARGET=./luvit ./build/luvi
# Run the new luvit binary
./luvit
# Run an app that layers on top of luvit (note trailing semicolon)
LUVI_APP=myapp; ./luvit
# Build your app
LUVI_APP=myapp; LUVI_TARGET=mybinary ./luvit
You can run the sample repl app by doing:
LUVI_APP=samples/repl.app build/luvi
Ot the test suite with:
LUVI_APP=samples/test.app build/luvi
Luvi also has a feature where you can reuse the same binary bundle for
multiple commands. This is done by reading the value of argv[0]
and looking
for a main in "main/" .. basename(args[0]) .. ".lua"
. before looking in
main.lua
.
To use this you will typically create multiple mains in your bundle, one for
each command you want to support. Then when installing your app/utility,
create a symlink to the main binary in the user's $PATH
but named after each
command.
Here is an example that has two entry points, add
and subtract
mkdir main
vi main/add.lua
vi main/subtract.lua
Then create symlinks somewhere points to luvi (or an old version of your binary)
ln -s luvi add
ln -s luvi subtract
Then when you run theses symlinks, luvi will use the custom mains.
All the previous rules about LUVI_APP and bundled zips in the binary still apply here.
You can use the predefined makefile targets if you want or use cmake directly for more control.
WithOpenSSL (Default: OFF) - Enable OpenSSL Support
WithSharedOpenSSL (Default: ON) - Use System OpenSSL Library
Otherwise use static library
OPENSSL_ROOT_DIR - Override the OpenSSL Root Directory
OPENSSL_INCLUDE_DIR - Override the OpenSSL Include Directory
OPENSSL_LIBRARIES - Override the OpenSSL Library Path
Example (Static OpenSSL):
cmake \
-DWithOpenSSL=ON \
-DWithSharedOpenSSL=OFF \
..
Example (Shared OpenSSL):
cmake \
-DWithSharedOpenSSL=ON \
-DWithOpenSSL=ON \
-DOPENSSL_ROOT_DIR=/usr/local/opt/openssl \
-DOPENSSL_INCLUDE_DIR=/usr/local/opt/openssl/include \
-DOPENSSL_LIBRARIES=/usr/local/opt/openssl/lib \
..