autoproj 1.7.21.b1 → 1.7.21.b2
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.
- data/bin/autoproj_bootstrap +691 -513
- data/lib/autoproj/default.osdeps +4 -2
- data/lib/autoproj/manifest.rb +1 -2
- data/lib/autoproj/osdeps.rb +688 -516
- data/lib/autoproj/version.rb +1 -1
- data/test/package_managers/test_gem.rb +151 -0
- data/test/test_os_dependencies.rb +441 -0
- metadata +8 -4
data/bin/autoproj_bootstrap
CHANGED
|
@@ -99,6 +99,374 @@ end
|
|
|
99
99
|
|
|
100
100
|
require 'tempfile'
|
|
101
101
|
module Autoproj
|
|
102
|
+
# Module that contains the package manager implementations for the
|
|
103
|
+
# OSDependencies class
|
|
104
|
+
module PackageManagers
|
|
105
|
+
# Base class for all package managers. Subclasses must add the
|
|
106
|
+
# #install(packages) method and may add the
|
|
107
|
+
# #filter_uptodate_packages(packages) method
|
|
108
|
+
class Manager
|
|
109
|
+
attr_reader :names
|
|
110
|
+
|
|
111
|
+
attr_writer :enabled
|
|
112
|
+
def enabled?; !!@enabled end
|
|
113
|
+
|
|
114
|
+
attr_writer :silent
|
|
115
|
+
def silent?; !!@silent end
|
|
116
|
+
|
|
117
|
+
def initialize(names = [])
|
|
118
|
+
@names = names.dup
|
|
119
|
+
@enabled = true
|
|
120
|
+
@silent = true
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Dummy package manager used for unknown OSes. It simply displays a
|
|
125
|
+
# message to the user when packages are needed
|
|
126
|
+
class UnknownOSManager < Manager
|
|
127
|
+
def osdeps_interaction_unknown_os(osdeps)
|
|
128
|
+
puts <<-EOMSG
|
|
129
|
+
#{Autoproj.color("The build process requires some other software packages to be installed on our operating system", :bold)}
|
|
130
|
+
#{Autoproj.color("If they are already installed, simply ignore this message", :red)}
|
|
131
|
+
|
|
132
|
+
#{osdeps.join("\n ")}
|
|
133
|
+
|
|
134
|
+
EOMSG
|
|
135
|
+
print Autoproj.color("Press ENTER to continue", :bold)
|
|
136
|
+
STDOUT.flush
|
|
137
|
+
STDIN.readline
|
|
138
|
+
puts
|
|
139
|
+
nil
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def install(osdeps)
|
|
144
|
+
if silent?
|
|
145
|
+
return false
|
|
146
|
+
else
|
|
147
|
+
return osdeps_interaction_unknown_os(osdeps)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Base class for all package managers that simply require the call of a
|
|
153
|
+
# shell script to install packages (e.g. yum, apt, ...)
|
|
154
|
+
class ShellScriptManager < Manager
|
|
155
|
+
def self.execute_as_root(script, with_locking)
|
|
156
|
+
if with_locking
|
|
157
|
+
File.open('/tmp/autoproj_osdeps_lock', 'w') do |lock_io|
|
|
158
|
+
begin
|
|
159
|
+
while !lock_io.flock(File::LOCK_EX | File::LOCK_NB)
|
|
160
|
+
Autoproj.message " waiting for other autoproj instances to finish their osdeps installation"
|
|
161
|
+
sleep 5
|
|
162
|
+
end
|
|
163
|
+
return execute_as_root(script, false)
|
|
164
|
+
ensure
|
|
165
|
+
lock_io.flock(File::LOCK_UN)
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
Tempfile.open('osdeps_sh') do |io|
|
|
172
|
+
io.puts "#! /bin/bash"
|
|
173
|
+
io.puts GAIN_ROOT_ACCESS
|
|
174
|
+
io.write script
|
|
175
|
+
io.flush
|
|
176
|
+
Autobuild::Subprocess.run 'autoproj', 'osdeps', '/bin/bash', io.path
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
GAIN_ROOT_ACCESS = <<-EOSCRIPT
|
|
181
|
+
# Gain root access using sudo
|
|
182
|
+
if test `id -u` != "0"; then
|
|
183
|
+
exec sudo /bin/bash $0 "$@"
|
|
184
|
+
|
|
185
|
+
fi
|
|
186
|
+
EOSCRIPT
|
|
187
|
+
|
|
188
|
+
attr_writer :needs_locking
|
|
189
|
+
def needs_locking?; !!@needs_locking end
|
|
190
|
+
|
|
191
|
+
def initialize(names, needs_locking, user_install_cmd, auto_install_cmd)
|
|
192
|
+
super(names)
|
|
193
|
+
@needs_locking, @user_install_cmd, @auto_install_cmd =
|
|
194
|
+
needs_locking, user_install_cmd, auto_install_cmd
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def generate_user_os_script(os_packages)
|
|
198
|
+
if user_install_cmd
|
|
199
|
+
(user_install_cmd % [os_packages.join("' '")])
|
|
200
|
+
else generate_auto_os_script(os_packages)
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def generate_auto_os_script(os_packages)
|
|
205
|
+
(auto_install_cmd % [os_packages.join("' '")])
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def osdeps_interaction(osdeps, os_packages, shell_script)
|
|
209
|
+
if OSDependencies.force_osdeps
|
|
210
|
+
return true
|
|
211
|
+
elsif enabled?
|
|
212
|
+
return true
|
|
213
|
+
elsif silent?
|
|
214
|
+
return false
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# We're asked to not install the OS packages but to display them
|
|
218
|
+
# anyway, do so now
|
|
219
|
+
puts <<-EOMSG
|
|
220
|
+
|
|
221
|
+
#{Autoproj.color("The build process and/or the packages require some other software to be installed", :bold)}
|
|
222
|
+
#{Autoproj.color("and you required autoproj to not install them itself", :bold)}
|
|
223
|
+
#{Autoproj.color("\nIf these packages are already installed, simply ignore this message\n", :red) if !respond_to?(:filter_uptodate_packages)}
|
|
224
|
+
The following packages are available as OS dependencies, i.e. as prebuilt
|
|
225
|
+
packages provided by your distribution / operating system. You will have to
|
|
226
|
+
install them manually if they are not already installed
|
|
227
|
+
|
|
228
|
+
#{os_packages.sort.join("\n ")}
|
|
229
|
+
|
|
230
|
+
the following command line(s) can be run as root to install them:
|
|
231
|
+
|
|
232
|
+
#{shell_script.split("\n").join("\n| ")}
|
|
233
|
+
|
|
234
|
+
EOMSG
|
|
235
|
+
print " #{Autoproj.color("Press ENTER to continue ", :bold)}"
|
|
236
|
+
STDOUT.flush
|
|
237
|
+
STDIN.readline
|
|
238
|
+
puts
|
|
239
|
+
false
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def install(packages)
|
|
243
|
+
handled_os = OSDependencies.supported_operating_system?
|
|
244
|
+
if handled_os
|
|
245
|
+
shell_script = generate_auto_os_script(os_packages)
|
|
246
|
+
user_shell_script = generate_user_os_script(os_packages)
|
|
247
|
+
end
|
|
248
|
+
if osdeps_interaction(osdeps, os_packages, user_shell_script)
|
|
249
|
+
Autoproj.message " installing OS packages: #{os_packages.sort.join(", ")}"
|
|
250
|
+
|
|
251
|
+
if Autoproj.verbose
|
|
252
|
+
Autoproj.message "Generating installation script for non-ruby OS dependencies"
|
|
253
|
+
Autoproj.message shell_script
|
|
254
|
+
end
|
|
255
|
+
ShellScriptManager.execute_as_root(shell_script, needs_locking?)
|
|
256
|
+
return true
|
|
257
|
+
end
|
|
258
|
+
false
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
# Package manager interface for systems that use APT and dpkg for
|
|
263
|
+
# package management
|
|
264
|
+
class AptDpkgManager < ShellScriptManager
|
|
265
|
+
def initialize
|
|
266
|
+
super(['apt-dpkg'], true,
|
|
267
|
+
"apt-get install '%s'",
|
|
268
|
+
"export DEBIAN_FRONTEND=noninteractive; apt-get install -y '%s'")
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# On a dpkg-enabled system, checks if the provided package is installed
|
|
272
|
+
# and returns true if it is the case
|
|
273
|
+
def installed?(package_name)
|
|
274
|
+
if !@installed_packages
|
|
275
|
+
@installed_packages = Set.new
|
|
276
|
+
dpkg_status = File.readlines('/var/lib/dpkg/status')
|
|
277
|
+
|
|
278
|
+
current_packages = []
|
|
279
|
+
is_installed = false
|
|
280
|
+
dpkg_status.each do |line|
|
|
281
|
+
line = line.chomp
|
|
282
|
+
if line == ""
|
|
283
|
+
if is_installed
|
|
284
|
+
current_packages.each do |pkg|
|
|
285
|
+
@installed_packages << pkg
|
|
286
|
+
end
|
|
287
|
+
current_packages.clear
|
|
288
|
+
is_installed = false
|
|
289
|
+
end
|
|
290
|
+
elsif line =~ /Package: (.*)$/
|
|
291
|
+
current_packages << $1
|
|
292
|
+
elsif line =~ /Provides: (.*)$/
|
|
293
|
+
current_packages.concat($1.split(',').map(&:strip))
|
|
294
|
+
elsif line == "Status: install ok installed"
|
|
295
|
+
is_installed = true
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
if package_name =~ /^(\w[a-z0-9+-.]+)/
|
|
301
|
+
@installed_packages.include?($1)
|
|
302
|
+
else
|
|
303
|
+
Autoproj.warn "#{package_name} is not a valid Debian package name"
|
|
304
|
+
false
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def filter_uptodate_packages(packages)
|
|
309
|
+
packages.find_all do |package_name|
|
|
310
|
+
!installed?(package_name)
|
|
311
|
+
end
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
# Package manager interface for the RubyGems system
|
|
316
|
+
class GemManager < Manager
|
|
317
|
+
class << self
|
|
318
|
+
attr_accessor :with_prerelease
|
|
319
|
+
attr_accessor :with_doc
|
|
320
|
+
end
|
|
321
|
+
@with_prerelease = false
|
|
322
|
+
@with_doc = false
|
|
323
|
+
|
|
324
|
+
def initialize
|
|
325
|
+
super(['gem'])
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
# Used to override the Gem::SpecFetcher object used by this gem
|
|
329
|
+
# manager. Useful mainly for testing
|
|
330
|
+
attr_writer :gem_fetcher
|
|
331
|
+
|
|
332
|
+
def gem_fetcher
|
|
333
|
+
if !@gem_fetcher
|
|
334
|
+
Autoproj.message "looking for RubyGems updates"
|
|
335
|
+
@gem_fetcher = Gem::SpecFetcher.fetcher
|
|
336
|
+
end
|
|
337
|
+
@gem_fetcher
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
def guess_gem_program
|
|
341
|
+
if Autobuild.programs['gem']
|
|
342
|
+
return Autobuild.programs['gem']
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
ruby_bin = Config::CONFIG['RUBY_INSTALL_NAME']
|
|
346
|
+
if ruby_bin =~ /^ruby(.+)$/
|
|
347
|
+
Autobuild.programs['gem'] = "gem#{$1}"
|
|
348
|
+
else
|
|
349
|
+
Autobuild.programs['gem'] = "gem"
|
|
350
|
+
end
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
def install(gems)
|
|
354
|
+
guess_gem_program
|
|
355
|
+
|
|
356
|
+
base_cmdline = [Autobuild.tool('gem'), 'install']
|
|
357
|
+
if !GemManager.with_doc
|
|
358
|
+
base_cmdline << '--no-rdoc' << '--no-ri'
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
if GemManager.with_prerelease
|
|
362
|
+
base_cmdline << "--prerelease"
|
|
363
|
+
end
|
|
364
|
+
with_version, without_version = gems.partition { |name, v| v }
|
|
365
|
+
|
|
366
|
+
cmdlines = []
|
|
367
|
+
if !without_version.empty?
|
|
368
|
+
cmdlines << (base_cmdline + without_version.flatten)
|
|
369
|
+
end
|
|
370
|
+
with_version.each do |name, v|
|
|
371
|
+
cmdlines << base_cmdline + [name, "-v", v]
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
if gems_interaction(gems, cmdlines)
|
|
375
|
+
Autoproj.message "installing/updating RubyGems dependencies: #{gems.map { |g| g.join(" ") }.sort.join(", ")}"
|
|
376
|
+
|
|
377
|
+
cmdlines.each do |c|
|
|
378
|
+
Autobuild::Subprocess.run 'autoproj', 'osdeps', *c
|
|
379
|
+
end
|
|
380
|
+
did_something = true
|
|
381
|
+
end
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
# Returns the set of RubyGem packages in +packages+ that are not already
|
|
385
|
+
# installed, or that can be upgraded
|
|
386
|
+
def filter_uptodate_packages(gems)
|
|
387
|
+
# Don't install gems that are already there ...
|
|
388
|
+
gems = gems.dup
|
|
389
|
+
gems.delete_if do |name, version|
|
|
390
|
+
version_requirements = Gem::Requirement.new(version || '>= 0')
|
|
391
|
+
installed =
|
|
392
|
+
if Gem::Specification.respond_to?(:find_by_name)
|
|
393
|
+
begin
|
|
394
|
+
[Gem::Specification.find_by_name(name, version_requirements)]
|
|
395
|
+
rescue Gem::LoadError
|
|
396
|
+
[]
|
|
397
|
+
end
|
|
398
|
+
else
|
|
399
|
+
Gem.source_index.find_name(name, version_requirements)
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
if !installed.empty? && Autobuild.do_update
|
|
403
|
+
# Look if we can update the package ...
|
|
404
|
+
dep = Gem::Dependency.new(name, version_requirements)
|
|
405
|
+
available = gem_fetcher.find_matching(dep, true, true, GemManager.with_prerelease)
|
|
406
|
+
installed_version = installed.map(&:version).max
|
|
407
|
+
available_version = available.map { |(name, v), source| v }.max
|
|
408
|
+
if !available_version
|
|
409
|
+
if version
|
|
410
|
+
raise ConfigError.new, "cannot find any gem with the name '#{name}' and version #{version}"
|
|
411
|
+
else
|
|
412
|
+
raise ConfigError.new, "cannot find any gem with the name '#{name}'"
|
|
413
|
+
end
|
|
414
|
+
end
|
|
415
|
+
needs_update = (available_version > installed_version)
|
|
416
|
+
!needs_update
|
|
417
|
+
else
|
|
418
|
+
!installed.empty?
|
|
419
|
+
end
|
|
420
|
+
end
|
|
421
|
+
gems
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
def parse_package_entry(entry)
|
|
425
|
+
if entry =~ /^([^><=~]*)([><=~]+.*)$/
|
|
426
|
+
[$1.strip, $2.strip]
|
|
427
|
+
else
|
|
428
|
+
[entry]
|
|
429
|
+
end
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
def gems_interaction(gems, cmdlines)
|
|
433
|
+
if OSDependencies.force_osdeps
|
|
434
|
+
return true
|
|
435
|
+
elsif enabled?
|
|
436
|
+
return true
|
|
437
|
+
elsif silent?
|
|
438
|
+
return false
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
# We're not supposed to install rubygem packages but silent is not
|
|
442
|
+
# set, so display information about them anyway
|
|
443
|
+
puts <<-EOMSG
|
|
444
|
+
#{Autoproj.color("The build process and/or the packages require some Ruby Gems to be installed", :bold)}
|
|
445
|
+
#{Autoproj.color("and you required autoproj to not do it itself", :bold)}
|
|
446
|
+
You can use the --all or --ruby options to autoproj osdeps to install these
|
|
447
|
+
packages anyway, and/or change to the osdeps handling mode by running an
|
|
448
|
+
autoproj operation with the --reconfigure option as for instance
|
|
449
|
+
autoproj build --reconfigure
|
|
450
|
+
|
|
451
|
+
The following command line can be used to install them manually
|
|
452
|
+
|
|
453
|
+
#{cmdlines.map { |c| c.join(" ") }.join("\n ")}
|
|
454
|
+
|
|
455
|
+
Autoproj expects these Gems to be installed in #{Autoproj.gem_home} This can
|
|
456
|
+
be overridden by setting the AUTOPROJ_GEM_HOME environment variable manually
|
|
457
|
+
|
|
458
|
+
EOMSG
|
|
459
|
+
print " #{Autoproj.color("Press ENTER to continue ", :bold)}"
|
|
460
|
+
|
|
461
|
+
STDOUT.flush
|
|
462
|
+
STDIN.readline
|
|
463
|
+
puts
|
|
464
|
+
false
|
|
465
|
+
end
|
|
466
|
+
end
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
# Manager for packages provided by external package managers
|
|
102
470
|
class OSDependencies
|
|
103
471
|
class << self
|
|
104
472
|
# When requested to load a file called X.Y, the osdeps code will
|
|
@@ -140,11 +508,8 @@ module Autoproj
|
|
|
140
508
|
class << self
|
|
141
509
|
attr_reader :aliases
|
|
142
510
|
attr_accessor :force_osdeps
|
|
143
|
-
attr_accessor :gem_with_prerelease
|
|
144
|
-
attr_accessor :gem_doc
|
|
145
511
|
end
|
|
146
512
|
@aliases = Hash.new
|
|
147
|
-
@gem_doc = false
|
|
148
513
|
|
|
149
514
|
attr_writer :silent
|
|
150
515
|
def silent?; @silent end
|
|
@@ -174,6 +539,11 @@ module Autoproj
|
|
|
174
539
|
OSDependencies.load(file)
|
|
175
540
|
end
|
|
176
541
|
|
|
542
|
+
PACKAGE_HANDLERS = [PackageManagers::AptDpkgManager, PackageManagers::GemManager]
|
|
543
|
+
OS_PACKAGE_HANDLERS = {
|
|
544
|
+
'debian' => 'apt-dpkg'
|
|
545
|
+
}
|
|
546
|
+
|
|
177
547
|
# The information contained in the OSdeps files, as a hash
|
|
178
548
|
attr_reader :definitions
|
|
179
549
|
# All the information contained in all the OSdeps files, as a mapping
|
|
@@ -184,22 +554,47 @@ module Autoproj
|
|
|
184
554
|
# package name to the osdeps file' full path
|
|
185
555
|
attr_reader :sources
|
|
186
556
|
|
|
187
|
-
#
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
557
|
+
# Use to override the autodetected OS-specific package handler
|
|
558
|
+
attr_writer :os_package_handler
|
|
559
|
+
|
|
560
|
+
# Returns the package manager object for the current OS
|
|
561
|
+
def os_package_handler
|
|
562
|
+
if @os_package_handler.nil?
|
|
563
|
+
os_names, _ = OSDependencies.operating_system
|
|
564
|
+
if os_names && (key = os_names.find { |name| OS_PACKAGE_HANDLERS[name] })
|
|
565
|
+
@os_package_handler = package_handlers[OS_PACKAGE_HANDLERS[key]]
|
|
566
|
+
if !@os_package_handler
|
|
567
|
+
raise ArgumentError, "found #{OS_PACKAGE_HANDLERS[name]} as the required package handler for #{os_names.join(", ")}, but it is not registered"
|
|
568
|
+
end
|
|
569
|
+
else
|
|
570
|
+
@os_package_handler = PackageManagers::UnknownOSManager.new
|
|
571
|
+
end
|
|
572
|
+
end
|
|
573
|
+
return @os_package_handler
|
|
574
|
+
end
|
|
575
|
+
|
|
576
|
+
# Returns the set of package managers
|
|
577
|
+
def package_handlers
|
|
578
|
+
if !@package_handlers
|
|
579
|
+
@package_handlers = Hash.new
|
|
580
|
+
PACKAGE_HANDLERS.each do |klass|
|
|
581
|
+
obj = klass.new
|
|
582
|
+
obj.names.each do |n|
|
|
583
|
+
@package_handlers[n] = obj
|
|
584
|
+
end
|
|
585
|
+
end
|
|
193
586
|
end
|
|
194
|
-
@
|
|
587
|
+
@package_handlers
|
|
195
588
|
end
|
|
196
589
|
|
|
590
|
+
# The Gem::SpecFetcher object that should be used to query RubyGems, and
|
|
591
|
+
# install RubyGems packages
|
|
197
592
|
def initialize(defs = Hash.new, file = nil)
|
|
198
593
|
@definitions = defs.to_hash
|
|
199
594
|
@all_definitions = Hash.new { |h, k| h[k] = Array.new }
|
|
200
595
|
|
|
201
596
|
@sources = Hash.new
|
|
202
|
-
@installed_packages =
|
|
597
|
+
@installed_packages = Set.new
|
|
203
598
|
if file
|
|
204
599
|
defs.each_key do |package_name|
|
|
205
600
|
sources[package_name] = file
|
|
@@ -268,12 +663,19 @@ module Autoproj
|
|
|
268
663
|
@supported_operating_system =
|
|
269
664
|
if !os_names then false
|
|
270
665
|
else
|
|
271
|
-
os_names.any? { |os_name|
|
|
666
|
+
os_names.any? { |os_name| OS_PACKAGE_HANDLERS.has_key?(os_name) }
|
|
272
667
|
end
|
|
273
668
|
end
|
|
274
669
|
return @supported_operating_system
|
|
275
670
|
end
|
|
276
671
|
|
|
672
|
+
# Used mainly during testing to bypass the operating system
|
|
673
|
+
# autodetection
|
|
674
|
+
def self.operating_system=(values)
|
|
675
|
+
@supported_operating_system = nil
|
|
676
|
+
@operating_system = values
|
|
677
|
+
end
|
|
678
|
+
|
|
277
679
|
# Autodetects the operating system name and version
|
|
278
680
|
#
|
|
279
681
|
# +osname+ is the operating system name, all in lowercase (e.g. ubuntu,
|
|
@@ -379,392 +781,303 @@ module Autoproj
|
|
|
379
781
|
return [distributor, [codename, version]]
|
|
380
782
|
end
|
|
381
783
|
|
|
382
|
-
#
|
|
383
|
-
#
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
current_packages = []
|
|
390
|
-
is_installed = false
|
|
391
|
-
dpkg_status.each do |line|
|
|
392
|
-
line = line.chomp
|
|
393
|
-
if line == ""
|
|
394
|
-
if is_installed
|
|
395
|
-
current_packages.each do |pkg|
|
|
396
|
-
@dpkg_installed_packages << pkg
|
|
397
|
-
end
|
|
398
|
-
current_packages.clear
|
|
399
|
-
is_installed = false
|
|
400
|
-
end
|
|
401
|
-
elsif line =~ /Package: (.*)$/
|
|
402
|
-
current_packages << $1
|
|
403
|
-
elsif line =~ /Provides: (.*)$/
|
|
404
|
-
current_packages.concat($1.split(',').map(&:strip))
|
|
405
|
-
elsif line == "Status: install ok installed"
|
|
406
|
-
is_installed = true
|
|
407
|
-
end
|
|
408
|
-
end
|
|
409
|
-
end
|
|
410
|
-
|
|
411
|
-
if package_name =~ /^(\w[a-z0-9+-.]+)/
|
|
412
|
-
@dpkg_installed_packages.include?($1)
|
|
413
|
-
else
|
|
414
|
-
Autoproj.warn "#{package_name} is not a valid Debian package name"
|
|
415
|
-
false
|
|
416
|
-
end
|
|
417
|
-
end
|
|
418
|
-
|
|
419
|
-
GAIN_ROOT_ACCESS = <<-EOSCRIPT
|
|
420
|
-
# Gain root access using sudo
|
|
421
|
-
if test `id -u` != "0"; then
|
|
422
|
-
exec sudo /bin/bash $0 "$@"
|
|
423
|
-
|
|
424
|
-
fi
|
|
425
|
-
EOSCRIPT
|
|
426
|
-
|
|
427
|
-
OS_PACKAGE_CHECK = {
|
|
428
|
-
'debian' => method(:dpkg_package_installed?),
|
|
429
|
-
'ubuntu' => method(:dpkg_package_installed?)
|
|
430
|
-
}
|
|
431
|
-
OS_USER_PACKAGE_INSTALL = {
|
|
432
|
-
'debian' => "apt-get install '%s'",
|
|
433
|
-
'ubuntu' => "apt-get install '%s'",
|
|
434
|
-
'gentoo' => "emerge '%s'",
|
|
435
|
-
'arch' => "pacman '%s'"
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
OS_AUTO_PACKAGE_INSTALL = {
|
|
439
|
-
'debian' => "export DEBIAN_FRONTEND=noninteractive; apt-get install -y '%s'",
|
|
440
|
-
'ubuntu' => "export DEBIAN_FRONTEND=noninteractive; apt-get install -y '%s'",
|
|
441
|
-
'gentoo' => "emerge --noreplace '%s'",
|
|
442
|
-
'arch' => "pacman -Sy --noconfirm '%s'"
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
NO_PACKAGE = 0
|
|
446
|
-
WRONG_OS = 1
|
|
447
|
-
WRONG_OS_VERSION = 2
|
|
448
|
-
IGNORE = 3
|
|
449
|
-
PACKAGES = 4
|
|
450
|
-
UNKNOWN_OS = 7
|
|
451
|
-
AVAILABLE = 10
|
|
452
|
-
|
|
453
|
-
# Check for the definition of +name+ for this operating system
|
|
784
|
+
# Return the list of packages that should be installed for +name+
|
|
785
|
+
#
|
|
786
|
+
# The following two simple return values are possible:
|
|
787
|
+
#
|
|
788
|
+
# nil:: +name+ has no definition
|
|
789
|
+
# []:: +name+ has no definition on this OS and/or for this specific OS
|
|
790
|
+
# version
|
|
454
791
|
#
|
|
455
|
-
#
|
|
792
|
+
# In all other cases, the method returns an array of triples:
|
|
456
793
|
#
|
|
457
|
-
#
|
|
458
|
-
#
|
|
459
|
-
#
|
|
460
|
-
#
|
|
461
|
-
#
|
|
462
|
-
#
|
|
463
|
-
#
|
|
464
|
-
# there is a package definition for this OS, but not for this
|
|
465
|
-
# particular version of the OS
|
|
466
|
-
# IGNORE::
|
|
467
|
-
# there is a package definition that told us to ignore the package
|
|
468
|
-
# [PACKAGES, definition]::
|
|
469
|
-
# +definition+ is an array of package names that this OS's package
|
|
470
|
-
# manager can understand
|
|
794
|
+
# [package_handler, status, package_list]
|
|
795
|
+
#
|
|
796
|
+
# where status is FOUND_PACKAGES if +package_list+ is the list of
|
|
797
|
+
# packages that should be installed with +package_handler+ for +name+,
|
|
798
|
+
# and FOUND_NONEXISTENT if the nonexistent keyword is used for this OS
|
|
799
|
+
# name and version. The package list might be empty even if status ==
|
|
800
|
+
# FOUND_PACKAGES, for instance if the ignore keyword is used.
|
|
471
801
|
def resolve_package(name)
|
|
472
802
|
os_names, os_versions = OSDependencies.operating_system
|
|
473
803
|
|
|
474
804
|
dep_def = definitions[name]
|
|
475
805
|
if !dep_def
|
|
476
|
-
return
|
|
477
|
-
elsif dep_def == 'ignore'
|
|
478
|
-
return IGNORE
|
|
479
|
-
elsif dep_def == 'nonexistent'
|
|
480
|
-
return WRONG_OS_VERSION
|
|
481
|
-
elsif !os_names
|
|
482
|
-
return UNKNOWN_OS
|
|
483
|
-
end
|
|
484
|
-
|
|
485
|
-
# Find a matching entry for the OS name
|
|
486
|
-
os_entry = nil
|
|
487
|
-
os_names.find do |os_name|
|
|
488
|
-
os_entry = dep_def.find do |name_list, _|
|
|
489
|
-
name_list.split(',').
|
|
490
|
-
map(&:downcase).
|
|
491
|
-
any? { |n| n == os_name }
|
|
492
|
-
end
|
|
806
|
+
return nil
|
|
493
807
|
end
|
|
494
808
|
|
|
495
|
-
|
|
496
|
-
|
|
809
|
+
# Partition the found definition in all entries that are interesting
|
|
810
|
+
# for us: toplevel os-independent package managers, os-dependent
|
|
811
|
+
# package managers and os-independent package managers selected by
|
|
812
|
+
# OS or version
|
|
813
|
+
if !os_names
|
|
814
|
+
os_names = ['default']
|
|
815
|
+
os_versions = ['default']
|
|
497
816
|
end
|
|
498
|
-
data = os_entry.last
|
|
499
817
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
818
|
+
package_handler_names = package_handlers.keys
|
|
819
|
+
|
|
820
|
+
result = []
|
|
821
|
+
found, pkg = partition_osdep_entry(name, dep_def, nil, (package_handler_names - os_package_handler.names), os_names, os_versions)
|
|
822
|
+
if found
|
|
823
|
+
result << [os_package_handler, found, pkg]
|
|
503
824
|
end
|
|
504
825
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
826
|
+
# NOTE: package_handlers might contain the same handler multiple
|
|
827
|
+
# times (when a package manager has multiple names). That's why we
|
|
828
|
+
# do a to_set.each
|
|
829
|
+
package_handlers.each_value.to_set.each do |handler|
|
|
830
|
+
found, pkg = partition_osdep_entry(name, dep_def, handler.names, [], os_names, os_versions)
|
|
831
|
+
if found
|
|
832
|
+
result << [handler, found, pkg]
|
|
833
|
+
end
|
|
834
|
+
end
|
|
835
|
+
|
|
836
|
+
result.map do |handler, status, entries|
|
|
837
|
+
if handler.respond_to?(:parse_package_entry)
|
|
838
|
+
[handler, status, entries.map { |s| handler.parse_package_entry(s) }]
|
|
839
|
+
else
|
|
840
|
+
[handler, status, entries]
|
|
518
841
|
end
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
842
|
+
end
|
|
843
|
+
end
|
|
844
|
+
|
|
845
|
+
# Value returned by #resolve_package and #partition_osdep_entry in
|
|
846
|
+
# the status field. See the documentation of these methods for more
|
|
847
|
+
# information
|
|
848
|
+
FOUND_PACKAGES = 0
|
|
849
|
+
# Value returned by #resolve_package and #partition_osdep_entry in
|
|
850
|
+
# the status field. See the documentation of these methods for more
|
|
851
|
+
# information
|
|
852
|
+
FOUND_NONEXISTENT = 1
|
|
853
|
+
|
|
854
|
+
# Helper method that parses the osdep definition to split between the
|
|
855
|
+
# parts needed for this OS and specific package handlers.
|
|
856
|
+
#
|
|
857
|
+
# +osdep_name+ is the name of the osdep. It is used to resolve explicit
|
|
858
|
+
# mentions of a package handler, i.e. so that:
|
|
859
|
+
#
|
|
860
|
+
# pkg: gem
|
|
861
|
+
#
|
|
862
|
+
# is resolved as the 'pkg' package to be installed by the 'gem' handler
|
|
863
|
+
#
|
|
864
|
+
# +dep_def+ is the content to parse. It can be a string, array or hash
|
|
865
|
+
#
|
|
866
|
+
# +handler_names+ is a list of entries that we are looking for. If it is
|
|
867
|
+
# not nil, only entries that explicitely refer to +handler_names+ will
|
|
868
|
+
# be browsed, i.e. in:
|
|
869
|
+
#
|
|
870
|
+
# pkg:
|
|
871
|
+
# - test: 1
|
|
872
|
+
# - [a, list, of, packages]
|
|
873
|
+
#
|
|
874
|
+
# partition_osdep_entry('osdep_name', data, ['test'], [])
|
|
875
|
+
#
|
|
876
|
+
# will ignore the toplevel list of packages, while
|
|
877
|
+
#
|
|
878
|
+
# partition_osdep_entry('osdep_name', data, nil, [])
|
|
879
|
+
#
|
|
880
|
+
# will return it.
|
|
881
|
+
#
|
|
882
|
+
# +excluded+ is a list of branches that should be ignored during
|
|
883
|
+
# parsing. It is used to e.g. ignore 'gem' when browsing for the main OS
|
|
884
|
+
# package list. For instance, in
|
|
885
|
+
#
|
|
886
|
+
# pkg:
|
|
887
|
+
# - test
|
|
888
|
+
# - [a, list, of, packages]
|
|
889
|
+
#
|
|
890
|
+
# partition_osdep_entry('osdep_name', data, nil, ['test'])
|
|
891
|
+
#
|
|
892
|
+
# the returned value will only include the list of packages (and not
|
|
893
|
+
# 'test')
|
|
894
|
+
#
|
|
895
|
+
# The rest of the arguments are array of strings that contain list of
|
|
896
|
+
# keys to browse for (usually, the OS names and version)
|
|
897
|
+
#
|
|
898
|
+
# The return value is either nil if no packages were found, or a pair
|
|
899
|
+
# [status, package_list] where status is FOUND_NONEXISTENT if the
|
|
900
|
+
# nonexistent keyword was found, and FOUND_PACKAGES if either packages
|
|
901
|
+
# or the ignore keyword were found.
|
|
902
|
+
#
|
|
903
|
+
def partition_osdep_entry(osdep_name, dep_def, handler_names, excluded, *keys)
|
|
904
|
+
keys, *additional_keys = *keys
|
|
905
|
+
found = false
|
|
906
|
+
nonexistent = false
|
|
907
|
+
result = []
|
|
908
|
+
found_keys = []
|
|
909
|
+
Array(dep_def).each do |names, values|
|
|
910
|
+
if !values
|
|
911
|
+
# Raw array of packages. Possible only if we are not at toplevel
|
|
912
|
+
# (i.e. if we already have a handler)
|
|
913
|
+
if names == 'ignore'
|
|
914
|
+
found = true if !handler_names
|
|
915
|
+
elsif names == 'nonexistent'
|
|
916
|
+
nonexistent = true if !handler_names
|
|
917
|
+
elsif !handler_names && names.kind_of?(Array)
|
|
918
|
+
result.concat(result)
|
|
919
|
+
found = true
|
|
920
|
+
elsif names.respond_to?(:to_str)
|
|
921
|
+
if excluded.include?(names)
|
|
922
|
+
elsif handler_names && handler_names.include?(names)
|
|
923
|
+
result << osdep_name
|
|
924
|
+
found = true
|
|
925
|
+
elsif !handler_names
|
|
926
|
+
result << names
|
|
927
|
+
found = true
|
|
526
928
|
end
|
|
929
|
+
elsif names.respond_to?(:to_hash)
|
|
930
|
+
rec_found, rec_result = partition_osdep_entry(osdep_name, names, handler_names, excluded, keys, *additional_keys)
|
|
931
|
+
if rec_found == FOUND_NONEXISTENT then nonexistent = true
|
|
932
|
+
elsif rec_found == FOUND_PACKAGES then found = true
|
|
933
|
+
end
|
|
934
|
+
result.concat(rec_result)
|
|
527
935
|
end
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
936
|
+
elsif keys
|
|
937
|
+
if names.respond_to?(:to_str) # names could be an array already
|
|
938
|
+
names = names.split(',')
|
|
939
|
+
end
|
|
940
|
+
|
|
941
|
+
if handler_names
|
|
942
|
+
if matching_name = handler_names.find { |k| names.any? { |name_tag| k == name_tag.downcase } }
|
|
943
|
+
rec_found, rec_result = partition_osdep_entry(osdep_name, values, nil, excluded, keys, *additional_keys)
|
|
944
|
+
if rec_found == FOUND_NONEXISTENT then nonexistent = true
|
|
945
|
+
elsif rec_found == FOUND_PACKAGES then found = true
|
|
946
|
+
end
|
|
947
|
+
result.concat(rec_result)
|
|
948
|
+
end
|
|
949
|
+
end
|
|
950
|
+
|
|
951
|
+
matching_name = keys.find { |k| names.any? { |name_tag| k == name_tag.downcase } }
|
|
952
|
+
if matching_name
|
|
953
|
+
rec_found, rec_result = partition_osdep_entry(osdep_name, values, handler_names, excluded, *additional_keys)
|
|
532
954
|
|
|
533
|
-
|
|
534
|
-
|
|
955
|
+
if rec_found
|
|
956
|
+
idx = keys.index(matching_name)
|
|
957
|
+
found_keys[idx] ||= [0, []]
|
|
958
|
+
found_keys[idx][0] += rec_found
|
|
959
|
+
found_keys[idx][1].concat(rec_result)
|
|
960
|
+
end
|
|
961
|
+
end
|
|
535
962
|
end
|
|
536
|
-
data = version_entry.last
|
|
537
963
|
end
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
964
|
+
if found_keys = found_keys.compact.first
|
|
965
|
+
if found_keys[0] > 0
|
|
966
|
+
nonexistent = true
|
|
967
|
+
else
|
|
968
|
+
found = true
|
|
969
|
+
end
|
|
970
|
+
result.concat(found_keys[1])
|
|
541
971
|
end
|
|
542
972
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
raise ConfigError.new, "invalid package specification #{data} in #{source_of(name)}"
|
|
551
|
-
end
|
|
973
|
+
found =
|
|
974
|
+
if nonexistent then FOUND_NONEXISTENT
|
|
975
|
+
elsif found then FOUND_PACKAGES
|
|
976
|
+
else false
|
|
977
|
+
end
|
|
978
|
+
|
|
979
|
+
return found, result
|
|
552
980
|
end
|
|
553
981
|
|
|
554
982
|
# Resolves the given OS dependencies into the actual packages that need
|
|
555
983
|
# to be installed on this particular OS.
|
|
556
984
|
#
|
|
557
|
-
# Raises ConfigError if some packages can't be found
|
|
985
|
+
# Raises ConfigError if some packages can't be found or if the
|
|
986
|
+
# nonexistent keyword was found for some of them
|
|
558
987
|
def resolve_os_dependencies(dependencies)
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
os_packages = []
|
|
988
|
+
all_packages = []
|
|
562
989
|
dependencies.each do |name|
|
|
563
990
|
result = resolve_package(name)
|
|
564
|
-
if result
|
|
991
|
+
if !result
|
|
565
992
|
raise ConfigError.new, "there is no osdeps definition for #{name}"
|
|
566
|
-
elsif result
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
993
|
+
elsif result.empty?
|
|
994
|
+
os_names, os_versions = OSDependencies.operating_system
|
|
995
|
+
raise ConfigError.new, "there is an osdeps definition for #{name}, but not for this operating system and version (resp. #{os_names.join(", ")} and #{os_versions.join(", ")})"
|
|
996
|
+
else
|
|
997
|
+
result.each do |handler, status, packages|
|
|
998
|
+
if status == FOUND_NONEXISTENT
|
|
999
|
+
raise ConfigError.new, "there is an osdep definition for #{name}, and it explicitely states that this package does not exist on your OS"
|
|
1000
|
+
end
|
|
1001
|
+
if entry = all_packages.find { |h, _| h == handler }
|
|
1002
|
+
entry[1].concat(packages)
|
|
1003
|
+
else
|
|
1004
|
+
all_packages << [handler, packages]
|
|
1005
|
+
end
|
|
1006
|
+
end
|
|
574
1007
|
end
|
|
575
1008
|
end
|
|
576
1009
|
|
|
577
|
-
|
|
578
|
-
|
|
1010
|
+
all_packages.delete_if do |handler, pkg|
|
|
1011
|
+
pkg.empty?
|
|
579
1012
|
end
|
|
580
|
-
|
|
581
|
-
return os_packages
|
|
1013
|
+
return all_packages
|
|
582
1014
|
end
|
|
583
1015
|
|
|
584
1016
|
|
|
585
|
-
def generate_user_os_script(os_names, os_packages)
|
|
586
|
-
user_package_install = nil
|
|
587
|
-
os_names.find do |os_name|
|
|
588
|
-
user_package_install = OS_USER_PACKAGE_INSTALL[os_name]
|
|
589
|
-
end
|
|
590
|
-
if user_package_install
|
|
591
|
-
(user_package_install % [os_packages.join("' '")])
|
|
592
|
-
else generate_auto_os_script(os_names, os_packages)
|
|
593
|
-
end
|
|
594
|
-
end
|
|
595
|
-
def generate_auto_os_script(os_names, os_packages)
|
|
596
|
-
auto_package_install = nil
|
|
597
|
-
os_names.find do |os_name|
|
|
598
|
-
auto_package_install = OS_AUTO_PACKAGE_INSTALL[os_name]
|
|
599
|
-
end
|
|
600
|
-
(auto_package_install % [os_packages.join("' '")])
|
|
601
|
-
end
|
|
602
|
-
|
|
603
1017
|
# Returns true if +name+ is an acceptable OS package for this OS and
|
|
604
1018
|
# version
|
|
605
1019
|
def has?(name)
|
|
606
|
-
availability_of(name)
|
|
1020
|
+
status = availability_of(name)
|
|
1021
|
+
status == AVAILABLE || status == IGNORE
|
|
607
1022
|
end
|
|
608
1023
|
|
|
1024
|
+
# Value returned by #availability_of if the required package has no
|
|
1025
|
+
# definition
|
|
1026
|
+
NO_PACKAGE = 0
|
|
1027
|
+
# Value returned by #availability_of if the required package has
|
|
1028
|
+
# definitions, but not for this OS name or version
|
|
1029
|
+
WRONG_OS = 1
|
|
1030
|
+
# Value returned by #availability_of if the required package has
|
|
1031
|
+
# definitions, but the local OS is unknown
|
|
1032
|
+
UNKNOWN_OS = 2
|
|
1033
|
+
# Value returned by #availability_of if the required package has
|
|
1034
|
+
# definitions, but the nonexistent keyword was used for this OS
|
|
1035
|
+
NONEXISTENT = 3
|
|
1036
|
+
# Value returned by #availability_of if the required package is
|
|
1037
|
+
# available
|
|
1038
|
+
AVAILABLE = 4
|
|
1039
|
+
# Value returned by #availability_of if the required package is
|
|
1040
|
+
# available, but no package needs to be installed to have it
|
|
1041
|
+
IGNORE = 5
|
|
1042
|
+
|
|
609
1043
|
# If +name+ is an osdeps that is available for this operating system,
|
|
610
|
-
# returns AVAILABLE. Otherwise, returns
|
|
611
|
-
#
|
|
1044
|
+
# returns AVAILABLE. Otherwise, returns one of:
|
|
1045
|
+
#
|
|
1046
|
+
# NO_PACKAGE:: the package has no definitions
|
|
1047
|
+
# WRONG_OS:: the package has a definition, but not for this OS
|
|
1048
|
+
# UNKNOWN_OS:: the package has a definition, but the local OS is unknown
|
|
1049
|
+
# NONEXISTENT:: the package has a definition, but the 'nonexistent'
|
|
1050
|
+
# keyword was found for this OS
|
|
1051
|
+
# AVAILABLE:: the package is available for this OS
|
|
1052
|
+
# IGNORE:: the package is available for this OS, but no packages need to
|
|
1053
|
+
# be installed for it
|
|
612
1054
|
def availability_of(name)
|
|
613
|
-
|
|
614
|
-
if !
|
|
615
|
-
|
|
616
|
-
status = resolve_package(dep_name)
|
|
617
|
-
if !status.respond_to?(:to_ary) && status != IGNORE
|
|
618
|
-
return status
|
|
619
|
-
end
|
|
620
|
-
end
|
|
621
|
-
AVAILABLE
|
|
622
|
-
elsif !gemdeps.empty?
|
|
623
|
-
AVAILABLE
|
|
624
|
-
else
|
|
625
|
-
NO_PACKAGE
|
|
1055
|
+
resolved = resolve_package(name)
|
|
1056
|
+
if !resolved
|
|
1057
|
+
return NO_PACKAGE
|
|
626
1058
|
end
|
|
627
|
-
end
|
|
628
|
-
|
|
629
|
-
# call-seq:
|
|
630
|
-
# partition_packages(package_names) => os_packages, gem_packages
|
|
631
|
-
#
|
|
632
|
-
# Resolves the package names listed in +package_set+, and returns a set
|
|
633
|
-
# of packages that have to be installed using the platform's native
|
|
634
|
-
# package manager, and the set of packages that have to be installed
|
|
635
|
-
# using Ruby's package manager, RubyGems.
|
|
636
|
-
#
|
|
637
|
-
# The os_packages arrays is a list of names (strings)
|
|
638
|
-
#
|
|
639
|
-
# The gem_packages arrays is a list of [name, version] pairs. +version+
|
|
640
|
-
# is a version string, that is set only if the package has been given
|
|
641
|
-
# with a =VERSION specification, e.g.
|
|
642
|
-
#
|
|
643
|
-
# gems:
|
|
644
|
-
# hoe=1.8.0
|
|
645
|
-
#
|
|
646
|
-
# Raises ConfigError if an error exists in the osdeps files, and returns
|
|
647
|
-
# empty sets if the package can't be found
|
|
648
|
-
def partition_packages(package_set)
|
|
649
|
-
package_set = package_set.
|
|
650
|
-
map { |name| OSDependencies.aliases[name] || name }.
|
|
651
|
-
to_set
|
|
652
|
-
|
|
653
|
-
osdeps, gems = [], []
|
|
654
|
-
package_set.to_set.each do |name|
|
|
655
|
-
pkg_def = definitions[name]
|
|
656
|
-
if !pkg_def
|
|
657
|
-
# Error cases are taken care of later, because that is were
|
|
658
|
-
# the automatic/manual osdeps logic lies
|
|
659
|
-
osdeps << name
|
|
660
|
-
next
|
|
661
|
-
end
|
|
662
|
-
|
|
663
|
-
pkg_def = pkg_def.dup
|
|
664
1059
|
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
osdeps << name
|
|
669
|
-
when "gem" then
|
|
670
|
-
gems << name
|
|
671
|
-
else
|
|
672
|
-
# This is *not* handled later, as is the absence of a
|
|
673
|
-
# package definition. The reason is that it is a bad
|
|
674
|
-
# configuration file, and should be fixed by the user
|
|
675
|
-
raise ConfigError.new, "unknown OS-independent package management type #{pkg_def} for #{name}"
|
|
676
|
-
end
|
|
677
|
-
else
|
|
678
|
-
pkg_def.delete_if do |distrib_name, defs|
|
|
679
|
-
if distrib_name == "gem"
|
|
680
|
-
gems.concat([*defs])
|
|
681
|
-
true
|
|
682
|
-
end
|
|
683
|
-
end
|
|
684
|
-
if !pkg_def.empty?
|
|
685
|
-
osdeps << name
|
|
686
|
-
end
|
|
687
|
-
end
|
|
688
|
-
end
|
|
689
|
-
gems.map! do |name|
|
|
690
|
-
if name =~ /^([^><=~]*)([><=~]+.*)$/
|
|
691
|
-
[$1.strip, $2.strip]
|
|
1060
|
+
if resolved.empty?
|
|
1061
|
+
if !OSDependencies.operating_system
|
|
1062
|
+
return UNKNOWN_OS
|
|
692
1063
|
else
|
|
693
|
-
|
|
1064
|
+
return WRONG_OS
|
|
694
1065
|
end
|
|
695
1066
|
end
|
|
696
|
-
return osdeps, gems
|
|
697
|
-
end
|
|
698
|
-
|
|
699
|
-
def guess_gem_program
|
|
700
|
-
if Autobuild.programs['gem']
|
|
701
|
-
return Autobuild.programs['gem']
|
|
702
|
-
end
|
|
703
1067
|
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
else
|
|
708
|
-
Autobuild.programs['gem'] = "gem"
|
|
1068
|
+
resolved = resolved.delete_if { |_, status, list| status == FOUND_PACKAGES && list.empty? }
|
|
1069
|
+
failed = resolved.find_all do |handler, status, list|
|
|
1070
|
+
status == FOUND_NONEXISTENT
|
|
709
1071
|
end
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
# packages from the needs-to-be-installed package list on this OS
|
|
714
|
-
def can_filter_uptodate_packages?
|
|
715
|
-
os_names, _ = OSDependencies.operating_system
|
|
716
|
-
!!os_names.any? { |os_name| OS_PACKAGE_CHECK[os_name] }
|
|
717
|
-
end
|
|
718
|
-
|
|
719
|
-
# Returns the set of packages in +packages+ that are not already
|
|
720
|
-
# installed on this OS, if it is supported
|
|
721
|
-
def filter_uptodate_os_packages(packages, os_names)
|
|
722
|
-
check_method = nil
|
|
723
|
-
os_names.find do |os_name|
|
|
724
|
-
check_method = OS_PACKAGE_CHECK[os_name]
|
|
725
|
-
end
|
|
726
|
-
return packages.dup if !check_method
|
|
727
|
-
|
|
728
|
-
packages.find_all { |pkg| !check_method[pkg] }
|
|
729
|
-
end
|
|
730
|
-
|
|
731
|
-
# Returns the set of RubyGem packages in +packages+ that are not already
|
|
732
|
-
# installed, or that can be upgraded
|
|
733
|
-
def filter_uptodate_gems(gems)
|
|
734
|
-
# Don't install gems that are already there ...
|
|
735
|
-
gems = gems.dup
|
|
736
|
-
gems.delete_if do |name, version|
|
|
737
|
-
version_requirements = Gem::Requirement.new(version || '>= 0')
|
|
738
|
-
installed =
|
|
739
|
-
if Gem::Specification.respond_to?(:find_by_name)
|
|
740
|
-
begin
|
|
741
|
-
[Gem::Specification.find_by_name(name, version_requirements)]
|
|
742
|
-
rescue Gem::LoadError
|
|
743
|
-
[]
|
|
744
|
-
end
|
|
745
|
-
else
|
|
746
|
-
Gem.source_index.find_name(name, version_requirements)
|
|
747
|
-
end
|
|
748
|
-
if !installed.empty? && Autobuild.do_update
|
|
749
|
-
# Look if we can update the package ...
|
|
750
|
-
dep = Gem::Dependency.new(name, version_requirements)
|
|
751
|
-
available = gem_fetcher.find_matching(dep, true, true, OSDependencies.gem_with_prerelease)
|
|
752
|
-
installed_version = installed.map(&:version).max
|
|
753
|
-
available_version = available.map { |(name, v), source| v }.max
|
|
754
|
-
if !available_version
|
|
755
|
-
if version
|
|
756
|
-
raise ConfigError.new, "cannot find any gem with the name '#{name}' and version #{version}"
|
|
757
|
-
else
|
|
758
|
-
raise ConfigError.new, "cannot find any gem with the name '#{name}'"
|
|
759
|
-
end
|
|
760
|
-
end
|
|
761
|
-
needs_update = (available_version > installed_version)
|
|
762
|
-
!needs_update
|
|
1072
|
+
if failed.empty?
|
|
1073
|
+
if resolved.empty?
|
|
1074
|
+
return IGNORE
|
|
763
1075
|
else
|
|
764
|
-
|
|
1076
|
+
return AVAILABLE
|
|
765
1077
|
end
|
|
1078
|
+
else
|
|
1079
|
+
return NONEXISTENT
|
|
766
1080
|
end
|
|
767
|
-
gems
|
|
768
1081
|
end
|
|
769
1082
|
|
|
770
1083
|
HANDLE_ALL = 'all'
|
|
@@ -873,7 +1186,15 @@ So, what do you want ? (all, ruby, os or none)
|
|
|
873
1186
|
# If set to true (the default), #install will try to remove the list of
|
|
874
1187
|
# already uptodate packages from the installed packages. Set to false to
|
|
875
1188
|
# install all packages regardless of their status
|
|
876
|
-
|
|
1189
|
+
attr_writer :filter_uptodate_packages
|
|
1190
|
+
|
|
1191
|
+
# If set to true (the default), #install will try to remove the list of
|
|
1192
|
+
# already uptodate packages from the installed packages. Use
|
|
1193
|
+
# #filter_uptodate_packages= to set it to false to install all packages
|
|
1194
|
+
# regardless of their status
|
|
1195
|
+
def filter_uptodate_packages?
|
|
1196
|
+
!!@filter_uptodate_packages
|
|
1197
|
+
end
|
|
877
1198
|
|
|
878
1199
|
# Override the osdeps mode
|
|
879
1200
|
def osdeps_mode=(value)
|
|
@@ -927,21 +1248,6 @@ So, what do you want ? (all, ruby, os or none)
|
|
|
927
1248
|
# The set of packages that have already been installed
|
|
928
1249
|
attr_reader :installed_packages
|
|
929
1250
|
|
|
930
|
-
def osdeps_interaction_unknown_os(osdeps)
|
|
931
|
-
puts <<-EOMSG
|
|
932
|
-
#{Autoproj.color("The build process requires some other software packages to be installed on our operating system", :bold)}
|
|
933
|
-
#{Autoproj.color("If they are already installed, simply ignore this message", :red)}
|
|
934
|
-
|
|
935
|
-
#{osdeps.join("\n ")}
|
|
936
|
-
|
|
937
|
-
EOMSG
|
|
938
|
-
print Autoproj.color("Press ENTER to continue", :bold)
|
|
939
|
-
STDOUT.flush
|
|
940
|
-
STDIN.readline
|
|
941
|
-
puts
|
|
942
|
-
nil
|
|
943
|
-
end
|
|
944
|
-
|
|
945
1251
|
def installs_os_packages?
|
|
946
1252
|
osdeps_mode == HANDLE_ALL || osdeps_mode == HANDLE_OS
|
|
947
1253
|
end
|
|
@@ -950,170 +1256,36 @@ So, what do you want ? (all, ruby, os or none)
|
|
|
950
1256
|
osdeps_mode == HANDLE_ALL || osdeps_mode == HANDLE_RUBY
|
|
951
1257
|
end
|
|
952
1258
|
|
|
953
|
-
def osdeps_interaction(osdeps, os_packages, shell_script, silent)
|
|
954
|
-
if !OSDependencies.supported_operating_system?
|
|
955
|
-
if silent
|
|
956
|
-
return false
|
|
957
|
-
else
|
|
958
|
-
return osdeps_interaction_unknown_os(osdeps)
|
|
959
|
-
end
|
|
960
|
-
elsif OSDependencies.force_osdeps
|
|
961
|
-
return true
|
|
962
|
-
elsif osdeps_mode == HANDLE_ALL || osdeps_mode == HANDLE_OS
|
|
963
|
-
return true
|
|
964
|
-
elsif silent
|
|
965
|
-
return false
|
|
966
|
-
end
|
|
967
|
-
|
|
968
|
-
# We're asked to not install the OS packages but to display them
|
|
969
|
-
# anyway, do so now
|
|
970
|
-
puts <<-EOMSG
|
|
971
|
-
|
|
972
|
-
#{Autoproj.color("The build process and/or the packages require some other software to be installed", :bold)}
|
|
973
|
-
#{Autoproj.color("and you required autoproj to not install them itself", :bold)}
|
|
974
|
-
#{Autoproj.color("\nIf these packages are already installed, simply ignore this message\n", :red) if !can_filter_uptodate_packages?}
|
|
975
|
-
The following packages are available as OS dependencies, i.e. as prebuilt
|
|
976
|
-
packages provided by your distribution / operating system. You will have to
|
|
977
|
-
install them manually if they are not already installed
|
|
978
|
-
|
|
979
|
-
#{os_packages.sort.join("\n ")}
|
|
980
|
-
|
|
981
|
-
the following command line(s) can be run as root to install them:
|
|
982
|
-
|
|
983
|
-
#{shell_script.split("\n").join("\n| ")}
|
|
984
|
-
|
|
985
|
-
EOMSG
|
|
986
|
-
print " #{Autoproj.color("Press ENTER to continue ", :bold)}"
|
|
987
|
-
STDOUT.flush
|
|
988
|
-
STDIN.readline
|
|
989
|
-
puts
|
|
990
|
-
false
|
|
991
|
-
end
|
|
992
|
-
|
|
993
|
-
def gems_interaction(gems, cmdlines, silent)
|
|
994
|
-
if OSDependencies.force_osdeps
|
|
995
|
-
return true
|
|
996
|
-
elsif osdeps_mode == HANDLE_ALL || osdeps_mode == HANDLE_RUBY
|
|
997
|
-
return true
|
|
998
|
-
elsif silent
|
|
999
|
-
return false
|
|
1000
|
-
end
|
|
1001
|
-
|
|
1002
|
-
# We're not supposed to install rubygem packages but silent is not
|
|
1003
|
-
# set, so display information about them anyway
|
|
1004
|
-
puts <<-EOMSG
|
|
1005
|
-
#{Autoproj.color("The build process and/or the packages require some Ruby Gems to be installed", :bold)}
|
|
1006
|
-
#{Autoproj.color("and you required autoproj to not do it itself", :bold)}
|
|
1007
|
-
You can use the --all or --ruby options to autoproj osdeps to install these
|
|
1008
|
-
packages anyway, and/or change to the osdeps handling mode by running an
|
|
1009
|
-
autoproj operation with the --reconfigure option as for instance
|
|
1010
|
-
autoproj build --reconfigure
|
|
1011
|
-
|
|
1012
|
-
The following command line can be used to install them manually
|
|
1013
|
-
|
|
1014
|
-
#{cmdlines.map { |c| c.join(" ") }.join("\n ")}
|
|
1015
|
-
|
|
1016
|
-
Autoproj expects these Gems to be installed in #{Autoproj.gem_home} This can
|
|
1017
|
-
be overridden by setting the AUTOPROJ_GEM_HOME environment variable manually
|
|
1018
|
-
|
|
1019
|
-
EOMSG
|
|
1020
|
-
print " #{Autoproj.color("Press ENTER to continue ", :bold)}"
|
|
1021
|
-
|
|
1022
|
-
STDOUT.flush
|
|
1023
|
-
STDIN.readline
|
|
1024
|
-
puts
|
|
1025
|
-
false
|
|
1026
|
-
end
|
|
1027
1259
|
|
|
1028
1260
|
# Requests the installation of the given set of packages
|
|
1029
1261
|
def install(packages, package_osdeps = Hash.new)
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
osdeps, gems = partition_packages(packages)
|
|
1036
|
-
if handled_os
|
|
1037
|
-
os_names, _ = OSDependencies.operating_system
|
|
1038
|
-
os_packages = resolve_os_dependencies(osdeps)
|
|
1039
|
-
if filter_uptodate_packages
|
|
1040
|
-
os_packages = filter_uptodate_os_packages(os_packages, os_names)
|
|
1041
|
-
end
|
|
1042
|
-
end
|
|
1043
|
-
if filter_uptodate_packages
|
|
1044
|
-
gems = filter_uptodate_gems(gems)
|
|
1262
|
+
os_package_handler.enabled = installs_os_packages?
|
|
1263
|
+
package_handlers['gem'].enabled = installs_ruby_packages?
|
|
1264
|
+
package_handlers.each_value do |v|
|
|
1265
|
+
v.silent = self.silent?
|
|
1045
1266
|
end
|
|
1046
1267
|
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
if handled_os
|
|
1051
|
-
shell_script = generate_auto_os_script(os_names, os_packages)
|
|
1052
|
-
user_shell_script = generate_user_os_script(os_names, os_packages)
|
|
1053
|
-
end
|
|
1054
|
-
if osdeps_interaction(osdeps, os_packages, user_shell_script, silent?)
|
|
1055
|
-
Autoproj.message " installing OS packages: #{os_packages.sort.join(", ")}"
|
|
1056
|
-
|
|
1057
|
-
if Autoproj.verbose
|
|
1058
|
-
Autoproj.message "Generating installation script for non-ruby OS dependencies"
|
|
1059
|
-
Autoproj.message shell_script
|
|
1060
|
-
end
|
|
1061
|
-
|
|
1062
|
-
File.open('/tmp/autoproj_osdeps_lock', 'w') do |lock_io|
|
|
1063
|
-
begin
|
|
1064
|
-
while !lock_io.flock(File::LOCK_EX | File::LOCK_NB)
|
|
1065
|
-
Autoproj.message " waiting for other autoproj instances to finish their osdeps installation"
|
|
1066
|
-
sleep 5
|
|
1067
|
-
end
|
|
1068
|
-
|
|
1069
|
-
Tempfile.open('osdeps_sh') do |io|
|
|
1070
|
-
io.puts "#! /bin/bash"
|
|
1071
|
-
io.puts GAIN_ROOT_ACCESS
|
|
1072
|
-
io.write shell_script
|
|
1073
|
-
io.flush
|
|
1074
|
-
Autobuild::Subprocess.run 'autoproj', 'osdeps', '/bin/bash', io.path
|
|
1075
|
-
end
|
|
1076
|
-
ensure
|
|
1077
|
-
lock_io.flock(File::LOCK_UN)
|
|
1078
|
-
end
|
|
1079
|
-
end
|
|
1080
|
-
did_something = true
|
|
1081
|
-
end
|
|
1082
|
-
end
|
|
1083
|
-
|
|
1084
|
-
# Now install the RubyGems
|
|
1085
|
-
if !gems.empty?
|
|
1086
|
-
guess_gem_program
|
|
1087
|
-
|
|
1088
|
-
base_cmdline = [Autobuild.tool('gem'), 'install']
|
|
1089
|
-
if !OSDependencies.gem_doc
|
|
1090
|
-
base_cmdline << '--no-rdoc' << '--no-ri'
|
|
1091
|
-
end
|
|
1268
|
+
# Remove the set of packages that have already been installed
|
|
1269
|
+
packages = packages.to_set - installed_packages
|
|
1270
|
+
return false if packages.empty?
|
|
1092
1271
|
|
|
1093
|
-
|
|
1094
|
-
|
|
1272
|
+
packages = resolve_os_dependencies(packages)
|
|
1273
|
+
packages = packages.map do |handler, list|
|
|
1274
|
+
if filter_uptodate_packages? && handler.respond_to?(:filter_uptodate_packages)
|
|
1275
|
+
list = handler.filter_uptodate_packages(list)
|
|
1095
1276
|
end
|
|
1096
|
-
with_version, without_version = gems.partition { |name, v| v }
|
|
1097
1277
|
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
cmdlines << (base_cmdline + without_version.flatten)
|
|
1101
|
-
end
|
|
1102
|
-
with_version.each do |name, v|
|
|
1103
|
-
cmdlines << base_cmdline + [name, "-v", v]
|
|
1278
|
+
if !list.empty?
|
|
1279
|
+
[handler, list]
|
|
1104
1280
|
end
|
|
1281
|
+
end.compact
|
|
1282
|
+
return false if packages.empty?
|
|
1105
1283
|
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
cmdlines.each do |c|
|
|
1110
|
-
Autobuild::Subprocess.run 'autoproj', 'osdeps', *c
|
|
1111
|
-
end
|
|
1112
|
-
did_something = true
|
|
1113
|
-
end
|
|
1284
|
+
packages.each do |handler, list|
|
|
1285
|
+
handler.install(list)
|
|
1286
|
+
@installed_packages |= list.to_set
|
|
1114
1287
|
end
|
|
1115
|
-
|
|
1116
|
-
did_something
|
|
1288
|
+
true
|
|
1117
1289
|
end
|
|
1118
1290
|
end
|
|
1119
1291
|
end
|
|
@@ -1558,27 +1730,33 @@ lsb_release:
|
|
|
1558
1730
|
ruby18:
|
|
1559
1731
|
gentoo:
|
|
1560
1732
|
- dev-lang/ruby:1.8
|
|
1733
|
+
- rake
|
|
1561
1734
|
debian,ubuntu:
|
|
1562
1735
|
- ruby1.8-dev
|
|
1563
1736
|
- ruby1.8
|
|
1564
1737
|
- rubygems1.8
|
|
1565
1738
|
- ri1.8
|
|
1566
1739
|
- libopenssl-ruby1.8
|
|
1740
|
+
- rake
|
|
1567
1741
|
ruby19:
|
|
1568
1742
|
debian:
|
|
1569
1743
|
- ruby1.9.1
|
|
1570
1744
|
- ruby1.9.1-dev
|
|
1571
1745
|
- rubygems1.9.1
|
|
1746
|
+
- rake
|
|
1572
1747
|
arch:
|
|
1573
1748
|
- ruby
|
|
1749
|
+
- rake
|
|
1574
1750
|
gentoo:
|
|
1575
1751
|
- dev-lang/ruby:1.9
|
|
1752
|
+
- rake
|
|
1576
1753
|
ubuntu:
|
|
1577
1754
|
- ruby1.9.1
|
|
1578
1755
|
- ruby1.9.1-dev
|
|
1579
1756
|
- rubygems1.9.1
|
|
1580
1757
|
- ri1.9.1
|
|
1581
1758
|
- libopenssl-ruby1.9.1
|
|
1759
|
+
- rake
|
|
1582
1760
|
git:
|
|
1583
1761
|
debian:
|
|
1584
1762
|
lenny: git
|