mock build
This commit is contained in:
270
locallibs/ruby-rpm-ffi/lib/rpm/transaction.rb
Normal file
270
locallibs/ruby-rpm-ffi/lib/rpm/transaction.rb
Normal 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
|
||||
Reference in New Issue
Block a user