autoproj 1.7.21.b1 → 1.7.21.b2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- # The Gem::SpecFetcher object that should be used to query RubyGems, and
188
- # install RubyGems packages
189
- def gem_fetcher
190
- if !@gem_fetcher
191
- Autoproj.message "looking for RubyGems updates"
192
- @gem_fetcher = Gem::SpecFetcher.fetcher
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
- @gem_fetcher
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 = Array.new
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| OS_AUTO_PACKAGE_INSTALL.has_key?(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
- # On a dpkg-enabled system, checks if the provided package is installed
383
- # and returns true if it is the case
384
- def self.dpkg_package_installed?(package_name)
385
- if !@dpkg_installed_packages
386
- @dpkg_installed_packages = Set.new
387
- dpkg_status = File.readlines('/var/lib/dpkg/status')
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
- # It can return
792
+ # In all other cases, the method returns an array of triples:
456
793
  #
457
- # NO_PACKAGE::
458
- # there are no package definition for +name
459
- # UNKNOWN_OS::
460
- # this is not an OS autoproj knows how to deal with
461
- # WRONG_OS::
462
- # there are a package definition, but not for this OS
463
- # WRONG_OS_VERSION::
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 NO_PACKAGE
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
- if !os_entry
496
- return WRONG_OS
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
- # This package does not need to be installed on this operating system (example: build tools on Gentoo)
501
- if !data || data == "ignore"
502
- return IGNORE
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
- # If data is a hash, it means we have to check the OS version as well
506
- if data.kind_of?(Hash)
507
- # Look in +data+ for specific version. We look, in order, in the
508
- # os_versions field (detected OS versions), and return the first
509
- # matching entry
510
- version_entry = nil
511
- # First, look for an exact match
512
- found = os_versions.find do |os_ver|
513
- version_entry = data.find do |version_list, _|
514
- version_list.to_s.split(',').
515
- map(&:downcase).
516
- any? { |v| v == os_ver }
517
- end
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
- if !found
520
- # Then relax the matching ...
521
- found = os_versions.find do |os_ver|
522
- version_entry = data.find do |version_list, _|
523
- version_list.to_s.split(',').
524
- map(&:downcase).
525
- any? { |v| Regexp.new(v) =~ os_ver }
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
- end
529
- if Autoproj.verbose
530
- Autoproj.message "selected OS version #{found} for osdep #{name}: #{version_entry.inspect}"
531
- end
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
- if !version_entry
534
- return WRONG_OS_VERSION
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
- if data.respond_to?(:to_str) && data.to_str == 'nonexistent'
540
- return WRONG_OS_VERSION
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
- if data.respond_to?(:to_ary)
544
- # List of packages
545
- return [PACKAGES, data]
546
- elsif data.respond_to?(:to_str)
547
- # Single package
548
- return [PACKAGES, [data.to_str]]
549
- else
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
- os_names, _ = OSDependencies.operating_system
560
-
561
- os_packages = []
988
+ all_packages = []
562
989
  dependencies.each do |name|
563
990
  result = resolve_package(name)
564
- if result == NO_PACKAGE
991
+ if !result
565
992
  raise ConfigError.new, "there is no osdeps definition for #{name}"
566
- elsif result == WRONG_OS
567
- raise ConfigError.new, "there is an osdeps definition for #{name}, but not for this operating system"
568
- elsif result == WRONG_OS_VERSION
569
- raise ConfigError.new, "there is an osdeps definition for #{name}, but not for this particular operating system version"
570
- elsif result == IGNORE
571
- next
572
- elsif result[0] == PACKAGES
573
- os_packages.concat(result[1])
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
- if !os_names.any? { |os_name| OS_AUTO_PACKAGE_INSTALL.has_key?(os_name) }
578
- raise ConfigError.new, "I don't know how to install packages on #{os_names.first}"
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) == AVAILABLE
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 the same error code than
611
- # resolve_package.
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
- osdeps, gemdeps = partition_packages([name].to_set)
614
- if !osdeps.empty?
615
- osdeps.each do |dep_name|
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
- if pkg_def.respond_to?(:to_str)
666
- case(pkg_def.to_str)
667
- when "ignore" then
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
- [name]
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
- ruby_bin = Config::CONFIG['RUBY_INSTALL_NAME']
705
- if ruby_bin =~ /^ruby(.+)$/
706
- Autobuild.programs['gem'] = "gem#{$1}"
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
- end
711
-
712
- # Returns true if the osdeps system knows how to remove uptodate
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
- !installed.empty?
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
- attr_accessor :filter_uptodate_packages
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
- handled_os = OSDependencies.supported_operating_system?
1031
- # Remove the set of packages that have already been installed
1032
- packages -= installed_packages
1033
- return if packages.empty?
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
- did_something = false
1048
-
1049
- if !osdeps.empty? && (!os_packages || !os_packages.empty?)
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
- if Autoproj::OSDependencies.gem_with_prerelease
1094
- base_cmdline << "--prerelease"
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
- cmdlines = []
1099
- if !without_version.empty?
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
- if gems_interaction(gems, cmdlines, silent?)
1107
- Autoproj.message "installing/updating RubyGems dependencies: #{gems.map { |g| g.join(" ") }.sort.join(", ")}"
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