mock build

This commit is contained in:
alexey
2025-03-15 00:01:18 +03:00
parent 9cf2aabc65
commit 998f87cee3
61 changed files with 3581 additions and 37 deletions

View File

@@ -0,0 +1,49 @@
require 'ffi'
module RPM
module C
extend ::FFI::Library
begin
ffi_lib ['rpm',
'librpm.so.9',
'librpm.so.8', # Tumbleweed
'librpm.so.7', # fedora 23
'librpm.so.3', 'librpm.so.2', 'librpm.so.1']
rescue LoadError => e
raise(
"Can't find rpm libs on your system: #{e.message}"
)
end
end
end
require 'rpm/c/rpmtypes'
require 'rpm/c/rpmcallback'
require 'rpm/c/rpmtag'
require 'rpm/c/rpmlib'
module RPM
module C
def self.rpm_version_code
ver = ::RPM::C.RPMVERSION.split('.', 3)
return (ver[0].to_i<<16) + (ver[1].to_i<<8) + (ver[2].to_i<<0)
end
end
end
require 'rpm/c/rpmlog'
require 'rpm/c/rpmmacro'
require 'rpm/c/rpmio'
require 'rpm/c/header'
require 'rpm/c/rpmprob'
require 'rpm/c/rpmps'
require 'rpm/c/rpmfi'
require 'rpm/c/rpmdb'
require 'rpm/c/rpmcallback'
require 'rpm/c/rpmcli'
require 'rpm/c/rpmts'
require 'rpm/c/rpmds'
require 'rpm/c/rpmtd'

View File

@@ -0,0 +1,36 @@
module RPM
module C
typedef :pointer, :header
attach_function 'headerNew', [], :header
attach_function 'headerFree', [:header], :header
attach_function 'headerLink', [:header], :header
# ..
HEADERGET_DEFAULT = [0,
HEADERGET_MINMEM = (1 << 0)].freeze
HEADERGET_EXT = (1 << 1)
HEADERGET_RAW = (1 << 2)
HEADERGET_ALLOC = (1 << 3)
HEADERGET_ARGV = (1 << 4)
# ..
attach_function 'headerGet', %i[header rpmTagVal pointer uint32], :int
attach_function 'headerPut', %i[header pointer uint32], :int
# ...
attach_function 'headerFormat', %i[header string pointer], :pointer
# ...
# http://rpm.org/wiki/Releases/4.14.0 deprecated addMacro/delMacro
unless rpm_version_code >= ((4 << 16) + (14 << 8) + (0 << 0))
attach_function 'headerNVR', [:header, :pointer, :pointer, :pointer], :int
end
# ...
attach_function 'headerGetAsString', %i[header rpmTagVal], :string
# ...
attach_function 'headerPutString', %i[header rpmTagVal string], :int
# ...
attach_function 'headerPutUint32', %i[header rpmTagVal pointer rpm_count_t], :int
# ...
attach_function 'rpmReadPackageFile', %i[header FD_t string pointer], Rc
end
end

View File

@@ -0,0 +1,27 @@
module RPM
module C
CallbackType = enum(:rpmCallbackType, [
:unknown, 0,
:inst_progress, (1 << 0),
:inst_start, (1 << 1),
:inst_open_file, (1 << 2),
:inst_close_file, (1 << 3),
:trans_progress, (1 << 4),
:trans_start, (1 << 5),
:trans_stop, (1 << 6),
:uninst_progress, (1 << 7),
:uninst_start, (1 << 8),
:uninst_stop, (1 << 9),
:repackage_progress, (1 << 10),
:repackage_start, (1 << 11),
:repackage_stop, (1 << 12),
:unpack_error, (1 << 13),
:cpio_error, (1 << 14),
:script_error, (1 << 15)
])
typedef :pointer, :rpmCallbackData
callback :rpmCallbackFunction, %i[pointer rpmCallbackType rpm_loff_t rpm_loff_t fnpyKey rpmCallbackData], :pointer
end
end

View File

@@ -0,0 +1,6 @@
module RPM
module C
attach_function 'rpmShowProgress', %i[pointer rpmCallbackType rpm_loff_t rpm_loff_t fnpyKey pointer], :pointer
end
end

View File

@@ -0,0 +1,20 @@
module RPM
module C
typedef :pointer, :rpmdb
typedef :pointer, :rpmdbMatchIterator
RegexpMode = enum(:rpmMireMode, %i[
default strcmp regex glob
])
attach_function 'rpmdbCountPackages', %i[rpmdb string], :int
attach_function 'rpmdbGetIteratorOffset', [:rpmdbMatchIterator], :uint
attach_function 'rpmdbGetIteratorCount', [:rpmdbMatchIterator], :int
attach_function 'rpmdbSetIteratorRE', %i[rpmdbMatchIterator rpmTagVal rpmMireMode string], :int
attach_function 'rpmdbInitIterator', %i[rpmdb rpmDbiTagVal pointer size_t], :rpmdbMatchIterator
attach_function 'rpmdbNextIterator', [:rpmdb], :header
attach_function 'rpmdbFreeIterator', [:rpmdb], :rpmdbMatchIterator
end
end

View File

@@ -0,0 +1,43 @@
module RPM
module C
typedef :pointer, :rpmds
Sense = enum(:rpmsenseFlags_e, [
:any, 0,
:less, (1 << 1),
:greater, (1 << 2),
:equal, (1 << 3),
# bit 4 unused
:posttrans, (1 << 5),
:prereq, (1 << 6),
#
:pretrans, (1 << 7),
:interp, (1 << 8),
:script_pre, (1 << 9),
:script_post, (1 << 10),
:script_preun, (1 << 11),
:script_postun, (1 << 12),
:script_verify, (1 << 13),
:find_requires, (1 << 14),
:find_provides, (1 << 15),
#
:triggerin, (1 << 16),
:triggerun, (1 << 17),
:triggerpostun, (1 << 18),
:missingok, (1 << 19),
# 20 23 unused
:rpmlib, (1 << 24),
:triggerprein, (1 << 25),
:keyring, (1 << 26),
:strong, (1 << 27),
:config, (1 << 28)
])
typedef :rpmFlags, :rpmsenseFlags
# ...
attach_function 'rpmdsSingle', %i[rpmTagVal string string rpmsenseFlags], :rpmds
# ...
attach_function 'rpmdsCompare', %i[rpmds rpmds], :int
end
end

View File

@@ -0,0 +1,31 @@
module RPM
module C
FileAttrs = enum(:rpmfileAttrs, [
:none, 0,
:config, (1 << 0),
:doc, (1 << 1),
:icon, (1 << 2),
:missingok, (1 << 3),
:noreplace, (1 << 4),
:specfile, (1 << 5),
:ghost, (1 << 6),
:license, (1 << 7),
:readme, (1 << 8),
:exclude, (1 << 9),
:unpatched, (1 << 10),
:pubkey, (1 << 11)
])
typedef :rpmFlags, :rpmfileAttrs
FileState = enum(:rpmfileState,
:missing, -1,
:normal, 0,
:replaced, 1,
:notinstalled, 2,
:netshared, 3,
:wrongcolor, 4)
typedef :pointer, :rpmRelocation
end
end

View File

@@ -0,0 +1,19 @@
module RPM
module C
typedef :pointer, :FD_t
# RPMIO
attach_function 'Fstrerror', [:FD_t], :string
# ...
attach_function 'Fclose', [:FD_t], :int
# ...
attach_function 'Fopen', %i[string string], :FD_t
# ...
attach_function 'Ferror', [:FD_t], :int
attach_function 'fdDup', [:int], :FD_t
attach_function 'Fstrerror', [:FD_t], :string
attach_function 'fdLink', [:pointer], :FD_t
end
end

View File

@@ -0,0 +1,12 @@
module RPM
module C
attach_variable :RPMVERSION, :RPMVERSION, :string
attach_variable :RPMEVR, :rpmEVR, :string
attach_function 'rpmReadConfigFiles', %i[string string], :int
# ...
attach_function 'rpmvercmp', %i[string string], :int
end
end

