Writing an otype
An otype describes how a concrete thing is built (e.g. a PC executable, a shared library or an Android application).
Setup
Naming Convention The package name of your rock should start with
lrocket-build-. This way, once published, yourotypecan be automatically installed by the LRocket LuaRocks Integration, when a user writes a rockspec withbuild.otypeset to the name of your type.
To create a custom otype, create a new folder containing a file lrocket/otype/<name>.lua and generate a rockspec via luarocks:
> mkdir -p <lrocket-build-my-otype>/lrocket/otype
> cd <lrocket-build-my-otype>
> touch lrocket/otype/<my-otype>.lua
> luarocks write_rockspec
Your directory should then look like this:
> tree
.
├── lrocket
│ └── otype
│ └── my-otype.lua
└── lrocket-build-my-otype-dev-1.rockspec
2 directories, 2 files
Implementation
The main file of your otype is lrocket/otype/<my-otype>.lua.
It should export a Lua table which implements the following functions:
Required Functions
Optional Functions
Basic Structure
--lr:abi 1.2.0
local <my-otype> = {}
-- implementation
...
return <my-otype>
function build (src, output, cfg)
Parameters
src– a table containing the entrypoint path and further source information (lrocket.Package)output– path to the destination file, that should be generated by this buildcfg– the LRocket compiler configuration (lrocket.FinalConf) containing all settings for this build
This function is required and should implement the main build logic of your otype.
The build should use the source information stored in src to produce the output file output using the configuration cfg.
To achieve this you need to interact with some LRocket interal APIs, likely:
local loader = require 'lrocket.loader'
local routines = require 'lrocket.routines'
local toolchain = require 'lrocket.toolchain'
Since the API interaction could be quite complex to learn step by step, it is a good idea to look at some existing otypes, to see which lrocket.<...> functions they call:
A very minimal build function including a main C function generator could look like this:
Example
-- example otype 'minimal'
local cbin = require 'lrocket.build.executable-c'
local loader = require 'lrocket.loader'
local genc = require 'lrocket.genc'
local routines = require 'lrocket.routines'
local toolchain = require 'lrocket.toolchain'
--lr:abi 1.2.0
local minimal = {}
function minimal.build(src, output, cfg)
-- configure the toolchain based on the settings from `cfg`
local conf = toolchain.compilerconf(cfg.cconf)
local backup = genc.clra_main
-- minimal main C function generator
genc.clra_main = function(fmt)
fmt:multiline [[
#include <lua.h>
int main(int argc, char *argv[]) {
lrocket_pushargs(L, C, argc, argv);
if (lra_loadchunk(L, C, LRA_OPT_DEF_GLOBAL_PRELOAD, "main.lua") != LRA_OK) {
// error message available at: lua_tostring(L, -1);
lua_close(L);
return 1;
}
return 0;
}
]]
end
-- bundle Lua code into a C file
cbin.build(src, 'program.c', cfg)
genc.clra_main = backup
-- link against the lrocket base object
conf:addarg('object', loader.BASE_O)
-- compile the generated C code
conf:addarg('c', 'program.c')
toolchain.compile(conf)
end
return minimal
function configure (cfg, userargs)
optional
Parameters
cfg– the read/write accessible LRocket compiler configuration (lrocket.FinalConf) containing all settings for pending buildsuserargs– the build arguments specified by the user (lrocket.BuildConf) e.g. via their rockspec.build config, global settings orlrccommand-line arguments
This optional function can perform initial configuration steps before compilation is performed.
Example
-- conditionally add '.example' file extension
function minimal.configure(cfg, userargs)
if userargs.entrypoint:match '^example%-' then
cfg.output = cfg.output .. '.example'
end
end
function supportedtargets ()
optional
This optional function should return a list of supported target triples, starting with the default triple.
It can be useful to automatically select a required toolchain.
If the function is not implemented, lrc will assume target support any, which defaults to the host platform.
Example
function minimal.supportedtargets()
return { 'wasm32-wasip1', 'wasm64-wasip1' }
end
ABI specification
When you are writing compiler extensions, you will likely be using parts of the compiler API that are not under semantic versioning.
It is therefor recommended to specify the LRocket ABI version by placing a comment in your file:
--lr:abi 1.2.0
This is technically optional, but lrc will throw a warning if the LRocket ABI is not specified.
Testing the otype
In order to test your newly written otype you can install it locally via luarocks. It will then become available in lrc and the LRocket LuaRocks Integration:
> luarocks make # install the otype
> lrc <test.lua> --otype <my-otype> -o <ouptut.xxx>
or in a test .rockspec file:
...
build = {
type = 'lrocket',
otype = '<my-otype>',
entrypoint = 'test.lua',
output = '<output.xxx>'
}
Publishing
Once you have tested your otype, move on to choosing a license and follow Publishing your code online at the LuaRocks documentation.