# 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, your `otype` can be automatically installed by the [LRocket LuaRocks Integration](#luarocks-integration), when a user writes a rockspec with `build.otype` set to the name of your type. To create a custom `otype`, create a new folder containing a file `lrocket/otype/.lua` and generate a [rockspec](https://github.com/luarocks/luarocks/blob/main/docs/luarocks_write_rockspec.md) via luarocks: ```bash > mkdir -p /lrocket/otype > cd > touch lrocket/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/.lua`. It should export a Lua table which implements the following functions: ### Required Functions * [`build (src, output, cfg)`](#function-build-src-output-cfg) ### Optional Functions * [`configure (cfg, userargs)`](#function-configure-cfg-userargs) * [`supportedtargets ()`](#function-supportedtargets) ### Basic Structure ```lua --lr:abi 1.2.0 local = {} -- implementation ... return ``` ### 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 build * `cfg` – 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: ```lua 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: * [The "Executable" `otype`](https://codeberg.org/lrocket/lrc/src/branch/main/lrocket/build/executable.lua) * [The "Lua" `otype`](https://codeberg.org/lrocket/lrc/src/branch/main/lrocket/build/lua.lua) * [The "WebAssembly" `otype` (`lrocket-build-wasm`)](https://codeberg.org/leso-kn/lrocket-build-wasm/src/branch/main/lrocket/build/wasm.lua) A very minimal `build` function including a main C function generator could look like this: **Example** ```lua -- 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 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 builds * `userargs` – the build arguments specified by the user (`lrocket.BuildConf`) e.g. via their rockspec.build config, global settings or `lrc` command-line arguments This optional function can perform initial configuration steps before compilation is performed. **Example** ```lua -- 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](#target-rules). If the function is not implemented, `lrc` will assume target support `any`, which defaults to the host platform. **Example** ```lua 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: ```lua --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-integration): ```bash > luarocks make # install the otype > lrc --otype -o ``` or in a test `.rockspec` file: ```lua ... build = { type = 'lrocket', otype = '', entrypoint = 'test.lua', output = '' } ``` ## Publishing Once you have [tested your `otype`](#testing-the-otype), move on to [choosing a license](https://choosealicense.com/) and follow [Publishing your code online](https://github.com/luarocks/luarocks/blob/main/docs/creating_a_rock.md#publishing-your-code-online) at the LuaRocks documentation.