View File

@@ -0,0 +1,23 @@
module RPM
module C
# rpmlog
RPMLOG_PRIMASK = 0x07
Log = enum(
:emerg, 0,
:alert, 1,
:crit, 2,
:err, 3,
:warning, 4,
:notice, 5,
:info, 6,
:debug, 7
)
attach_function 'rpmlogSetMask', [:int], :int
# TODO: defines to set verbosity
# ...
attach_function 'rpmlogMessage', [], :string
end
end

View File

@@ -0,0 +1,33 @@
module RPM
module C
attach_variable :MACROFILES, :macrofiles, :string
# ...
# Markers for sources of macros added throughout rpm.
RMIL_DEFAULT = -15
RMIL_MACROFILES = -13
RMIL_RPMRC = -11
RMIL_CMDLINE = -7
RMIL_TARBALL = -5
RMIL_SPEC = -3
RMIL_OLDSPEC = -1
RMIL_GLOBAL = 0
# ...
# http://rpm.org/wiki/Releases/4.14.0 deprecated addMacro/delMacro
if rpm_version_code >= ((4 << 16) + (14 << 8) + (0 << 0))
attach_function 'rpmPushMacro', [:pointer, :string, :string, :string, :int], :void
attach_function 'rpmPopMacro', [:pointer, :string], :void
attach_function 'rpmExpandMacros', [:pointer, :pointer, :pointer, :int], :int
else
attach_function 'addMacro', [:pointer, :string, :string, :string, :int], :void
attach_function 'delMacro', [:pointer, :string], :void
attach_function 'expandMacros', [:pointer, :pointer, :pointer, :size_t], :int
end
# ...
# ...
attach_function 'rpmInitMacros', %i[pointer string], :void
# ...
end
end

View File

@@ -0,0 +1,53 @@
module RPM
module C
typedef :pointer, :rpmProblem
ProbFilter = enum(:rpmprobFilterFlags_e, [
:none, 0,
:ignoreos, (1 << 0),
:ignorearch, (1 << 1),
:replacepkg, (1 << 2),
:forcerelocate, (1 << 3),
:replacenewfiles, (1 << 4),
:replaceoldfiles, (1 << 5),
:oldpackage, (1 << 6),
:diskspace, (1 << 7),
:disknodes, (1 << 8)
])
typedef :rpmFlags, :rpmprobFilterFlags
ProblemType = enum(:rpmProblemType, %i[
badarch
bados
pkg_installed
badrelocate
requires
conflict
new_file_conflict
file_conflict
oldpackage
diskspace
disknodes
obsoletes
])
attach_function 'rpmProblemCreate', %i[rpmProblemType string fnpyKey string string uint64], :rpmProblem
attach_function 'rpmProblemFree', [:rpmProblem], :rpmProblem
attach_function 'rpmProblemLink', [:rpmProblem], :rpmProblem
attach_function 'rpmProblemGetType', [:rpmProblem], :rpmProblemType
attach_function 'rpmProblemGetKey', [:rpmProblem], :fnpyKey
attach_function 'rpmProblemGetStr', [:rpmProblem], :string
attach_function 'rpmProblemString', [:rpmProblem], :string
begin
attach_function 'rpmProblemCompare', %i[rpmProblem rpmProblem], :int
rescue ::FFI::NotFoundError
# TODO: Implement this for librpm 4.8.
def self.rpmProblemCompare(_a, _b)
raise NotImplementedError, 'rpmProblemCompare is not present in librpm 4.8 and below'
end
end
end
end

View File

@@ -0,0 +1,11 @@
module RPM
module C
typedef :pointer, :rpmps
typedef :pointer, :rpmpsi
attach_function 'rpmpsInitIterator', [:rpmps], :rpmpsi
attach_function 'rpmpsNextIterator', [:rpmpsi], :int
attach_function 'rpmpsGetProblem', [:rpmpsi], :rpmProblem
attach_function 'rpmpsFree', [:rpmps], :rpmps
end
end

View File

