aur.rb 0.1.0 → 0.2.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 +4 -4
- data/ChangeLog.md +136 -2
- data/LICENSE.txt +1 -1
- data/README.md +21 -1
- data/Rakefile +7 -10
- data/TODO.md +69 -0
- data/aur.rb.gemspec +1 -0
- data/bin/aur.rb +6 -0
- data/lib/aur.rb +43 -1853
- data/lib/aur/aur_rpc.rb +178 -0
- data/lib/aur/cli.rb +334 -0
- data/lib/aur/config.rb +330 -0
- data/lib/aur/db.rb +328 -0
- data/lib/aur/devtools.rb +258 -0
- data/lib/aur/helpers.rb +56 -0
- data/lib/aur/install_packages.rb +158 -0
- data/lib/aur/load_config.rb +5 -0
- data/lib/aur/makepkg.rb +445 -0
- data/lib/aur/no_load_config.rb +5 -0
- data/lib/aur/packages.rb +599 -0
- data/lib/aur/repos.rb +343 -0
- data/lib/aur/version.rb +1 -1
- data/lib/aur/versions.rb +186 -0
- metadata +22 -5
data/lib/aur/packages.rb
ADDED
@@ -0,0 +1,599 @@
|
|
1
|
+
require 'tsort'
|
2
|
+
require 'time'
|
3
|
+
|
4
|
+
module Archlinux
|
5
|
+
|
6
|
+
class PackageClass #meta class for class that hold package infos
|
7
|
+
def self.packages(*repos)
|
8
|
+
pkgs=PackageList.new
|
9
|
+
repos.each do |repo|
|
10
|
+
npkg=case repo
|
11
|
+
when "@db"
|
12
|
+
Archlinux.config.db.packages
|
13
|
+
when "@dbdir"
|
14
|
+
Archlinux.config.db.dir_packages
|
15
|
+
when ":local"
|
16
|
+
LocalRepo.new.packages
|
17
|
+
when /^@get/, /^@rget/
|
18
|
+
new=Archlinux.config.install_list
|
19
|
+
m=repo.match(/^@r?get\((.*)\)/)
|
20
|
+
l=m[1].split(',')
|
21
|
+
#require 'pry'; binding.pry
|
22
|
+
if repo =~ /^@rget/
|
23
|
+
new.rget(*l)
|
24
|
+
else
|
25
|
+
new.get(*l)
|
26
|
+
end
|
27
|
+
new
|
28
|
+
else
|
29
|
+
if (m=repo.match(/^:(.*)\Z/))
|
30
|
+
Repo.new(m[1]).packages
|
31
|
+
else
|
32
|
+
path=Pathname.new(repo)
|
33
|
+
if path.file?
|
34
|
+
PackageFiles.new(path).packages
|
35
|
+
elsif path.directory?
|
36
|
+
PackageFiles.from_dir(path).packages
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
if npkg.nil?
|
41
|
+
SH.logger.warn "Unknown repo #{repo}"
|
42
|
+
else
|
43
|
+
pkgs.merge(npkg)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
pkgs
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.packages_list(*repos)
|
50
|
+
if repos.length == 2
|
51
|
+
pkg1=packages(*repos[0])
|
52
|
+
pkg2=packages(*repos[1])
|
53
|
+
else
|
54
|
+
i=repos.index('::')
|
55
|
+
unless i
|
56
|
+
SH.logger.warn "Only one list given"
|
57
|
+
i=0
|
58
|
+
end
|
59
|
+
pkg1=packages(*repos[0...i])
|
60
|
+
pkg2=packages(*repos[i+1..-1])
|
61
|
+
end
|
62
|
+
return pkg1, pkg2
|
63
|
+
end
|
64
|
+
end
|
65
|
+
PackageError=Class.new(ArchlinuxError)
|
66
|
+
class Package
|
67
|
+
def self.create(v)
|
68
|
+
v.is_a?(self) ? v : self.new(v)
|
69
|
+
end
|
70
|
+
|
71
|
+
Archlinux.delegate_h(self, :@props)
|
72
|
+
attr_reader :name, :props
|
73
|
+
|
74
|
+
def initialize(*args)
|
75
|
+
case args.length
|
76
|
+
when 2
|
77
|
+
name, props=args
|
78
|
+
when 1
|
79
|
+
props=args.first
|
80
|
+
name=nil
|
81
|
+
else
|
82
|
+
raise PackageError.new("Error the number of arguments should be 1 or 2")
|
83
|
+
end
|
84
|
+
@name=name
|
85
|
+
@props={}
|
86
|
+
self.props=(props)
|
87
|
+
@name=@props[:name] || @props[:pkgname] || @props[:pkgbase] unless @name
|
88
|
+
end
|
89
|
+
|
90
|
+
def props=(props)
|
91
|
+
[:groups, :depends, :make_depends, :check_depends, :conflicts, :replaces, :provides, :depends_for, :opt_depends_for].each do |k|
|
92
|
+
props.key?(k) or @props[k]=[]
|
93
|
+
end
|
94
|
+
@props[:opt_depends]||={}
|
95
|
+
props.each do |k,v|
|
96
|
+
k=Utils.to_snake_case(k.to_s).to_sym
|
97
|
+
k=:opt_depends if k==:optdepends or k==:optdepend or k==:optional_deps
|
98
|
+
k=:make_depends if k==:makedepends or k==:makedepend
|
99
|
+
k=:check_depends if k==:checkdepends or k==:checkdepend
|
100
|
+
k=:build_date if k==:builddate
|
101
|
+
k=:depends if k==:depends_on or k==:requires
|
102
|
+
k=:conflicts if k==:conflicts_with
|
103
|
+
k=:pkgbase if k==:base
|
104
|
+
k=:depends_for if k==:required_by
|
105
|
+
k=:opt_depends_for if k==:optional_for
|
106
|
+
k=:description if k==:desc or k==:pkgdesc
|
107
|
+
case k
|
108
|
+
when :first_submitted, :last_modified, :out_of_date, :build_date, :install_date
|
109
|
+
if v and !v.is_a?(Time)
|
110
|
+
v= v.is_a?(Integer) ? Time.at(v) : Time.parse(v)
|
111
|
+
end
|
112
|
+
when :repository, :groups, :depends, :make_depends, :check_depends, :conflicts, :replaces, :provides, :depends_for, :opt_depends_for, :license, :source
|
113
|
+
v=Array(v)
|
114
|
+
when :opt_depends
|
115
|
+
unless v.is_a?(Hash)
|
116
|
+
w={}
|
117
|
+
Array(v).each do |l|
|
118
|
+
l.match(/(\w*)\s+:\s+(.*)/) do |m|
|
119
|
+
w[m[1]]=m[2]
|
120
|
+
end
|
121
|
+
end
|
122
|
+
v=w
|
123
|
+
end
|
124
|
+
end
|
125
|
+
@props[k]=v
|
126
|
+
end
|
127
|
+
if !@props[:version] and @props[:pkgver]
|
128
|
+
@props[:version]=Version.new(@props[:epoch], @props[:pkgver], @props[:pkgrel]).to_s
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def merge(h)
|
133
|
+
h.each do |k,v|
|
134
|
+
if @props[k].nil? or ((k==:package_size or k==:download_size or k==:installed_size) and (@props[k]=="0.00 B" or @props[k]==0))
|
135
|
+
@props[k]=v
|
136
|
+
elsif k==:repository
|
137
|
+
@props[k]=(Array(@props[k])+v).uniq
|
138
|
+
end
|
139
|
+
end
|
140
|
+
self
|
141
|
+
end
|
142
|
+
|
143
|
+
def dependencies(l=%i(depends))
|
144
|
+
l.map {|i| a=@props[i]; a.is_a?(Hash) ? a.keys : Array(a)}.flatten.uniq
|
145
|
+
end
|
146
|
+
|
147
|
+
def version
|
148
|
+
Version.new(@props[:version])
|
149
|
+
end
|
150
|
+
|
151
|
+
def name_version
|
152
|
+
r=self.name
|
153
|
+
return r if r.nil?
|
154
|
+
version=self.version
|
155
|
+
r+="="+version.to_s if version
|
156
|
+
r
|
157
|
+
end
|
158
|
+
|
159
|
+
def file
|
160
|
+
@props[:filename] && Pathname.new(@props[:filename])
|
161
|
+
end
|
162
|
+
|
163
|
+
def path
|
164
|
+
file || Pathname.new(@props[:repo])
|
165
|
+
end
|
166
|
+
|
167
|
+
def same?(other)
|
168
|
+
# @props.slice(*(@props.keys - [:repo])) == other.props.slice(*(other.props.keys - [:repo]))
|
169
|
+
slice=%i(version description depends provides opt_depends replaces conflicts)
|
170
|
+
# p name, other.name, @props.slice(*slice), other.props.slice(*slice)
|
171
|
+
name == other.name && @props.slice(*slice) == other.props.slice(*slice)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
class PackageList
|
176
|
+
extend CreateHelper
|
177
|
+
|
178
|
+
Archlinux.delegate_h(self, :@l)
|
179
|
+
attr_accessor :children_mode, :ext_query, :ignore, :query_ignore, :install_list, :install_method, :install_list_of
|
180
|
+
attr_reader :l, :versions, :provides_for
|
181
|
+
|
182
|
+
def initialize(list=[], config: Archlinux.config)
|
183
|
+
@l={}
|
184
|
+
@versions={} #hash of versions for quick look ups
|
185
|
+
@provides_for={} #hash of provides for quick look ups
|
186
|
+
@ext_query=nil #how to get missing packages, default
|
187
|
+
@children_mode=%i(depends) #children, default
|
188
|
+
@ignore=[] #ignore packages update
|
189
|
+
@query_ignore=[] #query ignore packages (ie these won't be returned by a query; so stronger than @ignore)
|
190
|
+
@install_list=nil #how do we check for new packages / updates
|
191
|
+
@install_list_of=nil #are we used to check for new packages / updates
|
192
|
+
@install_method=nil #how to install stuff
|
193
|
+
@config=config
|
194
|
+
merge(list)
|
195
|
+
end
|
196
|
+
|
197
|
+
# the names without the versions. Use self.keys to get the name=version
|
198
|
+
def names
|
199
|
+
@versions.keys
|
200
|
+
end
|
201
|
+
|
202
|
+
def list(version=false)
|
203
|
+
l= version ? keys.sort : names.sort
|
204
|
+
l.each do |pkg|
|
205
|
+
SH.logger.info "- #{pkg}"
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def name_of(pkg)
|
210
|
+
pkg.name_version
|
211
|
+
end
|
212
|
+
|
213
|
+
def packages
|
214
|
+
self
|
215
|
+
end
|
216
|
+
|
217
|
+
def same?(other)
|
218
|
+
unless @l.keys == other.keys
|
219
|
+
SH.logger.warn("#{self.class}: Inconsistency in the package names")
|
220
|
+
return false
|
221
|
+
end
|
222
|
+
r=true
|
223
|
+
@l.each do |name, pkg|
|
224
|
+
unless pkg.same?(other[name])
|
225
|
+
SH.logger.warn("#{self.class}: Inconsistensy for the package #{name}")
|
226
|
+
r=false
|
227
|
+
end
|
228
|
+
end
|
229
|
+
return r
|
230
|
+
end
|
231
|
+
|
232
|
+
def merge(l)
|
233
|
+
l= case l
|
234
|
+
when PackageList
|
235
|
+
l.values
|
236
|
+
when Hash
|
237
|
+
l.values.compact
|
238
|
+
else
|
239
|
+
l.to_a
|
240
|
+
end
|
241
|
+
l.each do |pkg|
|
242
|
+
pkg=Package.new(pkg) unless pkg.is_a?(Package)
|
243
|
+
name=name_of(pkg)
|
244
|
+
if @l.key?(name)
|
245
|
+
@l[name].merge(pkg)
|
246
|
+
else
|
247
|
+
@l[name]=pkg
|
248
|
+
end
|
249
|
+
|
250
|
+
@versions[pkg.name]||={}
|
251
|
+
@versions[pkg.name][pkg.version.to_s]=name
|
252
|
+
|
253
|
+
pkg[:provides].each do |p|
|
254
|
+
pkg=Query.strip(p)
|
255
|
+
@provides_for[pkg]||={}
|
256
|
+
@provides_for[pkg][p]=name #todo: do we pass the name or the full pkg?
|
257
|
+
#todo: we need a list here
|
258
|
+
end
|
259
|
+
end
|
260
|
+
self
|
261
|
+
end
|
262
|
+
|
263
|
+
# return all packages that provides for pkg
|
264
|
+
# this is more complicated than @provides_for[pkg]
|
265
|
+
# because if b provides a and c provides a, then pacman assumes that b
|
266
|
+
# provides c
|
267
|
+
def all_provides_for(pkg)
|
268
|
+
provides=l.fetch(pkg,{}).fetch(:provides,[]).map {|q| Query.strip(q)}
|
269
|
+
provided=([Query.strip(pkg)]+provides).flat_map do |prov|
|
270
|
+
@provides_for.fetch(prov,{}).values.map {|v| Version.strip(v)}
|
271
|
+
end.uniq
|
272
|
+
provided
|
273
|
+
end
|
274
|
+
|
275
|
+
def latest
|
276
|
+
r={}
|
277
|
+
@versions.each do |pkg, versions|
|
278
|
+
v=versions.keys.max do |v1,v2|
|
279
|
+
Version.create(v1) <=> Version.create(v2)
|
280
|
+
end
|
281
|
+
r[pkg]=@l[versions[v]]
|
282
|
+
end
|
283
|
+
r
|
284
|
+
end
|
285
|
+
|
286
|
+
# select the most appropriate match (does not use ext_query), using #query
|
287
|
+
def find(q, **opts)
|
288
|
+
return q if @l.key?(q)
|
289
|
+
q=Query.create(q); pkg=q.name
|
290
|
+
query(q, **opts) do |found, type|
|
291
|
+
if type==:version
|
292
|
+
unless found.empty? #we select the most up to date
|
293
|
+
max = found.max { |v,w| Version.create(v) <=> Version.create(w) }
|
294
|
+
return @versions[pkg][max]
|
295
|
+
end
|
296
|
+
elsif type==:provides
|
297
|
+
max = found.max { |v,w| Query.create(v) <=> Query.create(w) }
|
298
|
+
return @provides_for[pkg][max]
|
299
|
+
end
|
300
|
+
end
|
301
|
+
return nil
|
302
|
+
end
|
303
|
+
|
304
|
+
# output all matches (does not use ext_query)
|
305
|
+
def query(q, provides: false) #provides: do we check Provides?
|
306
|
+
q=Query.new(q) unless q.is_a?(Query)
|
307
|
+
matches=[]; pkg=q.name
|
308
|
+
if @versions.key?(pkg)
|
309
|
+
matches+=(found=@versions[pkg].keys.select {|v| q.satisfy?(Version.create(v))}).map {|k| @versions[pkg][k]}
|
310
|
+
yield(found, :version) if block_given?
|
311
|
+
end
|
312
|
+
if provides and @provides_for.key?(pkg)
|
313
|
+
matches+=(found=@provides_for[pkg].keys.select {|v| q.satisfy?(Query.create(v))}).map {|k| @provides_for[pkg][k]}
|
314
|
+
yield(found, :provides) if block_given?
|
315
|
+
end
|
316
|
+
matches
|
317
|
+
end
|
318
|
+
|
319
|
+
# here the arguments are Strings
|
320
|
+
# return the arguments replaced by eventual provides + missing packages
|
321
|
+
# are added to @l
|
322
|
+
# So this is like find, except we respect @query_ignore, and call
|
323
|
+
# ext_query for missing packages
|
324
|
+
def resolve(*queries, provides: true, ext_query: @ext_query, fallback: true, log_missing: :warn, log_fallback: :warn, **opts)
|
325
|
+
got={}; missed=[]
|
326
|
+
pkgs=queries.map {|p| Query.strip(p)}
|
327
|
+
ignored = pkgs & @query_ignore
|
328
|
+
queries.each do |query|
|
329
|
+
if ignored.include?(Query.strip(query))
|
330
|
+
got[query]=nil #=> means the query was ignored
|
331
|
+
else
|
332
|
+
pkg=self.find(query, provides: provides, **opts)
|
333
|
+
if pkg
|
334
|
+
got[query]=pkg
|
335
|
+
else
|
336
|
+
missed << query
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
340
|
+
# we do it this way to call ext_query in batch
|
341
|
+
if ext_query and !missed.empty?
|
342
|
+
found, new_pkgs=ext_query.call(*missed, provides: provides)
|
343
|
+
self.merge(new_pkgs)
|
344
|
+
got.merge!(found)
|
345
|
+
missed-=found.keys
|
346
|
+
end
|
347
|
+
if fallback and !missed.empty?
|
348
|
+
new_queries={}
|
349
|
+
missed.each do |query|
|
350
|
+
if (query_pkg=Query.strip(query)) != query
|
351
|
+
new_queries[query]=query_pkg
|
352
|
+
# missed.delete(query)
|
353
|
+
end
|
354
|
+
end
|
355
|
+
unless new_queries.empty?
|
356
|
+
SH.log(log_fallback, "Trying fallback for packages: #{new_queries.keys.join(', ')}")
|
357
|
+
fallback_got=self.resolve(*new_queries.values, provides: provides, ext_query: ext_query, fallback: false, log_missing: :verbose, **opts)
|
358
|
+
got.merge!(fallback_got)
|
359
|
+
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?
|
360
|
+
end
|
361
|
+
else
|
362
|
+
SH.log(log_missing, "#{self.class}: Warning! Missing packages: #{missed.join(', ')}") unless missed.empty?
|
363
|
+
end
|
364
|
+
got
|
365
|
+
end
|
366
|
+
|
367
|
+
# this gives the keys of the packages we resolved
|
368
|
+
def get(*args)
|
369
|
+
#compact because the resolution can be nil for an ignored package
|
370
|
+
resolve(*args).values.compact
|
371
|
+
end
|
372
|
+
|
373
|
+
# this gives the values of the packages we resolved
|
374
|
+
def get_packages(*args)
|
375
|
+
l.values_at(*get(*args))
|
376
|
+
end
|
377
|
+
|
378
|
+
def get_package(pkg)
|
379
|
+
l[(get(pkg).first)]
|
380
|
+
end
|
381
|
+
|
382
|
+
# this is like a 'restrict' operation
|
383
|
+
def slice(*args)
|
384
|
+
self.class.new(l.slice(*get(*args)), **{})
|
385
|
+
end
|
386
|
+
|
387
|
+
# get children (non recursive)
|
388
|
+
def children(node, mode=@children_mode, log_level: :verbose2, **opts, &b)
|
389
|
+
deps=@l.fetch(node).dependencies(mode)
|
390
|
+
SH.log(log_level, "- #{node}: #{deps.join(', ')}")
|
391
|
+
deps=get(*deps, **opts)
|
392
|
+
SH.log(log_level, " => #{deps.join(', ')}") unless deps.empty?
|
393
|
+
if b
|
394
|
+
deps.each(&b)
|
395
|
+
else
|
396
|
+
deps
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
private def call_tsort(l, method: :tsort, **opts, &b)
|
401
|
+
each_node=l.method(:each)
|
402
|
+
s=self
|
403
|
+
each_child = lambda do |node, &b|
|
404
|
+
s.children(node, **opts, &b)
|
405
|
+
end
|
406
|
+
TSort.public_send(method, each_node, each_child, &b)
|
407
|
+
end
|
408
|
+
|
409
|
+
def tsort(l, **opts, &b)
|
410
|
+
if b
|
411
|
+
call_tsort(l, method: :each_strongly_connected_component, **opts, &b)
|
412
|
+
else
|
413
|
+
r=call_tsort(l, method: :strongly_connected_components, **opts)
|
414
|
+
cycles=r.select {|c| c.length > 1}
|
415
|
+
SH.logger.warn "Cycles detected: #{cycles}" unless cycles.empty?
|
416
|
+
r.flatten
|
417
|
+
end
|
418
|
+
end
|
419
|
+
|
420
|
+
# recursive get
|
421
|
+
def rget(*pkgs)
|
422
|
+
l=get(*pkgs)
|
423
|
+
tsort(l)
|
424
|
+
end
|
425
|
+
|
426
|
+
# check updates compared to another list
|
427
|
+
def check_updates(l, ignore: @ignore)
|
428
|
+
l=self.class.create(l)
|
429
|
+
a=self.latest; b=l.latest
|
430
|
+
r={}
|
431
|
+
b.each do |k, v|
|
432
|
+
next if ignore.include?(k)
|
433
|
+
if a.key?(k)
|
434
|
+
v1=a[k].version
|
435
|
+
v2=v.version
|
436
|
+
h={in: v1.to_s, out: v2.to_s, in_pkg: name_of(a[k]), out_pkg: name_of(v)}
|
437
|
+
case v1 <=> v2
|
438
|
+
when -1
|
439
|
+
h[:op]=:upgrade
|
440
|
+
when 0
|
441
|
+
h[:op]=:equal
|
442
|
+
when 1
|
443
|
+
h[:op]=:downgrade
|
444
|
+
end
|
445
|
+
r[k]=h
|
446
|
+
else
|
447
|
+
#new installation
|
448
|
+
r[k]={op: :install,
|
449
|
+
in: nil,
|
450
|
+
out: v.version.to_s, out_pkg: name_of(v)}
|
451
|
+
end
|
452
|
+
end
|
453
|
+
(a.keys-b.keys).each do |k|
|
454
|
+
next if ignore.include?(k)
|
455
|
+
r[k]={op: :obsolete,
|
456
|
+
in: a[k].version.to_s,
|
457
|
+
out: nil, in_pkg: name_of(a[k])}
|
458
|
+
end
|
459
|
+
r
|
460
|
+
end
|
461
|
+
|
462
|
+
def select_updates(r)
|
463
|
+
up=r.select {|_k,v| v[:op]==:upgrade or v[:op]==:install}
|
464
|
+
return up.map {|_k, v| v[:out_pkg]}, up
|
465
|
+
end
|
466
|
+
|
467
|
+
def get_updates(l, log_level: true, ignore: @ignore, rebuild: false, **showopts)
|
468
|
+
c=check_updates(l, ignore: ignore)
|
469
|
+
show_updates(c, log_level: log_level, **showopts)
|
470
|
+
if rebuild
|
471
|
+
# keep all packages
|
472
|
+
to_build=c.select {|_k,v| v[:out_pkg]}
|
473
|
+
return to_build.map {|_k, v| v[:out_pkg]}, to_build
|
474
|
+
else
|
475
|
+
select_updates(c)
|
476
|
+
end
|
477
|
+
end
|
478
|
+
|
479
|
+
#take the result of check_updates and pretty print them
|
480
|
+
#no_show has priority over :show
|
481
|
+
def show_updates(r, show: [:upgrade, :downgrade, :obsolete, :install], no_show: [], log_level: true)
|
482
|
+
r.each do |k,v|
|
483
|
+
next unless show.include?(v[:op]) and !no_show.include?(v[:op])
|
484
|
+
vin= v[:in] ? v[:in] : "(none)"
|
485
|
+
vout= v[:out] ? v[:out] : "(none)"
|
486
|
+
op=case v[:op]
|
487
|
+
when :downgrade
|
488
|
+
"<-"
|
489
|
+
when :upgrade, :install, :obsolete
|
490
|
+
"->"
|
491
|
+
when :equal
|
492
|
+
"="
|
493
|
+
end
|
494
|
+
extra=""
|
495
|
+
extra=" [#{v[:op]}]" if v[:op]!=:upgrade
|
496
|
+
|
497
|
+
SH.log(log_level, " -> #{k}: #{vin} #{op} #{vout}#{extra}")
|
498
|
+
end
|
499
|
+
end
|
500
|
+
|
501
|
+
# this take a list of packages which can be updates of ours
|
502
|
+
# return check_updates of this list (restricted to our current
|
503
|
+
# packages, so it won't show any 'install' operation
|
504
|
+
def check_update(updates=@install_list, ignore: @ignore)
|
505
|
+
return [] if updates.nil?
|
506
|
+
new_pkgs=updates.slice(*names) #ignore 'install' packages
|
507
|
+
check_updates(new_pkgs, ignore: ignore)
|
508
|
+
end
|
509
|
+
|
510
|
+
def update?(**opts)
|
511
|
+
install?(update: true, **opts)
|
512
|
+
end
|
513
|
+
|
514
|
+
# take a list of packages to install, return the new or updated
|
515
|
+
# packages to install with their dependencies
|
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)
|
517
|
+
packages+=self.names if update
|
518
|
+
if install_list
|
519
|
+
ignore -= packages.map {|p| Query.strip(p)}
|
520
|
+
SH.log(log_level_verbose, "# Checking packages #{packages.join(', ')}", color: :bold)
|
521
|
+
new_pkgs=install_list.slice(*packages)
|
522
|
+
u, u_infos=get_updates(new_pkgs, log_level: log_level_verbose, ignore: ignore, rebuild: rebuild, no_show: no_show, **showopts)
|
523
|
+
# todo: update this when we have a better preference mechanism
|
524
|
+
# (then we will need to put @official in the install package class)
|
525
|
+
new=self.class.new(l.values).merge(new_pkgs)
|
526
|
+
new.chain_query(install_list)
|
527
|
+
# The updates or new packages may need new deps
|
528
|
+
SH.log(log_level_verbose, "# Checking dependencies of #{u.join(', ')}", color: :bold) unless u.empty?
|
529
|
+
full=new.rget(*u)
|
530
|
+
SH.log(log_level, "New packages:", color: :bold)
|
531
|
+
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)
|
532
|
+
if rebuild and rebuild != "full" #we need to merge back u
|
533
|
+
full_updates |=u
|
534
|
+
full_infos.merge!(u_infos)
|
535
|
+
end
|
536
|
+
infos={top_pkgs: u_infos, all_pkgs: full_infos}
|
537
|
+
full_updates, infos=yield full_updates, infos if block_given?
|
538
|
+
return full_updates, infos
|
539
|
+
else
|
540
|
+
SH.logger.warn "External install list not defined"
|
541
|
+
end
|
542
|
+
end
|
543
|
+
|
544
|
+
def update(**opts, &b)
|
545
|
+
install(update: true, **opts, &b)
|
546
|
+
end
|
547
|
+
|
548
|
+
# the callback is passed to install? while the block is passed to
|
549
|
+
# @install_method
|
550
|
+
def install(*args, callback: nil, **opts, &b)
|
551
|
+
install_opts={}
|
552
|
+
keys=method(:install?).parameters.select {|arg| arg[0]==:key}.map {|arg| arg[1]}
|
553
|
+
keys.each do |key|
|
554
|
+
case key
|
555
|
+
when :rebuild
|
556
|
+
opts.key?(key) && install_opts[key]=opts.fetch(key)
|
557
|
+
else
|
558
|
+
opts.key?(key) && install_opts[key]=opts.delete(key)
|
559
|
+
end
|
560
|
+
end
|
561
|
+
l, l_info=install?(*args, **install_opts, &callback) #return false in the callback to prevent install
|
562
|
+
if @install_method
|
563
|
+
@install_method.call(l, pkgs_info: l_info, **opts, &b) unless !l or l.empty?
|
564
|
+
else
|
565
|
+
return l, l_info
|
566
|
+
end
|
567
|
+
end
|
568
|
+
|
569
|
+
#returns a Proc that can be used for another PackageList as an ext_query
|
570
|
+
def to_ext_query
|
571
|
+
method(:as_ext_query)
|
572
|
+
end
|
573
|
+
|
574
|
+
def chain_query(ext_query)
|
575
|
+
ext_query=ext_query.to_ext_query if ext_query.is_a?(PackageList)
|
576
|
+
if @ext_query
|
577
|
+
orig_query=@ext_query
|
578
|
+
@ext_query = lambda do |*args, **opts|
|
579
|
+
r, l=orig_query.call(*args, **opts)
|
580
|
+
missed = args-r.keys
|
581
|
+
r2, l2=ext_query.call(*missed, **opts)
|
582
|
+
return r.merge(r2), l.merge(l2)
|
583
|
+
end
|
584
|
+
else
|
585
|
+
@ext_query=ext_query
|
586
|
+
end
|
587
|
+
end
|
588
|
+
|
589
|
+
# essentially just a wrapper around resolve
|
590
|
+
def as_ext_query(*queries, provides: false, full_pkgs: false)
|
591
|
+
r=self.resolve(*queries, provides: provides, fallback: false)
|
592
|
+
# puts "#{self.class}: #{queries} => #{r}"
|
593
|
+
l= full_pkgs ? @l : slice(*r.values.compact)
|
594
|
+
return r, l
|
595
|
+
end
|
596
|
+
|
597
|
+
end
|
598
|
+
|
599
|
+
end
|