Implement a table-like shortcut to rpm macros in Lua
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.
This commit is contained in:
parent
c072ef6bb8
commit
986be669fb
|
@ -65,6 +65,19 @@ end
|
|||
}
|
||||
```
|
||||
|
||||
Macros can be accessed via a global `macros` table in the Lua environment.
|
||||
Lua makes no difference between index and field name syntax so
|
||||
`macros.foo` and `macros['foo']` are equivalent, use what better suits the
|
||||
purpose. Like any real Lua table, non-existent items are returned as `nil`,
|
||||
and assignment can be used to define or undefine macros.
|
||||
|
||||
Parametric macros (including all built-in macros) can be called in a Lua
|
||||
native manner via the `macros` table. The argument can be either a
|
||||
single string (`macros.with('thing')`), in which case it's expanded
|
||||
and split with the macro-native rules, or it can be a table
|
||||
`macros.dostuff({'one', 'two', 'three'})` in which case the table contents
|
||||
are used as literal arguments that are not expanded in any way.
|
||||
|
||||
## Available Lua extensions in RPM
|
||||
|
||||
In addition to all Lua standard libraries (subject to the Lua version rpm is linked to), a few custom extensions are available in the RPM internal Lua interpreter. These can be used in all contexts where the internal Lua can be used.
|
||||
|
|
102
rpmio/rpmlua.c
102
rpmio/rpmlua.c
|
@ -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},
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -593,6 +593,52 @@ 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}'}))}" \
|
||||
--eval "%{lua:macros.define({'this', 'that'}); print(macros.this)}"
|
||||
]],
|
||||
[0],
|
||||
[0 1
|
||||
nil
|
||||
|
||||
bbb zzz
|
||||
|
||||
nil
|
||||
1:a 2:c
|
||||
1:a 2: 3:c
|
||||
1:zzz
|
||||
1:%{?aaa} 2:%{yyy}
|
||||
that
|
||||
])
|
||||
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([
|
||||
|
|
Loading…
Reference in New Issue