@@ -0,0 +1,304 @@
module RPM
module C
Tag = enum(:rpmTag, [
:not_found, -1,
:headerimage, 61,
:headersignatures, 62,
:headerimmutable, 63,
:headerregions, 64,
:headeri18ntable, 100,
:sig_base, 256,
:sigsize, 256 + 1,
:siglemd5_1, 256 + 2,
:sigpgp, 256 + 3,
:siglemd5_2, 256 + 4,
:sigmd5, 256 + 5,
:siggpg, 256 + 6,
:sigpgp5, 256 + 7,
:badsha1_1, 256 + 8,
:badsha1_2, 256 + 9,
:pubkeys, 256 + 10,
:dsaheader, 256 + 11,
:rsaheader, 256 + 12,
:sha1header, 256 + 13,
:longsigsize, 256 + 14,
:longarchivesize, 256 + 15,
:name, 1000,
:version, 1001,
:release, 1002,
:epoch, 1003,
:summary, 1004,
:description, 1005,
:buildtime, 1006,
:buildhost, 1007,
:installtime, 1008,
:size, 1009,
:distribution, 1010,
:vendor, 1011,
:gif, 1012,
:xpm, 1013,
:license, 1014,
:packager, 1015,
:group, 1016,
:changelog, 1017,
:source, 1018,
:patch, 1019,
:url, 1020,
:os, 1021,
:arch, 1022,
:prein, 1023,
:postin, 1024,
:preun, 1025,
:postun, 1026,
:oldfilenames, 1027,
:filesizes, 1028,
:filestates, 1029,
:filemodes, 1030,
:fileuids, 1031,
:filegids, 1032,
:filerdevs, 1033,
:filemtimes, 1034,
:filedigests, 1035,
:filemd5s, 1035,
:filelinktos, 1036,
:fileflags, 1037,
:root, 1038,
:fileusername, 1039,
:filegroupname, 1040,
:exclude, 1041,
:exclusive, 1042,
:icon, 1043,
:sourcerpm, 1044,
:fileverifyflags, 1045,
:archivesize, 1046,
:providename, 1047,
:requireflags, 1048,
:requirename, 1049,
:requireversion, 1050,
:nosource, 1051,
:nopatch, 1052,
:conflictflags, 1053,
:conflictname, 1054,
:conflictversion, 1055,
:defaultprefix, 1056,
:buildroot, 1057,
:installprefix, 1058,
:excludearch, 1059,
:excludeos, 1060,
:exclusivearch, 1061,
:exclusiveos, 1062,
:autoreqprov, 1063,
:rpmversion, 1064,
:triggerscripts, 1065,
:triggername, 1066,
:triggerversion, 1067,
:triggerflags, 1068,
:triggerindex, 1069,
:verifyscript, 1079,
:changelogtime, 1080,
:changelogname, 1081,
:changelogtext, 1082,
:brokenmd5, 1083,
:prereq, 1084,
:preinprog, 1085,
:postinprog, 1086,
:preunprog, 1087,
:postunprog, 1088,
:buildarchs, 1089,
:obsoletename, 1090,
:verifyscriptprog, 1091,
:triggerscriptprog, 1092,
:docdir, 1093,
:cookie, 1094,
:filedevices, 1095,
:fileinodes, 1096,
:filelangs, 1097,
:prefixes, 1098,
:instprefixes, 1099,
:triggerin, 1100,
:triggerun, 1101,
:triggerpostun, 1102,
:autoreq, 1103,
:autoprov, 1104,
:capability, 1105,
:sourcepackage, 1106,
:oldorigfilenames, 1107,
:buildprereq, 1108,
:buildrequires, 1109,
:buildconflicts, 1110,
:buildmacros, 1111,
:provideflags, 1112,
:provideversion, 1113,
:obsoleteflags, 1114,
:obsoleteversion, 1115,
:dirindexes, 1116,
:basenames, 1117,
:dirnames, 1118,
:origdirindexes, 1119,
:origbasenames, 1120,
:origdirnames, 1121,
:optflags, 1122,
:disturl, 1123,
:payloadformat, 1124,
:payloadcompressor, 1125,
:payloadflags, 1126,
:installcolor, 1127,
:installtid, 1128,
:removetid, 1129,
:sha1rhn, 1130,
:rhnplatform, 1131,
:platform, 1132,
:patchesname, 1133,
:patchesflags, 1134,
:patchesversion, 1135,
:cachectime, 1136,
:cachepkgpath, 1137,
:cachepkgsize, 1138,
:cachepkgmtime, 1139,
:filecolors, 1140,
:fileclass, 1141,
:classdict, 1142,
:filedependsx, 1143,
:filedependsn, 1144,
:dependsdict, 1145,
:sourcepkgid, 1146,
:filecontexts, 1147,
:fscontexts, 1148,
:recontexts, 1149,
:policies, 1150,
:pretrans, 1151,
:posttrans, 1152,
:pretransprog, 1153,
:posttransprog, 1154,
:disttag, 1155,
:suggestsname, 1156,
:suggestsversion, 1157,
:suggestsflags, 1158,
:enhancesname, 1159,
:enhancesversion, 1160,
:enhancesflags, 1161,
:priority, 1162,
:cvsid, 1163,
:blinkpkgid, 1164,
:blinkhdrid, 1165,
:blinknevra, 1166,
:flinkpkgid, 1167,
:flinkhdrid, 1168,
:flinknevra, 1169,
:packageorigin, 1170,
:triggerprein, 1171,
:buildsuggests, 1172,
:buildenhances, 1173,
:scriptstates, 1174,
:scriptmetrics, 1175,
:buildcpuclock, 1176,
:filedigestalgos, 1177,
:variants, 1178,
:xmajor, 1179,
:xminor, 1180,
:repotag, 1181,
:keywords, 1182,
:buildplatforms, 1183,
:packagecolor, 1184,
:packageprefcolor, 1185,
:xattrsdict, 1186,
:filexattrsx, 1187,
:depattrsdict, 1188,
:conflictattrsx, 1189,
:obsoleteattrsx, 1190,
:provideattrsx, 1191,
:requireattrsx, 1192,
:buildprovides, 1193,
:buildobsoletes, 1194,
:dbinstance, 1195,
:nvra, 1196,
:filenames, 5000,
:fileprovide, 5001,
:filerequire, 5002,
:fsnames, 5003,
:fssizes, 5004,
:triggerconds, 5005,
:triggertype, 5006,
:origfilenames, 5007,
:longfilesizes, 5008,
:longsize, 5009,
:filecaps, 5010,
:filedigestalgo, 5011,
:bugurl, 5012,
:evr, 5013,
:nvr, 5014,
:nevr, 5015,
:nevra, 5016,
:headercolor, 5017,
:verbose, 5018,
:epochnum, 5019,
:preinflags, 5020,
:postinflags, 5021,
:preunflags, 5022,
:postunflags, 5023,
:pretransflags, 5024,
:posttransflags, 5025,
:verifyscriptflags, 5026,
:triggerscriptflags, 5027,
:collections, 5029,
:policynames, 5030,
:policytypes, 5031,
:policytypesindexes, 5032,
:policyflags, 5033,
:vcs, 5034,
:ordername, 5035,
:orderversion, 5036,
:orderflags, 5037,
:firstfree_tag
])
Dbi = enum(:rpmDbiTag_e, [
:packages, 0,
:label, 2,
:name, Tag[:name],
:basenames, Tag[:basenames],
:group, Tag[:group],
:requirename, Tag[:requirename],
:providename, Tag[:providename],
:conflictname, Tag[:conflictname],
:obsoletename, Tag[:obsoletename],
:triggername, Tag[:triggername],
:dirnames, Tag[:dirnames],
:installtid, Tag[:installtid],
:sigmd5, Tag[:sigmd5],
:sha1header, Tag[:sha1header]
])
TagType = enum(:rpmTagType, [
:null_type, 0,
:char_type, 1,
:int8_type, 2,
:int16_type, 3,
:int32_type, 4,
:int64_type, 5,
:string_type, 6,
:bin_type, 7,
:string_array_type, 8,
:i18nstring_type, 9
])
TagReturnType = enum(:rpmTagReturnType_e, [
:any_return_type, 0,
:scalar_return_type, 0x00010000,
:array_return_type, 0x00020000,
:mapping_return_type, 0x00040000,
:mask_return_type, 0xffff0000
])
typedef :rpmFlags, :rpmTagReturnType
begin
attach_function 'rpmTagGetReturnType', [:rpmTagVal], :rpmTagReturnType
rescue ::FFI::NotFoundError
attach_function 'rpmTagGetType', [:rpmTagVal], :rpmTagType
def self.rpmTagGetReturnType(tag)
TagReturnType[rpmTagGetType(tag) & TagReturnType[:mask_return_type]]
end
end
end
end

View File

@@ -0,0 +1,34 @@
module RPM
module C
typedef :pointer, :rpmtd
attach_function 'rpmtdNew', [], :pointer
attach_function 'rpmtdFree', [:rpmtd], :pointer
attach_function 'rpmtdReset', [:rpmtd], :void
attach_function 'rpmtdFreeData', [:rpmtd], :void
attach_function 'rpmtdCount', [:rpmtd], :uint32
attach_function 'rpmtdTag', [:rpmtd], :rpmTagVal
attach_function 'rpmtdType', [:rpmtd], TagType
# ...
attach_function 'rpmtdInit', [:rpmtd], :int
attach_function 'rpmtdNext', [:rpmtd], :int
# ...
attach_function 'rpmtdNextUint32', [:rpmtd], :pointer
attach_function 'rpmtdNextUint64', [:rpmtd], :pointer
attach_function 'rpmtdNextString', [:rpmtd], :string
attach_function 'rpmtdGetChar', [:rpmtd], :pointer
attach_function 'rpmtdGetUint16', [:rpmtd], :pointer
attach_function 'rpmtdGetUint32', [:rpmtd], :pointer
attach_function 'rpmtdGetUint64', [:rpmtd], :pointer
attach_function 'rpmtdGetString', [:rpmtd], :string
attach_function 'rpmtdGetNumber', [:rpmtd], :uint64
# ...
attach_function 'rpmtdFromUint8', %i[rpmtd rpmTagVal pointer rpm_count_t], :int
attach_function 'rpmtdFromUint16', %i[rpmtd rpmTagVal pointer rpm_count_t], :int
attach_function 'rpmtdFromUint32', %i[rpmtd rpmTagVal pointer rpm_count_t], :int
attach_function 'rpmtdFromUint64', %i[rpmtd rpmTagVal pointer rpm_count_t], :int
attach_function 'rpmtdFromString', %i[rpmtd rpmTagVal string], :int
attach_function 'rpmtdFromStringArray', %i[rpmtd rpmTagVal pointer rpm_count_t], :int
# ...
end
end

View File

