autoproj 2.0.0.b7 → 2.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +1 -0
  3. data/.gitignore +8 -0
  4. data/.travis.yml +5 -0
  5. data/Gemfile +6 -0
  6. data/README.md +4 -0
  7. data/Rakefile +40 -81
  8. data/autoproj.gemspec +29 -0
  9. data/lib/autoproj/cli/cache.rb +7 -2
  10. data/lib/autoproj/cli/main.rb +46 -25
  11. data/lib/autoproj/cli/main_test.rb +6 -4
  12. data/lib/autoproj/cli/status.rb +2 -2
  13. data/lib/autoproj/configuration.rb +5 -0
  14. data/lib/autoproj/git_server_configuration.rb +117 -0
  15. data/lib/autoproj/osdeps.rb +14 -914
  16. data/lib/autoproj/package_managers/apt_dpkg_manager.rb +70 -0
  17. data/lib/autoproj/package_managers/emerge_manager.rb +14 -0
  18. data/lib/autoproj/package_managers/gem_manager.rb +313 -0
  19. data/lib/autoproj/package_managers/homebrew_manager.rb +44 -0
  20. data/lib/autoproj/package_managers/manager.rb +45 -0
  21. data/lib/autoproj/package_managers/pacman_manager.rb +14 -0
  22. data/lib/autoproj/package_managers/pip_manager.rb +86 -0
  23. data/lib/autoproj/package_managers/pkg_manager.rb +14 -0
  24. data/lib/autoproj/package_managers/port_manager.rb +14 -0
  25. data/lib/autoproj/package_managers/shell_script_manager.rb +207 -0
  26. data/lib/autoproj/package_managers/unknown_os_manager.rb +42 -0
  27. data/lib/autoproj/package_managers/yum_manager.rb +56 -0
  28. data/lib/autoproj/package_managers/zypper_manager.rb +43 -0
  29. data/lib/autoproj/package_manifest.rb +1 -1
  30. data/lib/autoproj/test.rb +13 -14
  31. data/lib/autoproj/variable_expansion.rb +4 -2
  32. data/lib/autoproj/version.rb +1 -1
  33. data/lib/autoproj/workspace.rb +5 -1
  34. data/manifest.xml +20 -0
  35. metadata +73 -87
  36. data/.gemtest +0 -0
  37. data/Manifest.txt +0 -105
  38. data/test/data/empty_manifest.xml +0 -2
  39. data/test/data/full_manifest.xml +0 -19
  40. data/test/data/invalid_manifest.xml +0 -3
  41. data/test/data/os_release.with_complex_version_field +0 -3
  42. data/test/data/os_release.with_duplicate_values +0 -3
  43. data/test/data/os_release.with_missing_optional_fields +0 -2
  44. data/test/data/os_release.with_quoted_and_unquoted_fields +0 -2
  45. data/test/data/test_manifest/autoproj/local_set/local.autobuild +0 -0
  46. data/test/data/test_manifest/autoproj/local_set/source.yml +0 -1
  47. data/test/data/test_manifest/autoproj/manifest +0 -7
  48. data/test/ops/test_configuration.rb +0 -20
  49. data/test/ops/test_snapshot.rb +0 -26
  50. data/test/package_managers/apt-dpkg-status +0 -41
  51. data/test/package_managers/apt-dpkg-status.installed-last +0 -27
  52. data/test/package_managers/apt-dpkg-status.noninstalled-last +0 -12
  53. data/test/package_managers/test_apt_dpkg_manager.rb +0 -41
  54. data/test/package_managers/test_gem.rb +0 -156
  55. data/test/package_managers/test_pip.rb +0 -42
  56. data/test/test_manifest.rb +0 -48
  57. data/test/test_os_dependencies.rb +0 -586
  58. data/test/test_package.rb +0 -30
  59. data/test/test_package_manifest.rb +0 -65
  60. data/test/test_vcs_definition.rb +0 -46
@@ -1,923 +1,23 @@
1
1
  require 'tempfile'
2
2
  require 'json'
