class Archlinux::PackageList

Attributes

children_mode[RW]
ext_query[RW]
ignore[RW]
install_list[RW]
install_list_of[RW]
install_method[RW]
l[R]
provides_for[R]
query_ignore[RW]
versions[R]

Public Class Methods

new(list=[], config: Archlinux.config) click to toggle source
# 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

all_provides_for(pkg) click to toggle source

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
as_ext_query(*queries, provides: false, full_pkgs: false) click to toggle source

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
chain_query(ext_query) click to toggle source
# 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
check_update(updates=@install_list, ignore: @ignore) click to toggle source

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(l, ignore: @ignore) click to toggle source

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
children(node, mode=@children_mode, log_level: :verbose2, **opts, &b) click to toggle source

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
find(q, **opts) click to toggle source

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
get(*args) click to toggle source

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
get_package(pkg) click to toggle source
# File lib/aur/packages.rb, line 378
def get_package(pkg)
        l[(get(pkg).first)]
end
get_packages(*args) click to toggle source

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
get_updates(l, log_level: true, ignore: @ignore, rebuild: false, **showopts) click to toggle source
# 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
install(*args, callback: nil, **opts, &b) click to toggle source

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
install?(*packages, update: false, install_list: @install_list, log_level: true, log_level_verbose: :verbose, ignore: @ignore, rebuild: false, no_show: [:obsolete], **showopts) { |full_updates, infos| ... } click to toggle source

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
latest() click to toggle source
# 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
list(version=false) click to toggle source
# 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
merge(l) click to toggle source
# 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
name_of(pkg) click to toggle source
# File lib/aur/packages.rb, line 209
def name_of(pkg)
        pkg.name_version
end
names() click to toggle source

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
packages() click to toggle source
# File lib/aur/packages.rb, line 213
def packages
        self
end
query(q, provides: false) { |found, :version| ... } click to toggle source

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
resolve(*queries, provides: true, ext_query: @ext_query, fallback: true, log_missing: :warn, log_fallback: :warn, **opts) click to toggle source

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
rget(*pkgs) click to toggle source

recursive get

# File lib/aur/packages.rb, line 421
def rget(*pkgs)
        l=get(*pkgs)
        tsort(l)
end
same?(other) click to toggle source
# 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
select_updates(r) click to toggle source
# 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
show_updates(r, show: [:upgrade, :downgrade, :obsolete, :install], no_show: [], log_level: true) click to toggle source

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
slice(*args) click to toggle source

this is like a 'restrict' operation

# File lib/aur/packages.rb, line 383
def slice(*args)
        self.class.new(l.slice(*get(*args)), **{})
end
to_ext_query() click to toggle source

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
tsort(l, **opts, &b) click to toggle source
# 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
update(**opts, &b) click to toggle source
# File lib/aur/packages.rb, line 544
def update(**opts, &b)
        install(update: true, **opts, &b)
end
update?(**opts) click to toggle source
# File lib/aur/packages.rb, line 510
def update?(**opts)
        install?(update: true, **opts)
end

Private Instance Methods

call_tsort(l, method: :tsort, **opts, &b) click to toggle source
# 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