@@ -0,0 +1,71 @@
module RPM
module C
TransFlags = enum(:rpmtransFlags_e, [
:none, 0,
:test, (1 << 0),
:build_probs, (1 << 1),
:noscripts, (1 << 2),
:justdb, (1 << 3),
:notriggers, (1 << 4),
:nodocs, (1 << 5),
:allfiles, (1 << 6),
# bit 7 unused
:nocontexts, (1 << 8),
# bits 9-15 unused
:notriggerprein, (1 << 16),
:nopre, (1 << 17),
:nopost, (1 << 18),
:notriggerin, (1 << 19),
:notriggerun, (1 << 20),
:nopreun, (1 << 21),
:nopostun, (1 << 22),
:notriggerpostun, (11 << 23),
# bits 24-25 unused
:nocollections, (1 << 26),
:nomd5, (1 << 27),
:nofiledigest, (1 << 27),
# bits 28-29 unused
:noconfigs, (1 << 30),
:deploops, (1 << 31)
])
typedef :pointer, :rpmts
typedef :pointer, :rpmps
typedef :rpmFlags, :rpmtransFlags
attach_function 'rpmtsCheck', [:rpmts], :int
attach_function 'rpmtsOrder', [:rpmts], :int
attach_function 'rpmtsRun', %i[rpmts rpmps int], :int
attach_function 'rpmtsLink', [:rpmts], :rpmts
attach_function 'rpmtsCloseDB', [:rpmts], :int
attach_function 'rpmtsOpenDB', %i[rpmts int], :int
attach_function 'rpmtsInitDB', %i[rpmts int], :int
attach_function 'rpmtsGetDBMode', [:rpmts], :int
attach_function 'rpmtsSetDBMode', %i[rpmts int], :int
attach_function 'rpmtsRebuildDB', [:rpmts], :int
attach_function 'rpmtsVerifyDB', [:rpmts], :int
attach_function 'rpmtsInitIterator', %i[rpmts rpmDbiTagVal pointer int], :rpmdbMatchIterator
# ...
attach_function 'rpmtsProblems', [:rpmts], :rpmps
# ...
attach_function 'rpmtsClean', [:rpmts], :void
# more...
attach_function 'rpmtsFree', [:rpmts], :pointer
# ..
attach_function 'rpmtsSetNotifyCallback', %i[rpmts rpmCallbackFunction rpmCallbackData], :int
# ...
attach_function 'rpmtsRootDir', [:rpmts], :string
attach_function 'rpmtsSetRootDir', %i[rpmts string], :int
# ...
attach_function 'rpmtsGetRdb', [:rpmts], :rpmdb
# ..
attach_function 'rpmtsFlags', [:rpmts], :rpmtransFlags
attach_function 'rpmtsSetFlags', %i[rpmts rpmtransFlags], :rpmtransFlags
# ...
attach_function 'rpmtsSetNotifyCallback', %i[rpmts rpmCallbackFunction rpmCallbackData], :int
# ...
attach_function 'rpmtsCreate', [], :rpmts
attach_function 'rpmtsAddInstallElement', %i[rpmts header fnpyKey int rpmRelocation], :int
attach_function 'rpmtsAddEraseElement', %i[rpmts header int], :int
end
end

View File

@@ -0,0 +1,28 @@
module RPM
module C
Rc = enum(
:ok, 0,
:notfound, 1,
:fail, 2,
:nottrusted, 3,
:nokey, 4
)
typedef :int32, :rpm_tag_t
typedef :uint32, :rpm_tagtype_t
typedef :uint32, :rpm_count_t
typedef :rpm_tag_t, :rpmTagVal
typedef :rpm_tag_t, :rpmDbiTagVal
typedef :uint32, :rpmFlags
typedef :uint32, :rpm_off_t
typedef :uint64, :rpm_loff_t
typedef :pointer, :FD_t
typedef :pointer, :fnpyKey
typedef :pointer, :rpmCallbackData
typedef :uint64, :rpm_loff_t
end
end

View File

@@ -0,0 +1,40 @@
module RPM
# compatibility
TAG.to_h.each do |k, v|
const_set "TAG_#{k.to_s.upcase}", v.to_i
end
LOG.to_h.each do |k, v|
const_set "LOG_#{k.to_s.upcase}", v.to_i
end
SENSE.to_h.each do |k, v|
const_set "SENSE_#{k.to_s.upcase}", v.to_i
end
# RPMFILE_*
FILE.to_h.each do |k, v|
const_set "FILE_#{k.to_s.upcase}", v.to_i
end
# RPMFILE_STATE_*
FILE_STATE.to_h.each do |k, v|
const_set "FILE_STATE_#{k.to_s.upcase}", v.to_i
end
# RPMTRANS_FLAG_*
TRANS_FLAG.to_h.each do |k, v|
const_set "TRANS_FLAG_#{k.to_s.upcase}", v.to_i
end
# RPMPROB_FILTER_*
PROB_FILTER.to_h.each do |k, v|
const_set "PROB_FILTER_#{k.to_s.upcase}", v.to_i
end
# RPMPROB_FILTER_*
MIRE.to_h.each do |k, v|
const_set "MIRE_#{k.to_s.upcase}", v.to_i
end
end

View File

@@ -0,0 +1,117 @@
require 'fcntl'
module RPM
class DB
include Enumerable
# @visibility private
# @param ts [Transaction] transaction object
def initialize(ts, opts = {})
opts[:writable] ||= false
@ts = ts
RPM::C.rpmtsOpenDB(@ts.ptr, opts[:writable] ? Fcntl::O_RDWR | Fcntl::O_CREAT : Fcntl::O_RDONLY)
end
# @return [RPM::MatchIterator] Creates an iterator for +tag+ and +val+
def init_iterator(tag, val)
@ts.init_iterator(tag, val)
end
#
# @yield [Package] Called for each match
# @param [Number] key RPM tag key
# @param [String] val Value to match
# @example
# RPM.transaction do |t|
# t.each_match(RPM::TAG_ARCH, "x86_64") do |pkg|
# puts pkg.name
# end
# end
#
def each_match(key, val, &block)
@ts.each_match(key, val, &block)
end
#
# @yield [Package] Called for each package in the database
# @example
# db.each do |pkg|
# puts pkg.name
# end
#
def each(&block)
@ts.each(&block)
end
# @visibility private
def ptr
RPM::C.rpmtsGetRdb(@ts.ptr)
end
def close
RPM::C.rpmtsCloseDB(@ts.ptr)
end
def closed?
ptr.null?
end
#
# The package database is opened, but transactional processing
# (@see RPM::DB#transaction) cannot be done for when +writable+ is false.
# When +writable+ is +false+ then the generated object gets freezed.
# @param [Boolean] writable Whether the database is writable. Default is +false+.
# @param [String] root Root path for the database, default is empty.
# @return [RPM::DB]
#
# @example
# db = RPM::DB.open
# db.each do |pkg|
# puts pkg.name
# end
#
def self.open(_writable = false, root = '/', &block)
open_for_transaction(Transaction.new(root: root), writable: false, &block)
end
# @visibility private
def self.open_for_transaction(ts, opts = {})
db = new(ts, opts)
return db unless block_given?
begin
yield db
ensure
db.close unless db.closed?
end
end
# @deprecated Not possible to get home value in
# newer RPM versions
def home
raise NotImplementedError
end
# @return [String] The root path of the database
def root
RPM::C.rpmtsRootDir(@ts.ptr)
end
# @deprecated Use RPM::Transaction#each
def self.each
DB.open do |db|
it = MatchIterator.from_ptr(RPM::C.rpmdbInitIterator(db.ptr, 0, nil, 0))
if block_given?
it.each do |pkg|
yield pkg
end
end
end
end
# @return number of instances of +name+ in the
# database
def count_packages(name); end
end
end

View File

