Skip to content

Commit

Permalink
Implement a table-like shortcut to rpm macros in Lua
Browse files Browse the repository at this point in the history
Add rpm macro context as a global table-like entity named 'macros' for
a more Lua-native experience with rpm macros.

Support basic indexing syntaxes (macros[k] and macros.k) for access, define
and undefine operations. Undefined macros return nil here and assigning
to nil will undefine (pop) the macro.

As a specialty, parametric macros are returned as native callable
variadic Lua functions. A string argument is passed to expand as is,
but if arguments are passed as a table, eg `r = macros.foo({1, 2, 3})`,
they are passed entirely as-is.

The macro context pointer in the userdata is not consistently used for
all operations here, but then all the macro operations in Lua are hardwired
to the global context anyway so it doesn't matter in practise. Properly
passing the context around in all cases is left for later commits.
  • Loading branch information
pmatilai committed Oct 30, 2020
1 parent 98b71f7 commit 33f9541
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 0 deletions.
102 changes: 102 additions & 0 deletions rpmio/rpmlua.c
Expand Up @@ -1034,6 +1034,104 @@ static const luaL_Reg fd_m[] = {
{NULL, NULL}
};

static rpmMacroContext *checkmc(lua_State *L, int ix)
{
rpmMacroContext *mc = lua_touserdata(L, ix);
luaL_checkudata(L, ix, "rpm.mc");
return mc;
}

static int mc_call(lua_State *L)
{
rpmMacroContext *mc = checkmc(L, lua_upvalueindex(1));
const char *name = lua_tostring(L, lua_upvalueindex(2));
int rc = 0;

if (lua_gettop(L) > 1)
luaL_error(L, "too many arguments");

if (lua_isstring(L, 1)) {
lua_pushfstring(L, "%%{%s %s}", name, lua_tostring(L, 1));
/* throw out previous args and call expand() with our result string */
lua_rotate(L, 1, 1);
lua_settop(L, 1);
rc = rpm_expand(L);
} else if (lua_istable(L, 1)) {
ARGV_t argv = NULL;
char *buf = NULL;
int nitem = lua_rawlen(L, 1);

for (int i = 1; i <= nitem; i++) {
lua_rawgeti(L, 1, i);
argvAdd(&argv, lua_tostring(L, -1));
lua_pop(L, 1);
}

if (rpmExpandThisMacro(*mc, name, argv, &buf, 0) >= 0) {
rc = 1;
lua_pushstring(L, buf);
free(buf);
}
argvFree(argv);
} else {
luaL_argerror(L, 1, "string or table expected");
}

return rc;
}

static int mc_index(lua_State *L)
{
rpmMacroContext *mc = checkmc(L, 1);
const char *a = luaL_checkstring(L, 2);
int rc = 0;

if (rpmMacroIsDefined(*mc, a)) {
if (rpmMacroIsParametric(*mc, a)) {;
/* closure with the macro context and the name */
lua_pushcclosure(L, &mc_call, 2);
rc = 1;
} else {
lua_pushfstring(L, "%%{%s}", a);
lua_rotate(L, 1, 1);
lua_settop(L, 1);
rc = rpm_expand(L);
}
}
return rc;
}

static int mc_newindex(lua_State *L)
{
rpmMacroContext *mc = checkmc(L, 1);
const char *name = luaL_checkstring(L, 2);
if (lua_isnil(L, 3)) {
if (rpmPopMacro(*mc, name))
luaL_error(L, "error undefining macro %s", name);
} else {
const char *body = luaL_checkstring(L, 3);
char *s = rstrscat(NULL, name, " ", body, NULL);
if (rpmDefineMacro(*mc, s, 0))
luaL_error(L, "error defining macro %s", name);
free(s);
}
return 0;
}

static const luaL_Reg mc_m[] = {
{"__index", mc_index},
{"__newindex", mc_newindex},
{NULL, NULL}
};

static void createmt(lua_State *L, const char *name, rpmMacroContext mc)
{
lua_pushglobaltable(L);
newinstance(L, "rpm.mc", mc);
lua_setfield(L, -2, name);
lua_pop(L, 1);
}

static const luaL_Reg rpmlib[] = {
{"b64encode", rpm_b64encode},
{"b64decode", rpm_b64decode},
Expand All @@ -1059,6 +1157,10 @@ static int luaopen_rpm(lua_State *L)
{
createclass(L, "rpm.ver", ver_m);
createclass(L, "rpm.fd", fd_m);
createclass(L, "rpm.mc", mc_m);

createmt(L, "macros", rpmGlobalMacroContext);

luaL_newlib(L, rpmlib);
return 1;
}
44 changes: 44 additions & 0 deletions tests/rpmmacro.at
Expand Up @@ -551,6 +551,50 @@ runroot rpm \
])
AT_CLEANUP

AT_SETUP([lua macros table])
AT_KEYWORDS([macros lua])
AT_SKIP_IF([$LUA_DISABLED])
AT_CHECK([[
runroot rpm \
--define "qtst() %{lua:for i=1, #arg do print(' '..i..':'..arg[i]) end}"\
--eval "%{lua:print(macros.with('zap'), macros.without('zap'))}" \
--eval "%{lua:print(macros.aaa)}" \
--eval "%{lua:macros.aaa='bbb'; macros['yyy']='zzz'}" \
--eval "%{lua:print(macros['aaa'], macros.yyy)}" \
--eval "%{lua:macros.aaa=nil"} \
--eval "%{lua:print(macros.aaa)}" \
--eval "%{lua:print(macros.qtst('a c'))}" \
--eval "%{lua:print(macros.qtst({'a', '', 'c'}))}" \
--eval "%{lua:print(macros.qtst('%{?aaa} %{yyy}'))}" \
--eval "%{lua:print(macros.qtst({'%{?aaa}', '%{yyy}'}))}"
]],
[0],
[0 1
nil

bbb zzz

nil
1:a 2:c
1:a 2: 3:c
1:zzz
1:%{?aaa} 2:%{yyy}
])
AT_CLEANUP

AT_SETUP([lua macros recursion])
AT_KEYWORDS([macros lua])
AT_SKIP_IF([$LUA_DISABLED])
AT_CHECK([[
runroot rpm \
--define "%recurse() %{lua:io.write(' '..#arg); if #arg < 16 then table.insert(arg, #arg); macros.recurse(arg) end;}" \
--eval "%recurse"
]],
[0],
[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
])
AT_CLEANUP

AT_SETUP([lua rpm extensions 1])
AT_KEYWORDS([macros lua])
AT_CHECK([
Expand Down

0 comments on commit 33f9541

Please sign in to comment.