port-compare/port_stats/rpm_ffi.py
Ivan A. Melnikov 2077d293f5 side report!
2019-03-22 17:13:07 +04:00

106 lines
3.3 KiB
Python

import cffi
_CDEF = """
void
parseEVR(const char* evr,
const char **e, const char **v, const char **r);
int
rpmEVRcmp(const char * const aE, const char * const aV, const char * const aR,
const char * const aDepend,
const char * const bE, const char * const bV, const char * const bR,
const char * const bDepend);
"""
_FFI = cffi.FFI()
_FFI.cdef(_CDEF)
_LIBRPM = _FFI.dlopen('librpm-4.0.4.so')
def _pp_str(p_str):
"""Convert FFI's char** to Python string.
Assumes that pointer points to a zero-terminated C-string.
Returns None if the pointer is NULL.
"""
if p_str and p_str[0]:
return _FFI.string(p_str[0])
return None
def parse_evr(evr):
"""Returns 3-tuple (epoch, version, release)"""
p_evr = _FFI.new('char[]', evr)
e = _FFI.new('char**')
v = _FFI.new('char**')
r = _FFI.new('char**')
_LIBRPM.parseEVR(p_evr, e, v, r)
epoch = _pp_str(e)
if epoch:
epoch = int(epoch)
return epoch, _pp_str(v), _pp_str(r)
def _epoch_to_pchar(epoch, mode):
if mode not in ('pkg', 'deps'):
raise ValueError("Epoch mode should be one of "
"'pkg', 'deps' -- not " + mode)
if epoch is not None:
return _FFI.new('char[]', str(epoch))
elif mode == 'pkg':
# for packages no epoch is the same as zero epoch
return _FFI.new('char[]', '0')
else:
return _FFI.NULL
def evr_cmp(evr1, evr2, mode):
p_e1 = _epoch_to_pchar(evr1[0], mode)
p_v1 = _FFI.new('char[]', str(evr1[1]))
p_r1 = _FFI.new('char[]', str(evr1[2]))
p_e2 = _epoch_to_pchar(evr2[0], mode)
p_v2 = _FFI.new('char[]', str(evr2[1]))
p_r2 = _FFI.new('char[]', str(evr2[2]))
dep = _FFI.new('char[]', '')
return _LIBRPM.rpmEVRcmp(p_e1, p_v1, p_r1, dep,
p_e2, p_v2, p_r2, dep)
def ver_cmp(ver1, ver2):
return evr_cmp((None, ver1, 1), (None, ver2, 1), 'pkg')
def _tests():
assert parse_evr('3:42.8.4-alt1.mipsel0') == (3, '42.8.4', 'alt1.mipsel0')
assert parse_evr('0:42.8.4-alt1.mipsel0') == (0, '42.8.4', 'alt1.mipsel0')
assert parse_evr('42.8.4-alt1.mipsel0') == (None, '42.8.4', 'alt1.mipsel0')
assert parse_evr('1.20.0-alt1_1') == (None, '1.20.0', 'alt1_1')
assert parse_evr('1:1.20.1-alt1') == (1, '1.20.1', 'alt1')
assert evr_cmp((3, '42.8.4', 'alt1'), (3, '42.8.4', 'alt2'), 'pkg') < 0
assert evr_cmp((3, '42.9.4', 'alt1'), (3, '42.8.4', 'alt2'), 'pkg') > 0
assert evr_cmp((3, '42.8.4', 'alt1'), (3, '42.8.4', 'alt1'), 'pkg') == 0
assert evr_cmp((3, '42.9.4', 'alt1'), (5, '42.8.4', 'alt1'), 'pkg') < 0
assert evr_cmp((1, '1.2.0', 'alt1'), (None, '1.2.0', 'alt1_1'), 'pkg') > 0
assert evr_cmp((None, '1.2.0', 'alt1_1'), (1, '1.2.0', 'alt1'), 'pkg') < 0
# 'deps' mode means that first argument satisfies requirement
# specified as second argument; here, if epoch is absent on the right
# side, it's ignored
assert evr_cmp((1, '1.2.0', 'alt1'), (None, '1.2.0', 'alt1_1'), 'deps') < 0
assert evr_cmp((None, '1.2.0', 'alt1_1'), (1, '1.2.0', 'alt1'), 'deps') < 0
assert evr_cmp((1, '1.3.0', 'alt1'), (None, '1.2.0', 'alt1_1'), 'deps') > 0
assert evr_cmp((None, '1.3.0', 'alt1_1'), (1, '1.2.0', 'alt1'), 'deps') < 0
if __name__ == '__main__':
_tests()