@@ -0,0 +1,120 @@
module RPM
class Dependency
# @return [String] dependency name
attr_accessor :name
# @return [String] dependency version
attr_accessor :version
# @return [String] dependency flags
attr_accessor :flags
# @return [Package] package this dependency belongs to
attr_accessor :owner
attr_accessor :nametag
attr_accessor :versiontag
attr_accessor :flagstag
def initialize(name, version, flags, owner)
RPM::Utils.check_type(version, RPM::Version)
@name = name
@version = version
@flags = flags
@owner = owner
end
# @param [Package, Dependency, Version] other
# @return [Boolean] true if +other+ satisfies this dependency
def satisfy?(other)
case other
when RPM::Package then
other.provides.each do |prov|
return true if satisfy?(prov)
end
false
when RPM::Dependency then
RPM::C.rpmdsCompare(
RPM::C.rpmdsSingle(:providename, other.name,
other.version.to_vre, other.flags),
RPM::C.rpmdsSingle(:providename, name,
version.to_vre, flags)
) != 0
when RPM::Version then
RPM::C.rpmdsCompare(
RPM::C.rpmdsSingle(:providename, name,
other.to_vre, other.to_vre.empty? ? 0 : :equal),
RPM::C.rpmdsSingle(:providename, name,
version.to_vre, flags)
) != 0
else
raise(TypeError, "#{other} is not a Version or Dependency")
end
end
# @return [Boolean] true if '<' or '=<' are used to compare the version
def lt?
flags & RPM::SENSE[:less]
end
# @return [Boolean] true if '>' or '>=' are used to compare the version
def gt?
flags & RPM::SENSE[:greater]
end
# @return [Boolean] true if '=', '=<' or '>=' are used to compare the version
def eq?
flags & RPM::SENSE[:equal]
end
# @return [Boolean] true if '=<' is used to compare the version
def le?
(flags & RPM::SENSE[:less]) && (flags & RPM::SENSE[:equal])
end
# @return [Boolean] true if '>=' is used to compare the version
def ge?
(flags & RPM::SENSE[:greater]) && (flags & RPM::SENSE[:equal])
end
# @return [Boolean] true if this is a pre-requires
def pre?
flags & RPM::SENSE[:prereq]
end
end
class Provide < Dependency
def initialize(name, version, flags, owner)
super(name, version, flags, owner)
@nametag = RPM::TAG[:providename]
@versiontag = RPM::TAG[:provideversion]
@flagstag = RPM::TAG[:provideflags]
end
end
class Require < Dependency
def initialize(name, version, flags, owner)
super(name, version, flags, owner)
@nametag = RPM::TAG[:requirename]
@versiontag = RPM::TAG[:requireversion]
@flagstag = RPM::TAG[:requireflags]
end
end
class Conflict < Dependency
def initialize(name, version, flags, owner)
super(name, version, flags, owner)
@nametag = RPM::TAG[:conflictname]
@versiontag = RPM::TAG[:conflictversion]
@flagstag = RPM::TAG[:conflictflags]
end
end
class Obsolete < Dependency
def initialize(name, version, flags, owner)
super(name, version, flags, owner)
@nametag = RPM::TAG[:obsoletename]
@versiontag = RPM::TAG[:obsoleteversion]
@flagstag = RPM::TAG[:obsoleteflags]
end
end
end

View File

@@ -0,0 +1,134 @@
# coding: utf-8
module RPM
class File
# @return [String] file path
attr_accessor :path
# @return [String] md5sum as string
attr_accessor :md5sum
# @return [String] Path to the destination if the file is a symbolic link
# @note
# This path is sometimes relative. To convert an absolute path from relative path:
# File.expand_path (file.link_to, File.dirname (file.path))
attr_accessor :link_to
# @return [Number] File size
attr_accessor :size
# @return [Time] File modification time.
attr_accessor :mtime
# @return [String] File owner. Nil may be returned.
attr_accessor :owner
# @return [String] Group that owns the file. Nil may be returned.
attr_accessor :group
# @return [Number] Device type of the file
attr_accessor :mode
attr_accessor :attr
attr_accessor :state
attr_accessor :rdev
# @return [Boolean] True if the file is a symbolic link
def symlink?
!@link_to.nil?
end
# @return [Boolean] True if the file is marked as a configuration file
def config?
!(@attr & RPM::C::FileAttrs[:config]).zero?
end
# @return [Boolean] True if the file is marked as documentation
def doc?
!(@attr & RPM::C::FileAttrs[:doc]).zero?
end
# @return [Boolean] True if the file is marked as do not use
# @deprecated RPMFILE_DONOTUSE was removed in recent versions of RPM.
def donotuse?
msg = 'RPMFILE_DONOTUSE was removed in recent versions of RPM.'
warn "#{Kernel.caller.first} #{msg}"
raise NotImplementedError
end
# @return [Boolean] True if the file is marked that can be missing on disk
#
# This modifier is used for files or links that are created during the %post scripts
# but will need to be removed if the package is removed
def is_missingok?
!(@attr & RPM::C::FileAttrs[:missingok]).zero?
end
# @return [Boolean] True if the file is marked as configuration not to be replaced
#
# This flag is used to protect local modifications.
# If used, the file will not overwrite an existing file that has been modified.
# If the file has not been modified on disk, the rpm command will overwrite the file. But,
# if the file has been modified on disk, the rpm command will copy the new file with an extra
# file-name extension of .rpmnew.
def is_noreplace?
!(@attr & RPM::C::FileAttrs[:noreplace]).zero?
end
# @return [Boolean] True if the file is marked as a spec file
def is_specfile?
!(@attr & RPM::C::FileAttrs[:specfile]).zero?
end
# @return [Boolean] True if the file is marked as ghost
#
# This flag indicates the file should not be included in the package.
# It can be used to name the needed attributes for a file that the program, when installed,
# will create.
# For example, you may want to ensure that a programs log file has certain attributes.
def ghost?
!(@attr & RPM::C::FileAttrs[:ghost]).zero?
end
# @return [Boolean] True if the file is a license
def license?
!(@attr & RPM::C::FileAttrs[:license]).zero?
end
# @return [Boolean] True if the file is a README
def readme?
!(@attr & RPM::C::FileAttrs[:readme]).zero?
end
# @raise NotImplementedError
# @deprecated RPMFILE_EXCLUDE was removed in recent versions of RPM.
def exclude?
msg = 'RPMFILE_EXCLUDE was removed in recent versions of RPM.'
warn "#{Kernel.caller.first} #{msg}"
raise NotImplementedError
end
# @return [Boolean] True if the file is replaced during installation
def replaced?
!(@attr & RPM::C::FileState[:replaced]).zero?
end
# @return [Boolean] True if the file is not installed
def notinstalled?
!(@attr & RPM::C::FileState[:notinstalled]).zero?
end
# @return [Boolean] True if the file is shared over the network
def netshared?
!(@attr & RPM::C::FileState[:netshared]).zero?
end
def initialize(path, md5sum, link_to, size, mtime, owner, group, rdev, mode, attr, state)
@path = path
@md5sum = md5sum
# If link_to is "" save it as nil
@link_to = (link_to && link_to.empty? ? nil : link_to)
@size = size
@mtime = mtime
@owner = owner
@group = group
@rdev = rdev
@mode = mode
@attr = attr
@state = state
end
end
end

View File

@@ -0,0 +1,7 @@
# The reason this file is gem_version.rb and not version.rb
# is because it conflicts with the version.rb class
module RPM
PKG_NAME = 'ruby-rpm'.freeze
GEM_VERSION = '0.0.5'.freeze
end

View File

@@ -0,0 +1,66 @@
module RPM
class MatchIterator
include Enumerable
# @visibility private
def self.release(ptr)
RPM::C.rpmdbFreeIterator(ptr)
end
# Creates a managed MatchIterator from a raw pointer
# @visibility private
def self.from_ptr(ptr)
new(::FFI::AutoPointer.new(ptr, MatchIterator.method(:release)))
end
def initialize(ptr)
@ptr = ptr
end
def each
while (pkg = next_iterator)
yield pkg
end
end
def next_iterator
pkg_ptr = RPM::C.rpmdbNextIterator(@ptr)
return RPM::Package.new(pkg_ptr) unless pkg_ptr.null?
nil
end
# @ return header join key for current position of rpm
# database iterator
def offset
RPM::C.rpmdbGetIteratorOffset(@ptr)
end
def set_iterator_re(tag, mode, string)
ret = RPM::C.rpmdbSetIteratorRE(@ptr, tag, mode, string)
raise "Error when setting regular expression '#{string}'" if ret != 0
self
end
alias regexp set_iterator_re
def set_iterator_version(version)
unless version.is_a?(RPM::Version)
raise TypeError, 'illegal argument type'
end
set_iterator_re(:version, :default, version.v)
set_iterator_re(:release, :default, version.r) if version.r
self
end
alias version set_iterator_version
def get_iterator_count
RPM::C.rpmdbGetIteratorCount(@ptr)
end
alias count get_iterator_count
alias length get_iterator_count
end
end

View File