3
- module Autoproj
4
- # Module that contains the package manager implementations for the
5
- # OSDependencies class
6
- module PackageManagers
7
- # Base class for all package managers. Subclasses must add the
8
- # #install(packages) method and may add the
9
- # #filter_uptodate_packages(packages) method
10
- #
11
- # Package managers must be registered in PACKAGE_HANDLERS and
12
- # (if applicable) OS_PACKAGE_HANDLERS.
13
- class Manager
14
- # @return [Array<String>] the various names this package manager is
15
- # known about
16
- attr_reader :names
17
-
18
- attr_writer :enabled
19
- def enabled?; !!@enabled end
20
-
21
- attr_writer :silent
22
- def silent?; !!@silent end
23
-
24
- # Create a package manager registered with various names
25
- #
26
- # @param [Array<String>] names the package manager names. It MUST be
27
- # different from the OS names that autoproj uses. See the comment
28
- # for OS_PACKAGE_HANDLERS for an explanation
29
- def initialize(names = [])
30
- @names = names.dup
31
- @enabled = true
32
- @silent = true
33
- end
34
-
35
- # The primary name for this package manager
36
- def name
37
- names.first
38
- end
39
-
40
- # Overload to perform initialization of environment variables in
41
- # order to have a properly functioning package manager
42
- #
43
- # This is e.g. needed for python pip or rubygems
44
- def self.initialize_environment(_env = nil, _manifest = nil, _root_dir = Autoproj.root_dir)
45
- end
46
- end
47
-
48
- # Dummy package manager used for unknown OSes. It simply displays a
49
- # message to the user when packages are needed
50
- class UnknownOSManager < Manager
51
- def initialize
52
- super(['unknown'])
53
- @installed_osdeps = Set.new
54
- end
55
-
56
- def osdeps_interaction_unknown_os(osdeps)
57
- puts <<-EOMSG
58
- #{Autoproj.color("The build process requires some other software packages to be installed on our operating system", :bold)}
59
- #{Autoproj.color("If they are already installed, simply ignore this message", :red)}
60
-
61
- #{osdeps.to_a.sort.join("\n ")}
62
-
63
- EOMSG
64
- print Autoproj.color("Press ENTER to continue", :bold)
65
- STDOUT.flush
66
- STDIN.readline
67
- puts
68
- nil
69
- end
70
-
71
- def install(osdeps)
72
- if silent?
73
- return false
74
- else
75
- osdeps = osdeps.to_set
76
- osdeps -= @installed_osdeps
77
- if !osdeps.empty?
78
- result = osdeps_interaction_unknown_os(osdeps)
79
- end
80
- @installed_osdeps |= osdeps
81
- return result
82
- end
83
- end
84
- end
85
-
86
- # Base class for all package managers that simply require the call of a
87
- # shell script to install packages (e.g. yum, apt, ...)
88
- class ShellScriptManager < Manager
89
- def self.execute(script, with_locking, with_root)
90
- if with_locking
91
- File.open('/tmp/autoproj_osdeps_lock', 'w') do |lock_io|
92
- begin
93
- while !lock_io.flock(File::LOCK_EX | File::LOCK_NB)
94
- Autoproj.message " waiting for other autoproj instances to finish their osdeps installation"
95
- sleep 5
96
- end
97
- return execute(script, false,with_root)
98
- ensure
99
- lock_io.flock(File::LOCK_UN)
100
- end
101
- end
102
- end
103
-
104
- sudo = Autobuild.tool_in_path('sudo')
105
- Tempfile.open('osdeps_sh') do |io|
106
- io.puts "#! /bin/bash"
107
- io.puts GAIN_ROOT_ACCESS % [sudo] if with_root
108
- io.write script
109
- io.flush
110
- Autobuild::Subprocess.run 'autoproj', 'osdeps', '/bin/bash', io.path
111
- end
112
- end
113
-
114
- GAIN_ROOT_ACCESS = <<-EOSCRIPT
115
- # Gain root access using sudo
116
- if test `id -u` != "0"; then
117
- exec %s /bin/bash $0 "$@"
118
-
119
- fi
120
- EOSCRIPT
121
-
122
- # Overrides the {#needs_locking?} flag
123
- attr_writer :needs_locking
124
- # Whether two autoproj instances can run this package manager at the
125
- # same time
126
- #
127
- # This declares if this package manager cannot be used concurrently.
128
- # If it is the case, autoproj will ensure that there is no two
129
- # autoproj instances running this package manager at the same time
130
- #
131
- # @return [Boolean]
132
- # @see needs_locking=
133
- def needs_locking?; !!@needs_locking end
134
-
135
- # Overrides the {#needs_root?} flag
136
- attr_writer :needs_root
137
- # Whether this package manager needs root access.
138
- #
139
- # This declares if the command line(s) for this package manager
140
- # should be started as root. Root access is provided using sudo
141
- #
142
- # @return [Boolean]
143
- # @see needs_root=
144
- def needs_root?; !!@needs_root end
145
-
146
- # Command line used by autoproj to install packages
147
- #
148
- # Since it is to be used for automated install by autoproj, it
149
- # should not require any interaction with the user. When generating
150
- # the command line, the %s slot is replaced by the quoted package
151
- # name(s).
152
- #
153
- # @return [String] a command line pattern that allows to install
154
- # packages without user interaction. It is used when a package
155
- # should be installed by autoproj automatically
156
- attr_reader :auto_install_cmd
157
- # Command line displayed to the user to install packages
158
- #
159
- # When generating the command line, the %s slot is replaced by the
160
- # quoted package name(s).
161
- #
162
- # @return [String] a command line pattern that allows to install
163
- # packages with user interaction. It is displayed to the
164
- # user when it chose to not let autoproj install packages for this
165
- # package manager automatically
166
- attr_reader :user_install_cmd
167
-
168
- # @param [Array<String>] names the package managers names, see
169
- # {#names}
170
- # @param [Boolean] needs_locking whether this package manager can be
171
- # started by two separate autoproj instances at the same time. See
172
- # {#needs_locking?}
173
- # @param [String] user_install_cmd the user-visible command line. See
174
- # {#user_install_cmd}
175
- # @param [String] auto_install_cmd the command line used by autoproj
176
- # itself, see {#auto_install_cmd}.
177
- # @param [Boolean] needs_root if the command lines should be started
178
- # as root or not. See {#needs_root?}
179
- def initialize(names, needs_locking, user_install_cmd, auto_install_cmd,needs_root=true)
180
- super(names)
181
- @needs_locking, @user_install_cmd, @auto_install_cmd,@needs_root =
182
- needs_locking, user_install_cmd, auto_install_cmd, needs_root
183
- end
184
-
185
- # Generate the shell script that would allow the user to install
186
- # the given packages
187
- #
188
- # @param [Array<String>] os_packages the name of the packages to be
189
- # installed
190
- # @option options [String] :user_install_cmd (#user_install_cmd) the
191
- # command-line pattern that should be used to generate the script.
192
- # If given, it overrides the default value stored in
193
- # {#user_install_cmd]
194
- def generate_user_os_script(os_packages, options = Hash.new)
195
- user_install_cmd = options[:user_install_cmd] || self.user_install_cmd
196
- if user_install_cmd
197
- (user_install_cmd % [os_packages.join("' '")])
198
- else generate_auto_os_script(os_packages)
199
- end
200
- end
201
-
202
- # Generate the shell script that should be executed by autoproj to
203
- # install the given packages
204
- #
205
- # @param [Array<String>] os_packages the name of the packages to be
206
- # installed
207
- # @option options [String] :auto_install_cmd (#auto_install_cmd) the
208
- # command-line pattern that should be used to generate the script.
209
- # If given, it overrides the default value stored in
210
- # {#auto_install_cmd]
211
- def generate_auto_os_script(os_packages, options = Hash.new)
212
- auto_install_cmd = options[:auto_install_cmd] || self.auto_install_cmd
213
- (auto_install_cmd % [os_packages.join("' '")])
214
- end
215
-
216
- # Handles interaction with the user
217
- #
218
- # This method will verify whether the user required autoproj to
219
- # install packages from this package manager automatically. It
220
- # displays a relevant message if it is not the case.
221
- #
222
- # @return [Boolean] true if the packages should be installed
223
- # automatically, false otherwise
224
- def osdeps_interaction(os_packages, shell_script)
225
- if OSDependencies.force_osdeps
226
- return true
227
- elsif enabled?
228
- return true
229
- elsif silent?
230
- return false
231
- end
232
-
233
- # We're asked to not install the OS packages but to display them
234
- # anyway, do so now
235
- puts <<-EOMSG
236
-
237
- #{Autoproj.color("The build process and/or the packages require some other software to be installed", :bold)}
238
- #{Autoproj.color("and you required autoproj to not install them itself", :bold)}
239
- #{Autoproj.color("\nIf these packages are already installed, simply ignore this message\n", :red) if !respond_to?(:filter_uptodate_packages)}
240
- The following packages are available as OS dependencies, i.e. as prebuilt
241
- packages provided by your distribution / operating system. You will have to
242
- install them manually if they are not already installed
243
-
244
- #{os_packages.sort.join("\n ")}
245
-
246
- the following command line(s) can be run as root to install them:
247
-
248
- #{shell_script.split("\n").join("\n| ")}
249
-
250
- EOMSG
251
- print " #{Autoproj.color("Press ENTER to continue ", :bold)}"
252
- STDOUT.flush
253
- STDIN.readline
254
- puts
255
- false
256
- end
257
-
258
- # Install packages using this package manager
259
- #
260
- # @param [Array<String>] packages the name of the packages that
261
- # should be installed
262
- # @option options [String] :user_install_cmd (#user_install_cmd) the
263
- # command line that should be displayed to the user to install said
264
- # packages. See the option in {#generate_user_os_script}
265
- # @option options [String] :auto_install_cmd (#auto_install_cmd) the
266
- # command line that should be used by autoproj to install said
267
- # packages. See the option in {#generate_auto_os_script}
268
- # @return [Boolean] true if packages got installed, false otherwise
269
- def install(packages, options = Hash.new)
270
- handled_os = OSDependencies.supported_operating_system?
271
- if handled_os
272
- shell_script = generate_auto_os_script(packages, options)
273
- user_shell_script = generate_user_os_script(packages, options)
274
- end
275
- if osdeps_interaction(packages, user_shell_script)
276
- Autoproj.message " installing OS packages: #{packages.sort.join(", ")}"
277
-
278
- if Autoproj.verbose
279
- Autoproj.message "Generating installation script for non-ruby OS dependencies"
280
- Autoproj.message shell_script
281
- end
282
- ShellScriptManager.execute(shell_script, needs_locking?,needs_root?)
283
- return true
284
- end
285
- false
286
- end
287
- end
288
-
289
- # Package manager interface for systems that use port (i.e. MacPorts/Darwin) as
290
- # their package manager
291
- class PortManager < ShellScriptManager
292
- def initialize
293
- super(['macports'], true,
294
- "port install '%s'",
295
- "port install '%s'")
296
- end
297
- end
298
-
299
- # Package manager interface for Mac OS using homebrew as
300
- # its package manager
301
- class HomebrewManager < ShellScriptManager
302
- def initialize
303
- super(['brew'], true,
304
- "brew install '%s'",
305
- "brew install '%s'",
306
- false)
307
- end
308
-
309
- def filter_uptodate_packages(packages, options = Hash.new)
310
- # TODO there might be duplicates in packages which should be fixed
311
- # somewhere else
312
- packages = packages.uniq
313
- result = `brew info --json=v1 '#{packages.join("' '")}'`
314
- result = begin
315
- JSON.parse(result)
316
- rescue JSON::ParserError
317
- if result && !result.empty?
318
- Autoproj.warn "Error while parsing result of brew info --json=v1"
319
- else
320
- # one of the packages is unknown fallback to install all
321
- # packaes which will complain about it
322
- end
323
- return packages
324
- end
325
- # fall back if something else went wrong
326
- if packages.size != result.size
327
- Autoproj.warn "brew info returns less or more packages when requested. Falling back to install all packages"
328
- return packages
329
- end
330
-
331
- new_packages = []
332
- result.each do |pkg|
333
- new_packages << pkg["name"] if pkg["installed"].empty?
334
- end
335
- new_packages
336
- end
337
- end
338
-
339
- # Package manager interface for systems that use pacman (i.e. arch) as
340
- # their package manager
341
- class PacmanManager < ShellScriptManager
342
- def initialize
343
- super(['pacman'], true,
344
- "pacman -Sy --needed '%s'",
345
- "pacman -Sy --needed --noconfirm '%s'")
346
- end
347
- end
348
-
349
- # Package manager interface for systems that use emerge (i.e. gentoo) as
350
- # their package manager
351
- class EmergeManager < ShellScriptManager
352
- def initialize
353
- super(['emerge'], true,
354
- "emerge '%s'",
355
- "emerge --noreplace '%s'")
356
- end
357
- end
358
- # Package manager interface for systems that use pkg (i.e. FreeBSD) as
359
- # their package manager
360
- class PkgManager < ShellScriptManager
361
- def initialize
362
- super(['pkg'], true,
363
- "pkg install -y '%s'",
364
- "pkg install -y '%s'")
365
- end
366
- end
367
-
368
- #Package manger for OpenSuse and Suse (untested)
369
- class ZypperManager < ShellScriptManager
370
- def initialize
371
- super(['zypper'], true,
372
- "zypper install '%s'",
373
- "zypper -n install '%s'")
374
- end
375
-
376
- def filter_uptodate_packages(packages, options = Hash.new)
377
- result = `LANG=C rpm -q --whatprovides '#{packages.join("' '")}'`
378
- has_all_pkgs = $?.success?
379
-
380
- if !has_all_pkgs
381
- return packages # let zypper filter, we need root now anyways
382
- else
383
- return []
384
- end
385
- end
386
-
387
- def install(packages)
388
- patterns, packages = packages.partition { |pkg| pkg =~ /^@/ }
389
- patterns = patterns.map { |str| str[1..-1] }
390
- result = false
391
- if !patterns.empty?
392
- result |= super(patterns,
393
- :auto_install_cmd => "zypper --non-interactive install --type pattern '%s'",
394
- :user_install_cmd => "zypper install --type pattern '%s'")
395
- end
396
- if !packages.empty?
397
- result |= super(packages)
398
- end
399
- if result
400
- # Invalidate caching of installed packages, as we just
401
- # installed new packages !
402
- @installed_packages = nil
403
- end
404
- end
405
- end
406
-
407
- # Package manager interface for systems that use yum
408
- class YumManager < ShellScriptManager
409
- def initialize
410
- super(['yum'], true,
411
- "yum install '%s'",
412
- "yum install -y '%s'")
413
- end
414
-
415
- def filter_uptodate_packages(packages, options = Hash.new)
416
- result = `LANG=C rpm -q --queryformat "%{NAME}\n" '#{packages.join("' '")}'`
417
-
418
- installed_packages = []
419
- new_packages = []
420
- result.split("\n").each_with_index do |line, index|
421
- line = line.strip
422
- if line =~ /package (.*) is not installed/
423
- package_name = $1
424
- if !packages.include?(package_name) # something is wrong, fallback to installing everything
425
- return packages
426
- end
427
- new_packages << package_name
428
- else
429
- package_name = line.strip
430
- if !packages.include?(package_name) # something is wrong, fallback to installing everything
431
- return packages
432
- end
433
- installed_packages << package_name
434
- end
435
- end
436
- new_packages
437
- end
438
-
439
- def install(packages)
440
- patterns, packages = packages.partition { |pkg| pkg =~ /^@/ }
441
- patterns = patterns.map { |str| str[1..-1] }
442
- result = false
443
- if !patterns.empty?
444
- result |= super(patterns,
445
- :auto_install_cmd => "yum groupinstall -y '%s'",
446
- :user_install_cmd => "yum groupinstall '%s'")
447
- end
448
- if !packages.empty?
449
- result |= super(packages)
450
- end
451
- if result
452
- # Invalidate caching of installed packages, as we just
453
- # installed new packages !
454
- @installed_packages = nil
455
- end
456
- end
457
- end
458
-
459
- # Package manager interface for systems that use APT and dpkg for
460
- # package management
461
- class AptDpkgManager < ShellScriptManager
462
- attr_accessor :status_file
463
-
464
- def initialize(status_file = "/var/lib/dpkg/status")
465
- @status_file = status_file
466
- super(['apt-dpkg'], true,
467
- "apt-get install '%s'",
468
- "export DEBIAN_FRONTEND=noninteractive; apt-get install -y '%s'")
469
- end
470
-
471
- # On a dpkg-enabled system, checks if the provided package is installed
472
- # and returns true if it is the case
473
- def installed?(package_name)
474
- if !@installed_packages
475
- @installed_packages = Set.new
476
- dpkg_status = File.readlines(status_file)
477
- dpkg_status << ""
478
-
479
- current_packages = []
480
- is_installed = false
481
- dpkg_status.each do |line|
482
- line = line.chomp
483
- line = line.encode( "UTF-8", "binary", :invalid => :replace, :undef => :replace)
484
- if line == ""
485
- if is_installed
486
- current_packages.each do |pkg|
487
- @installed_packages << pkg
488
- end
489
- is_installed = false
490
- end
491
- current_packages.clear
492
- elsif line =~ /Package: (.*)$/
493
- current_packages << $1
494
- elsif line =~ /Provides: (.*)$/
495
- current_packages.concat($1.split(',').map(&:strip))
496
- elsif line == "Status: install ok installed"
497
- is_installed = true
498
- end
499
- end
500
- end
501
-
502
- if package_name =~ /^(\w[a-z0-9+-.]+)/
503
- @installed_packages.include?($1)
504
- else
505
- Autoproj.warn "#{package_name} is not a valid Debian package name"
506
- false
507
- end
508
- end
509
-
510
- def install(packages)
511
- if super
512
- # Invalidate caching of installed packages, as we just
513
- # installed new packages !
514
- @installed_packages = nil
515
- end
516
- end
517
-
518
- def filter_uptodate_packages(packages, options = Hash.new)
519
- packages.find_all do |package_name|
520
- !installed?(package_name)
521
- end
522
- end
523
- end
524
-
525
- # Package manager interface for the RubyGems system
526
- class GemManager < Manager
527
- class << self
528
- attr_writer :with_prerelease
529
- attr_accessor :with_doc
530
- end
531
- @with_prerelease = false
532
- @with_doc = false
533
-
534
- def self.with_prerelease(*value)
535
- if value.empty?
536
- @with_prerelease
537
- else
538
- begin
539
- saved_flag = @with_prerelease
540
- @with_prerelease = value.first
541
- yield
542
- ensure
543
- @with_prerelease = saved_flag
544
- end
545
- end
546
- end
547
-
548
- # Filters all paths that come from other autoproj installations out
549
- # of GEM_PATH
550
- def self.initialize_environment(env = Autobuild.env, manifest = Autoproj.manifest, root_dir = Autoproj.root_dir)
551
- env.original_env['GEM_PATH'] =
552
- (env['GEM_PATH'] || "").split(File::PATH_SEPARATOR).find_all do |p|
553
- !Autoproj.in_autoproj_installation?(p)
554
- end.join(File::PATH_SEPARATOR)
555
- env.inherit 'GEM_PATH'
556
- env.init_from_env 'GEM_PATH'
557
-
558
- orig_gem_path = env.original_env['GEM_PATH'].split(File::PATH_SEPARATOR)
559
- env.system_env['GEM_PATH'] = Gem.default_path
560
- env.original_env['GEM_PATH'] = orig_gem_path.join(File::PATH_SEPARATOR)
561
-
562
- manifest.each_reused_autoproj_installation do |p|
563
- p_gems = File.join(p, '.gems')
564
- if File.directory?(p_gems)
565
- env.push_path 'GEM_PATH', p_gems
566
- env.push_path 'PATH', File.join(p_gems, 'bin')
567
- end
568
- end
569
-
570
- @gem_home = (ENV['AUTOPROJ_GEM_HOME'] || File.join(root_dir, ".gems"))
571
- env.push_path 'GEM_PATH', gem_home
572
- env.set 'GEM_HOME', gem_home
573
- env.push_path 'PATH', "#{gem_home}/bin"
574
-
575
- # Now, reset the directories in our own RubyGems instance
576
- Gem.paths = env.resolved_env
577
-
578
- use_cache_dir
579
- end
580
-
581
- # Override the gem home detected by {initialize_environment}, or set
582
- # it in cases where calling {initialize_environment} is not possible
583
- def self.gem_home=(gem_home)
584
- @gem_home = gem_home
585
- end
586
-
587
- # A global cache directory that should be used to avoid
588
- # re-downloading gems
589
- def self.cache_dir
590
- if dir = ENV['AUTOBUILD_CACHE_DIR']
591
- dir = File.join(dir, 'gems')
592
- FileUtils.mkdir_p dir
593
- dir
594
- end
595
- end
596
-
597
- def self.use_cache_dir
598
- # If there is a cache directory, make sure .gems/cache points to
599
- # it (there are no programmatic ways to override this)
600
- if cache = cache_dir
601
- gem_cache_dir = File.join(gem_home, 'cache')
602
- if !File.symlink?(gem_cache_dir) || File.readlink(gem_cache_dir) != cache
603
- FileUtils.mkdir_p gem_home
604
- FileUtils.rm_rf gem_cache_dir
605
- Autoproj.create_symlink(cache, gem_cache_dir)
606
- end
607
- end
608
- end
609
-
610
- # Return the directory in which RubyGems package should be installed
611
- def self.gem_home
612
- @gem_home
613
- end
614
-
615
- # Returns the set of default options that are added to gem
616
- #
617
- # By default, we add --no-user-install to un-break distributions
618
- # like Arch that set --user-install by default (thus disabling the
619
- # role of GEM_HOME)
620
- def self.default_install_options
621
- @default_install_options ||= ['--no-user-install', '--no-format-executable']
622
- end
623
3
 
