class Archlinux::PackageList
Attributes
Public Class Methods
# File lib/aur/packages.rb, line 182 def initialize(list=[], config: Archlinux.config) @l={} @versions={} #hash of versions for quick look ups @provides_for={} #hash of provides for quick look ups @ext_query=nil #how to get missing packages, default @children_mode=%i(depends) #children, default @ignore=[] #ignore packages update @query_ignore=[] #query ignore packages (ie these won't be returned by a query; so stronger than @ignore) @install_list=nil #how do we check for new packages / updates @install_list_of=nil #are we used to check for new packages / updates @install_method=nil #how to install stuff @config=config merge(list) end
Public Instance Methods
return all packages that provides for pkg this is more complicated than @provides_for because if b provides a and c provides a, then pacman assumes that b provides c
# File lib/aur/packages.rb, line 267 def all_provides_for(pkg) provides=l.fetch(pkg,{}).fetch(:provides,[]).map {|q| Query.strip(q)} provided=([Query.strip(pkg)]+provides).flat_map do |prov| @provides_for.fetch(prov,{}).values.map {|v| Version.strip(v)} end.uniq provided end
essentially just a wrapper around resolve
# File lib/aur/packages.rb, line 590 def as_ext_query(*queries, provides: false, full_pkgs: false) r=self.resolve(*queries, provides: provides, fallback: false) # puts "#{self.class}: #{queries} => #{r}" l= full_pkgs ? @l : slice(*r.values.compact) return r, l end
# File lib/aur/packages.rb, line 574 def chain_query(ext_query) ext_query=ext_query.to_ext_query if ext_query.is_a?(PackageList) if @ext_query orig_query=@ext_query @ext_query = lambda do |*args, **opts| r, l=orig_query.call(*args, **opts) missed = args-r.keys r2, l2=ext_query.call(*missed, **opts) return r.merge(r2), l.merge(l2) end else @ext_query=ext_query end end
this take a list of packages which can be updates of ours return check_updates
of this list (restricted to our current packages, so it won't show any 'install' operation
# File lib/aur/packages.rb, line 504 def check_update(updates=@install_list, ignore: @ignore) return [] if updates.nil? new_pkgs=updates.slice(*names) #ignore 'install' packages check_updates(new_pkgs, ignore: ignore) end
check updates compared to another list
# File lib/aur/packages.rb, line 427 def check_updates(l, ignore: @ignore) l=self.class.create(l) a=self.latest; b=l.latest r={} b.each do |k, v| next if ignore.include?(k) if a.key?(k) v1=a[k].version v2=v.version h={in: v1.to_s, out: v2.to_s, in_pkg: name_of(a[k]), out_pkg: name_of(v)} case v1 <=> v2 when -1 h[:op]=:upgrade when 0 h[:op]=:equal when 1 h[:op]=:downgrade end r[k]=h else #new installation r[k]={op: :install, in: nil, out: v.version.to_s, out_pkg: name_of(v)} end end (a.keys-b.keys).each do |k| next if ignore.include?(k) r[k]={op: :obsolete, in: a[k].version.to_s, out: nil, in_pkg: name_of(a[k])} end r end
get children (non recursive)
# File lib/aur/packages.rb, line 388 def children(node, mode=@children_mode, log_level: :verbose2, **opts, &b) deps=@l.fetch(node).dependencies(mode) SH.log(log_level, "- #{node}: #{deps.join(', ')}") deps=get(*deps, **opts) SH.log(log_level, " => #{deps.join(', ')}") unless deps.empty? if b deps.each(&b) else deps end end
select the most appropriate match (does not use ext_query
), using query
# File lib/aur/packages.rb, line 287 def find(q, **opts) return q if @l.key?(q) q=Query.create(q); pkg=q.name query(q, **opts) do |found, type| if type==:version unless found.empty? #we select the most up to date max = found.max { |v,w| Version.create(v) <=> Version.create(w) } return @versions[pkg][max] end elsif type==:provides max = found.max { |v,w| Query.create(v) <=> Query.create(w) } return @provides_for[pkg][max] end end return nil end
this gives the keys of the packages we resolved
# File lib/aur/packages.rb, line 368 def get(*args) #compact because the resolution can be nil for an ignored package resolve(*args).values.compact end
# File lib/aur/packages.rb, line 378 def get_package(pkg) l[(get(pkg).first)] end
this gives the values of the packages we resolved
# File lib/aur/packages.rb, line 374 def get_packages(*args) l.values_at(*get(*args)) end
# File lib/aur/packages.rb, line 467 def get_updates(l, log_level: true, ignore: @ignore, rebuild: false, **showopts) c=check_updates(l, ignore: ignore) show_updates(c, log_level: log_level, **showopts) if rebuild # keep all packages to_build=c.select {|_k,v| v[:out_pkg]} return to_build.map {|_k, v| v[:out_pkg]}, to_build else select_updates(c) end end
the callback is passed to install? while the block is passed to @install_method
# File lib/aur/packages.rb, line 550 def install(*args, callback: nil, **opts, &b) install_opts={} keys=method(:install?).parameters.select {|arg| arg[0]==:key}.map {|arg| arg[1]} keys.each do |key| case key when :rebuild opts.key?(key) && install_opts[key]=opts.fetch(key) else opts.key?(key) && install_opts[key]=opts.delete(key) end end l, l_info=install?(*args, **install_opts, &callback) #return false in the callback to prevent install if @install_method @install_method.call(l, pkgs_info: l_info, **opts, &b) unless !l or l.empty? else return l, l_info end end
take a list of packages to install, return the new or updated packages to install with their dependencies
# File lib/aur/packages.rb, line 516 def install?(*packages, update: false, install_list: @install_list, log_level: true, log_level_verbose: :verbose, ignore: @ignore, rebuild: false, no_show: [:obsolete], **showopts) packages+=self.names if update if install_list ignore -= packages.map {|p| Query.strip(p)} SH.log(log_level_verbose, "# Checking packages #{packages.join(', ')}", color: :bold) new_pkgs=install_list.slice(*packages) u, u_infos=get_updates(new_pkgs, log_level: log_level_verbose, ignore: ignore, rebuild: rebuild, no_show: no_show, **showopts) # todo: update this when we have a better preference mechanism # (then we will need to put @official in the install package class) new=self.class.new(l.values).merge(new_pkgs) new.chain_query(install_list) # The updates or new packages may need new deps SH.log(log_level_verbose, "# Checking dependencies of #{u.join(', ')}", color: :bold) unless u.empty? full=new.rget(*u) SH.log(log_level, "New packages:", color: :bold) full_updates, full_infos=get_updates(new.slice(*full), log_level: log_level, ignore: ignore, rebuild: rebuild=="full" ? true : false, no_show: no_show, **showopts) if rebuild and rebuild != "full" #we need to merge back u full_updates |=u full_infos.merge!(u_infos) end infos={top_pkgs: u_infos, all_pkgs: full_infos} full_updates, infos=yield full_updates, infos if block_given? return full_updates, infos else SH.logger.warn "External install list not defined" end end
# File lib/aur/packages.rb, line 275 def latest r={} @versions.each do |pkg, versions| v=versions.keys.max do |v1,v2| Version.create(v1) <=> Version.create(v2) end r[pkg]=@l[versions[v]] end r end
# File lib/aur/packages.rb, line 202 def list(version=false) l= version ? keys.sort : names.sort l.each do |pkg| SH.logger.info "- #{pkg}" end end
# File lib/aur/packages.rb, line 232 def merge(l) l= case l when PackageList l.values when Hash l.values.compact else l.to_a end l.each do |pkg| pkg=Package.new(pkg) unless pkg.is_a?(Package) name=name_of(pkg) if @l.key?(name) @l[name].merge(pkg) else @l[name]=pkg end @versions[pkg.name]||={} @versions[pkg.name][pkg.version.to_s]=name pkg[:provides].each do |p| pkg=Query.strip(p) @provides_for[pkg]||={} @provides_for[pkg][p]=name #todo: do we pass the name or the full pkg? #todo: we need a list here end end self end
# File lib/aur/packages.rb, line 209 def name_of(pkg) pkg.name_version end
the names without the versions. Use self.keys to get the name=version
# File lib/aur/packages.rb, line 198 def names @versions.keys end
# File lib/aur/packages.rb, line 213 def packages self end
output all matches (does not use ext_query
)
# File lib/aur/packages.rb, line 305 def query(q, provides: false) #provides: do we check Provides? q=Query.new(q) unless q.is_a?(Query) matches=[]; pkg=q.name if @versions.key?(pkg) matches+=(found=@versions[pkg].keys.select {|v| q.satisfy?(Version.create(v))}).map {|k| @versions[pkg][k]} yield(found, :version) if block_given? end if provides and @provides_for.key?(pkg) matches+=(found=@provides_for[pkg].keys.select {|v| q.satisfy?(Query.create(v))}).map {|k| @provides_for[pkg][k]} yield(found, :provides) if block_given? end matches end
here the arguments are Strings return the arguments replaced by eventual provides + missing packages are added to @l So this is like find, except we respect @query_ignore, and call ext_query
for missing packages
# File lib/aur/packages.rb, line 324 def resolve(*queries, provides: true, ext_query: @ext_query, fallback: true, log_missing: :warn, log_fallback: :warn, **opts) got={}; missed=[] pkgs=queries.map {|p| Query.strip(p)} ignored = pkgs & @query_ignore queries.each do |query| if ignored.include?(Query.strip(query)) got[query]=nil #=> means the query was ignored else pkg=self.find(query, provides: provides, **opts) if pkg got[query]=pkg else missed << query end end end # we do it this way to call ext_query in batch if ext_query and !missed.empty? found, new_pkgs=ext_query.call(*missed, provides: provides) self.merge(new_pkgs) got.merge!(found) missed-=found.keys end if fallback and !missed.empty? new_queries={} missed.each do |query| if (query_pkg=Query.strip(query)) != query new_queries[query]=query_pkg # missed.delete(query) end end unless new_queries.empty? SH.log(log_fallback, "Trying fallback for packages: #{new_queries.keys.join(', ')}") fallback_got=self.resolve(*new_queries.values, provides: provides, ext_query: ext_query, fallback: false, log_missing: :verbose, **opts) got.merge!(fallback_got) SH.log(log_missing, "#{self.class}: Warning! Missing packages: #{missed.map {|m| r=m; r<<" [fallback: #{fallback}]" if (fallback=fallback_got[new_queries[m]]); r}.join(', ')}") unless missed.empty? end else SH.log(log_missing, "#{self.class}: Warning! Missing packages: #{missed.join(', ')}") unless missed.empty? end got end
recursive get
# File lib/aur/packages.rb, line 421 def rget(*pkgs) l=get(*pkgs) tsort(l) end
# File lib/aur/packages.rb, line 217 def same?(other) unless @l.keys == other.keys SH.logger.warn("#{self.class}: Inconsistency in the package names") return false end r=true @l.each do |name, pkg| unless pkg.same?(other[name]) SH.logger.warn("#{self.class}: Inconsistensy for the package #{name}") r=false end end return r end
# File lib/aur/packages.rb, line 462 def select_updates(r) up=r.select {|_k,v| v[:op]==:upgrade or v[:op]==:install} return up.map {|_k, v| v[:out_pkg]}, up end
take the result of check_updates
and pretty print them no_show has priority over :show
# File lib/aur/packages.rb, line 481 def show_updates(r, show: [:upgrade, :downgrade, :obsolete, :install], no_show: [], log_level: true) r.each do |k,v| next unless show.include?(v[:op]) and !no_show.include?(v[:op]) vin= v[:in] ? v[:in] : "(none)" vout= v[:out] ? v[:out] : "(none)" op=case v[:op] when :downgrade "<-" when :upgrade, :install, :obsolete "->" when :equal "=" end extra="" extra=" [#{v[:op]}]" if v[:op]!=:upgrade SH.log(log_level, " -> #{k}: #{vin} #{op} #{vout}#{extra}") end end
this is like a 'restrict' operation
# File lib/aur/packages.rb, line 383 def slice(*args) self.class.new(l.slice(*get(*args)), **{}) end
returns a Proc that can be used for another PackageList
as an ext_query
# File lib/aur/packages.rb, line 570 def to_ext_query method(:as_ext_query) end
# File lib/aur/packages.rb, line 409 def tsort(l, **opts, &b) if b call_tsort(l, method: :each_strongly_connected_component, **opts, &b) else r=call_tsort(l, method: :strongly_connected_components, **opts) cycles=r.select {|c| c.length > 1} SH.logger.warn "Cycles detected: #{cycles}" unless cycles.empty? r.flatten end end
# File lib/aur/packages.rb, line 544 def update(**opts, &b) install(update: true, **opts, &b) end
# File lib/aur/packages.rb, line 510 def update?(**opts) install?(update: true, **opts) end
Private Instance Methods
# File lib/aur/packages.rb, line 400 def call_tsort(l, method: :tsort, **opts, &b) each_node=l.method(:each) s=self each_child = lambda do |node, &b| s.children(node, **opts, &b) end TSort.public_send(method, each_node, each_child, &b) end