@@ -0,0 +1,333 @@
module RPM
class ChangeLog
attr_accessor :time, :name, :text
end
class Package
# Create a new package object from data
# @param [String] str Header data
# @return [Package]
def load(_data)
raise NotImplementedError
end
def self.create(name, version)
unless name.is_a?(String)
raise TypeError, 'illegal argument type: name should be String'
end
unless version.is_a?(RPM::Version)
raise TypeError, 'illegal argument type: version should be RPM::Version'
end
hdr = RPM::C.headerNew
if RPM::C.headerPutString(hdr, :name, name) != 1
raise "Can't set package name: #{name}"
end
if RPM::C.headerPutString(hdr, :version, version.v) != 1
raise "Can't set package version: #{version.v}"
end
if version.e
if RPM::C.headerPutUint32(hdr, :epoch, version.e) != 1
raise "Can't set package epoch: #{version.e}"
end
end
Package.new(hdr)
end
# Add a dependency to the package header
# @param [Dependency] dep Dependency to add
def add_dependency(dep)
unless dep.is_a?(Dependency)
raise TypeError, 'illegal argument type: must be a Dependency'
end
raise NotImplementedError
end
# Add a int32 value to the package header
# @param [Number] tag Tag
# @param [Number] val Value
def add_int32(_tag, _val)
raise NotImplementedError
end
# Add a list of strings to the package header
# @param [Number] tag Tag
# @param [Array<String>] val Strings to add
def add_string_array(_tag, _val)
raise NotImplementedError
end
# Add a binary value to the package header
# @param [Number] tag Tag
# @param [String] val String to add
def add_string(_tag, _val)
raise NotImplementedError
end
# Add a binary value to the package header
# @param [Number] tag Tag
# @param [String] val Value
def add_binary(_tag, _val)
raise NotImplementedError
end
# Deletes a tag of the package header
# @param [Number] tag Tag
def delete_tag(_tag)
raise NotImplementedError
end
# @return a formated string
# @example
# pkg.sprintf("%{name}") => "apache2"
def sprintf(fmt)
error = ::FFI::MemoryPointer.new(:pointer, 1)
val = RPM::C.headerFormat(@hdr, fmt, error)
raise error.get_pointer(0).read_string if val.null?
val.read_string
end
# @return [Number] This package signature
def signature
sprintf('%{sigmd5}')
end
# @return [Array<RPM::File>] File list for this package
def files
basenames = self[:basenames]
return [] if basenames.nil?
dirnames = self[:dirnames]
diridxs = self[:dirindexes]
statelist = self[:filestates]
flaglist = self[:fileflags]
sizelist = self[:filesizes]
modelist = self[:filemodes]
mtimelist = self[:filemtimes]
rdevlist = self[:filerdevs]
linklist = self[:filelinktos]
md5list = self[:filemd5s]
ownerlist = self[:fileusername]
grouplist = self[:filegroupname]
ret = []
basenames.each_with_index do |_basename, i|
file = RPM::File.new("#{dirnames[diridxs[i]]}#{basenames[i]}",
md5list[i],
linklist[i],
sizelist[i],
mtimelist[i],
ownerlist[i],
grouplist[i],
rdevlist[i],
modelist[i],
flaglist.nil? ? RPM::C::FileAttrs[:none] : flaglist[i],
statelist.nil? ? RPM::C::FileState[:normal] : statelist[i])
ret << file
end
ret
end
# @return [Array<RPM::Dependency>] Dependencies for +klass+
# @example
# dependencies(RPM::Provide, :providename, :provideversion, :provideflags)
#
# @visibility private
def dependencies(klass, nametag, versiontag, flagtag)
deps = []
nametd = ::FFI::AutoPointer.new(RPM::C.rpmtdNew, Package.method(:release_td))
versiontd = ::FFI::AutoPointer.new(RPM::C.rpmtdNew, Package.method(:release_td))
flagtd = ::FFI::AutoPointer.new(RPM::C.rpmtdNew, Package.method(:release_td))
min = RPM::C::HEADERGET_MINMEM
return deps if RPM::C.headerGet(@hdr, nametag, nametd, min) != 1
return deps if RPM::C.headerGet(@hdr, versiontag, versiontd, min) != 1
return deps if RPM::C.headerGet(@hdr, flagtag, flagtd, min) != 1
RPM::C.rpmtdInit(nametd)
while RPM::C.rpmtdNext(nametd) != -1
deps << klass.new(RPM::C.rpmtdGetString(nametd),
RPM::Version.new(RPM::C.rpmtdNextString(versiontd)),
RPM::C.rpmtdNextUint32(flagtd).read_uint, self)
end
deps
end
# @return [Array<RPM::Provide>] Provides list for this package
def provides
dependencies(RPM::Provide, :providename, :provideversion, :provideflags)
end
# @return [Array<RPM::Require>] Requires list for this package
def requires
dependencies(RPM::Require, :requirename, :requireversion, :requireflags)
end
# @return [Array<RPM::Conflicts>] Conflicts list for this package
def conflicts
dependencies(RPM::Conflict, :conflictname, :conflictversion, :conflictflags)
end
# @return [Array<RPM::Obsolete>] Obsoletes list for this package
def obsoletes
dependencies(RPM::Obsolete, :obsoletename, :obsoleteversion, :obsoleteflags)
end
# @return [Array<RPM::Changelog>] changelog of the package as an array
def changelog
entries = []
nametd = ::FFI::AutoPointer.new(RPM::C.rpmtdNew, Package.method(:release_td))
timetd = ::FFI::AutoPointer.new(RPM::C.rpmtdNew, Package.method(:release_td))
texttd = ::FFI::AutoPointer.new(RPM::C.rpmtdNew, Package.method(:release_td))
min = RPM::C::HEADERGET_MINMEM
return deps if RPM::C.headerGet(@hdr, :changelogtime, timetd, min) != 1
return deps if RPM::C.headerGet(@hdr, :changelogname, nametd, min) != 1
return deps if RPM::C.headerGet(@hdr, :changelogtext, texttd, min) != 1
RPM::C.rpmtdInit(timetd)
while RPM::C.rpmtdNext(timetd) != -1
entry = RPM::ChangeLog.new
entry.time = RPM::C.rpmtdGetUint32(timetd)
entry.name = RPM::C.rpmtdNextString(nametd)
entry.text = RPM::C.rpmtdNextString(texttd)
entries << entry
end
entries
end
# Access a header entry
# @param [Number] tag Tag to return
# @return [] Value of the entry
# @example
# pkg => #<RPM::Package name="xmlgraphics-fop", version=#<RPM::Version v="1.0", r="22.4">>
# pkg[:name] => "xmlgraphics-fop"
#
# or if you have the old ruby-rpm compat loaded
#
# require 'rpm/compat'
# pkg[RPM::TAG_NAME] => "xmlgraphics-fop"
#
# @return [String, Fixnum, Array<String>, Array<Fixnum>, nil]
# The value of the entry
def [](tag)
val = nil
tagc = ::FFI::AutoPointer.new(RPM::C.rpmtdNew, Package.method(:release_td))
return nil if RPM::C.headerGet(ptr, tag, tagc,
RPM::C::HEADERGET_MINMEM) == 0
type = RPM::C.rpmtdType(tagc)
count = RPM::C.rpmtdCount(tagc)
ret_type = RPM::C.rpmTagGetReturnType(tag)
method_name = case type
when :int8_type, :char_type, :int16_type, :int32_type, :int64_type then :rpmtdGetNumber
when :string_type, :string_array_type, :bin_type then :rpmtdGetString
else raise NotImplementedError, "Don't know how to retrieve type '#{type}'"
end
is_array = if count > 1 then true
elsif ret_type == :array_return_type then true
elsif type == :string_array_type then true
else false
end
if is_array
ret = []
RPM::C.rpmtdInit(tagc)
ret << RPM::C.send(method_name, tagc) while RPM::C.rpmtdNext(tagc) != -1
return ret
end
RPM::C.send(method_name, tagc)
end
# @return [String] This package name
def name
self[:name]
end
# @return [String] This package architecture
def arch
self[:arch]
end
# TODO: signature
# @return [Version] Version for this package
def version
Version.new(self[:version], self[:release], self[:epoch])
end
# String representation of the package: "name-version-release-arch"
# @return [String]
def to_s
return '' if name.nil?
return name if version.nil?
return "#{name}-#{version}" if arch.nil?
"#{name}-#{version}-#{arch}"
end
def self.open(filename)
Package.new(filename)
end
# @visibility private
def self.release(ptr)
RPM::C.headerFree(ptr)
end
# @visibility private
def self.release_td(ptr)
RPM::C.rpmtdFree(ptr)
end
# @visibility private
def initialize(what)
case what
when String then initialize_from_filename(what)
else initialize_from_header(what)
end
end
# @visibility private
def initialize_from_header(hdr = nil)
if hdr.nil?
@hdr = ::FFI::AutoPointer.new(RPM::C.headerNew, Header.method(:release))
elsif hdr.is_a?(::FFI::Pointer)
# ref
hdr = RPM::C.headerLink(hdr)
@hdr = ::FFI::AutoPointer.new(hdr, Package.method(:release))
else
raise "Can't initialize header with '#{hdr}'"
end
end
def initialize_from_filename(filename)
# it sucks not using the std File.open here
hdr = ::FFI::MemoryPointer.new(:pointer)
fd = nil
begin
fd = RPM::C.Fopen(filename, 'r')
raise "#{filename} : #{RPM::C.Fstrerror(fd)}" if RPM::C.Ferror(fd) != 0
RPM.transaction do |ts|
rc = RPM::C.rpmReadPackageFile(ts.ptr, fd, filename, hdr)
end
ensure
RPM::C.Fclose(fd) unless fd.nil?
end
initialize_from_header(hdr.get_pointer(0))
end
# @return [RPM::C::Header] header pointer
# @visibility private
def ptr
@hdr
end
end
end

