1
0
mirror of https://github.com/openSUSE/libsolv.git synced 2026-02-05 12:45:46 +01:00

Add lua bindings

This commit is contained in:
Michael Schroeder
2024-03-25 12:38:34 +01:00
parent e87ef6038a
commit 0d6e498aa0
5 changed files with 923 additions and 0 deletions

View File

@@ -9,6 +9,7 @@ OPTION (ENABLE_PERL "Build the perl bindings?" OFF)
OPTION (ENABLE_PYTHON "Build the python bindings?" OFF)
OPTION (ENABLE_RUBY "Build the ruby bindings?" OFF)
OPTION (ENABLE_TCL "Build the Tcl bindings?" OFF)
OPTION (ENABLE_LUA "Build the lua bindings?" OFF)
OPTION (USE_VENDORDIRS "Install the bindings in vendor directories?" OFF)
@@ -326,6 +327,7 @@ SET (PACKAGE "libsolv")
SET (VERSION "${LIBSOLV_MAJOR}.${LIBSOLV_MINOR}.${LIBSOLV_PATCH}")
ADD_DEFINITIONS (-D_FILE_OFFSET_BITS=64)
#ADD_DEFINITIONS (-D_TIME_BITS=64)
CONFIGURE_FILE (src/solvversion.h.in src/solvversion.h)
SET (CPACK_PACKAGE_DESCRIPTION_SUMMARY "Package dependency solver library")

View File

@@ -20,3 +20,6 @@ ENDIF (ENABLE_RUBY)
IF (ENABLE_TCL)
ADD_SUBDIRECTORY (tcl)
ENDIF (ENABLE_TCL)
IF (ENABLE_LUA)
ADD_SUBDIRECTORY (lua)
ENDIF (ENABLE_LUA)

View File

@@ -0,0 +1,21 @@
FIND_PACKAGE (Lua)
ADD_CUSTOM_COMMAND (
OUTPUT solv_lua.c
COMMAND ${SWIG_EXECUTABLE} ${SWIG_FLAGS} -lua -I${CMAKE_SOURCE_DIR}/src -o solv_lua.c ${CMAKE_SOURCE_DIR}/bindings/solv.i
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS ${CMAKE_SOURCE_DIR}/bindings/solv.i
)
INCLUDE_DIRECTORIES (${LUA_INCLUDE_DIR})
IF (NOT LUA_INSTALL_DIR)
SET(LUA_INSTALL_DIR ${CMAKE_INSTALL_FULL_LIBDIR}/lua/${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR})
ENDIF (NOT LUA_INSTALL_DIR)
MESSAGE (STATUS "Lua installation dir: ${LUA_INSTALL_DIR}")
ADD_LIBRARY (bindings_lua SHARED solv_lua.c)
SET_TARGET_PROPERTIES (bindings_lua PROPERTIES PREFIX "" OUTPUT_NAME "solv" INSTALL_NAME_DIR "${LUA_INSTALL_DIR}")
TARGET_LINK_LIBRARIES (bindings_lua libsolvext libsolv ${LUA_LIBRARY} ${SYSTEM_LIBRARIES})
INSTALL (TARGETS bindings_lua LIBRARY DESTINATION ${LUA_INSTALL_DIR})

View File

