Index: makefile ================================================================== --- makefile +++ makefile @@ -13,13 +13,27 @@ # # if any are not present, the build will fail, although a missing # `which` can be worked around by specifying the paths to lua, luac, # and `sh` directly # -# eventually you will be able to set a "standalone" variable to -# create a truly standalone binary, by embedding the binary in a -# C program and statically linking it to lua. +# to create a truly standalone cortav binary, build the target +# $(build)/cortav.bin - this will wrap the cortav bytecode in a C +# shim and produce a binary executable. depending on your needs, +# you may want to link lua statically or as a shared library. +# to do this, you'll need to set the variable lua-prefix to the +# location of your lua build. if you have lua installed systemwide, +# this will probably be something like `lua-lib-prefix=/usr/lib`, +# or on Nix `lua-lib-prefix=$(nix path-info lua5_3)/lib`. normally +# however if you're building a standalone binary, you probably don't +# have lua installed systemwide, so you'll want to set this to the +# directory where liblua.a is to be found. +# +# note that the standalone build script assumes you're using a +# relatively standard cc, not something like msvc. if your cc +# doesn't accept options like -l -x -o then you'll have to build +# the binary by hand, sorry. but if you want to contribute a build +# script to the repository, i'll happily take merge requests :) lua != which lua luac != which luac sh != which sh @@ -33,10 +47,24 @@ default-format-flags = -m html:width 40em prefix = $(HOME)/.local bin-prefix = $(prefix)/bin share-prefix = $(prefix)/share/$(executable) + +lua-standalone = $(if $(lua-lib-prefix),$(lua-lib-prefix)/liblua.a,-llua) +lua-bindeps = -lm -ldl + +dbg-flags-luac = $(if $(debug),,-s) +dbg-flags-cc = $(if $(debug),-g,-s) + +# sterilize the operating theatre +export LUA_PATH=./?.lua;./?.lc +export LUA_PATH_5_3=./?.lc;./?.lua +export LUA_PATH_5_4=./?.lc;./?.lua +export LUA_INIT= +export LUA_INIT_5_3= +export LUA_INIT_5_4= # by default, we fetch and parse information about encodings we # support so that cortav can do fancy things like format math # equations by character class (e.g. italicizing variables) # this is not necessary for parsing the format, and can be @@ -44,28 +72,42 @@ # ($ make encoding-data=) encoding-data = ucstbls encoding-files = $(patsubst %,$(build)/%.lc,$(encoding-data)) encoding-data-ucs = https://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt -$(build)/$(executable): sirsem.lua $(encoding-files) cortav.lua $(rendrs) $(extens) cli.lua | $(build)/ +# "standalone" bytecode file that can be run directly as a script +$(build)/$(executable): $(build)/$(executable).ct + echo '#!$(lua)' > $@ + cat $< >>$@ + chmod +x $@ + +# raw bytecode without shebang header, must be run as `lua cortav.lc` +$(build)/$(executable).lc: sirsem.lua $(encoding-files) cortav.lua $(rendrs) $(extens) cli.lua | $(build)/ @echo ' » building with extensions $(extens-names)' @echo ' » building with renderers $(rendrs-names)' - echo '#!$(lua)' > $@ - luac -o - $^ >> $@ - chmod +x $@ + $(luac) $(dbg-flags-luac) -o $@ $^ + +# true standalone binary, wraps bytecode file and (optionally) lua +$(build)/$(executable).bin: $(build)/$(executable).lc + $(lua) tools/makeshim.lua $< |\ + $(CC) -o$@ -xc - -xnone $(lua-standalone) $(lua-bindeps) $(build)/cortav.html: cortav.ct $(build)/$(executable) | $(build)/ $(build)/$(executable) $< -o $@ -m render:format html -y html:fossil-uv .PHONY: syncdoc syncdoc: $(build)/cortav.html fossil uv add $< --as cortav.html fossil uv sync +# clean is written in overly cautious fashion to minimize damage, +# just in case it ever gets invoked in a bad way .PHONY: clean clean: - rm -f $(build)/cortav $(build)/cortav.html $(build)/velartrill-cortav-view.desktop $(build)/cortav-view.sh + rm -f $(build)/*.{html,lc,sh,txt,desktop} \ + $(build)/$(executable){,.bin} + rmdir $(build) $(build)/%.sh: desk/%.sh echo >$@ "#!$(sh)" echo >>$@ 'cortav_exec="$(bin-prefix)/$(executable)"' echo >>$@ 'cortav_flags="$${ct_format_flags-$(default-format-flags)}"' ADDED tools/makeshim.lua Index: tools/makeshim.lua ================================================================== --- tools/makeshim.lua +++ tools/makeshim.lua @@ -0,0 +1,86 @@ +-- [ʞ] tools/makeshim.lua +-- ~ lexi hale +-- 🄯 AGPLv3 +-- ? this program creates a C source file embedding +-- cortav, for the purposes of standalone deployment +-- without a lua interpreter, or for the purposes of +-- giving cortav extra privileges + +local includes = [[ +#include +#include +#include +#include +extern int luaL_openlibs(lua_State* l); +]] + + +local main = [[ +int main(int argc, char** argv) { + lua_State* l = luaL_newstate(); + luaL_openlibs(l); + + // pass arguments thru to lua + lua_newtable(l); + for(size_t i = argc; i < argc; ++i) { + lua_pushstring(l,argv[i]); + lua_rawseti(l, -2, i); + } + lua_setglobal(l, "arg"); + + // load and run our payload + int e = luaL_loadbufferx(l, ct_bytecode, sizeof(ct_bytecode), "cortav", "b"); + if (e != LUA_OK) { + printf("some kind of error idk fam\n"); + return -1; + } + + lua_call(l, 0, 0); + + // normal termination is by the os.exit() call + return -1; +} +]] + +local function setfile(i, dflt, mode) + if arg[i] and arg[i] ~= '' then + local fn = io.open(arg[i], mode) + if fn then + return fn + end + io.stderr:write('(' .. arg[0]..' fatal) cannot open file '..arg[i]) + end + return dflt +end + +local src = setfile(1, io.stdin, "rb") +local dest = setfile(2, io.stdout, "w") + +local cstr = {} +local strtpl = 'static char ct_bytecode [%u] = {%s};' +local lines = {includes} + +local bytes = {} + +local bn = 1 +local len = 0 +while true do + local byte = src:read(1) + if not byte then break end + local str = tostring(byte:byte(1))..',' + -- make sure our source file is parseable by + -- a compliant C compiler + len = len + string.len(str) + if len >= 4096 then + len = 0 + bytes[bn]='\n' + bn = bn + 1 + end + bytes[bn] = str + bn = bn + 1 +end + +table.insert(lines, strtpl:format(#bytes, table.concat(bytes))) +table.insert(lines, main) + +dest:write(table.concat(lines, '\n'))