rpm2 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.github/CODEOWNERS +1 -0
- data/.github/workflows/ci.yaml +25 -0
- data/.gitignore +8 -0
- data/.rubocop.yml +1 -0
- data/.rubocop_todo.yml +111 -0
- data/.whitesource +3 -0
- data/CHANGELOG.md +66 -0
- data/Gemfile +9 -0
- data/MIT-LICENSE +23 -0
- data/README.md +374 -0
- data/Rakefile +58 -0
- data/_docker/Dockerfile.ubi8 +8 -0
- data/_docker/Dockerfile.ubi9 +8 -0
- data/lib/rpm/c/header.rb +36 -0
- data/lib/rpm/c/rpmcallback.rb +27 -0
- data/lib/rpm/c/rpmcli.rb +6 -0
- data/lib/rpm/c/rpmdb.rb +20 -0
- data/lib/rpm/c/rpmds.rb +43 -0
- data/lib/rpm/c/rpmfi.rb +31 -0
- data/lib/rpm/c/rpmio.rb +17 -0
- data/lib/rpm/c/rpmlib.rb +12 -0
- data/lib/rpm/c/rpmlog.rb +23 -0
- data/lib/rpm/c/rpmmacro.rb +33 -0
- data/lib/rpm/c/rpmprob.rb +53 -0
- data/lib/rpm/c/rpmps.rb +11 -0
- data/lib/rpm/c/rpmtag.rb +304 -0
- data/lib/rpm/c/rpmtd.rb +34 -0
- data/lib/rpm/c/rpmts.rb +69 -0
- data/lib/rpm/c/rpmtypes.rb +28 -0
- data/lib/rpm/c.rb +49 -0
- data/lib/rpm/compat.rb +40 -0
- data/lib/rpm/db.rb +117 -0
- data/lib/rpm/dependency.rb +120 -0
- data/lib/rpm/file.rb +134 -0
- data/lib/rpm/gem_version.rb +7 -0
- data/lib/rpm/match_iterator.rb +66 -0
- data/lib/rpm/package.rb +332 -0
- data/lib/rpm/problem.rb +65 -0
- data/lib/rpm/transaction.rb +268 -0
- data/lib/rpm/utils.rb +7 -0
- data/lib/rpm/version.rb +146 -0
- data/lib/rpm.rb +88 -0
- data/lib/rpm2.rb +1 -0
- data/rpm2.gemspec +29 -0
- data/test/data/a.spec +49 -0
- data/test/data/simple-1.0-0.i586.rpm +0 -0
- data/test/data/simple.spec +38 -0
- data/test/data/simple_with_deps-1.0-0.i586.rpm +0 -0
- data/test/data/simple_with_deps.spec +41 -0
- data/test/helper.rb +7 -0
- data/test/test_dependency.rb +28 -0
- data/test/test_file.rb +36 -0
- data/test/test_lib.rb +33 -0
- data/test/test_package.rb +76 -0
- data/test/test_problem.rb +18 -0
- data/test/test_rpm.rb +33 -0
- data/test/test_transaction.rb +156 -0
- data/test/test_version.rb +64 -0
- metadata +133 -0
data/lib/rpm/package.rb
ADDED
@@ -0,0 +1,332 @@
|
|
1
|
+
|
2
|
+
module RPM
|
3
|
+
class ChangeLog
|
4
|
+
attr_accessor :time, :name, :text
|
5
|
+
end
|
6
|
+
|
7
|
+
class Package
|
8
|
+
# Create a new package object from data
|
9
|
+
# @param [String] str Header data
|
10
|
+
# @return [Package]
|
11
|
+
def load(_data)
|
12
|
+
raise NotImplementedError
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.create(name, version)
|
16
|
+
unless name.is_a?(String)
|
17
|
+
raise TypeError, 'illegal argument type: name should be String'
|
18
|
+
end
|
19
|
+
unless version.is_a?(RPM::Version)
|
20
|
+
raise TypeError, 'illegal argument type: version should be RPM::Version'
|
21
|
+
end
|
22
|
+
hdr = RPM::C.headerNew
|
23
|
+
if RPM::C.headerPutString(hdr, :name, name) != 1
|
24
|
+
raise "Can't set package name: #{name}"
|
25
|
+
end
|
26
|
+
if RPM::C.headerPutString(hdr, :version, version.v) != 1
|
27
|
+
raise "Can't set package version: #{version.v}"
|
28
|
+
end
|
29
|
+
if version.e
|
30
|
+
if RPM::C.headerPutUint32(hdr, :epoch, version.e) != 1
|
31
|
+
raise "Can't set package epoch: #{version.e}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
Package.new(hdr)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Add a dependency to the package header
|
38
|
+
# @param [Dependency] dep Dependency to add
|
39
|
+
def add_dependency(dep)
|
40
|
+
unless dep.is_a?(Dependency)
|
41
|
+
raise TypeError, 'illegal argument type: must be a Dependency'
|
42
|
+
end
|
43
|
+
|
44
|
+
raise NotImplementedError
|
45
|
+
end
|
46
|
+
|
47
|
+
# Add a int32 value to the package header
|
48
|
+
# @param [Number] tag Tag
|
49
|
+
# @param [Number] val Value
|
50
|
+
def add_int32(_tag, _val)
|
51
|
+
raise NotImplementedError
|
52
|
+
end
|
53
|
+
|
54
|
+
# Add a list of strings to the package header
|
55
|
+
# @param [Number] tag Tag
|
56
|
+
# @param [Array<String>] val Strings to add
|
57
|
+
def add_string_array(_tag, _val)
|
58
|
+
raise NotImplementedError
|
59
|
+
end
|
60
|
+
|
61
|
+
# Add a binary value to the package header
|
62
|
+
# @param [Number] tag Tag
|
63
|
+
# @param [String] val String to add
|
64
|
+
def add_string(_tag, _val)
|
65
|
+
raise NotImplementedError
|
66
|
+
end
|
67
|
+
|
68
|
+
# Add a binary value to the package header
|
69
|
+
# @param [Number] tag Tag
|
70
|
+
# @param [String] val Value
|
71
|
+
def add_binary(_tag, _val)
|
72
|
+
raise NotImplementedError
|
73
|
+
end
|
74
|
+
|
75
|
+
# Deletes a tag of the package header
|
76
|
+
# @param [Number] tag Tag
|
77
|
+
def delete_tag(_tag)
|
78
|
+
raise NotImplementedError
|
79
|
+
end
|
80
|
+
|
81
|
+
# @return a formated string
|
82
|
+
# @example
|
83
|
+
# pkg.sprintf("%{name}") => "apache2"
|
84
|
+
def sprintf(fmt)
|
85
|
+
error = ::FFI::MemoryPointer.new(:pointer, 1)
|
86
|
+
val = RPM::C.headerFormat(@hdr, fmt, error)
|
87
|
+
raise error.get_pointer(0).read_string if val.null?
|
88
|
+
val.read_string
|
89
|
+
end
|
90
|
+
|
91
|
+
# @return [Number] This package signature
|
92
|
+
def signature
|
93
|
+
sprintf('%{sigmd5}')
|
94
|
+
end
|
95
|
+
|
96
|
+
# @return [Array<RPM::File>] File list for this package
|
97
|
+
def files
|
98
|
+
basenames = self[:basenames]
|
99
|
+
|
100
|
+
return [] if basenames.nil?
|
101
|
+
|
102
|
+
dirnames = self[:dirnames]
|
103
|
+
diridxs = self[:dirindexes]
|
104
|
+
statelist = self[:filestates]
|
105
|
+
flaglist = self[:fileflags]
|
106
|
+
sizelist = self[:filesizes]
|
107
|
+
modelist = self[:filemodes]
|
108
|
+
mtimelist = self[:filemtimes]
|
109
|
+
rdevlist = self[:filerdevs]
|
110
|
+
linklist = self[:filelinktos]
|
111
|
+
md5list = self[:filemd5s]
|
112
|
+
ownerlist = self[:fileusername]
|
113
|
+
grouplist = self[:filegroupname]
|
114
|
+
|
115
|
+
ret = []
|
116
|
+
|
117
|
+
basenames.each_with_index do |_basename, i|
|
118
|
+
file = RPM::File.new("#{dirnames[diridxs[i]]}#{basenames[i]}",
|
119
|
+
md5list[i],
|
120
|
+
linklist[i],
|
121
|
+
sizelist[i],
|
122
|
+
mtimelist[i],
|
123
|
+
ownerlist[i],
|
124
|
+
grouplist[i],
|
125
|
+
rdevlist[i],
|
126
|
+
modelist[i],
|
127
|
+
flaglist.nil? ? RPM::C::FileAttrs[:none] : flaglist[i],
|
128
|
+
statelist.nil? ? RPM::C::FileState[:normal] : statelist[i])
|
129
|
+
ret << file
|
130
|
+
end
|
131
|
+
ret
|
132
|
+
end
|
133
|
+
|
134
|
+
# @return [Array<RPM::Dependency>] Dependencies for +klass+
|
135
|
+
# @example
|
136
|
+
# dependencies(RPM::Provide, :providename, :provideversion, :provideflags)
|
137
|
+
#
|
138
|
+
# @visibility private
|
139
|
+
def dependencies(klass, nametag, versiontag, flagtag)
|
140
|
+
deps = []
|
141
|
+
|
142
|
+
nametd = ::FFI::AutoPointer.new(RPM::C.rpmtdNew, Package.method(:release_td))
|
143
|
+
versiontd = ::FFI::AutoPointer.new(RPM::C.rpmtdNew, Package.method(:release_td))
|
144
|
+
flagtd = ::FFI::AutoPointer.new(RPM::C.rpmtdNew, Package.method(:release_td))
|
145
|
+
|
146
|
+
min = RPM::C::HEADERGET_MINMEM
|
147
|
+
return deps if RPM::C.headerGet(@hdr, nametag, nametd, min) != 1
|
148
|
+
return deps if RPM::C.headerGet(@hdr, versiontag, versiontd, min) != 1
|
149
|
+
return deps if RPM::C.headerGet(@hdr, flagtag, flagtd, min) != 1
|
150
|
+
|
151
|
+
RPM::C.rpmtdInit(nametd)
|
152
|
+
while RPM::C.rpmtdNext(nametd) != -1
|
153
|
+
deps << klass.new(RPM::C.rpmtdGetString(nametd),
|
154
|
+
RPM::Version.new(RPM::C.rpmtdNextString(versiontd)),
|
155
|
+
RPM::C.rpmtdNextUint32(flagtd).read_uint, self)
|
156
|
+
end
|
157
|
+
deps
|
158
|
+
end
|
159
|
+
|
160
|
+
# @return [Array<RPM::Provide>] Provides list for this package
|
161
|
+
def provides
|
162
|
+
dependencies(RPM::Provide, :providename, :provideversion, :provideflags)
|
163
|
+
end
|
164
|
+
|
165
|
+
# @return [Array<RPM::Require>] Requires list for this package
|
166
|
+
def requires
|
167
|
+
dependencies(RPM::Require, :requirename, :requireversion, :requireflags)
|
168
|
+
end
|
169
|
+
|
170
|
+
# @return [Array<RPM::Conflicts>] Conflicts list for this package
|
171
|
+
def conflicts
|
172
|
+
dependencies(RPM::Conflict, :conflictname, :conflictversion, :conflictflags)
|
173
|
+
end
|
174
|
+
|
175
|
+
# @return [Array<RPM::Obsolete>] Obsoletes list for this package
|
176
|
+
def obsoletes
|
177
|
+
dependencies(RPM::Obsolete, :obsoletename, :obsoleteversion, :obsoleteflags)
|
178
|
+
end
|
179
|
+
|
180
|
+
# @return [Array<RPM::Changelog>] changelog of the package as an array
|
181
|
+
def changelog
|
182
|
+
entries = []
|
183
|
+
nametd = ::FFI::AutoPointer.new(RPM::C.rpmtdNew, Package.method(:release_td))
|
184
|
+
timetd = ::FFI::AutoPointer.new(RPM::C.rpmtdNew, Package.method(:release_td))
|
185
|
+
texttd = ::FFI::AutoPointer.new(RPM::C.rpmtdNew, Package.method(:release_td))
|
186
|
+
|
187
|
+
min = RPM::C::HEADERGET_MINMEM
|
188
|
+
return deps if RPM::C.headerGet(@hdr, :changelogtime, timetd, min) != 1
|
189
|
+
return deps if RPM::C.headerGet(@hdr, :changelogname, nametd, min) != 1
|
190
|
+
return deps if RPM::C.headerGet(@hdr, :changelogtext, texttd, min) != 1
|
191
|
+
|
192
|
+
RPM::C.rpmtdInit(timetd)
|
193
|
+
while RPM::C.rpmtdNext(timetd) != -1
|
194
|
+
entry = RPM::ChangeLog.new
|
195
|
+
entry.time = RPM::C.rpmtdGetUint32(timetd)
|
196
|
+
entry.name = RPM::C.rpmtdNextString(nametd)
|
197
|
+
entry.text = RPM::C.rpmtdNextString(texttd)
|
198
|
+
entries << entry
|
199
|
+
end
|
200
|
+
entries
|
201
|
+
end
|
202
|
+
|
203
|
+
# Access a header entry
|
204
|
+
# @param [Number] tag Tag to return
|
205
|
+
# @return [] Value of the entry
|
206
|
+
# @example
|
207
|
+
# pkg => #<RPM::Package name="xmlgraphics-fop", version=#<RPM::Version v="1.0", r="22.4">>
|
208
|
+
# pkg[:name] => "xmlgraphics-fop"
|
209
|
+
#
|
210
|
+
# or if you have the old ruby-rpm compat loaded
|
211
|
+
#
|
212
|
+
# require 'rpm/compat'
|
213
|
+
# pkg[RPM::TAG_NAME] => "xmlgraphics-fop"
|
214
|
+
#
|
215
|
+
# @return [String, Fixnum, Array<String>, Array<Fixnum>, nil]
|
216
|
+
# The value of the entry
|
217
|
+
def [](tag)
|
218
|
+
tagc = ::FFI::AutoPointer.new(RPM::C.rpmtdNew, Package.method(:release_td))
|
219
|
+
|
220
|
+
return nil if RPM::C.headerGet(ptr, tag, tagc,
|
221
|
+
RPM::C::HEADERGET_MINMEM) == 0
|
222
|
+
|
223
|
+
type = RPM::C.rpmtdType(tagc)
|
224
|
+
count = RPM::C.rpmtdCount(tagc)
|
225
|
+
ret_type = RPM::C.rpmTagGetReturnType(tag)
|
226
|
+
|
227
|
+
method_name = case type
|
228
|
+
when :int8_type, :char_type, :int16_type, :int32_type, :int64_type then :rpmtdGetNumber
|
229
|
+
when :string_type, :string_array_type, :bin_type then :rpmtdGetString
|
230
|
+
else raise NotImplementedError, "Don't know how to retrieve type '#{type}'"
|
231
|
+
end
|
232
|
+
|
233
|
+
is_array = if count > 1 then true
|
234
|
+
elsif ret_type == :array_return_type then true
|
235
|
+
elsif type == :string_array_type then true
|
236
|
+
else false
|
237
|
+
end
|
238
|
+
|
239
|
+
if is_array
|
240
|
+
ret = []
|
241
|
+
RPM::C.rpmtdInit(tagc)
|
242
|
+
ret << RPM::C.send(method_name, tagc) while RPM::C.rpmtdNext(tagc) != -1
|
243
|
+
return ret
|
244
|
+
end
|
245
|
+
|
246
|
+
RPM::C.send(method_name, tagc)
|
247
|
+
end
|
248
|
+
|
249
|
+
# @return [String] This package name
|
250
|
+
def name
|
251
|
+
self[:name]
|
252
|
+
end
|
253
|
+
|
254
|
+
# @return [String] This package architecture
|
255
|
+
def arch
|
256
|
+
self[:arch]
|
257
|
+
end
|
258
|
+
|
259
|
+
# TODO: signature
|
260
|
+
|
261
|
+
# @return [Version] Version for this package
|
262
|
+
def version
|
263
|
+
Version.new(self[:version], self[:release], self[:epoch])
|
264
|
+
end
|
265
|
+
|
266
|
+
# String representation of the package: "name-version-release-arch"
|
267
|
+
# @return [String]
|
268
|
+
def to_s
|
269
|
+
return '' if name.nil?
|
270
|
+
return name if version.nil?
|
271
|
+
return "#{name}-#{version}" if arch.nil?
|
272
|
+
"#{name}-#{version}-#{arch}"
|
273
|
+
end
|
274
|
+
|
275
|
+
def self.open(filename)
|
276
|
+
Package.new(filename)
|
277
|
+
end
|
278
|
+
|
279
|
+
# @visibility private
|
280
|
+
def self.release(ptr)
|
281
|
+
RPM::C.headerFree(ptr)
|
282
|
+
end
|
283
|
+
|
284
|
+
# @visibility private
|
285
|
+
def self.release_td(ptr)
|
286
|
+
RPM::C.rpmtdFree(ptr)
|
287
|
+
end
|
288
|
+
|
289
|
+
# @visibility private
|
290
|
+
def initialize(what)
|
291
|
+
case what
|
292
|
+
when String then initialize_from_filename(what)
|
293
|
+
else initialize_from_header(what)
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
# @visibility private
|
298
|
+
def initialize_from_header(hdr = nil)
|
299
|
+
if hdr.nil?
|
300
|
+
@hdr = ::FFI::AutoPointer.new(RPM::C.headerNew, Header.method(:release))
|
301
|
+
elsif hdr.is_a?(::FFI::Pointer)
|
302
|
+
# ref
|
303
|
+
hdr = RPM::C.headerLink(hdr)
|
304
|
+
@hdr = ::FFI::AutoPointer.new(hdr, Package.method(:release))
|
305
|
+
else
|
306
|
+
raise "Can't initialize header with '#{hdr}'"
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
def initialize_from_filename(filename)
|
311
|
+
# it sucks not using the std File.open here
|
312
|
+
hdr = ::FFI::MemoryPointer.new(:pointer)
|
313
|
+
fd = nil
|
314
|
+
begin
|
315
|
+
fd = RPM::C.Fopen(filename, 'r')
|
316
|
+
raise "#{filename} : #{RPM::C.Fstrerror(fd)}" if RPM::C.Ferror(fd) != 0
|
317
|
+
RPM.transaction do |ts|
|
318
|
+
RPM::C.rpmReadPackageFile(ts.ptr, fd, filename, hdr)
|
319
|
+
end
|
320
|
+
ensure
|
321
|
+
RPM::C.Fclose(fd) unless fd.nil?
|
322
|
+
end
|
323
|
+
initialize_from_header(hdr.get_pointer(0))
|
324
|
+
end
|
325
|
+
|
326
|
+
# @return [RPM::C::Header] header pointer
|
327
|
+
# @visibility private
|
328
|
+
def ptr
|
329
|
+
@hdr
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|
data/lib/rpm/problem.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
|
2
|
+
module RPM
|
3
|
+
class Problem
|
4
|
+
def self.release(ptr)
|
5
|
+
RPM::C.rpmProblemFree(ptr)
|
6
|
+
end
|
7
|
+
|
8
|
+
# Creates a problem from an existing C pointer, refcounting it
|
9
|
+
# first.
|
10
|
+
# @param [FFI::Pointer] ptr existing C pointer
|
11
|
+
# @return [RPM::Problem] wrapped object
|
12
|
+
def self.from_ptr(ptr)
|
13
|
+
case ptr
|
14
|
+
when FFI::Pointer
|
15
|
+
new(FFI::AutoPointer.new(RPM::C.rpmProblemLink(ptr), Problem.method(:release)))
|
16
|
+
else
|
17
|
+
raise "Can't initialize header with '#{ptr}'"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Create a problem item.
|
22
|
+
# @param [RPM::ProblemType] type problem type
|
23
|
+
# @param [String] pkg_nver name-version-edition-release of the related package
|
24
|
+
# @param [String] key key of the related package
|
25
|
+
# @param [String] alt_nver name-version-edition-release of the other related package
|
26
|
+
# @param [String] str generic data string from a problem
|
27
|
+
def self.create(type, pkg_nevr, key, alt_nevr, str, number)
|
28
|
+
ptr = ::FFI::AutoPointer.new(RPM::C.rpmProblemCreate(type, pkg_nevr, key, alt_nevr, str, number), Problem.method(:release))
|
29
|
+
new(ptr)
|
30
|
+
end
|
31
|
+
|
32
|
+
# @visibility private
|
33
|
+
def initialize(ptr)
|
34
|
+
@ptr = ptr
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [RPM::ProblemType] type of problem (dependency, diskpace etc).
|
38
|
+
def type
|
39
|
+
RPM::C.rpmProblemGetType(@ptr)
|
40
|
+
end
|
41
|
+
|
42
|
+
# @return [String] filename or python object address of a problem.
|
43
|
+
def key
|
44
|
+
RPM::C.rpmProblemGetKey(@ptr).read_string
|
45
|
+
end
|
46
|
+
|
47
|
+
# @return [String] a generic data string from a problem.
|
48
|
+
def str
|
49
|
+
RPM::C.rpmProblemGetStr(@ptr)
|
50
|
+
end
|
51
|
+
|
52
|
+
# @return [String] formatted string representation of a problem
|
53
|
+
def to_s
|
54
|
+
RPM::C.rpmProblemString(@ptr)
|
55
|
+
end
|
56
|
+
|
57
|
+
# @return [Fixnum] compare two problems for equality.
|
58
|
+
def <=>(other)
|
59
|
+
RPM::C.rpmProblemCompare(@ptr, other.ptr)
|
60
|
+
end
|
61
|
+
|
62
|
+
# @visibility private
|
63
|
+
attr_reader :ptr
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,268 @@
|
|
1
|
+
|
2
|
+
module RPM
|
3
|
+
CallbackData = Struct.new(:type, :key, :package, :amount, :total) do
|
4
|
+
def to_s
|
5
|
+
"#{type} #{key} #{package} #{amount} #{total}"
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class Transaction
|
10
|
+
def self.release(ptr)
|
11
|
+
RPM::C.rpmtsFree(ptr)
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(opts = {})
|
15
|
+
# http://markmail.org/message/ypsiqxop442p7rzz
|
16
|
+
# The key pointer needs to stay valid during commit
|
17
|
+
# so we keep a reference to them mapping from
|
18
|
+
# object_id to ruby object.
|
19
|
+
@keys = {}
|
20
|
+
opts[:root] ||= '/'
|
21
|
+
|
22
|
+
@ptr = ::FFI::AutoPointer.new(RPM::C.rpmtsCreate, Transaction.method(:release))
|
23
|
+
RPM::C.rpmtsSetRootDir(@ptr, opts[:root])
|
24
|
+
end
|
25
|
+
|
26
|
+
# @return [RPM::MatchIterator] Creates an iterator for +tag+ and +val+
|
27
|
+
def init_iterator(tag, val)
|
28
|
+
raise TypeError if val && !val.is_a?(String)
|
29
|
+
|
30
|
+
it_ptr = RPM::C.rpmtsInitIterator(@ptr, tag.nil? ? 0 : tag, val, 0)
|
31
|
+
|
32
|
+
raise "Can't init iterator for [#{tag}] -> '#{val}'" if it_ptr.null?
|
33
|
+
MatchIterator.from_ptr(it_ptr)
|
34
|
+
end
|
35
|
+
|
36
|
+
# @visibility private
|
37
|
+
attr_reader :ptr
|
38
|
+
|
39
|
+
#
|
40
|
+
# @yield [Package] Called for each match
|
41
|
+
# @param [Number] key RPM tag key
|
42
|
+
# @param [String] val Value to match
|
43
|
+
# @example
|
44
|
+
# RPM.transaction do |t|
|
45
|
+
# t.each_match(RPM::TAG_ARCH, "x86_64") do |pkg|
|
46
|
+
# puts pkg.name
|
47
|
+
# end
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
def each_match(key, val, &block)
|
51
|
+
it = init_iterator(key, val)
|
52
|
+
|
53
|
+
return it unless block_given?
|
54
|
+
|
55
|
+
it.each(&block)
|
56
|
+
end
|
57
|
+
|
58
|
+
#
|
59
|
+
# @yield [Package] Called for each package in the database
|
60
|
+
# @example
|
61
|
+
# db.each do |pkg|
|
62
|
+
# puts pkg.name
|
63
|
+
# end
|
64
|
+
#
|
65
|
+
def each(&block)
|
66
|
+
each_match(0, nil, &block)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Add a install operation to the transaction
|
70
|
+
# @param [Package] pkg Package to install
|
71
|
+
# @param [String] key e.g. filename where to install from
|
72
|
+
def install(pkg, key)
|
73
|
+
install_element(pkg, key, upgrade: false)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Add an upgrade operation to the transaction
|
77
|
+
# @param [Package] pkg Package to upgrade
|
78
|
+
# @param [String] key e.g. filename where to install from
|
79
|
+
def upgrade(pkg, key)
|
80
|
+
install_element(pkg, key, upgrade: true)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Add a delete operation to the transaction
|
84
|
+
# @param [String, Package, Dependency] pkg Package to delete
|
85
|
+
def delete(pkg)
|
86
|
+
iterator = case pkg
|
87
|
+
when Package
|
88
|
+
pkg[:sigmd5] ? each_match(:sigmd5, pkg[:sigmd5]) : each_match(:label, pkg[:label])
|
89
|
+
when String
|
90
|
+
each_match(:label, pkg)
|
91
|
+
when Dependency
|
92
|
+
each_match(:label, pkg.name).set_iterator_version(pkg.version)
|
93
|
+
else
|
94
|
+
raise TypeError, 'illegal argument type'
|
95
|
+
end
|
96
|
+
|
97
|
+
iterator.each do |header|
|
98
|
+
ret = RPM::C.rpmtsAddEraseElement(@ptr, header.ptr, iterator.offset)
|
99
|
+
raise "Error while adding erase/#{pkg} to transaction" if ret != 0
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Sets the root directory for this transaction
|
104
|
+
# @param [String] root directory
|
105
|
+
def root_dir=(dir)
|
106
|
+
rc = RPM::C.rpmtsSetRootDir(@ptr, dir)
|
107
|
+
raise "Can't set #{dir} as root directory" if rc < 0
|
108
|
+
end
|
109
|
+
|
110
|
+
# @return [String ] the root directory for this transaction
|
111
|
+
def root_dir
|
112
|
+
RPM::C.rpmtsRootDir(@ptr)
|
113
|
+
end
|
114
|
+
|
115
|
+
def flags=(fl)
|
116
|
+
RPM::C.rpmtsSetFlags(@ptr, fl)
|
117
|
+
end
|
118
|
+
|
119
|
+
def flags
|
120
|
+
RPM::C.rpmtsFlags(@ptr)
|
121
|
+
end
|
122
|
+
|
123
|
+
# Determine package order in the transaction according to dependencies
|
124
|
+
#
|
125
|
+
# The final order ends up as installed packages followed by removed
|
126
|
+
# packages, with packages removed for upgrades immediately following
|
127
|
+
# the new package to be installed.
|
128
|
+
#
|
129
|
+
# @returns [Fixnum] no. of (added) packages that could not be ordered
|
130
|
+
def order
|
131
|
+
RPM::C.rpmtsOrder(@ptr)
|
132
|
+
end
|
133
|
+
|
134
|
+
# Free memory needed only for dependency checks and ordering.
|
135
|
+
def clean
|
136
|
+
RPM::C.rpmtsClean(@ptr)
|
137
|
+
end
|
138
|
+
|
139
|
+
def check
|
140
|
+
rc = RPM::C.rpmtsCheck(@ptr)
|
141
|
+
probs = RPM::C.rpmtsProblems(@ptr)
|
142
|
+
|
143
|
+
return if rc < 0
|
144
|
+
begin
|
145
|
+
psi = RPM::C.rpmpsInitIterator(probs)
|
146
|
+
while RPM::C.rpmpsNextIterator(psi) >= 0
|
147
|
+
problem = Problem.from_ptr(RPM::C.rpmpsGetProblem(psi))
|
148
|
+
yield problem
|
149
|
+
end
|
150
|
+
ensure
|
151
|
+
RPM::C.rpmpsFree(probs)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# Performs the transaction.
|
156
|
+
# @param [Number] flag Transaction flags, default +RPM::TRANS_FLAG_NONE+
|
157
|
+
# @param [Number] filter Transaction filter, default +RPM::PROB_FILTER_NONE+
|
158
|
+
# @example
|
159
|
+
# transaction.commit
|
160
|
+
# You can supply your own callback
|
161
|
+
# @example
|
162
|
+
# transaction.commit do |data|
|
163
|
+
# end
|
164
|
+
# end
|
165
|
+
# @yield [CallbackData] sig Transaction progress
|
166
|
+
def commit
|
167
|
+
callback = proc do |hdr, type, amount, total, key_ptr, data_ignored|
|
168
|
+
key_id = key_ptr.address
|
169
|
+
key = @keys.include?(key_id) ? @keys[key_id] : nil
|
170
|
+
|
171
|
+
if block_given?
|
172
|
+
package = hdr.null? ? nil : Package.new(hdr)
|
173
|
+
data = CallbackData.new(type, key, package, amount, total)
|
174
|
+
yield(data)
|
175
|
+
else
|
176
|
+
RPM::C.rpmShowProgress(hdr, type, amount, total, key, data_ignored)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
# We create a callback to pass to the C method and we
|
180
|
+
# call the user supplied callback from there
|
181
|
+
#
|
182
|
+
# The C callback expects you to return a file handle,
|
183
|
+
# We expect from the user to get a File, which we
|
184
|
+
# then convert to a file handle to return.
|
185
|
+
callback = proc do |hdr, type, amount, total, key_ptr, data_ignored|
|
186
|
+
key_id = key_ptr.address
|
187
|
+
key = @keys.include?(key_id) ? @keys[key_id] : nil
|
188
|
+
|
189
|
+
if block_given?
|
190
|
+
package = hdr.null? ? nil : Package.new(hdr)
|
191
|
+
data = CallbackData.new(type, key, package, amount, total)
|
192
|
+
ret = yield(data)
|
193
|
+
|
194
|
+
# For OPEN_FILE we need to do some type conversion
|
195
|
+
# for certain callback types we need to do some
|
196
|
+
case type
|
197
|
+
when :inst_open_file
|
198
|
+
# For :inst_open_file the user callback has to
|
199
|
+
# return the open file
|
200
|
+
unless ret.is_a?(::File)
|
201
|
+
raise TypeError, "illegal return value type #{ret.class}. Expected File."
|
202
|
+
end
|
203
|
+
fdt = RPM::C.fdDup(ret.to_i)
|
204
|
+
if fdt.null? || RPM::C.Ferror(fdt) != 0
|
205
|
+
raise "Can't use opened file #{data.key}: #{RPM::C.Fstrerror(fdt)}"
|
206
|
+
RPM::C.Fclose(fdt) unless fdt.nil?
|
207
|
+
else
|
208
|
+
fdt = RPM::C.fdLink(fdt)
|
209
|
+
@fdt = fdt
|
210
|
+
end
|
211
|
+
# return the (RPM type) file handle
|
212
|
+
fdt
|
213
|
+
when :inst_close_file
|
214
|
+
fdt = @fdt
|
215
|
+
RPM::C.Fclose(fdt)
|
216
|
+
@fdt = nil
|
217
|
+
else
|
218
|
+
ret
|
219
|
+
end
|
220
|
+
else
|
221
|
+
# No custom callback given, use the default to show progress
|
222
|
+
RPM::C.rpmShowProgress(hdr, type, amount, total, key, data_ignored)
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
rc = RPM::C.rpmtsSetNotifyCallback(@ptr, callback, nil)
|
227
|
+
raise "Can't set commit callback" if rc != 0
|
228
|
+
|
229
|
+
rc = RPM::C.rpmtsRun(@ptr, nil, :none)
|
230
|
+
|
231
|
+
raise "#{self}: #{RPM::C.rpmlogMessage}" if rc < 0
|
232
|
+
|
233
|
+
if rc > 0
|
234
|
+
ps = RPM::C.rpmtsProblems(@ptr)
|
235
|
+
psi = RPM::C.rpmpsInitIterator(ps)
|
236
|
+
while RPM::C.rpmpsNextIterator(psi) >= 0
|
237
|
+
problem = Problem.from_ptr(RPM::C.rpmpsGetProblem(psi))
|
238
|
+
STDERR.puts problem
|
239
|
+
end
|
240
|
+
RPM::C.rpmpsFree(ps)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
# @return [DB] the database associated with this transaction
|
245
|
+
def db
|
246
|
+
RPM::DB.new(self)
|
247
|
+
end
|
248
|
+
|
249
|
+
private
|
250
|
+
|
251
|
+
# @param [Package] pkg package to install
|
252
|
+
# @param [String] key e.g. filename where to install from
|
253
|
+
# @param opts options
|
254
|
+
# @option :upgrade Upgrade packages if true
|
255
|
+
def install_element(pkg, key, opts = {})
|
256
|
+
raise TypeError, 'illegal argument type' unless pkg.is_a?(RPM::Package)
|
257
|
+
raise ArgumentError, "#{self}: key '#{key}' must be unique" if @keys.include?(key.object_id)
|
258
|
+
|
259
|
+
# keep a reference to the key as rpmtsAddInstallElement will keep a copy
|
260
|
+
# of the passed pointer (we pass the object_id)
|
261
|
+
@keys[key.object_id] = key
|
262
|
+
|
263
|
+
ret = RPM::C.rpmtsAddInstallElement(@ptr, pkg.ptr, FFI::Pointer.new(key.object_id), opts[:upgrade] ? 1 : 0, nil)
|
264
|
+
raise RuntimeError if ret != 0
|
265
|
+
nil
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|