@@ -16,6 +16,54 @@
%}
#endif
/**
** lua object stashing
**/
#if defined(SWIGLUA)
%{
SWIGINTERN
void prep_stashed_lua_var(lua_State* L, char *name, void *ptr)
{
lua_getglobal(L, "solv");
if (lua_getfield(L, -1, "_stash") == LUA_TNIL) {
lua_pop(L, 1);
lua_newtable(L);
lua_pushvalue(L, -1);
lua_setfield(L, -3, "_stash");
}
lua_remove(L, -2);
lua_pushfstring(L, "%s:%p", name, ptr);
}
SWIGINTERN
void set_stashed_lua_var(lua_State* L, int idx, char *name, void *ptr)
{
lua_pushvalue(L, idx);
prep_stashed_lua_var(L, name, ptr);
lua_pushvalue(L, -3);
lua_settable(L, -3);
lua_pop(L, 2);
}
SWIGINTERN
void get_stashed_lua_var(lua_State* L, char *name, void *ptr)
{
prep_stashed_lua_var(L, name, ptr);
lua_gettable(L, -2);
lua_remove(L, -2);
}
SWIGINTERN
void clr_stashed_lua_var(lua_State* L, char *name, void *ptr)
{
prep_stashed_lua_var(L, name, ptr);
lua_pushnil(L);
lua_settable(L, -3);
lua_pop(L, 1);
}
%}
#endif
/**
** binaryblob handling
**/
@@ -27,6 +75,24 @@ typedef struct {
} BinaryBlob;
%}
#if defined(SWIGLUA)
%typemap(in,noblock=1) (const unsigned char *str, size_t len) (char *buf = 0, size_t size = 0) {
if (!lua_isstring(L, $input)) SWIG_fail_arg($symname, $input, "const char *");
buf = (char *)lua_tolstring(L, $input, &size);
$1 = (unsigned char *)buf;
$2 = size;
}
%typemap(out,noblock=1) BinaryBlob {
if ($1.data) {
lua_pushlstring(L, $1.data, $1.len);
} else {
lua_pushnil(L);
}
SWIG_arg++;
}
#else
%typemap(in,noblock=1,fragment="SWIG_AsCharPtrAndSize") (const unsigned char *str, size_t len) (int res, char *buf = 0, size_t size = 0, int alloc = 0) {
#if defined(SWIGTCL)
{
@@ -86,6 +152,8 @@ typedef struct {
#endif
}
#endif
/**
** Queue handling
**/
@@ -389,6 +457,101 @@ typedef struct {
#endif /* SWIGTCL */
#if defined(SWIGLUA)
%typemap(out) Queue {
int i;
lua_newtable(L);
for (i = 0; i < $1.count; i++) {
lua_pushnumber(L, $1.elements[i]);
lua_rawseti(L, -2, i + 1);
}
queue_free(&$1);
SWIG_arg = 1;
}
%define Queue2Array(type, step, con) %{
int i;
int cnt = $1.count / step;
Id *idp = $1.elements;
lua_newtable(L);
for (i = 0; i < cnt; i++, idp += step)
{
Id id = *idp;
#define result resultx
type result = con;
$typemap(out, type)
lua_rawseti(L, -2, i+1);
#undef result
}
queue_free(&$1);
SWIG_arg = 1;
%}
%enddef
%define Array2Queue(asval_meth,typestr) %{ {
int i;
luaL_checktype(L, -1, LUA_TTABLE);
for (i = 1; i; i++) {
lua_rawgeti(L, -1, i);
if (lua_type(L, -1) == LUA_TNIL)
i = -1;
else
{
int v;
int e = asval_meth(L, -1, &v);
if (!SWIG_IsOK(e)) {
lua_pop(L, 1);
SWIG_Lua_pusherrstring(L,"list in argument $argnum must contain only " typestr);
SWIG_fail;
}
queue_push(&$1, v);
}
lua_pop(L, 1);
}
}
%}
%enddef
%define ObjArray2Queue(type, obj2queue) %{ {
int i;
luaL_checktype(L, -1, LUA_TTABLE);
for (i = 1; i; i++) {
lua_rawgeti(L, -1, i);
if (lua_type(L, -1) == LUA_TNIL)
i = -1;
else
{
type obj;
int e = SWIG_ConvertPtr(L, -1, (void **)&obj, $descriptor(type), 0 | 0);
if (!SWIG_IsOK(e))
{
lua_pop(L, 1);
SWIG_Lua_pusherrstring(L,"list in argument $argnum must contain only "`type`);
SWIG_fail;
}
obj2queue;
}
lua_pop(L, 1);
}
}
%}
%enddef
%{
SWIGINTERN int
SWIG_AsVal_int(lua_State* L, int idx, int *val) {
int ecode = lua_isnumber(L, idx) ? SWIG_OK : SWIG_TypeError;
if (ecode == SWIG_OK)
*val = (int)lua_tonumber(L, idx);
return ecode;
}
%}
#endif /* SWIGLUA */
%typemap(in) Queue Array2Queue(SWIG_AsVal_int, "integers")
%typemap(in) Queue solvejobs ObjArray2Queue(Job *, queue_push2(&$1, obj->how, obj->what))
%typemap(in) Queue solvables ObjArray2Queue(XSolvable *, queue_push(&$1, obj->id))
@@ -544,6 +707,15 @@ typedef Tcl_Obj *AppObjectPtr;
%typemap(out) AppObjectPtr {
Tcl_SetObjResult(interp, $1 ? $1 : Tcl_NewObj());
}
#elif defined(SWIGLUA)
typedef void *AppObjectPtr;
%typemap(in) AppObjectPtr {
$1 = (void *)L;
}
%typemap(out) AppObjectPtr {
get_stashed_lua_var(L, "appdata", $1);
SWIG_arg++;
}
#else
#warning AppObjectPtr not defined for this language!
#endif
@@ -566,15 +738,23 @@ SWIGINTERN int
SWIG_AsValSolvFpPtr(VALUE obj, FILE **val) {
#elif defined(SWIGTCL)
SWIG_AsValSolvFpPtr SWIG_TCL_DECL_ARGS_2(void *obj, FILE **val) {
#elif defined(SWIGLUA)
SWIG_AsValSolvFpPtr(lua_State *L, int idx, FILE **val) {
#else
SWIG_AsValSolvFpPtr(void *obj, FILE **val) {
#endif
static swig_type_info* desc = 0;
void *vptr = 0;
#ifdef SWIGPYTHON
int ecode;
#endif
if (!desc) desc = SWIG_TypeQuery("SolvFp *");
#if defined(SWIGLUA)
if ((SWIG_ConvertPtr(L, idx, &vptr, desc, 0)) == SWIG_OK) {
#else
if ((SWIG_ConvertPtr(obj, &vptr, desc, 0)) == SWIG_OK) {
#endif
if (val)
*val = vptr ? ((SolvFp *)vptr)->fp : 0;
return SWIG_OK;
@@ -605,6 +785,8 @@ SWIGINTERN int
SWIG_AsValDepId(VALUE obj, int *val) {
#elif defined(SWIGTCL)
SWIG_AsValDepId SWIG_TCL_DECL_ARGS_2(void *obj, int *val) {
#elif defined(SWIGLUA)
SWIG_AsValDepId(lua_State *L, int idx, int *val) {
#else
SWIG_AsValDepId(void *obj, int *val) {
#endif
@@ -614,12 +796,18 @@ SWIG_AsValDepId(void *obj, int *val) {
if (!desc) desc = SWIG_TypeQuery("Dep *");
#ifdef SWIGTCL
ecode = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(obj, val);
#elif defined(SWIGLUA)
ecode = SWIG_AsVal_int(L, idx, val);
#else
ecode = SWIG_AsVal_int(obj, val);
#endif
if (SWIG_IsOK(ecode))
return ecode;
#if defined(SWIGLUA)
if ((SWIG_ConvertPtr(L, idx, &vptr, desc, 0)) == SWIG_OK) {
#else
if ((SWIG_ConvertPtr(obj, &vptr, desc, 0)) == SWIG_OK) {
#endif
if (val)
*val = vptr ? ((Dep *)vptr)->id : 0;
return SWIG_OK;
@@ -645,12 +833,17 @@ SWIG_AsValDepId(void *obj, int *val) {
SWIG_ConvertPtr(ST(0), &argp1,SWIGTYPE_p_Pool, SWIG_POINTER_DISOWN | 0 );
#elif defined(SWIGTCL)
SWIG_ConvertPtr(objv[1], &argp1, SWIGTYPE_p_Pool, SWIG_POINTER_DISOWN | 0);
#elif defined(SWIGLUA)
SWIG_ConvertPtr(L, 1, (void **)&arg1, SWIGTYPE_p_Pool, SWIG_POINTER_DISOWN | 0);
#else
#warning disown_helper not implemented for this language, this is likely going to leak memory
#endif
#ifdef SWIGTCL
Tcl_SetObjResult(interp, SWIG_From_int((int)(0)));
#elif defined(SWIGLUA)
$result = 0;
lua_pushnumber(L, $result); SWIG_arg++;
#else
$result = SWIG_From_int((int)(0));
#endif
@@ -689,6 +882,22 @@ SWIG_AsValDepId(void *obj, int *val) {
**/
%include "typemaps.i"
#if defined(SWIGLUA)
%runtime "swigerrors.swg";
%include "typemaps/swigmacros.swg"
%include "typemaps/valtypes.swg"
%include "typemaps/inoutlist.swg"
%rename(__call) *::__next__;
%rename(__tostring) *::__str__;
%rename(__index) *::__getitem__;
%rename(__eq) *::__eq__;
%rename(__ne) *::__ne__;
%typemap(in) void *ign1 {};
%typemap(in) void *ign2 {};
%typemap(in,checkfn="lua_isfunction") int lua_function_idx { $1 = $input; };
#endif
#if defined(SWIGTCL)
%rename("==") *::__eq__;
@@ -698,12 +907,45 @@ SWIG_AsValDepId(void *obj, int *val) {
%typemap(in,numinputs=0,noblock=1) XRule **OUTPUT ($*1_ltype temp) {
$1 = &temp;
}
#if defined(SWIGLUA)
%typemap(argout,noblock=1) XRule **OUTPUT {
SWIG_NewPointerObj(L, (void *)(*$1), SWIGTYPE_p_XRule, SWIG_POINTER_OWN); SWIG_arg++;
}
#else
%typemap(argout,noblock=1) XRule **OUTPUT {
%append_output(SWIG_NewPointerObj((void*)(*$1), SWIGTYPE_p_XRule, SWIG_POINTER_OWN | %newpointer_flags));
}
#endif
#if defined(SWIGLUA)
%typemap(in,noblock=1,fragment="SWIG_AsValSolvFpPtr") FILE * {
{
FILE *val;
int ecode = SWIG_AsValSolvFpPtr(L, $input, &val);
if (!SWIG_IsOK(ecode)) SWIG_fail;
$1 = val;
}
}
%typemap(typecheck,precedence=%checkcode(POINTER),fragment="SWIG_AsValSolvFpPtr") FILE * {
int res = SWIG_AsValSolvFpPtr(L, $input, NULL);
$1 = SWIG_CheckState(res);
}
%typemap(in,noblock=1,fragment="SWIG_AsValDepId") DepId {
{
int val;
int ecode = SWIG_AsValDepId(L, $input, &val);
if (!SWIG_IsOK(ecode)) SWIG_fail;
$1 = val;
}
}
%typemap(typecheck,precedence=%checkcode(INT32),fragment="SWIG_AsValDepId") DepId {
int res = SWIG_AsValDepId(L, $input, NULL);
$1 = SWIG_CheckState(res);
}
#else
%typemaps_asval(%checkcode(POINTER), SWIG_AsValSolvFpPtr, "SWIG_AsValSolvFpPtr", FILE*);
%typemaps_asval(%checkcode(INT32), SWIG_AsValDepId, "SWIG_AsValDepId", DepId);
#endif
/**
@@ -1058,6 +1300,27 @@ SWIGINTERN void *appdata_get_helper(void **appdatap) {
%}
#elif defined(SWIGLUA)
%{
SWIGINTERN void appdata_disown_helper(void *appdata) {
}
SWIGINTERN void appdata_clr_helper(void **appdatap) {
if (*appdatap) {
void *appdata = *appdatap;
clr_stashed_lua_var((lua_State*)appdata, "appdata", (void *)appdatap);
*appdatap = 0;
}
}
SWIGINTERN void appdata_set_helper(void **appdatap, void *appdata) {
*appdatap = appdata;
set_stashed_lua_var((lua_State*)appdata, -1, "appdata", (void *)appdatap);
}
SWIGINTERN void *appdata_get_helper(void **appdatap) {
return (void *)appdatap;
}
%}
#else
#warning appdata helpers not implemented for this language
#endif
@@ -1852,6 +2115,34 @@ returnself(matchsolvable)
pool_setloadcallback($self, loadcallback, callback_var);
}
}
#elif defined(SWIGLUA)
%{
SWIGINTERN int loadcallback(Pool *pool, Repodata *data, void *d) {
lua_State* L = d;
get_stashed_lua_var(L, "loadcallback", pool);
XRepodata *xd = new_XRepodata(data->repo, data->repodataid);
SWIG_NewPointerObj(L,SWIG_as_voidptr(xd), SWIGTYPE_p_XRepodata, 0);
int res = lua_pcall(L, 1, 1, 0);
res = res == LUA_OK ? lua_toboolean(L, -1) : 0;
lua_pop(L, 1);
return res;
}
%}
void clr_loadcallback() {
if ($self->loadcallback == loadcallback) {
lua_State* L = $self->loadcallbackdata;
clr_stashed_lua_var(L, "loadcallback", $self);
pool_setloadcallback($self, 0, 0);
}
}
void set_loadcallback(int lua_function_idx, lua_State* L) {
clr_stashed_lua_var(L, "loadcallback", $self);
if (!lua_isnil(L, lua_function_idx)) {
set_stashed_lua_var(L, lua_function_idx, "loadcallback", $self);
pool_setloadcallback($self, loadcallback, L);
}
}
#else
#warning loadcallback not implemented for this language
#endif
@@ -2616,7 +2907,11 @@ returnself(matchsolvable)
perliter(solv::Dataiterator)
#endif
%newobject __next__;
#ifdef SWIGLUA
Datamatch *__next__(void *ign1=0, void *ign2=0) {
#else
Datamatch *__next__() {
#endif
Dataiterator *ndi;
if (!dataiterator_step($self)) {
return 0;
@@ -2865,7 +3160,11 @@ returnself(matchsolvable)
perliter(solv::Pool_solvable_iterator)
#endif
%newobject __next__;
#ifdef SWIGLUA
XSolvable *__next__(void *ign1=0, void *ign2=0) {
#else
XSolvable *__next__() {
#endif
Pool *pool = $self->pool;
if ($self->id >= pool->nsolvables)
return 0;
@@ -2919,7 +3218,11 @@ returnself(matchsolvable)
#ifdef SWIGPERL
perliter(solv::Pool_repo_iterator)
#endif
#ifdef SWIGLUA
Repo *__next__(void *ign1=0, void *ign2=0) {
#else
Repo *__next__() {
#endif
Pool *pool = $self->pool;
if ($self->id >= pool->nrepos)
return 0;
@@ -2975,7 +3278,11 @@ returnself(matchsolvable)
perliter(solv::Repo_solvable_iterator)
#endif
%newobject __next__;
#ifdef SWIGLUA
XSolvable *__next__(void *ign1=0, void *ign2=0) {
#else
XSolvable *__next__() {
#endif
Repo *repo = $self->repo;
Pool *pool = repo->pool;
if (repo->start > 0 && $self->id < repo->start)

590
examples/luasolv Normal file
View File

@@ -0,0 +1,590 @@
#!/usr/bin/lua
releasever = nil
solvcachedir = '/var/cache/solv'
posix = require("posix")
posix.sys.stat = require("posix.sys.stat")
require("solv")
-- helpers
function parse_ini_file(fn)
local sections = {}
for line in io.open(fn, 'r'):lines() do
local match = line:match('^%[([^%[%]]+)%]$')
if match then
section = match
sections[section] = sections[section] or {}
else
local param, value = line:match('^([%w|_]+)%s-=%s-(.+)$')
if param then sections[section][param] = value end
end
end
return sections
end
function die(str)
io.stdout:write(str.."\n")
os.exit(1)
end
function isdir(path)
local st = posix.sys.stat.stat(path)
return st and posix.S_ISDIR(st.st_mode) ~= 0
end
function lsdir(path)
local content = posix.dirent.dir(path) or {}
table.sort(content)
return content
end
function load_stub(repodata)
local repo = repodata.repo.appdata
if repo then
return repo:load_ext(repodata)
end
return false
end
-- generic repo implementation
Repo = {}
function Repo.calc_cookie_filename(filename)
chksum = solv.Chksum(solv.REPOKEY_TYPE_SHA256)
chksum:add("1.1")
chksum:add_stat(filename)
return chksum:raw()
end
function Repo.calc_cookie_fp(fp)
chksum = solv.Chksum(solv.REPOKEY_TYPE_SHA256)
chksum:add("1.1")
chksum:add_fp(fp)
return chksum:raw()
end
function Repo.calc_cookie_ext(f, cookie)
chksum = solv.Chksum(solv.REPOKEY_TYPE_SHA256)
chksum:add("1.1")
chksum:add(cookie)
chksum:add_fstat(f:fileno())
return chksum:raw()
end
function Repo:download(file, uncompress, chksum, markincomplete)
if not self.baseurl then
io.stdout:write(self.alias..": no baseurl\n")
return nil
end
local url = self.baseurl:gsub('/$', '')..'/'..file
local tmpfile = io.tmpfile()
local fd = posix.stdio.fileno(tmpfile)
local status, reason = posix.spawn({'curl', '-f', '-s', '-L', '-o', '/dev/fd/'..fd, '--', url})
if posix.unistd.lseek(fd, 0, posix.unistd.SEEK_END) == 0 and (status == 0 or not chksum) then
tmpfile:close()
return nil
end
posix.unistd.lseek(fd, 0, posix.unistd.SEEK_SET);
if status ~= 0 then
print(file..": download error "..status.."\n");
tmpfile:close()
return nil
end
if chksum then
local fchksum = solv.Chksum(chksum.type);
fchksum:add_fd(fd)
if fchksum ~= chksum then
print(file..": checksum error")
if markincomplete then self.incomplete = 1 end
return nil
end
end
local ret
if uncompress then
ret = solv.xfopen_fd(file, fd);
else
ret = solv.xfopen_fd(nil, fd);
end
tmpfile:close()
return ret
end
function Repo:load(pool)
self.handle = pool:add_repo(self.alias)
self.handle.appdata = self
self.handle.priority = 99 - self.priority
if self:usecachedrepo() then
io.stdout:write("repo '"..self.alias.."': cached\n")
return true
end
return false
end
function Repo:cachepath(ext)
local path = self.alias:gsub('^%.', '_');
if ext then
path = path .. '_' .. ext ..'.solvx'
else
path = path ..'.solv'
end
return '/var/cache/solv/' .. path:gsub('/', '_');
end
function Repo:usecachedrepo(ext, mark)
local cookie
if ext then
cookie = self.extcookie
else
cookie = self.cookie
end
local repopath = self:cachepath(ext)
local f = io.open(repopath, 'rb')
if not f then
return false
end
f:seek('end', -32)
local fcookie = f:read(32)
if not fcookie or fcookie:len() ~= 32 then
f:close()
return false
end
if cookie and cookie ~= fcookie then
f:close()
return false
end
local fextcookie
if self.type ~= 'system' and not ext then
f:seek('end', -64)
fextcookie = f:read(32)
if not fcookie or fcookie:len() ~= 32 then
f:close()
return false
end
end
f:seek('set')
posix.unistd.lseek(posix.stdio.fileno(f), 0, posix.unistd.SEEK_SET)
local ff = solv.xfopen_fd('', posix.stdio.fileno(f))
f:close()
local flags = 0
if ext then
flags = flags | solv.Repo.REPO_USE_LOADING|solv.Repo.REPO_EXTEND_SOLVABLES
if ext ~= 'DL' then flags = flags | solv.Repo.REPO_LOCALPOOL end
end
if not self.handle:add_solv(ff, flags) then
ff:close()
return false
end
if self.type ~= 'system' and not ext then
self.cookie = fcookie
self.extcookie = fextcookie
end
if mark then
posix.utime(repopath)
end
return true
end
function Repo:writecachedrepo(ext, repodata)
if self.incomplete then return end
if not isdir(solvcachedir) then
posix.sys.stat.mkdir(solvcachedir, 7 * 64 + 5 * 8 + 5)
end
local fd, tmpname = posix.stdlib.mkstemp(solvcachedir..'/.newsolv-XXXXXX')
if not fd then
return
end
local ff = solv.xfopen_fd('', fd)
if not repodata then
self.handle:write(ff)
elseif ext then
repodata:write(ff)
else
self.handle:write_first_repodata(ff)
end
ff:flush()
if self.type ~= 'system' and not ext then
self.extcookie = self.extcookie or Repo.calc_cookie_ext(ff, self.cookie)
ff:write(self.extcookie)
end
if not ext then
ff:write(self.cookie)
else
ff:write(self.extcookie)
end
ff:close()
os.rename(tmpname, self:cachepath(ext))
end
function Repo:add_ext_keys(ext, repodata, handle)
if ext == 'DL' then
repodata:add_idarray(handle, solv.REPOSITORY_KEYS, solv.REPOSITORY_DELTAINFO)
repodata:add_idarray(handle, solv.REPOSITORY_KEYS, solv.REPOKEY_TYPE_FLEXARRAY)
elseif ext == 'DU' then
repodata:add_idarray(handle, solv.REPOSITORY_KEYS, solv.SOLVABLE_DISKUSAGE)
repodata:add_idarray(handle, solv.REPOSITORY_KEYS, solv.REPOKEY_TYPE_DIRNUMNUMARRAY)
elseif ext == 'FL' then
repodata:add_idarray(handle, solv.REPOSITORY_KEYS, solv.SOLVABLE_FILELIST)
repodata:add_idarray(handle, solv.REPOSITORY_KEYS, solv.REPOKEY_TYPE_DIRSTRARRAY)
end
end
-- rpmmd repo implementation
Repo_rpmmd = {}
setmetatable(Repo_rpmmd, {__index = Repo })
function Repo_rpmmd:find(what)
local di = self.handle:Dataiterator_meta(solv.REPOSITORY_REPOMD_TYPE, what, solv.Dataiterator.SEARCH_STRING)
di:prepend_keyname(solv.REPOSITORY_REPOMD)
for d in di do
local dp = d:parentpos()
local filename = dp:lookup_str(solv.REPOSITORY_REPOMD_LOCATION)
if filename then
local chksum = dp:lookup_checksum(solv.REPOSITORY_REPOMD_CHECKSUM)
if not chksum then
print("no "..filename.." file checksum!\n")
return
end
return filename, chksum
end
end
end
function Repo_rpmmd:add_ext(repodata, what, ext)
local filename, filechksum = self:find(what)
if not filename and what == 'deltainfo' then
filename, filechksum = self:find('prestodelta')
end
if filename then
local handle = repodata:new_handle()
repodata:set_poolstr(handle, solv.REPOSITORY_REPOMD_TYPE, what)
repodata:set_str(handle, solv.REPOSITORY_REPOMD_LOCATION, filename)
repodata:set_checksum(handle, solv.REPOSITORY_REPOMD_CHECKSUM, filechksum)
self:add_ext_keys(ext, repodata, handle)
repodata:add_flexarray(solv.SOLVID_META, solv.REPOSITORY_EXTERNAL, handle)
end
end
function Repo_rpmmd:add_exts()
repodata = self.handle:add_repodata(0)
repodata:extend_to_repo()
self:add_ext(repodata, 'deltainfo', 'DL')
self:add_ext(repodata, 'filelists', 'FL')
repodata:internalize()
end
function Repo_rpmmd:load(pool)
if Repo.load(self, pool) then return true end
io.stdout:write("rpmmd repo '"..self.alias.."': ")
local f = self:download("repodata/repomd.xml");
if not f then
print("no repomd.xml file, skipped");
self.handle:free(true)
self.handle = nil
return false
end
self.cookie = self.calc_cookie_fp(f)
if self:usecachedrepo(nil, True) then
print("cached")
return true
end
self.handle:add_repomdxml(f, 0)
f:close()
print("fetching")
local filename, filechksum
filename, filechksum = self:find('primary')
if filename then
f = self:download(filename, true, filechksum, true)
if f then
self.handle:add_rpmmd(f, nil, 0)
f:close()
end
if self.incomplete then return false end
end
filename, filechksum = self:find('updateinfo')
if filename then
f = self:download(filename, true, filechksum, true)
if f then
self.handle:add_updateinfoxml(f, 0)
f:close()
end
end
self:add_exts()
self:writecachedrepo()
self.handle:create_stubs()
return true
end
function Repo_rpmmd:load_ext(repodata)
local repomdtype = repodata:lookup_str(solv.SOLVID_META, solv.REPOSITORY_REPOMD_TYPE)
local ext
if repomdtype == 'filelists' then
ext = 'FL'
elseif repomdtype == 'deltainfo' then
ext = 'DL'
else
return false
end
io.stdout:write("["..self.alias..":"..ext..": ")
if self:usecachedrepo(ext) then
io.stdout:write("cached]\n")
return true
end
io.stdout:write("fetching]\n")
local filename = repodata:lookup_str(solv.SOLVID_META, solv.REPOSITORY_REPOMD_LOCATION)
local filechksum = repodata:lookup_str(solv.SOLVID_META, solv.REPOSITORY_REPOMD_CHECKSUM)
local f = self:download(filename, true, filechksum)
if not f then
print("download failed")
return false
end
if ext == 'FL' then
self.handle:add_rpmmd(f, 'FL', solv.Repo.REPO_USE_LOADING|solv.Repo.REPO_EXTEND_SOLVABLES|solv.Repo.REPO_LOCALPOOL)
elseif ext == 'DL' then
self.handle:add_deltainfoxml(f, solv.Repo.REPO_USE_LOADING)
end
self:writecachedrepo(ext, repodata)
return true
end
-- susetags repo implementation
Repo_susetags = {}
setmetatable(Repo_susetags, {__index = Repo })
-- unknown repo implementation
Repo_unknown = {}
setmetatable(Repo_unknown, {__index = Repo })
function Repo_unknown:load()
print("unsupported repo '"..self.alias.."': skipped")
return false
end
-- system repo implementation
Repo_system = {}
setmetatable(Repo_system, {__index = Repo })
function Repo_system:load(pool)
self.handle = pool:add_repo(self.alias)
self.handle.appdata = self
pool.installed = self.handle
io.stdout:write("rpm database: ")
self.cookie = Repo.calc_cookie_filename("/var/lib/rpm/Packages")
if self:usecachedrepo() then
print("cached")
return true
end
io.stdout:write("reading\n")
local oldf = solv.xfopen(self:cachepath())
self.handle:add_rpmdb_reffp(oldf, solv.Repo.REPO_REUSE_REPODATA)
self:writecachedrepo()
end
-- main
cmd = arg[1]
if not cmd then die("Usage: luasolv COMMAND [ARGS]") end
table.remove(arg, 1)
cmdabbrev = { ['ls']='list'; ['in']='install'; ['rm']='erase'; ['ve']='verify'; ['se']='search' }
cmd = cmdabbrev[cmd] or cmd
cmdactions = { ['install']=solv.Job.SOLVER_INSTALL; ['erase']=solv.Job.SOLVER_ERASE; ['up']=solv.Job.SOLVER_UPDATE; ['dup']=solv.Job.SOLVER_DISTUPGRADE; ['verify']=solv.Job.SOLVER_VERIFY; ['list']=0; ['info']=0 }
repos = {}
reposdir = {}
if isdir('/etc/zypp/repos.d') then
table.insert(reposdir, '/etc/zypp/repos.d')
elseif isdir('/etc/yum/repos.d') then
table.insert(reposdir, '/etc/yum/repos.d')
end
for _, repodir in ipairs(reposdir) do
for _, e in ipairs(lsdir(repodir)) do
if e:sub(-5) == '.repo' then
sections = parse_ini_file(repodir..'/'..e)
for alias, section in pairs(sections) do
repo = { ['alias']=alias; ['enabled']=false; ['priority']=99; ['autorefresh']=true; ['type']='rpm-md'; ['metadata_expire']=900 }
if section.name then repo.name = section.name end
if section.type then repo.type = section.type end
if section.baseurl then repo.baseurl = section.baseurl end
if section.metadata_expire then repo.metadata_expire = tonumber(section.metadata_expire) end
if section.enabled then repo.enabled = tonumber(section.enabled) ~= 0 end
if section.autorefresh then repo.autorefresh = tonumber(section.autorefresh) ~= 0 end
if section.gpgcheck then repo.gpgcheck = tonumber(section.gpgcheck) ~= 0 end
if section.priority then repo.priority = tonumber(section.priority) end
if repo.baseurl and releasever then repo.baseurl = repo.baseurl:gsub('$releasever', releasever) end
if repo.type == 'rpm-md' then
setmetatable(repo , {__index = Repo_rpmmd })
elseif repo['type'] == 'yast2' then
setmetatable(repo , {__index = Repo_susetags })
else
setmetatable(repo , {__index = Repo_unknown })
end
table.insert(repos, repo)
end
end
end
end
local pool = solv.Pool()
pool:setarch()
pool:set_loadcallback(load_stub)
sysrepo = { ['alias']='@System', ['type']='system' }
setmetatable(sysrepo , {__index = Repo_system })
sysrepo:load(pool)
for _, repo in ipairs(repos) do
if repo.enabled then
repo:load(pool)
end
end
if cmd == 'search' then
pool:createwhatprovides()
sel = pool:Selection()
di = pool:Dataiterator(solv.SOLVABLE_NAME, arg[1], solv.Dataiterator.SEARCH_SUBSTRING | solv.Dataiterator.SEARCH_NOCASE)
for d in di do
sel:add_raw(solv.Job.SOLVER_SOLVABLE, d.solvid)
end
for _, s in ipairs(sel:solvables()) do
print(' - '..tostring(s)..' ['..s.repo.name..']: '..s:lookup_str(solv.SOLVABLE_SUMMARY))
end
os.exit(0)
end
if not cmdactions[cmd] then die("unknown command '"..cmd.."'") end
pool:addfileprovides()
pool:createwhatprovides()
jobs = {}
for _, a in ipairs(arg) do
flags = solv.Selection.SELECTION_NAME | solv.Selection.SELECTION_PROVIDES | solv.Selection.SELECTION_GLOB
flags = flags | solv.Selection.SELECTION_CANON | solv.Selection.SELECTION_DOTARCH | solv.Selection.SELECTION_REL
if a:sub(1, 1) == '/' then
flags = flags | solv.Selection.SELECTION_FILELIST
if cmd == 'erase' then
flags = flags | solv.Selection.SELECTION_INSTALLED_ONLY
end
end
local sel = pool:select(a, flags)
if sel:isempty() then
sel = pool:select(a, flags | solv.Selection.SELECTION_NOCASE)
if not sel:isempty() then
print("[ignoring case for '"..a.."']")
end
end
if (sel.flags & solv.Selection.SELECTION_FILELIST) ~= 0 then
print("[using file list match for '"..a.."']")
end
if (sel.flags & solv.Selection.SELECTION_PROVIDES) ~= 0 then
print("[using capability match for '"..a.."']")
end
for _, j in ipairs(sel:jobs(cmdactions[cmd])) do
table.insert(jobs, j)
end
end
if #jobs == 0 and (cmd == 'up' or cmd == 'dup' or cmd == 'verify') then
for _, j in ipairs(pool:Selection_all():jobs(cmdactions[cmd])) do
table.insert(jobs, j)
end
end
if #jobs == 0 then die("no package matched.") end
if cmd == 'list' or cmd == 'info' then
for _, job in ipairs(jobs) do
for _, s in ipairs(job:solvables()) do
if cmd == 'info' then
local str
print('Name: '..tostring(s))
print('Repo: '..s.repo.name)
print('Summary: '..s:lookup_str(solv.SOLVABLE_SUMMARY))
str = s:lookup_str(solv.SOLVABLE_URL)
if str then print('Url: '..str) end
str = s:lookup_str(solv.SOLVABLE_LICENSE)
if str then print('License: '..str) end
print("Description\n"..s:lookup_str(solv.SOLVABLE_DESCRIPTION))
print('')
else
print(' - '..tostring(s)..' ['..s.repo.name..']')
print(' '..s:lookup_str(solv.SOLVABLE_SUMMARY))
end
end
end
os.exit(0)
end
local solver = pool:Solver()
if cmd == 'erase' then
solver:set_flag(solv.Solver.SOLVER_FLAG_ALLOW_UNINSTALL, 1)
end
while true do
local problems = solver:solve(jobs)
if #problems == 0 then break end
for _, problem in ipairs(problems) do
print(("Problem %d/%d:"):format(problem.id, #problems))
print(problem)
local solutions = problem:solutions()
for _, solution in ipairs(solutions) do
print((" Solution %d:"):format(solution.id))
local elements = solution:elements(true)
for _, element in ipairs(elements) do
print(" - ".. element:str())
end
print('')
end
end
os.exit(1)
end
local trans = solver:transaction()
if trans:isempty() then
print("Nothing to do.")
os.exit(0)
end
print('')
print("Transaction summary:")
print('')
for _, cl in ipairs(trans:classify(solv.Transaction.SOLVER_TRANSACTION_SHOW_OBSOLETES | solv.Transaction.SOLVER_TRANSACTION_OBSOLETE_IS_UPGRADE)) do
if cl.type == solv.Transaction.SOLVER_TRANSACTION_ERASE then
print(("%d erased packages:"):format(cl.count))
elseif cl.type == solv.Transaction.SOLVER_TRANSACTION_INSTALL then
print(("%d installed packages:"):format(cl.count))
elseif cl.type == solv.Transaction.SOLVER_TRANSACTION_REINSTALLED then
print(("%d reinstalled packages:"):format(cl.count))
elseif cl.type == solv.Transaction.SOLVER_TRANSACTION_DOWNGRADED then
print(("%d downgraded packages:"):format(cl.count))
elseif cl.type == solv.Transaction.SOLVER_TRANSACTION_CHANGED then
print(("%d changed packages:"):format(cl.count))
elseif cl.type == solv.Transaction.SOLVER_TRANSACTION_UPGRADED then
print(("%d upgraded packages:"):format(cl.count))
elseif cl.type == solv.Transaction.SOLVER_TRANSACTION_VENDORCHANGE then
print(("%d vendor changes from '%s' to '%s':"):format(cl.count, cl.fromstr, cl.tostr))
elseif cl.type == solv.Transaction.SOLVER_TRANSACTION_ARCHCHANGE then
print(("%d arch changes from '%s' to '%s':"):format(cl.count, cl.fromstr, cl.tostr))
else
cl = nil
end
if cl then
for _, p in ipairs(cl:solvables()) do
if cl.type == solv.Transaction.SOLVER_TRANSACTION_UPGRADED or cl.type == solv.Transaction.SOLVER_TRANSACTION_DOWNGRADED then
print((" - %s -> %s"):format(p, trans:othersolvable(p)))
else
print((" - %s"):format(p))
end
end
print('')
end
end
print(("install size change: %d K"):format(trans:calc_installsizechange()))
print('')