[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: script for converting a Lua module to a C module
- From: Norman Ramsey <nr@...>
- Date: Sat, 29 Nov 2008 21:20:15 -0500
For a class I'm teaching, students know C and not Lua.
I wanted to write some code in Lua without having them
worry about what Lua was, where their path was set, and
so on. So I just wrote a short Lua script to take a
Lua file, turn either the source code or a compiled binary
into a C program, and write the C wrapper function that
loads it into C. So for example, if you have module
uname.lua, you can turn it into a shared library uname.so
as follows:
lua2c uname.lua > luname.c
gcc -I/usr/include/lua5.1 -fPIC -shared \
-Wl,-soname,uname.so -o uname.so luname.c
Or in my case you can turn it into an ordinary .o file
and link it in with a .a file. So I can hand my students
a libmumble.a and they don't even know they're using Lua.
Plus I know the code is always the code that is compiled in---
there is never any worry about paths or versions.
Luiz encouraged me to post the code. I'm embarrassed to
have anyone else see it, since there are all sorts of gross
Unixy OS hacks in there, but I have patched it only to
the point where it doesn't use 'require', so others
might be able to use it. Lots of ugliness packed in
a mere 110 lines!
If somebody feels like cleaning it up and putting it on the
Lua Users' Wiki, we'll all be in your debt. The worst thing
in there now is a call to the Unix function 'tempfile', which
is a Debianism. The right thing would be to try mktemp if
that fails, and so on.
Norman
P.S. This script highlights the property that compiled Lua
binaries are *not* portable across platforms! I made
compilation the default because I didn't want people snooping
around with strings(1) or mucking with the source code...
#!/usr/bin/env lua
----- lua 5.1 script to convert a lua file into a C file
-----
----- Usage: lua2c foo.lua > lfoo.c # compiled, so not portable
----- lua2c -s foo.lua > lfoo.c # source, and so portable
---------------- general utility functions -------------
function io.contents(filename)
local f, msg = io.open(filename, 'r')
if not f then return f, msg end
local s = assert(f:read '*a')
f:close()
return s
end
function os.capture(cmd, raw)
assert(io.popen)
local f = assert(io.popen(cmd, 'r'))
local s = assert(f:read('*a'))
f:close()
if raw then return s end
s = string.gsub(s, '^%s+', '')
s = string.gsub(s, '%s+$', '')
s = string.gsub(s, '[\n\r]+', ' ')
return s
end
local quote_me = '[^%w%+%-%=%@%_%/]'
-- easier to complement what doesn't need quotes
local strfind = string.find
function os.quote(s)
if strfind(s, quote_me) or s == '' then
return "'" .. string.gsub(s, "'", [['"'"']]) .. "'"
else
return s
end
end
function os.runf(...) return os.execute(string.format(...)) end
local function printf(...) return outfile:write(string.format(...)) end
-------------------------------------------------------
local lua -- program to be loaded (source or binary)
if arg[1] == '-a' or arg[1] == '-s' then -- don't compile;
-- use ascii source
table.remove(arg, 1)
assert(#arg == 1)
lua = io.contents(arg[1]):gsub('^#!.-\n', '')
else
assert(#arg == 1)
local bin = os.capture 'tempfile'
assert(os.runf('luac -o %s %s', os.quote(bin), os.quote(arg[1])) == 0)
lua = io.contents(bin)
os.remove(bin)
end
local libname = arg[1]:gsub('.*/', ''):gsub('%.lua$', '')
outfile = io.stdout
------------------------
outfile:write [[
#include <lua.h>
#include <lauxlib.h>
static unsigned char program[] = {
]]
outfile:write ' '
local last = lua:len()
for i = 1, last do
local n = string.byte(lua, i)
printf('%3d%s', n, i < last and ', ' or '')
if i % 10 == 0 then printf '\n ' end
end
printf '\n};\n\n'
outfile:write((([[
int luaload_$lib(lua_State *L) {
return luaL_loadbuffer(L, (const char*)program, sizeof(program), "@$source");
}
int luaopen_$lib(lua_State *L) {
if (luaL_loadbuffer(L, (const char*)program, sizeof(program), "@$source"))
return luaL_error(L, "Internal library '%s' failed to parse", "$lib");
if (lua_pcall(L, 0, 1, 0))
return luaL_error(L, "Internal library '%s' failed to run: %s", "$lib",
lua_tostring(L, -1));
if (lua_isnil(L, -1)) {
lua_pop(L, 1);
lua_pushboolean(L, 1);
}
lua_getglobal(L, "package"); // s: lib package
lua_getfield(L, -1, "loaded"); // s: lib package loaded
lua_remove(L, -2); // s: lib loaded
lua_pushstring(L, "$lib"); // s: lib loaded libname
lua_pushvalue(L, -3); // s: lib loaded libname lib
lua_settable(L, -3); // s: lib loaded
lua_pop(L, 1); // s: lib
return 1;
}
]]):gsub('$(%a+)', { lib = libname, source = arg[1]:gsub('.*/', '') })))