624
- def initialize
625
- super(['gem'])
626
- @installed_gems = Set.new
627
- end
4
+ require 'autoproj/package_managers/manager'
5
+ require 'autoproj/package_managers/unknown_os_manager'
6
+ require 'autoproj/package_managers/shell_script_manager'
628
7
 
629
- # Used to override the Gem::SpecFetcher object used by this gem
630
- # manager. Useful mainly for testing
631
- attr_writer :gem_fetcher
632
-
633
- # The set of gems installed during this autoproj session
634
- attr_reader :installed_gems
635
-
636
- def gem_fetcher
637
- if !@gem_fetcher
638
- Autoproj.message " looking for RubyGems updates"
639
- @gem_fetcher = Gem::SpecFetcher.fetcher
640
- end
641
- @gem_fetcher
642
- end
643
-
644
- def guess_gem_program
645
- if Autobuild.programs['gem']
646
- return Autobuild.programs['gem']
647
- end
648
-
649
- ruby_bin = RbConfig::CONFIG['RUBY_INSTALL_NAME']
650
- ruby_bindir = RbConfig::CONFIG['bindir']
651
-
652
- candidates = ['gem']
653
- if ruby_bin =~ /^ruby(.+)$/
654
- candidates << "gem#{$1}"
655
- end
656
-
657
- candidates.each do |gem_name|
658
- if File.file?(gem_full_path = File.join(ruby_bindir, gem_name))
659
- Autobuild.programs['gem'] = gem_full_path
660
- return
661
- end
662
- end
663
-
664
- raise ArgumentError, "cannot find a gem program (tried #{candidates.sort.join(", ")} in #{ruby_bindir})"
665
- end
666
-
667
- def build_gem_cmdlines(gems)
668
- with_version, without_version = gems.partition { |name, v| v }
669
-
670
- cmdlines = []
671
- if !without_version.empty?
672
- cmdlines << without_version.flatten
673
- end
674
- with_version.each do |name, v|
675
- cmdlines << [name, "-v", v]
676
- end
677
- cmdlines
678
- end
679
-
680
- def pristine(gems)
681
- guess_gem_program
682
- base_cmdline = [Autobuild.tool_in_path('ruby'), '-S', Autobuild.tool('gem')]
683
- cmdlines = [
684
- [*base_cmdline, 'clean'],
685
- ]
686
- cmdlines += build_gem_cmdlines(gems).map do |line|
687
- base_cmdline + ["pristine", "--extensions"] + line
688
- end
689
- if gems_interaction(gems, cmdlines)
690
- Autoproj.message " restoring RubyGems: #{gems.map { |g| g.join(" ") }.sort.join(", ")}"
691
- cmdlines.each do |c|
692
- Autobuild::Subprocess.run 'autoproj', 'osdeps', *c
693
- end
694
- end
695
- end
696
-
697
- def install(gems)
698
- guess_gem_program
699
-
700
- base_cmdline = [Autobuild.tool_in_path('ruby'), '-S', Autobuild.tool('gem'), 'install', *GemManager.default_install_options]
701
- if !GemManager.with_doc
702
- base_cmdline << '--no-rdoc' << '--no-ri'
703
- end
704
-
705
- if GemManager.with_prerelease
706
- base_cmdline << "--prerelease"
707
- end
708
-
709
- cmdlines = build_gem_cmdlines(gems).map do |line|
710
- base_cmdline + line
711
- end
712
- if gems_interaction(gems, cmdlines)
713
- Autoproj.message " installing/updating RubyGems dependencies: #{gems.map { |g| g.join(" ") }.sort.join(", ")}"
714
-
715
- cmdlines.each do |c|
716
- Autobuild::Subprocess.run 'autoproj', 'osdeps', *c,
717
- env: Hash['GEM_HOME' => Gem.paths.home,
718
- 'GEM_PATH' => Gem.paths.path.join(":")]
719
- end
720
- gems.each do |name, v|
721
- installed_gems << name
722
- end
723
- true
724
- end
725
- end
726
-
727
- # Returns the set of RubyGem packages in +packages+ that are not already
728
- # installed, or that can be upgraded
729
- def filter_uptodate_packages(gems, options = Hash.new)
730
- options = validate_options options,
731
- install_only: !Autobuild.do_update
732
-
733
- # Don't install gems that are already there ...
734
- gems = gems.dup
735
- gems.delete_if do |name, version|
736
- next(true) if installed_gems.include?(name)
737
-
738
- version_requirements = Gem::Requirement.new(version || '>= 0')
739
- installed =
740
- if Gem::Specification.respond_to?(:find_by_name)
741
- begin
742
- [Gem::Specification.find_by_name(name, version_requirements)]
743
- rescue Gem::LoadError
744
- []
745
- end
746
- else
747
- Gem.source_index.find_name(name, version_requirements)
748
- end
749
-
750
- if !installed.empty? && !options[:install_only]
751
- # Look if we can update the package ...
752
- dep = Gem::Dependency.new(name, version_requirements)
753
- available =
754
- if gem_fetcher.respond_to?(:find_matching)
755
- non_prerelease = gem_fetcher.find_matching(dep, true, true).map(&:first)
756
- if GemManager.with_prerelease
757
- prerelease = gem_fetcher.find_matching(dep, false, true, true).map(&:first)
758
- else prerelease = Array.new
759
- end
760
- (non_prerelease + prerelease).
761
- map { |n, v, _| [n, v] }
762
-
763
- else # Post RubyGems-2.0
764
- type = if GemManager.with_prerelease then :complete
765
- else :released
766
- end
767
-
768
- gem_fetcher.detect(type) do |tuple|
769
- tuple.name == name && dep.match?(tuple)
770
- end.map { |tuple, _| [tuple.name, tuple.version] }
771
- end
772
- installed_version = installed.map(&:version).max
773
- available_version = available.map { |_, v| v }.max
774
- if !available_version
775
- if version
776
- raise ConfigError.new, "cannot find any gem with the name '#{name}' and version #{version}"
777
- else
778
- raise ConfigError.new, "cannot find any gem with the name '#{name}'"
779
- end
780
- end
781
- needs_update = (available_version > installed_version)
782
- !needs_update
783
- else
784
- !installed.empty?
785
- end
786
- end
787
- gems
788
- end
789
-
790
- def parse_package_entry(entry)
791
- if entry =~ /^([^><=~]*)([><=~]+.*)$/
792
- [$1.strip, $2.strip]
793
- else
794
- [entry]
795
- end
796
- end
797
-
798
- def gems_interaction(gems, cmdlines)
799
- if OSDependencies.force_osdeps
800
- return true
801
- elsif enabled?
802
- return true
803
- elsif silent?
804
- return false
805
- end
806
-
807
- # We're not supposed to install rubygem packages but silent is not
808
- # set, so display information about them anyway
809
- puts <<-EOMSG
810
- #{Autoproj.color("The build process and/or the packages require some Ruby Gems to be installed", :bold)}
811
- #{Autoproj.color("and you required autoproj to not do it itself", :bold)}
812
- You can use the --all or --ruby options to autoproj osdeps to install these
813
- packages anyway, and/or change to the osdeps handling mode by running an
814
- autoproj operation with the --reconfigure option as for instance
815
- autoproj build --reconfigure
816
-
817
- The following command line can be used to install them manually
818
-
819
- #{cmdlines.map { |c| c.join(" ") }.join("\n ")}
820
-
821
- Autoproj expects these Gems to be installed in #{GemManager.gem_home} This can
822
- be overridden by setting the AUTOPROJ_GEM_HOME environment variable manually
823
-
824
- EOMSG
825
- print " #{Autoproj.color("Press ENTER to continue ", :bold)}"
826
-
827
- STDOUT.flush
828
- STDIN.readline
829
- puts
830
- false
831
- end
832
- end
833
-
834
- # Using pip to install python packages
835
- class PipManager < Manager
836
-
837
- attr_reader :installed_gems
838
-
839
- def self.initialize_environment(env = Autobuild.env, _manifest = nil, root_dir = Autoproj.root_dir)
840
- env.set 'PYTHONUSERBASE', pip_home(env, root_dir)
841
- end
842
-
843
- # Return the directory where python packages are installed to.
844
- # The actual path is pip_home/lib/pythonx.y/site-packages.
845
- def self.pip_home(env = Autobuild.env, root_dir = Autobuild.root_dir)
846
- env['AUTOPROJ_PYTHONUSERBASE'] || File.join(root_dir,".pip")
847
- end
848
-
849
-
850
- def initialize
851
- super(['pip'])
852
- @installed_pips = Set.new
853
- end
854
-
855
- def guess_pip_program
856
- if Autobuild.programs['pip']
857
- return Autobuild.programs['pip']
858
- end
859
-
860
- Autobuild.programs['pip'] = "pip"
861
- end
862
-
863
- def install(pips)
864
- guess_pip_program
865
- if pips.is_a?(String)
866
- pips = [pips]
867
- end
868
-
869
- base_cmdline = [Autobuild.tool('pip'), 'install','--user']
870
-
871
- cmdlines = [base_cmdline + pips]
872
-
873
- if pips_interaction(pips, cmdlines)
874
- Autoproj.message " installing/updating Python dependencies: "+
875
- "#{pips.sort.join(", ")}"
876
-
877
- cmdlines.each do |c|
878
- Autobuild::Subprocess.run 'autoproj', 'osdeps', *c
879
- end
880
-
881
- pips.each do |p|
882
- @installed_pips << p
883
- end
884
- end
885
- end
886
-
887
- def pips_interaction(pips, cmdlines)
888
- if OSDependencies.force_osdeps
889
- return true
890
- elsif enabled?
891
- return true
892
- elsif silent?
893
- return false
894
- end
895
-
896
- # We're not supposed to install rubygem packages but silent is not
897
- # set, so display information about them anyway
898
- puts <<-EOMSG
899
- #{Autoproj.color("The build process and/or the packages require some Python packages to be installed", :bold)}
900
- #{Autoproj.color("and you required autoproj to not do it itself", :bold)}
901
- The following command line can be used to install them manually
902
-
903
- #{cmdlines.map { |c| c.join(" ") }.join("\n ")}
904
-
905
- Autoproj expects these Python packages to be installed in #{PipManager.pip_home} This can
906
- be overridden by setting the AUTOPROJ_PYTHONUSERBASE environment variable manually
907
-
908
- EOMSG
909
- print " #{Autoproj.color("Press ENTER to continue ", :bold)}"
910
-
911
- STDOUT.flush
912
- STDIN.readline
913
- puts
914
- false
915
- end
916
- end
917
-
918
- end
8
+ require 'autoproj/package_managers/apt_dpkg_manager'
9
+ require 'autoproj/package_managers/emerge_manager'
10
+ require 'autoproj/package_managers/homebrew_manager'
11
+ require 'autoproj/package_managers/pacman_manager'
12
+ require 'autoproj/package_managers/pkg_manager'
13
+ require 'autoproj/package_managers/port_manager'
14
+ require 'autoproj/package_managers/yum_manager'
15
+ require 'autoproj/package_managers/zypper_manager'
919
16
 
17
+ require 'autoproj/package_managers/gem_manager'
18
+ require 'autoproj/package_managers/pip_manager'
920
19
 
20
+ module Autoproj
921
21
  # Manager for packages provided by external package managers
922
22
  class OSDependencies
923
23
  class << self