View File

@@ -0,0 +1,65 @@
module RPM
class Problem
def self.release(ptr)
RPM::C.rpmProblemFree(ptr)
end
# Creates a problem from an existing C pointer, refcounting it
# first.
# @param [FFI::Pointer] ptr existing C pointer
# @return [RPM::Problem] wrapped object
def self.from_ptr(ptr)
case ptr
when FFI::Pointer
new(FFI::AutoPointer.new(RPM::C.rpmProblemLink(ptr), Problem.method(:release)))
else
raise "Can't initialize header with '#{ptr}'"
end
end
# Create a problem item.
# @param [RPM::ProblemType] type problem type
# @param [String] pkg_nver name-version-edition-release of the related package
# @param [String] key key of the related package
# @param [String] alt_nver name-version-edition-release of the other related package
# @param [String] str generic data string from a problem
def self.create(type, pkg_nevr, key, alt_nevr, str, number)
ptr = ::FFI::AutoPointer.new(RPM::C.rpmProblemCreate(type, pkg_nevr, key, alt_nevr, str, number), Problem.method(:release))
new(ptr)
end
# @visibility private
def initialize(ptr)
@ptr = ptr
end
# @return [RPM::ProblemType] type of problem (dependency, diskpace etc).
def type
RPM::C.rpmProblemGetType(@ptr)
end
# @return [String] filename or python object address of a problem.
def key
RPM::C.rpmProblemGetKey(@ptr).read_string
end
# @return [String] a generic data string from a problem.
def str
RPM::C.rpmProblemGetStr(@ptr)
end
# @return [String] formatted string representation of a problem
def to_s
RPM::C.rpmProblemString(@ptr)
end
# @return [Fixnum] compare two problems for equality.
def <=>(other)
RPM::C.rpmProblemCompare(@ptr, other.ptr)
end
# @visibility private
attr_reader :ptr
end
end

View File

@@ -0,0 +1,270 @@
module RPM
CallbackData = Struct.new(:type, :key, :package, :amount, :total) do
def to_s
"#{type} #{key} #{package} #{amount} #{total}"
end
end
class Transaction
def self.release(ptr)
RPM::C.rpmtsFree(ptr)
end
def initialize(opts = {})
# http://markmail.org/message/ypsiqxop442p7rzz
# The key pointer needs to stay valid during commit
# so we keep a reference to them mapping from
# object_id to ruby object.
@keys = {}
opts[:root] ||= '/'
@ptr = ::FFI::AutoPointer.new(RPM::C.rpmtsCreate, Transaction.method(:release))
RPM::C.rpmtsSetRootDir(@ptr, opts[:root])
end
# @return [RPM::MatchIterator] Creates an iterator for +tag+ and +val+
def init_iterator(tag, val)
raise TypeError if val && !val.is_a?(String)
it_ptr = RPM::C.rpmtsInitIterator(@ptr, tag.nil? ? 0 : tag, val, 0)
raise "Can't init iterator for [#{tag}] -> '#{val}'" if it_ptr.null?
MatchIterator.from_ptr(it_ptr)
end
# @visibility private
attr_reader :ptr
#
# @yield [Package] Called for each match
# @param [Number] key RPM tag key
# @param [String] val Value to match
# @example
# RPM.transaction do |t|
# t.each_match(RPM::TAG_ARCH, "x86_64") do |pkg|
# puts pkg.name
# end
# end
#
def each_match(key, val, &block)
it = init_iterator(key, val)
return it unless block_given?
it.each(&block)
end
#
# @yield [Package] Called for each package in the database
# @example
# db.each do |pkg|
# puts pkg.name
# end
#
def each(&block)
each_match(0, nil, &block)
end
# Add a install operation to the transaction
# @param [Package] pkg Package to install
# @param [String] key e.g. filename where to install from
def install(pkg, key)
install_element(pkg, key, upgrade: false)
end
# Add an upgrade operation to the transaction
# @param [Package] pkg Package to upgrade
# @param [String] key e.g. filename where to install from
def upgrade(pkg, key)
install_element(pkg, key, upgrade: true)
end
# Add a delete operation to the transaction
# @param [String, Package, Dependency] pkg Package to delete
def delete(pkg)
iterator = case pkg
when Package
pkg[:sigmd5] ? each_match(:sigmd5, pkg[:sigmd5]) : each_match(:label, pkg[:label])
when String
each_match(:label, pkg)
when Dependency
each_match(:label, pkg.name).set_iterator_version(pkg.version)
else
raise TypeError, 'illegal argument type'
end
iterator.each do |header|
ret = RPM::C.rpmtsAddEraseElement(@ptr, header.ptr, iterator.offset)
raise "Error while adding erase/#{pkg} to transaction" if ret != 0
end
end
# Sets the root directory for this transaction
# @param [String] root directory
def root_dir=(dir)
rc = RPM::C.rpmtsSetRootDir(@ptr, dir)
raise "Can't set #{dir} as root directory" if rc < 0
end
# @return [String ] the root directory for this transaction
def root_dir
RPM::C.rpmtsRootDir(@ptr)
end
def flags=(fl)
RPM::C.rpmtsSetFlags(@ptr, fl)
end
def flags
RPM::C.rpmtsFlags(@ptr)
end
# Determine package order in the transaction according to dependencies
#
# The final order ends up as installed packages followed by removed
# packages, with packages removed for upgrades immediately following
# the new package to be installed.
#
# @returns [Fixnum] no. of (added) packages that could not be ordered
def order
RPM::C.rpmtsOrder(@ptr)
end
# Free memory needed only for dependency checks and ordering.
def clean
RPM::C.rpmtsClean(@ptr)
end
def check
rc = RPM::C.rpmtsCheck(@ptr)
probs = RPM::C.rpmtsProblems(@ptr)
return if rc < 0
begin
psi = RPM::C.rpmpsInitIterator(probs)
while RPM::C.rpmpsNextIterator(psi) >= 0
problem = Problem.from_ptr(RPM::C.rpmpsGetProblem(psi))
yield problem
end
ensure
RPM::C.rpmpsFree(probs)
end
end
# Performs the transaction.
# @param [Number] flag Transaction flags, default +RPM::TRANS_FLAG_NONE+
# @param [Number] filter Transaction filter, default +RPM::PROB_FILTER_NONE+
# @example
# transaction.commit
# You can supply your own callback
# @example
# transaction.commit do |data|
# end
# end
# @yield [CallbackData] sig Transaction progress
def commit
flags = RPM::C::TransFlags[:none]
callback = proc do |hdr, type, amount, total, key_ptr, data_ignored|
key_id = key_ptr.address
key = @keys.include?(key_id) ? @keys[key_id] : nil
if block_given?
package = hdr.null? ? nil : Package.new(hdr)
data = CallbackData.new(type, key, package, amount, total)
yield(data)
else
RPM::C.rpmShowProgress(hdr, type, amount, total, key, data_ignored)
end
end
# We create a callback to pass to the C method and we
# call the user supplied callback from there
#
# The C callback expects you to return a file handle,
# We expect from the user to get a File, which we
# then convert to a file handle to return.
callback = proc do |hdr, type, amount, total, key_ptr, data_ignored|
key_id = key_ptr.address
key = @keys.include?(key_id) ? @keys[key_id] : nil
if block_given?
package = hdr.null? ? nil : Package.new(hdr)
data = CallbackData.new(type, key, package, amount, total)
ret = yield(data)
# For OPEN_FILE we need to do some type conversion
# for certain callback types we need to do some
case type
when :inst_open_file
# For :inst_open_file the user callback has to
# return the open file
unless ret.is_a?(::File)
raise TypeError, "illegal return value type #{ret.class}. Expected File."
end
fdt = RPM::C.fdDup(ret.to_i)
if fdt.null? || RPM::C.Ferror(fdt) != 0
raise "Can't use opened file #{data.key}: #{RPM::C.Fstrerror(fdt)}"
RPM::C.Fclose(fdt) unless fdt.nil?
else
fdt = RPM::C.fdLink(fdt)
@fdt = fdt
end
# return the (RPM type) file handle
fdt
when :inst_close_file
fdt = @fdt
RPM::C.Fclose(fdt)
@fdt = nil
else
ret
end
else
# No custom callback given, use the default to show progress
RPM::C.rpmShowProgress(hdr, type, amount, total, key, data_ignored)
end
end
rc = RPM::C.rpmtsSetNotifyCallback(@ptr, callback, nil)
raise "Can't set commit callback" if rc != 0
rc = RPM::C.rpmtsRun(@ptr, nil, :none)
raise "#{self}: #{RPM::C.rpmlogMessage}" if rc < 0
if rc > 0
ps = RPM::C.rpmtsProblems(@ptr)
psi = RPM::C.rpmpsInitIterator(ps)
while RPM::C.rpmpsNextIterator(psi) >= 0
problem = Problem.from_ptr(RPM::C.rpmpsGetProblem(psi))
STDERR.puts problem
end
RPM::C.rpmpsFree(ps)
end
end
# @return [DB] the database associated with this transaction
def db
RPM::DB.new(self)
end
private
# @param [Package] pkg package to install
# @param [String] key e.g. filename where to install from
# @param opts options
# @option :upgrade Upgrade packages if true
def install_element(pkg, key, opts = {})
raise TypeError, 'illegal argument type' unless pkg.is_a?(RPM::Package)
raise ArgumentError, "#{self}: key '#{key}' must be unique" if @keys.include?(key.object_id)
# keep a reference to the key as rpmtsAddInstallElement will keep a copy
# of the passed pointer (we pass the object_id)
@keys[key.object_id] = key
ret = RPM::C.rpmtsAddInstallElement(@ptr, pkg.ptr, FFI::Pointer.new(key.object_id), opts[:upgrade] ? 1 : 0, nil)
raise RuntimeError if ret != 0
nil
end
end
end

View File

@@ -0,0 +1,7 @@
module RPM
module Utils
def self.check_type(var, type)
raise(TypeError, "wrong argument type #{var.class} (expected #{type.class})") unless var.is_a?(type)
end
end
end

View File

@@ -0,0 +1,146 @@
module RPM
class Version
include Comparable
# Parses a "epoch:version-release" string
# @return [Array] tuple [epoch, version, release]
def self.parse_evr(evr)
raise ArgumentError, "version can't be nil" if evr.nil?
version = evr
epoch = nil
release = nil
idx = version.rindex('-')
if idx
release = version[idx + 1..-1]
version = version[0..idx - 1]
end
idx = version.index(/\D/)
if idx && version[idx] == ':'
epoch = version[0..idx - 1]
version = version[idx + 1..-1]
end
[epoch ? epoch.to_i : nil, version, release]
end
#
# @overload new(vr, e = nil)
# Creates a version object from a string representation
# @param [String] vr version and release in the form "v-r"
# @param [Number] e epoch
# @return [Version]
# @overload new(v, r, e = nil)
# Creates a version object from a string representation
# @param [String] v version
# @param [String] r release
# @param [Number] e epoch
# @return [Version]
# @example
# RPM:: Version.new "1.0.0-3"
# RPM:: Version.new "1.04"
# RPM:: Version.new "1.0.0-3k", 1
# RPM:: Version.new "2.0.3", "5k"
#
def initialize(*argv)
case argv.size
when 0
raise(ArgumentError('wrong number of arguments (0 for 1..3)'))
when 1
RPM::Utils.check_type(argv[0], String)
@e, @v, @r = RPM::Version.parse_evr(argv[0])
when 2
# (vr, e)
RPM::Utils.check_type(argv[0], String)
@e, @v, @r = RPM::Version.parse_evr(argv[0])
raise(TypeError, 'illegal argument value') unless e.nil?
@e = argv[1].to_i
when 3
RPM::Utils.check_type(argv[0], String)
RPM::Utils.check_type(argv[1], String)
@v = argv[0]
@r = argv[1]
@e = argv[2].to_i
else
raise(ArgumentError("too many arguments (#{args.size} for 1..3)"))
end
end
# @return [String] the version component
attr_reader :v
# @return [String] the release component
# or +nil+
attr_reader :r
# @return [String] the epoch component
# or +nil+
attr_reader :e
# Comparison between versions
# @param [Version] other
# @return [Number] -1 if +other+ is greater than, 0 if +other+ is equal to,
# and +1 if other is less than version.
#
# @example
# v1 = RPM::Version.new("3.0-0",1)
# v2 = RPM::Version.new("3.1-0",1)
# v1 <=> v2
# => -1
#
def <=>(other)
RPM::Utils.check_type(other, RPM::Version)
ret = RPM::C.rpmvercmp(to_vre_epoch_zero, other.to_vre_epoch_zero)
end
# @param [Version] other Version to compare against
# @return [Boolean] true if the version is newer than +other+
def newer?(other)
self > other
end
# @param [Version] other Version to compare against
# @return [Boolean] true if the version is older than +other+
def older?(other)
self < other
end
# String representation in the form "v-r"
# @return [String]
# @note The epoch is not included
def to_vr
vr = @r.nil? ? @v.to_s : "#{@v}-#{@r}"
end
# String representation in the form "e:v-r"
# @return [String]
# @note The epoch is included if present
def to_vre(_opts = {})
vr = to_vr
vre = @e.nil? ? vr : "#{@e}:#{vr}"
end
# Alias for +to_vr+
# @see Version#to_vr
def to_s
to_vr
end
# Hash based on the version content
# @return [String]
def hash
h = @e.nil? ? 0 : @e
h = (h << 1) ^ @r.hash
h = (h << 1) ^ @v.hash
end
# String representation in the form "e:v-r"
# @return [String]
# @note The epoch is included always. As 0 if not present
def to_vre_epoch_zero
vr = to_vr
vre = @e.nil? ? "0:#{vr}" : "#{@e}:#{vr}"
end
end
end