autoproj 2.0.0.rc3 → 2.0.0.rc4

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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +9 -29
  3. data/bin/autoproj_bootstrap +159 -3150
  4. data/bin/autoproj_bootstrap.in +4 -256
  5. data/bin/autoproj_install +225 -0
  6. data/bin/autoproj_install.in +14 -0
  7. data/lib/autoproj.rb +2 -1
  8. data/lib/autoproj/autobuild.rb +2 -2
  9. data/lib/autoproj/cli/bootstrap.rb +0 -39
  10. data/lib/autoproj/cli/build.rb +0 -3
  11. data/lib/autoproj/cli/main.rb +13 -1
  12. data/lib/autoproj/cli/osdeps.rb +1 -1
  13. data/lib/autoproj/cli/show.rb +1 -1
  14. data/lib/autoproj/cli/update.rb +4 -4
  15. data/lib/autoproj/cli/upgrade.rb +71 -0
  16. data/lib/autoproj/configuration.rb +18 -1
  17. data/lib/autoproj/exceptions.rb +7 -0
  18. data/lib/autoproj/installation_manifest.rb +23 -12
  19. data/lib/autoproj/manifest.rb +22 -48
  20. data/lib/autoproj/ops/build.rb +2 -2
  21. data/lib/autoproj/ops/configuration.rb +1 -1
  22. data/lib/autoproj/ops/import.rb +1 -1
  23. data/lib/autoproj/ops/install.rb +211 -0
  24. data/lib/autoproj/ops/main_config_switcher.rb +1 -5
  25. data/lib/autoproj/os_package_installer.rb +348 -0
  26. data/lib/autoproj/{osdeps.rb → os_package_resolver.rb} +56 -392
  27. data/lib/autoproj/package_managers/apt_dpkg_manager.rb +2 -2
  28. data/lib/autoproj/package_managers/bundler_manager.rb +179 -0
  29. data/lib/autoproj/package_managers/emerge_manager.rb +2 -2
  30. data/lib/autoproj/package_managers/gem_manager.rb +7 -6
  31. data/lib/autoproj/package_managers/homebrew_manager.rb +2 -2
  32. data/lib/autoproj/package_managers/manager.rb +5 -6
  33. data/lib/autoproj/package_managers/pacman_manager.rb +2 -2
  34. data/lib/autoproj/package_managers/pip_manager.rb +8 -8
  35. data/lib/autoproj/package_managers/pkg_manager.rb +2 -2
  36. data/lib/autoproj/package_managers/port_manager.rb +2 -2
  37. data/lib/autoproj/package_managers/shell_script_manager.rb +4 -4
  38. data/lib/autoproj/package_managers/unknown_os_manager.rb +2 -2
  39. data/lib/autoproj/package_managers/yum_manager.rb +2 -2
  40. data/lib/autoproj/package_managers/zypper_manager.rb +2 -2
  41. data/lib/autoproj/package_set.rb +10 -10
  42. data/lib/autoproj/reporter.rb +3 -2
  43. data/lib/autoproj/system.rb +1 -4
  44. data/lib/autoproj/version.rb +1 -1
  45. data/lib/autoproj/workspace.rb +155 -32
  46. metadata +9 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 425aa354d14128a49c6ffce6fe8a74a125149854
4
- data.tar.gz: e1c079a19b39a2ad53fd09763cd956591f711ac4
3
+ metadata.gz: bbe30e7cd487de7bf07f9cf6b19d1766caa87aa4
4
+ data.tar.gz: e88e2b24c5e121542212cb253296b7c73fa01681
5
5
  SHA512:
6
- metadata.gz: b60b6bc45e92d65579f36bac81767cddc15a3c428623614782d619c10cbe2f82121b64c3c0ab48a96c742bd4c25f8c8b17e970e50216a05f5e9b0ebabfaacc5e
7
- data.tar.gz: 896cd4c47e788f1ed0ce17af52d5b26da1ca82077246f84b9600acbf424d19d49683ab991623e17439c31a363b1dcb84fdf2ff9eb95f5551cfae220437d888fa
6
+ metadata.gz: bfdabcb92034ade239d6e7df0438b5e4e77b1cf3ad88a1b05d1cdc3e3b265ec351523511b9ca89e878d898928ae64e287d7f3486394e29fcc1e1da77687f3dd6
7
+ data.tar.gz: 07506f2a88a2f999b91faa521475bfd203ae24a6f5587f00f3bd1fc49c60865807501f4b205f91bf0cc7abd6840ed58813810d23500b5e5f69d65cbded0b76ef
data/Rakefile CHANGED
@@ -12,40 +12,20 @@ end
12
12
  desc "generate the bootstrap script"
13
13
  task 'bootstrap' do
14
14
  require 'yaml'
15
- build_option_code = File.read(File.join(Dir.pwd, 'lib', 'autoproj', 'build_option.rb'))
16
- config_code = File.read(File.join(Dir.pwd, 'lib', 'autoproj', 'configuration.rb'))
17
- osdeps_code = File.read(File.join(Dir.pwd, 'lib', 'autoproj', 'osdeps.rb'))
18
- system_code = File.read(File.join(Dir.pwd, 'lib', 'autoproj', 'system.rb'))
19
- osdeps_defaults = File.read(File.join(Dir.pwd, 'lib', 'autoproj', 'default.osdeps'))
20
- require 'autobuild'
21
- tools_code = File.read(File.join(Autobuild::LIB_DIR, 'autobuild', 'tools.rb'))
22
- # Filter rubygems dependencies from the OSdeps default. They will be
23
- # installed at first build
24
- osdeps = YAML.load(osdeps_defaults)
25
- osdeps.delete_if do |name, content|
26
- if content.respond_to?(:delete)
27
- content.delete('gem')
28
- content.empty?
29
- else
30
- content == 'gem'
31
- end
32
- end
33
- osdeps_defaults = YAML.dump(osdeps)
15
+ autoproj_ops_install = File.read(File.join(Dir.pwd, 'lib', 'autoproj', 'ops', 'install.rb'))
16
+
34
17
  # Since we are using gsub to replace the content in the bootstrap file,
35
18
  # we have to quote all \
36
- [osdeps_code, system_code, osdeps_defaults, tools_code].each do |text|
19
+ [autoproj_ops_install].each do |text|
37
20
  text.gsub! /\\/, '\\\\\\\\'
38
21
  end
39
22
 
40
- bootstrap_code = File.read(File.join(Dir.pwd, 'bin', 'autoproj_bootstrap.in')).
41
- gsub('BUILD_OPTION_CODE', build_option_code).
42
- gsub('CONFIG_CODE', config_code).
43
- gsub('OSDEPS_CODE', osdeps_code).
44
- gsub('SYSTEM_CODE', system_code).
45
- gsub('OSDEPS_DEFAULTS', osdeps_defaults).
46
- gsub('TOOLS_CODE', tools_code)
47
- File.open(File.join(Dir.pwd, 'bin', 'autoproj_bootstrap'), 'w') do |io|
48
- io.write bootstrap_code
23
+ %w{bootstrap install}.each do |install_script|
24
+ bootstrap_code = File.read(File.join(Dir.pwd, 'bin', "autoproj_#{install_script}.in")).
25
+ gsub('AUTOPROJ_OPS_INSTALL', autoproj_ops_install)
26
+ File.open(File.join(Dir.pwd, 'bin', "autoproj_#{install_script}"), 'w') do |io|
27
+ io.write bootstrap_code
28
+ end
49
29
  end
50
30
  end
51
31
  file 'bin/autoproj_bootstrap' => 'bootstrap'
@@ -5,2849 +5,212 @@ if RUBY_VERSION < "1.9.2"
5
5
  exit 1
6
6
  end
7
7
 
8
- require 'rbconfig'
9
- module Autobuild
10
- @windows = RbConfig::CONFIG["host_os"] =~ %r!(msdos|mswin|djgpp|mingw|[Ww]indows)!
11
- def self.windows?
12
- @windows
13
- end
14
-
15
- @macos = RbConfig::CONFIG["host_os"] =~ %r!([Dd]arwin)!
16
- def self.macos?
17
- @macos
18
- end
19
- end
20
-
21
- require 'yaml'
22
- require 'set'
23
-
24
- module Autoproj
25
- class ConfigError < RuntimeError; end
26
- class << self
27
- attr_reader :verbose
28
- end
29
-
30
- def self.color(string, *args)
31
- string
32
- end
33
-
34
- def self.warn(str, *args)
35
- STDERR.puts "WARN #{str}"
36
- end
37
- def self.message(str)
38
- STDERR.puts " #{str}"
39
- end
40
- end
41
-
42
- module Autobuild
43
- class Exception < RuntimeError; end
44
-
45
- def self.do_update
46
- true
47
- end
48
- def self.message(str)
49
- STDERR.puts " #{str}"
50
- end
51
- def self.progress(key, str)
52
- STDERR.puts " #{str}"
53
- end
54
- def self.progress_done(key)
55
- end
56
- def self.message(str)
57
- STDERR.puts " #{str}"
58
- end
59
-
60
- class << self
61
- attr_reader :programs
62
- end
63
- @programs = Hash.new
64
- def self.tool(name)
65
- # Let the ability to set programs[name] to nil to make sure we don't use
66
- # that program. This is used later on in this file to make sure we
67
- # aren't using the wrong rubygems binary
68
- if programs.has_key?(name)
69
- programs[name]
70
- else
71
- name
72
- end
73
- end
74
-
75
- module Subprocess
76
- def self.run(name, phase, *cmd)
77
- if cmd.last.kind_of?(Hash)
78
- options = cmd.pop
79
- (options[:env] || Hash.new).each do |k, v|
80
- ENV[k] = v
81
- end
82
- end
83
-
84
- output = `#{cmd.join(" ")}`
85
- if $?.exitstatus != 0
86
- STDERR.puts "ERROR: failed to run #{cmd.join(" ")}"
87
- STDERR.puts "ERROR: command output is: #{output}"
88
- exit 1
89
- end
90
- end
91
- end
92
- end
93
-
94
- module Autoproj
95
- # Definition of an autoproj option as defined by
96
- # {Configuration#declare}
97
- class BuildOption
98
- attr_reader :name
99
- attr_reader :type
100
- attr_reader :options
101
-
102
- attr_reader :validator
103
-
104
- TRUE_STRINGS = %w{on yes y true}
105
- FALSE_STRINGS = %w{off no n false}
106
- def initialize(name, type, options, validator)
107
- @name, @type, @options = name.to_str, type.to_str, options.to_hash
108
- @validator = validator.to_proc if validator
109
- if !BuildOption.respond_to?("validate_#{type}")
110
- raise ConfigError.new, "invalid option type #{type}"
111
- end
112
- end
113
-
114
- def short_doc
115
- if short_doc = options[:short_doc]
116
- short_doc
117
- elsif doc = options[:doc]
118
- if doc.respond_to?(:to_ary) then doc.first
119
- else doc
120
- end
121
- else "#{name} (no documentation for this option)"
122
- end
123
- end
124
-
125
- def doc
126
- doc = (options[:doc] || "#{name} (no documentation for this option)")
127
- if doc.respond_to?(:to_ary) # multi-line
128
- first_line = doc[0]
129
- remaining = doc[1..-1]
130
- if remaining.empty?
131
- first_line
132
- else
133
- remaining = remaining.join("\n").split("\n").join("\n ")
134
- Autoproj.color(first_line, :bold) + "\n " + remaining
135
- end
136
- else
137
- doc
138
- end
139
- end
140
-
141
- def ask(current_value, doc = nil)
142
- default_value =
143
- if !current_value.nil? then current_value.to_s
144
- elsif options[:default] then options[:default].to_str
145
- else ''
146
- end
147
-
148
- STDOUT.print " #{doc || self.doc} [#{default_value}] "
149
- STDOUT.flush
150
- answer = STDIN.readline.chomp
151
- if answer == ''
152
- answer = default_value
153
- end
154
- validate(answer)
155
-
156
- rescue InputError => e
157
- Autoproj.message("invalid value: #{e.message}", :red)
158
- retry
159
- end
160
-
161
- def validate(value)
162
- value = BuildOption.send("validate_#{type}", value, options)
163
- if validator
164
- value = validator[value]
165
- end
166
- value
167
- end
168
-
169
- def self.validate_boolean(value, options)
170
- if TRUE_STRINGS.include?(value.downcase)
171
- true
172
- elsif FALSE_STRINGS.include?(value.downcase)
173
- false
174
- else
175
- raise InputError, "invalid boolean value '#{value}', accepted values are '#{TRUE_STRINGS.join(", ")}' for true, and '#{FALSE_STRINGS.join(", ")} for false"
176
- end
177
- end
178
-
179
- def self.validate_string(value, options)
180
- if possible_values = options[:possible_values]
181
- if options[:lowercase]
182
- value = value.downcase
183
- elsif options[:uppercase]
184
- value = value.upcase
185
- end
186
-
187
- if !possible_values.include?(value)
188
- raise InputError, "invalid value '#{value}', accepted values are '#{possible_values.join("', '")}' (without the quotes)"
189
- end
190
- end
191
- value
192
- end
193
- end
194
- end
195
-
196
-
197
- module Autoproj
198
- # Class that does the handling of configuration options as well as
199
- # loading/saving on disk
200
- class Configuration
201
- # Set of currently known options
202
- #
203
- # These are the values that are going to be saved on disk. Use
204
- # {override} to change a value without changing the saved configuration
205
- # file.
206
- attr_reader :config
207
- # Set of overriden option values that won't get written to file
208
- attr_reader :overrides
209
- # Set of options that have been declared with {declare}
210
- attr_reader :declared_options
211
- # The options that have already been shown to the user
212
- attr_reader :displayed_options
213
- # The path to the underlying configuration file
214
- attr_reader :path
215
-
216
- def initialize(path = nil)
217
- @config = Hash.new
218
- @overrides = Hash.new
219
- @declared_options = Hash.new
220
- @displayed_options = Hash.new
221
- @path = path
222
- end
223
-
224
- # Deletes the current value for an option
225
- #
226
- # The user will be asked for a new value next time the option is needed
227
- #
228
- # @param [String] the option name
229
- # @return the deleted value
230
- def reset(name)
231
- config.delete(name)
232
- end
233
-
234
- # Sets a configuration option
235
- #
236
- # @param [String] key the option name
237
- # @param [Object] value the option value
238
- # @param [Boolean] user_validated if true, autoproj will not ask the
239
- # user about this value next time it is needed. Otherwise, it will be
240
- # asked about it, the new value being used as default
241
- def set(key, value, user_validated = false)
242
- config[key] = [value, user_validated]
243
- end
244
-
245
- # Override a known option value
246
- #
247
- # The new value will not be saved to disk, unlike with {set}
248
- def override(option_name, value)
249
- overrides[option_name] = value
250
- end
251
-
252
- # Tests whether a value is set for the given option name
253
- #
254
- # @return [Boolean]
255
- def has_value_for?(name)
256
- config.has_key?(name) || overrides.has_key?(name)
257
- end
258
-
259
- # Get the value for a given option
260
- def get(key, *default_value)
261
- if overrides.has_key?(key)
262
- return overrides[key]
263
- end
264
-
265
- value, validated = config[key]
266
- if value.nil? && !declared?(key) && !default_value.empty?
267
- default_value.first
268
- elsif value.nil? || (declared?(key) && !validated)
269
- value = configure(key)
270
- else
271
- if declared?(key) && (displayed_options[key] != value)
272
- doc = declared_options[key].short_doc
273
- if doc[-1, 1] != "?"
274
- doc = "#{doc}:"
275
- end
276
- displayed_options[key] = value
277
- end
278
- value
279
- end
280
- end
281
-
282
- # Returns the option's name-value pairs for the options that do not
283
- # require user input
284
- def validated_values
285
- config.inject(Hash.new) do |h, (k, v)|
286
- h[k] =
287
- if overrides.has_key?(k) then overrides[k]
288
- elsif v.last || !declared?(k) then v.first
289
- end
290
- h
291
- end
292
- end
293
-
294
- # Declare an option
295
- #
296
- # This declares a given option, thus allowing to ask the user about it
297
- #
298
- # @param [String] name the option name
299
- # @param [String] type the option type (can be 'boolean' or 'string')
300
- # @option options [String] :short_doc the one-line documentation string
301
- # that is displayed when the user does not have to be queried. It
302
- # defaults to the first line of :doc if not given
303
- # @option options [String] :doc the full option documentation. It is
304
- # displayed to the user when he is explicitly asked about the option's
305
- # value
306
- # @option options [Object] :default the default value this option should
307
- # take
308
- # @option options [Array] :possible_values list of possible values (only
309
- # if the option type is 'string')
310
- # @option options [Boolean] :lowercase (false) whether the user's input
311
- # should be converted to lowercase before it gets validated / saved.
312
- # @option options [Boolean] :uppercase (false) whether the user's input
313
- # should be converted to uppercase before it gets validated / saved.
314
- def declare(name, type, options, &validator)
315
- declared_options[name] = BuildOption.new(name, type, options, validator)
316
- end
317
-
318
- # Checks if an option exists
319
- # @return [Boolean]
320
- def declared?(name)
321
- declared_options.has_key?(name)
322
- end
323
-
324
- # Configures a given option by asking the user about its desired value
325
- #
326
- # @return [Object] the new option value
327
- # @raise ConfigError if the option is not declared
328
- def configure(option_name)
329
- if opt = declared_options[option_name]
330
- if current_value = config[option_name]
331
- current_value = current_value.first
332
- end
333
- value = opt.ask(current_value)
334
- config[option_name] = [value, true]
335
- displayed_options[option_name] = value
336
- value
337
- else
338
- raise ConfigError.new, "undeclared option '#{option_name}'"
339
- end
340
- end
341
-
342
- def load(options = Hash.new)
343
- options = validate_options options,
344
- path: self.path,
345
- reconfigure: false
346
-
347
- if h = YAML.load(File.read(options[:path]))
348
- h.each do |key, value|
349
- set(key, value, !options[:reconfigure])
350
- end
351
- end
352
- end
353
-
354
- def reconfigure!
355
- new_config = Hash.new
356
- config.each do |key, (value, user_validated)|
357
- new_config[key] = [value, false]
358
- end
359
- @config = new_config
360
- end
361
-
362
- def save(path = self.path)
363
- File.open(path, "w") do |io|
364
- h = Hash.new
365
- config.each do |key, value|
366
- h[key] = value.first
367
- end
368
-
369
- io.write YAML.dump(h)
370
- end
371
- end
372
-
373
- def each_reused_autoproj_installation
374
- if has_value_for?('reused_autoproj_installations')
375
- get('reused_autoproj_installations').each(&proc)
376
- else [].each(&proc)
377
- end
378
- end
379
-
380
- def import_log_enabled?
381
- get('import_log_enabled', true)
382
- end
383
-
384
- def import_log_enabled=(value)
385
- set('import_log_enabled', !!value)
386
- end
387
-
388
- def parallel_build_level
389
- get('parallel_build_level', nil) || Autobuild.parallel_build_level
390
- end
391
-
392
- def parallel_build_level=(level)
393
- set('parallel_build_level', level)
394
- Autobuild.parallel_build_level = level
395
- end
396
-
397
- def parallel_import_level
398
- get('parallel_import_level', 10)
399
- end
400
-
401
- def parallel_import_level=(level)
402
- set('parallel_import_level', level)
403
- end
404
-
405
- def ruby_executable
406
- if path = get('ruby_executable', nil)
407
- path
408
- else
409
- path = OSDependencies.autodetect_ruby_program
410
- set('ruby_executable', path, true)
411
- path
412
- end
413
- end
414
-
415
- def validate_ruby_executable
416
- if has_value_for?('ruby_executable')
417
- expected = get('ruby_executable')
418
- if expected != ruby_executable
419
- raise ConfigError.new, "this autoproj installation was bootstrapped using #{expected}, but you are currently running under #{ruby_executable}. This is usually caused by calling a wrong gem program (for instance, gem1.8 instead of gem1.9.1)"
420
- end
421
- end
422
- set('ruby_executable', ruby_executable, true)
423
- end
424
-
425
- def use_prerelease?
426
- use_prerelease =
427
- if env_flag = ENV['AUTOPROJ_USE_PRERELEASE']
428
- env_flag == '1'
429
- elsif has_value_for?('autoproj_use_prerelease')
430
- get('autoproj_use_prerelease')
431
- end
432
- set "autoproj_use_prerelease", (use_prerelease ? true : false), true
433
- use_prerelease
434
- end
435
-
436
- def shell_helpers?
437
- get 'shell_helpers', true
438
- end
439
-
440
- def shell_helpers=(flag)
441
- set 'shell_helpers', flag, true
442
- end
443
-
444
- def apply_autobuild_configuration
445
- if has_value_for?('autobuild')
446
- params = get('autobuild')
447
- if params.kind_of?(Hash)
448
- params.each do |k, v|
449
- Autobuild.send("#{k}=", v)
450
- end
451
- end
452
- end
453
- end
454
-
455
- # The directory in which packages will be installed.
456
- #
457
- # If it is a relative path, it is relative to the root dir of the
458
- # installation.
459
- #
460
- # The default is "install"
461
- #
462
- # @return [String]
463
- def prefix_dir
464
- get('prefix', 'install')
465
- end
466
-
467
- # Defines the temporary area in which packages should put their build
468
- # files
469
- #
470
- # If absolute, it is handled as {#prefix_dir}: the package name will be
471
- # appended to it. If relative, it is relative to the package's source
472
- # directory
473
- #
474
- # The default is "build"
475
- #
476
- # @return [String]
477
- def build_dir
478
- get('build', 'build')
479
- end
480
-
481
- # Returns true if there should be one prefix per package
482
- #
483
- # The default is false (disabled)
484
- #
485
- # @return [Boolean]
486
- def separate_prefixes?
487
- get('separate_prefixes', false)
488
- end
489
-
490
- # Controls whether there should be one prefix per package
491
- #
492
- # @see separate_prefixes?
493
- def separate_prefixes=(flag)
494
- set('separate_prefixes', flag, true)
495
- end
496
-
497
- # Returns true if packages and prefixes should be auto-generated, based
498
- # on the SHA of the package names. This is meant to be used for build
499
- # services that want to check that dependencies are properly set
500
- #
501
- # The default is false (disabled)
502
- #
503
- # @return [Boolean]
504
- def randomize_layout?
505
- get('randomize_layout', false)
506
- end
507
-
508
- # Sets whether the layout should be randomized
509
- #
510
- # @return [Boolean]
511
- # @see randomize_layout?
512
- def randomize_layout=(value)
513
- set('randomize_layout', value, true)
514
- end
515
-
516
- DEFAULT_UTILITY_SETUP = Hash[
517
- 'doc' => true,
518
- 'test' => false]
519
-
520
- # The configuration key that should be used to store the utility
521
- # enable/disable information
522
- #
523
- # @param [String] the utility name
524
- # @return [String] the config key
525
- def utility_key(utility)
526
- "autoproj_#{utility}_utility"
527
- end
528
-
529
- # Returns whether a given utility is enabled for the package
530
- #
531
- # If there is no specific configuration for the package, uses the global
532
- # default set with utility_enable_all or utility_disable_all. If none of
533
- # these methods has been called, uses the default in
534
- # {DEFAULT_UTILITY_SETUP}
535
- #
536
- # @param [String] utility the utility name (e.g. 'doc' or 'test')
537
- # @param [String] package the package name
538
- # @return [Boolean] true if the utility should be enabled for the
539
- # requested package and false otherwise
540
- def utility_enabled_for?(utility, package)
541
- utility_config = get(utility_key(utility), Hash.new)
542
- if utility_config.has_key?(package)
543
- utility_config[package]
544
- else get("#{utility_key(utility)}_default", DEFAULT_UTILITY_SETUP[utility])
545
- end
546
- end
547
-
548
- # Enables a utility for all packages
549
- #
550
- # This both sets the default value for all packages and resets all
551
- # package-specific values set with {utility_enable_for} and
552
- # {utility_disable_for}
553
- #
554
- # @param [String] utility the utility name (e.g. 'doc' or 'test')
555
- # @return [void]
556
- def utility_enable_all(utility)
557
- reset(utility_key(utility))
558
- set("#{utility_key(utility)}_default", true)
559
- end
560
-
561
- # Enables a utility for a set of packages
562
- #
563
- # @param [String] utility the utility name (e.g. 'doc' or 'test')
564
- # @param [String] packages the package names
565
- # @return [void]
566
- def utility_enable(utility, *packages)
567
- utility_config = get(utility_key(utility), Hash.new)
568
- packages.each do |pkg_name|
569
- utility_config[pkg_name] = true
570
- end
571
- set(utility_key(utility), utility_config)
572
- end
573
-
574
- # Disables a utility for all packages
575
- #
576
- # This both sets the default value for all packages and resets all
577
- # package-specific values set with {utility_enable_for} and
578
- # {utility_disable_for}
579
- #
580
- # @param [String] utility the utility name (e.g. 'doc' or 'test')
581
- # @return [void]
582
- def utility_disable_all(utility)
583
- reset(utility_key(utility))
584
- set("#{utility_key(utility)}_default", false)
585
- end
586
-
587
- # Disables a utility for a specific package
588
- #
589
- # Note that if the default for this utility is to be disabled, this is
590
- # essentially a no-op.
591
- #
592
- # @param [String] utility the utility name (e.g. 'doc' or 'test')
593
- # @param [String] packages the package names
594
- # @return [void]
595
- def utility_disable(utility, *packages)
596
- utility_config = get(utility_key(utility), Hash.new)
597
- packages.each do |pkg_name|
598
- utility_config[pkg_name] = false
599
- end
600
- set(utility_key(utility), utility_config)
601
- end
602
-
603
- def merge(conf)
604
- config.merge!(conf.config)
605
- end
606
- end
607
- end
608
-
609
-
610
- module Autoproj
611
- def self.config
612
- @config ||= Configuration.new
613
- end
614
- end
615
-
616
- require 'tempfile'
617
- require 'json'
618
- module Autoproj
619
- # Module that contains the package manager implementations for the
620
- # OSDependencies class
621
- module PackageManagers
622
- # Base class for all package managers. Subclasses must add the
623
- # #install(packages) method and may add the
624
- # #filter_uptodate_packages(packages) method
625
- #
626
- # Package managers must be registered in PACKAGE_HANDLERS and
627
- # (if applicable) OS_PACKAGE_HANDLERS.
628
- class Manager
629
- # @return [Array<String>] the various names this package manager is
630
- # known about
631
- attr_reader :names
632
-
633
- attr_writer :enabled
634
- def enabled?; !!@enabled end
635
-
636
- attr_writer :silent
637
- def silent?; !!@silent end
638
-
639
- # Create a package manager registered with various names
640
- #
641
- # @param [Array<String>] names the package manager names. It MUST be
642
- # different from the OS names that autoproj uses. See the comment
643
- # for OS_PACKAGE_HANDLERS for an explanation
644
- def initialize(names = [])
645
- @names = names.dup
646
- @enabled = true
647
- @silent = true
648
- end
649
-
650
- # The primary name for this package manager
651
- def name
652
- names.first
653
- end
654
-
655
- # Overload to perform initialization of environment variables in
656
- # order to have a properly functioning package manager
657
- #
658
- # This is e.g. needed for python pip or rubygems
659
- def self.initialize_environment(_env = nil, _manifest = nil, _root_dir = Autoproj.root_dir)
660
- end
661
- end
662
-
663
- # Dummy package manager used for unknown OSes. It simply displays a
664
- # message to the user when packages are needed
665
- class UnknownOSManager < Manager
666
- def initialize
667
- super(['unknown'])
668
- @installed_osdeps = Set.new
669
- end
670
-
671
- def osdeps_interaction_unknown_os(osdeps)
672
- puts <<-EOMSG
673
- #{Autoproj.color("The build process requires some other software packages to be installed on our operating system", :bold)}
674
- #{Autoproj.color("If they are already installed, simply ignore this message", :red)}
675
-
676
- #{osdeps.to_a.sort.join("\n ")}
677
-
678
- EOMSG
679
- print Autoproj.color("Press ENTER to continue", :bold)
680
- STDOUT.flush
681
- STDIN.readline
682
- puts
683
- nil
684
- end
685
-
686
- def install(osdeps)
687
- if silent?
688
- return false
689
- else
690
- osdeps = osdeps.to_set
691
- osdeps -= @installed_osdeps
692
- if !osdeps.empty?
693
- result = osdeps_interaction_unknown_os(osdeps)
694
- end
695
- @installed_osdeps |= osdeps
696
- return result
697
- end
698
- end
699
- end
700
-
701
- # Base class for all package managers that simply require the call of a
702
- # shell script to install packages (e.g. yum, apt, ...)
703
- class ShellScriptManager < Manager
704
- def self.execute(script, with_locking, with_root)
705
- if with_locking
706
- File.open('/tmp/autoproj_osdeps_lock', 'w') do |lock_io|
707
- begin
708
- while !lock_io.flock(File::LOCK_EX | File::LOCK_NB)
709
- Autoproj.message " waiting for other autoproj instances to finish their osdeps installation"
710
- sleep 5
711
- end
712
- return execute(script, false,with_root)
713
- ensure
714
- lock_io.flock(File::LOCK_UN)
715
- end
716
- end
717
- end
718
-
719
- sudo = Autobuild.tool_in_path('sudo')
720
- Tempfile.open('osdeps_sh') do |io|
721
- io.puts "#! /bin/bash"
722
- io.puts GAIN_ROOT_ACCESS % [sudo] if with_root
723
- io.write script
724
- io.flush
725
- Autobuild::Subprocess.run 'autoproj', 'osdeps', '/bin/bash', io.path
726
- end
727
- end
728
-
729
- GAIN_ROOT_ACCESS = <<-EOSCRIPT
730
- # Gain root access using sudo
731
- if test `id -u` != "0"; then
732
- exec %s /bin/bash $0 "$@"
733
-
734
- fi
735
- EOSCRIPT
736
-
737
- # Overrides the {#needs_locking?} flag
738
- attr_writer :needs_locking
739
- # Whether two autoproj instances can run this package manager at the
740
- # same time
741
- #
742
- # This declares if this package manager cannot be used concurrently.
743
- # If it is the case, autoproj will ensure that there is no two
744
- # autoproj instances running this package manager at the same time
745
- #
746
- # @return [Boolean]
747
- # @see needs_locking=
748
- def needs_locking?; !!@needs_locking end
749
-
750
- # Overrides the {#needs_root?} flag
751
- attr_writer :needs_root
752
- # Whether this package manager needs root access.
753
- #
754
- # This declares if the command line(s) for this package manager
755
- # should be started as root. Root access is provided using sudo
756
- #
757
- # @return [Boolean]
758
- # @see needs_root=
759
- def needs_root?; !!@needs_root end
760
-
761
- # Command line used by autoproj to install packages
762
- #
763
- # Since it is to be used for automated install by autoproj, it
764
- # should not require any interaction with the user. When generating
765
- # the command line, the %s slot is replaced by the quoted package
766
- # name(s).
767
- #
768
- # @return [String] a command line pattern that allows to install
769
- # packages without user interaction. It is used when a package
770
- # should be installed by autoproj automatically
771
- attr_reader :auto_install_cmd
772
- # Command line displayed to the user to install packages
773
- #
774
- # When generating the command line, the %s slot is replaced by the
775
- # quoted package name(s).
776
- #
777
- # @return [String] a command line pattern that allows to install
778
- # packages with user interaction. It is displayed to the
779
- # user when it chose to not let autoproj install packages for this
780
- # package manager automatically
781
- attr_reader :user_install_cmd
782
-
783
- # @param [Array<String>] names the package managers names, see
784
- # {#names}
785
- # @param [Boolean] needs_locking whether this package manager can be
786
- # started by two separate autoproj instances at the same time. See
787
- # {#needs_locking?}
788
- # @param [String] user_install_cmd the user-visible command line. See
789
- # {#user_install_cmd}
790
- # @param [String] auto_install_cmd the command line used by autoproj
791
- # itself, see {#auto_install_cmd}.
792
- # @param [Boolean] needs_root if the command lines should be started
793
- # as root or not. See {#needs_root?}
794
- def initialize(names, needs_locking, user_install_cmd, auto_install_cmd,needs_root=true)
795
- super(names)
796
- @needs_locking, @user_install_cmd, @auto_install_cmd,@needs_root =
797
- needs_locking, user_install_cmd, auto_install_cmd, needs_root
798
- end
799
-
800
- # Generate the shell script that would allow the user to install
801
- # the given packages
802
- #
803
- # @param [Array<String>] os_packages the name of the packages to be
804
- # installed
805
- # @option options [String] :user_install_cmd (#user_install_cmd) the
806
- # command-line pattern that should be used to generate the script.
807
- # If given, it overrides the default value stored in
808
- # {#user_install_cmd]
809
- def generate_user_os_script(os_packages, options = Hash.new)
810
- user_install_cmd = options[:user_install_cmd] || self.user_install_cmd
811
- if user_install_cmd
812
- (user_install_cmd % [os_packages.join("' '")])
813
- else generate_auto_os_script(os_packages)
814
- end
815
- end
816
-
817
- # Generate the shell script that should be executed by autoproj to
818
- # install the given packages
819
- #
820
- # @param [Array<String>] os_packages the name of the packages to be
821
- # installed
822
- # @option options [String] :auto_install_cmd (#auto_install_cmd) the
823
- # command-line pattern that should be used to generate the script.
824
- # If given, it overrides the default value stored in
825
- # {#auto_install_cmd]
826
- def generate_auto_os_script(os_packages, options = Hash.new)
827
- auto_install_cmd = options[:auto_install_cmd] || self.auto_install_cmd
828
- (auto_install_cmd % [os_packages.join("' '")])
829
- end
830
-
831
- # Handles interaction with the user
832
- #
833
- # This method will verify whether the user required autoproj to
834
- # install packages from this package manager automatically. It
835
- # displays a relevant message if it is not the case.
836
- #
837
- # @return [Boolean] true if the packages should be installed
838
- # automatically, false otherwise
839
- def osdeps_interaction(os_packages, shell_script)
840
- if OSDependencies.force_osdeps
841
- return true
842
- elsif enabled?
843
- return true
844
- elsif silent?
845
- return false
846
- end
847
-
848
- # We're asked to not install the OS packages but to display them
849
- # anyway, do so now
850
- puts <<-EOMSG
851
-
852
- #{Autoproj.color("The build process and/or the packages require some other software to be installed", :bold)}
853
- #{Autoproj.color("and you required autoproj to not install them itself", :bold)}
854
- #{Autoproj.color("\nIf these packages are already installed, simply ignore this message\n", :red) if !respond_to?(:filter_uptodate_packages)}
855
- The following packages are available as OS dependencies, i.e. as prebuilt
856
- packages provided by your distribution / operating system. You will have to
857
- install them manually if they are not already installed
858
-
859
- #{os_packages.sort.join("\n ")}
860
-
861
- the following command line(s) can be run as root to install them:
862
-
863
- #{shell_script.split("\n").join("\n| ")}
864
-
865
- EOMSG
866
- print " #{Autoproj.color("Press ENTER to continue ", :bold)}"
867
- STDOUT.flush
868
- STDIN.readline
869
- puts
870
- false
871
- end
872
-
873
- # Install packages using this package manager
874
- #
875
- # @param [Array<String>] packages the name of the packages that
876
- # should be installed
877
- # @option options [String] :user_install_cmd (#user_install_cmd) the
878
- # command line that should be displayed to the user to install said
879
- # packages. See the option in {#generate_user_os_script}
880
- # @option options [String] :auto_install_cmd (#auto_install_cmd) the
881
- # command line that should be used by autoproj to install said
882
- # packages. See the option in {#generate_auto_os_script}
883
- # @return [Boolean] true if packages got installed, false otherwise
884
- def install(packages, options = Hash.new)
885
- handled_os = OSDependencies.supported_operating_system?
886
- if handled_os
887
- shell_script = generate_auto_os_script(packages, options)
888
- user_shell_script = generate_user_os_script(packages, options)
889
- end
890
- if osdeps_interaction(packages, user_shell_script)
891
- Autoproj.message " installing OS packages: #{packages.sort.join(", ")}"
892
-
893
- if Autoproj.verbose
894
- Autoproj.message "Generating installation script for non-ruby OS dependencies"
895
- Autoproj.message shell_script
896
- end
897
- ShellScriptManager.execute(shell_script, needs_locking?,needs_root?)
898
- return true
899
- end
900
- false
901
- end
902
- end
903
-
904
- # Package manager interface for systems that use port (i.e. MacPorts/Darwin) as
905
- # their package manager
906
- class PortManager < ShellScriptManager
907
- def initialize
908
- super(['macports'], true,
909
- "port install '%s'",
910
- "port install '%s'")
911
- end
912
- end
913
-
914
- # Package manager interface for Mac OS using homebrew as
915
- # its package manager
916
- class HomebrewManager < ShellScriptManager
917
- def initialize
918
- super(['brew'], true,
919
- "brew install '%s'",
920
- "brew install '%s'",
921
- false)
922
- end
923
-
924
- def filter_uptodate_packages(packages, options = Hash.new)
925
- # TODO there might be duplicates in packages which should be fixed
926
- # somewhere else
927
- packages = packages.uniq
928
- result = `brew info --json=v1 '#{packages.join("' '")}'`
929
- result = begin
930
- JSON.parse(result)
931
- rescue JSON::ParserError
932
- if result && !result.empty?
933
- Autoproj.warn "Error while parsing result of brew info --json=v1"
934
- else
935
- # one of the packages is unknown fallback to install all
936
- # packaes which will complain about it
937
- end
938
- return packages
939
- end
940
- # fall back if something else went wrong
941
- if packages.size != result.size
942
- Autoproj.warn "brew info returns less or more packages when requested. Falling back to install all packages"
943
- return packages
944
- end
945
-
946
- new_packages = []
947
- result.each do |pkg|
948
- new_packages << pkg["name"] if pkg["installed"].empty?
949
- end
950
- new_packages
951
- end
952
- end
953
-
954
- # Package manager interface for systems that use pacman (i.e. arch) as
955
- # their package manager
956
- class PacmanManager < ShellScriptManager
957
- def initialize
958
- super(['pacman'], true,
959
- "pacman -Sy --needed '%s'",
960
- "pacman -Sy --needed --noconfirm '%s'")
961
- end
962
- end
963
-
964
- # Package manager interface for systems that use emerge (i.e. gentoo) as
965
- # their package manager
966
- class EmergeManager < ShellScriptManager
967
- def initialize
968
- super(['emerge'], true,
969
- "emerge '%s'",
970
- "emerge --noreplace '%s'")
971
- end
972
- end
973
- # Package manager interface for systems that use pkg (i.e. FreeBSD) as
974
- # their package manager
975
- class PkgManager < ShellScriptManager
976
- def initialize
977
- super(['pkg'], true,
978
- "pkg install -y '%s'",
979
- "pkg install -y '%s'")
980
- end
981
- end
982
-
983
- #Package manger for OpenSuse and Suse (untested)
984
- class ZypperManager < ShellScriptManager
985
- def initialize
986
- super(['zypper'], true,
987
- "zypper install '%s'",
988
- "zypper -n install '%s'")
989
- end
990
-
991
- def filter_uptodate_packages(packages, options = Hash.new)
992
- result = `LANG=C rpm -q --whatprovides '#{packages.join("' '")}'`
993
- has_all_pkgs = $?.success?
994
-
995
- if !has_all_pkgs
996
- return packages # let zypper filter, we need root now anyways
997
- else
998
- return []
999
- end
1000
- end
1001
-
1002
- def install(packages)
1003
- patterns, packages = packages.partition { |pkg| pkg =~ /^@/ }
1004
- patterns = patterns.map { |str| str[1..-1] }
1005
- result = false
1006
- if !patterns.empty?
1007
- result |= super(patterns,
1008
- :auto_install_cmd => "zypper --non-interactive install --type pattern '%s'",
1009
- :user_install_cmd => "zypper install --type pattern '%s'")
1010
- end
1011
- if !packages.empty?
1012
- result |= super(packages)
1013
- end
1014
- if result
1015
- # Invalidate caching of installed packages, as we just
1016
- # installed new packages !
1017
- @installed_packages = nil
1018
- end
1019
- end
1020
- end
1021
-
1022
- # Package manager interface for systems that use yum
1023
- class YumManager < ShellScriptManager
1024
- def initialize
1025
- super(['yum'], true,
1026
- "yum install '%s'",
1027
- "yum install -y '%s'")
1028
- end
1029
-
1030
- def filter_uptodate_packages(packages, options = Hash.new)
1031
- result = `LANG=C rpm -q --queryformat "%{NAME}\n" '#{packages.join("' '")}'`
1032
-
1033
- installed_packages = []
1034
- new_packages = []
1035
- result.split("\n").each_with_index do |line, index|
1036
- line = line.strip
1037
- if line =~ /package (.*) is not installed/
1038
- package_name = $1
1039
- if !packages.include?(package_name) # something is wrong, fallback to installing everything
1040
- return packages
1041
- end
1042
- new_packages << package_name
1043
- else
1044
- package_name = line.strip
1045
- if !packages.include?(package_name) # something is wrong, fallback to installing everything
1046
- return packages
1047
- end
1048
- installed_packages << package_name
1049
- end
1050
- end
1051
- new_packages
1052
- end
1053
-
1054
- def install(packages)
1055
- patterns, packages = packages.partition { |pkg| pkg =~ /^@/ }
1056
- patterns = patterns.map { |str| str[1..-1] }
1057
- result = false
1058
- if !patterns.empty?
1059
- result |= super(patterns,
1060
- :auto_install_cmd => "yum groupinstall -y '%s'",
1061
- :user_install_cmd => "yum groupinstall '%s'")
1062
- end
1063
- if !packages.empty?
1064
- result |= super(packages)
1065
- end
1066
- if result
1067
- # Invalidate caching of installed packages, as we just
1068
- # installed new packages !
1069
- @installed_packages = nil
1070
- end
1071
- end
1072
- end
1073
-
1074
- # Package manager interface for systems that use APT and dpkg for
1075
- # package management
1076
- class AptDpkgManager < ShellScriptManager
1077
- attr_accessor :status_file
1078
-
1079
- def initialize(status_file = "/var/lib/dpkg/status")
1080
- @status_file = status_file
1081
- super(['apt-dpkg'], true,
1082
- "apt-get install '%s'",
1083
- "export DEBIAN_FRONTEND=noninteractive; apt-get install -y '%s'")
1084
- end
1085
-
1086
- # On a dpkg-enabled system, checks if the provided package is installed
1087
- # and returns true if it is the case
1088
- def installed?(package_name)
1089
- if !@installed_packages
1090
- @installed_packages = Set.new
1091
- dpkg_status = File.readlines(status_file)
1092
- dpkg_status << ""
1093
-
1094
- current_packages = []
1095
- is_installed = false
1096
- dpkg_status.each do |line|
1097
- line = line.chomp
1098
- line = line.encode( "UTF-8", "binary", :invalid => :replace, :undef => :replace)
1099
- if line == ""
1100
- if is_installed
1101
- current_packages.each do |pkg|
1102
- @installed_packages << pkg
1103
- end
1104
- is_installed = false
1105
- end
1106
- current_packages.clear
1107
- elsif line =~ /Package: (.*)$/
1108
- current_packages << $1
1109
- elsif line =~ /Provides: (.*)$/
1110
- current_packages.concat($1.split(',').map(&:strip))
1111
- elsif line == "Status: install ok installed"
1112
- is_installed = true
1113
- end
1114
- end
1115
- end
1116
-
1117
- if package_name =~ /^(\w[a-z0-9+-.]+)/
1118
- @installed_packages.include?($1)
1119
- else
1120
- Autoproj.warn "#{package_name} is not a valid Debian package name"
1121
- false
1122
- end
1123
- end
1124
-
1125
- def install(packages)
1126
- if super
1127
- # Invalidate caching of installed packages, as we just
1128
- # installed new packages !
1129
- @installed_packages = nil
1130
- end
1131
- end
1132
-
1133
- def filter_uptodate_packages(packages, options = Hash.new)
1134
- packages.find_all do |package_name|
1135
- !installed?(package_name)
1136
- end
1137
- end
1138
- end
1139
-
1140
- # Package manager interface for the RubyGems system
1141
- class GemManager < Manager
1142
- class << self
1143
- attr_writer :with_prerelease
1144
- attr_accessor :with_doc
1145
- end
1146
- @with_prerelease = false
1147
- @with_doc = false
1148
-
1149
- def self.with_prerelease(*value)
1150
- if value.empty?
1151
- @with_prerelease
1152
- else
1153
- begin
1154
- saved_flag = @with_prerelease
1155
- @with_prerelease = value.first
1156
- yield
1157
- ensure
1158
- @with_prerelease = saved_flag
1159
- end
1160
- end
1161
- end
1162
-
1163
- # Filters all paths that come from other autoproj installations out
1164
- # of GEM_PATH
1165
- def self.initialize_environment(env = Autobuild.env, manifest = Autoproj.manifest, root_dir = Autoproj.root_dir)
1166
- env.original_env['GEM_PATH'] =
1167
- (env['GEM_PATH'] || "").split(File::PATH_SEPARATOR).find_all do |p|
1168
- !Autoproj.in_autoproj_installation?(p)
1169
- end.join(File::PATH_SEPARATOR)
1170
- env.inherit 'GEM_PATH'
1171
- env.init_from_env 'GEM_PATH'
1172
-
1173
- orig_gem_path = env.original_env['GEM_PATH'].split(File::PATH_SEPARATOR)
1174
- env.system_env['GEM_PATH'] = Gem.default_path
1175
- env.original_env['GEM_PATH'] = orig_gem_path.join(File::PATH_SEPARATOR)
1176
-
1177
- manifest.each_reused_autoproj_installation do |p|
1178
- p_gems = File.join(p, '.gems')
1179
- if File.directory?(p_gems)
1180
- env.push_path 'GEM_PATH', p_gems
1181
- env.push_path 'PATH', File.join(p_gems, 'bin')
1182
- end
1183
- end
1184
-
1185
- @gem_home = (ENV['AUTOPROJ_GEM_HOME'] || File.join(root_dir, ".gems"))
1186
- env.push_path 'GEM_PATH', gem_home
1187
- env.set 'GEM_HOME', gem_home
1188
- env.push_path 'PATH', "#{gem_home}/bin"
1189
-
1190
- # Now, reset the directories in our own RubyGems instance
1191
- Gem.paths = env.resolved_env
1192
-
1193
- use_cache_dir
1194
- end
1195
-
1196
- # Override the gem home detected by {initialize_environment}, or set
1197
- # it in cases where calling {initialize_environment} is not possible
1198
- def self.gem_home=(gem_home)
1199
- @gem_home = gem_home
1200
- end
1201
-
1202
- # A global cache directory that should be used to avoid
1203
- # re-downloading gems
1204
- def self.cache_dir
1205
- if dir = ENV['AUTOBUILD_CACHE_DIR']
1206
- dir = File.join(dir, 'gems')
1207
- FileUtils.mkdir_p dir
1208
- dir
1209
- end
1210
- end
1211
-
1212
- def self.use_cache_dir
1213
- # If there is a cache directory, make sure .gems/cache points to
1214
- # it (there are no programmatic ways to override this)
1215
- if cache = cache_dir
1216
- gem_cache_dir = File.join(gem_home, 'cache')
1217
- if !File.symlink?(gem_cache_dir) || File.readlink(gem_cache_dir) != cache
1218
- FileUtils.mkdir_p gem_home
1219
- FileUtils.rm_rf gem_cache_dir
1220
- Autoproj.create_symlink(cache, gem_cache_dir)
1221
- end
1222
- end
1223
- end
1224
-
1225
- # Return the directory in which RubyGems package should be installed
1226
- def self.gem_home
1227
- @gem_home
1228
- end
1229
-
1230
- # Returns the set of default options that are added to gem
1231
- #
1232
- # By default, we add --no-user-install to un-break distributions
1233
- # like Arch that set --user-install by default (thus disabling the
1234
- # role of GEM_HOME)
1235
- def self.default_install_options
1236
- @default_install_options ||= ['--no-user-install', '--no-format-executable']
1237
- end
1238
-
1239
- def initialize
1240
- super(['gem'])
1241
- @installed_gems = Set.new
1242
- end
1243
-
1244
- # Used to override the Gem::SpecFetcher object used by this gem
1245
- # manager. Useful mainly for testing
1246
- attr_writer :gem_fetcher
1247
-
1248
- # The set of gems installed during this autoproj session
1249
- attr_reader :installed_gems
1250
-
1251
- def gem_fetcher
1252
- if !@gem_fetcher
1253
- Autoproj.message " looking for RubyGems updates"
1254
- @gem_fetcher = Gem::SpecFetcher.fetcher
1255
- end
1256
- @gem_fetcher
1257
- end
1258
-
1259
- def guess_gem_program
1260
- if Autobuild.programs['gem']
1261
- return Autobuild.programs['gem']
1262
- end
1263
-
1264
- ruby_bin = RbConfig::CONFIG['RUBY_INSTALL_NAME']
1265
- ruby_bindir = RbConfig::CONFIG['bindir']
1266
-
1267
- candidates = ['gem']
1268
- if ruby_bin =~ /^ruby(.+)$/
1269
- candidates << "gem#{$1}"
1270
- end
1271
-
1272
- candidates.each do |gem_name|
1273
- if File.file?(gem_full_path = File.join(ruby_bindir, gem_name))
1274
- Autobuild.programs['gem'] = gem_full_path
1275
- return
1276
- end
1277
- end
1278
-
1279
- raise ArgumentError, "cannot find a gem program (tried #{candidates.sort.join(", ")} in #{ruby_bindir})"
1280
- end
1281
-
1282
- def build_gem_cmdlines(gems)
1283
- with_version, without_version = gems.partition { |name, v| v }
1284
-
1285
- cmdlines = []
1286
- if !without_version.empty?
1287
- cmdlines << without_version.flatten
1288
- end
1289
- with_version.each do |name, v|
1290
- cmdlines << [name, "-v", v]
1291
- end
1292
- cmdlines
1293
- end
1294
-
1295
- def pristine(gems)
1296
- guess_gem_program
1297
- base_cmdline = [Autobuild.tool_in_path('ruby'), '-S', Autobuild.tool('gem')]
1298
- cmdlines = [
1299
- [*base_cmdline, 'clean'],
1300
- ]
1301
- cmdlines += build_gem_cmdlines(gems).map do |line|
1302
- base_cmdline + ["pristine", "--extensions"] + line
1303
- end
1304
- if gems_interaction(gems, cmdlines)
1305
- Autoproj.message " restoring RubyGems: #{gems.map { |g| g.join(" ") }.sort.join(", ")}"
1306
- cmdlines.each do |c|
1307
- Autobuild::Subprocess.run 'autoproj', 'osdeps', *c
1308
- end
1309
- end
1310
- end
1311
-
1312
- def install(gems)
1313
- guess_gem_program
1314
-
1315
- base_cmdline = [Autobuild.tool_in_path('ruby'), '-S', Autobuild.tool('gem'), 'install', *GemManager.default_install_options]
1316
- if !GemManager.with_doc
1317
- base_cmdline << '--no-rdoc' << '--no-ri'
1318
- end
1319
-
1320
- if GemManager.with_prerelease
1321
- base_cmdline << "--prerelease"
1322
- end
1323
-
1324
- cmdlines = build_gem_cmdlines(gems).map do |line|
1325
- base_cmdline + line
1326
- end
1327
- if gems_interaction(gems, cmdlines)
1328
- Autoproj.message " installing/updating RubyGems dependencies: #{gems.map { |g| g.join(" ") }.sort.join(", ")}"
1329
-
1330
- cmdlines.each do |c|
1331
- Autobuild::Subprocess.run 'autoproj', 'osdeps', *c,
1332
- env: Hash['GEM_HOME' => Gem.paths.home,
1333
- 'GEM_PATH' => Gem.paths.path.join(":")]
1334
- end
1335
- gems.each do |name, v|
1336
- installed_gems << name
1337
- end
1338
- true
1339
- end
1340
- end
1341
-
1342
- # Returns the set of RubyGem packages in +packages+ that are not already
1343
- # installed, or that can be upgraded
1344
- def filter_uptodate_packages(gems, options = Hash.new)
1345
- options = validate_options options,
1346
- install_only: !Autobuild.do_update
1347
-
1348
- # Don't install gems that are already there ...
1349
- gems = gems.dup
1350
- gems.delete_if do |name, version|
1351
- next(true) if installed_gems.include?(name)
1352
-
1353
- version_requirements = Gem::Requirement.new(version || '>= 0')
1354
- installed =
1355
- if Gem::Specification.respond_to?(:find_by_name)
1356
- begin
1357
- [Gem::Specification.find_by_name(name, version_requirements)]
1358
- rescue Gem::LoadError
1359
- []
1360
- end
1361
- else
1362
- Gem.source_index.find_name(name, version_requirements)
1363
- end
1364
-
1365
- if !installed.empty? && !options[:install_only]
1366
- # Look if we can update the package ...
1367
- dep = Gem::Dependency.new(name, version_requirements)
1368
- available =
1369
- if gem_fetcher.respond_to?(:find_matching)
1370
- non_prerelease = gem_fetcher.find_matching(dep, true, true).map(&:first)
1371
- if GemManager.with_prerelease
1372
- prerelease = gem_fetcher.find_matching(dep, false, true, true).map(&:first)
1373
- else prerelease = Array.new
1374
- end
1375
- (non_prerelease + prerelease).
1376
- map { |n, v, _| [n, v] }
1377
-
1378
- else # Post RubyGems-2.0
1379
- type = if GemManager.with_prerelease then :complete
1380
- else :released
1381
- end
1382
-
1383
- gem_fetcher.detect(type) do |tuple|
1384
- tuple.name == name && dep.match?(tuple)
1385
- end.map { |tuple, _| [tuple.name, tuple.version] }
1386
- end
1387
- installed_version = installed.map(&:version).max
1388
- available_version = available.map { |_, v| v }.max
1389
- if !available_version
1390
- if version
1391
- raise ConfigError.new, "cannot find any gem with the name '#{name}' and version #{version}"
1392
- else
1393
- raise ConfigError.new, "cannot find any gem with the name '#{name}'"
1394
- end
1395
- end
1396
- needs_update = (available_version > installed_version)
1397
- !needs_update
1398
- else
1399
- !installed.empty?
1400
- end
1401
- end
1402
- gems
1403
- end
1404
-
1405
- def parse_package_entry(entry)
1406
- if entry =~ /^([^><=~]*)([><=~]+.*)$/
1407
- [$1.strip, $2.strip]
1408
- else
1409
- [entry]
1410
- end
1411
- end
1412
-
1413
- def gems_interaction(gems, cmdlines)
1414
- if OSDependencies.force_osdeps
1415
- return true
1416
- elsif enabled?
1417
- return true
1418
- elsif silent?
1419
- return false
1420
- end
1421
-
1422
- # We're not supposed to install rubygem packages but silent is not
1423
- # set, so display information about them anyway
1424
- puts <<-EOMSG
1425
- #{Autoproj.color("The build process and/or the packages require some Ruby Gems to be installed", :bold)}
1426
- #{Autoproj.color("and you required autoproj to not do it itself", :bold)}
1427
- You can use the --all or --ruby options to autoproj osdeps to install these
1428
- packages anyway, and/or change to the osdeps handling mode by running an
1429
- autoproj operation with the --reconfigure option as for instance
1430
- autoproj build --reconfigure
1431
-
1432
- The following command line can be used to install them manually
1433
-
1434
- #{cmdlines.map { |c| c.join(" ") }.join("\n ")}
1435
-
1436
- Autoproj expects these Gems to be installed in #{GemManager.gem_home} This can
1437
- be overridden by setting the AUTOPROJ_GEM_HOME environment variable manually
1438
-
1439
- EOMSG
1440
- print " #{Autoproj.color("Press ENTER to continue ", :bold)}"
1441
-
1442
- STDOUT.flush
1443
- STDIN.readline
1444
- puts
1445
- false
1446
- end
1447
- end
1448
-
1449
- # Using pip to install python packages
1450
- class PipManager < Manager
1451
-
1452
- attr_reader :installed_gems
1453
-
1454
- def self.initialize_environment(env = Autobuild.env, _manifest = nil, root_dir = Autoproj.root_dir)
1455
- env.set 'PYTHONUSERBASE', pip_home(env, root_dir)
1456
- end
1457
-
1458
- # Return the directory where python packages are installed to.
1459
- # The actual path is pip_home/lib/pythonx.y/site-packages.
1460
- def self.pip_home(env = Autobuild.env, root_dir = Autobuild.root_dir)
1461
- env['AUTOPROJ_PYTHONUSERBASE'] || File.join(root_dir,".pip")
1462
- end
1463
-
1464
-
1465
- def initialize
1466
- super(['pip'])
1467
- @installed_pips = Set.new
1468
- end
1469
-
1470
- def guess_pip_program
1471
- if Autobuild.programs['pip']
1472
- return Autobuild.programs['pip']
1473
- end
1474
-
1475
- Autobuild.programs['pip'] = "pip"
1476
- end
1477
-
1478
- def install(pips)
1479
- guess_pip_program
1480
- if pips.is_a?(String)
1481
- pips = [pips]
1482
- end
1483
-
1484
- base_cmdline = [Autobuild.tool('pip'), 'install','--user']
1485
-
1486
- cmdlines = [base_cmdline + pips]
1487
-
1488
- if pips_interaction(pips, cmdlines)
1489
- Autoproj.message " installing/updating Python dependencies: "+
1490
- "#{pips.sort.join(", ")}"
1491
-
1492
- cmdlines.each do |c|
1493
- Autobuild::Subprocess.run 'autoproj', 'osdeps', *c
1494
- end
1495
-
1496
- pips.each do |p|
1497
- @installed_pips << p
1498
- end
1499
- end
1500
- end
1501
-
1502
- def pips_interaction(pips, cmdlines)
1503
- if OSDependencies.force_osdeps
1504
- return true
1505
- elsif enabled?
1506
- return true
1507
- elsif silent?
1508
- return false
1509
- end
1510
-
1511
- # We're not supposed to install rubygem packages but silent is not
1512
- # set, so display information about them anyway
1513
- puts <<-EOMSG
1514
- #{Autoproj.color("The build process and/or the packages require some Python packages to be installed", :bold)}
1515
- #{Autoproj.color("and you required autoproj to not do it itself", :bold)}
1516
- The following command line can be used to install them manually
1517
-
1518
- #{cmdlines.map { |c| c.join(" ") }.join("\n ")}
1519
-
1520
- Autoproj expects these Python packages to be installed in #{PipManager.pip_home} This can
1521
- be overridden by setting the AUTOPROJ_PYTHONUSERBASE environment variable manually
1522
-
1523
- EOMSG
1524
- print " #{Autoproj.color("Press ENTER to continue ", :bold)}"
1525
-
1526
- STDOUT.flush
1527
- STDIN.readline
1528
- puts
1529
- false
1530
- end
1531
- end
1532
-
1533
- end
1534
-
1535
-
1536
- # Manager for packages provided by external package managers
1537
- class OSDependencies
1538
- class << self
1539
- # When requested to load a file called '$FILE', the osdeps code will
1540
- # also look for files called '$FILE-suffix', where 'suffix' is an
1541
- # element in +suffixes+
1542
- #
1543
- # A usage of this functionality is to make loading conditional to
1544
- # the available version of certain tools, namely Ruby. Autoproj for
1545
- # instance adds ruby18 when started on Ruby 1.8 and ruby19 when
1546
- # started on Ruby 1.9
1547
- attr_reader :suffixes
1548
- end
1549
- @suffixes = []
1550
-
1551
- def self.load(file)
1552
- if !File.file?(file)
1553
- raise ArgumentError, "no such file or directory #{file}"
1554
- end
1555
-
1556
- candidates = [file]
1557
- candidates.concat(suffixes.map { |s| "#{file}-#{s}" })
1558
-
1559
- error_t = if defined? Psych::SyntaxError then [ArgumentError, Psych::SyntaxError]
1560
- else ArgumentError
1561
- end
1562
-
1563
- result = OSDependencies.new
1564
- candidates.each do |file|
1565
- next if !File.file?(file)
1566
- file = File.expand_path(file)
1567
- begin
1568
- data = YAML.load(File.read(file)) || Hash.new
1569
- verify_definitions(data)
1570
- rescue *error_t => e
1571
- raise ConfigError.new, "error in #{file}: #{e.message}", e.backtrace
1572
- end
1573
-
1574
- result.merge(OSDependencies.new(data, file))
1575
- end
1576
- result
1577
- end
1578
-
1579
- class << self
1580
- attr_reader :aliases
1581
- attr_accessor :force_osdeps
1582
- end
1583
- @aliases = Hash.new
1584
-
1585
- attr_writer :silent
1586
- def silent?; @silent end
1587
-
1588
- def self.alias(old_name, new_name)
1589
- @aliases[new_name] = old_name
1590
- end
1591
-
1592
- def self.ruby_version_keyword
1593
- "ruby#{RUBY_VERSION.split('.')[0, 2].join("")}"
1594
- end
1595
-
1596
- def self.autodetect_ruby_program
1597
- ruby = RbConfig::CONFIG['RUBY_INSTALL_NAME']
1598
- ruby_bindir = RbConfig::CONFIG['bindir']
1599
- ruby_executable = File.join(ruby_bindir, ruby)
1600
- Autobuild.programs['ruby'] = ruby_executable
1601
- ruby_executable
1602
- end
1603
-
1604
- def self.autodetect_ruby
1605
- self.alias(ruby_version_keyword, "ruby")
1606
- end
1607
- self.suffixes << ruby_version_keyword
1608
- autodetect_ruby
1609
-
1610
- AUTOPROJ_OSDEPS = File.join(File.expand_path(File.dirname(__FILE__)), 'default.osdeps')
1611
- def self.load_default
1612
- file = ENV['AUTOPROJ_DEFAULT_OSDEPS'] || AUTOPROJ_OSDEPS
1613
- if !File.file?(file)
1614
- Autoproj.warn "#{file} (from AUTOPROJ_DEFAULT_OSDEPS) is not a file, falling back to #{AUTOPROJ_OSDEPS}"
1615
- file = AUTOPROJ_OSDEPS
1616
- end
1617
- OSDependencies.load(file)
1618
- end
1619
-
1620
- def load_default
1621
- merge(self.class.load_default)
1622
- end
1623
-
1624
- PACKAGE_HANDLERS = [PackageManagers::AptDpkgManager,
1625
- PackageManagers::GemManager,
1626
- PackageManagers::EmergeManager,
1627
- PackageManagers::PacmanManager,
1628
- PackageManagers::HomebrewManager,
1629
- PackageManagers::YumManager,
1630
- PackageManagers::PortManager,
1631
- PackageManagers::ZypperManager,
1632
- PackageManagers::PipManager ,
1633
- PackageManagers::PkgManager]
1634
-
1635
- # Mapping from OS name to package manager name
1636
- #
1637
- # Package handlers and OSes MUST have different names. The former are
1638
- # used to resolve packages and the latter to resolve OSes in the osdeps.
1639
- # Since one can force the use of a package manager in any OS by adding a
1640
- # package manager entry, as e.g.
1641
- #
1642
- # ubuntu:
1643
- # homebrew: package
1644
- #
1645
- # we need to be able to separate between OS and package manager names.
1646
- OS_PACKAGE_HANDLERS = {
1647
- 'debian' => 'apt-dpkg',
1648
- 'gentoo' => 'emerge',
1649
- 'arch' => 'pacman',
1650
- 'fedora' => 'yum',
1651
- 'macos-port' => 'macports',
1652
- 'macos-brew' => 'brew',
1653
- 'opensuse' => 'zypper',
1654
- 'freebsd' => 'pkg'
1655
- }
1656
-
1657
- # The information contained in the OSdeps files, as a hash
1658
- attr_reader :definitions
1659
- # All the information contained in all the OSdeps files, as a mapping
1660
- # from the OSdeps package name to [osdeps_file, definition] pairs
1661
- attr_reader :all_definitions
1662
- # The information as to from which osdeps file the current package
1663
- # information in +definitions+ originates. It is a mapping from the
1664
- # package name to the osdeps file' full path
1665
- attr_reader :sources
1666
-
1667
- # Use to override the autodetected OS-specific package handler
1668
- attr_writer :os_package_handler
1669
-
1670
- # Returns the package manager object for the current OS
1671
- def os_package_handler
1672
- if @os_package_handler.nil?
1673
- os_names, _ = OSDependencies.operating_system
1674
- if os_names && (key = os_names.find { |name| OS_PACKAGE_HANDLERS[name] })
1675
- @os_package_handler = package_handlers[OS_PACKAGE_HANDLERS[key]]
1676
- if !@os_package_handler
1677
- raise ArgumentError, "found #{OS_PACKAGE_HANDLERS[name]} as the required package handler for #{os_names.join(", ")}, but it is not registered"
1678
- end
1679
- else
1680
- @os_package_handler = PackageManagers::UnknownOSManager.new
1681
- end
1682
- end
1683
- return @os_package_handler
1684
- end
1685
-
1686
- # Returns the set of package managers
1687
- def package_handlers
1688
- if !@package_handlers
1689
- @package_handlers = Hash.new
1690
- PACKAGE_HANDLERS.each do |klass|
1691
- obj = klass.new
1692
- obj.names.each do |n|
1693
- @package_handlers[n] = obj
1694
- end
1695
- end
1696
- end
1697
- @package_handlers
1698
- end
1699
-
1700
- # The Gem::SpecFetcher object that should be used to query RubyGems, and
1701
- # install RubyGems packages
1702
- def initialize(defs = Hash.new, file = nil)
1703
- @definitions = defs.to_hash
1704
- @all_definitions = Hash.new { |h, k| h[k] = Array.new }
1705
-
1706
- @sources = Hash.new
1707
- @installed_packages = Set.new
1708
- if file
1709
- defs.each_key do |package_name|
1710
- sources[package_name] = file
1711
- all_definitions[package_name] << [[file], defs[package_name]]
1712
- end
1713
- end
1714
- @silent = true
1715
- @filter_uptodate_packages = true
1716
- end
1717
-
1718
- # Returns the name of all known OS packages
1719
- #
1720
- # It includes even the packages for which there are no definitions on
1721
- # this OS
1722
- def all_package_names
1723
- all_definitions.keys
1724
- end
1725
-
1726
- # Returns the full path to the osdeps file from which the package
1727
- # definition for +package_name+ has been taken
1728
- def source_of(package_name)
1729
- sources[package_name]
1730
- end
1731
-
1732
- # Merges the osdeps information of +info+ into +self+. If packages are
1733
- # defined in both OSDependencies objects, the information in +info+
1734
- # takes precedence
1735
- def merge(info)
1736
- root_dir = nil
1737
- @definitions = definitions.merge(info.definitions) do |h, v1, v2|
1738
- if v1 != v2
1739
- root_dir ||= "#{Autoproj.root_dir}/"
1740
- old = source_of(h).gsub(root_dir, '')
1741
- new = info.source_of(h).gsub(root_dir, '')
1742
-
1743
- # Warn if the new osdep definition resolves to a different
1744
- # set of packages than the old one
1745
- old_resolved = resolve_package(h).inject(Hash.new) do |osdep_h, (handler, status, list)|
1746
- osdep_h[handler.name] = [status, list]
1747
- osdep_h
1748
- end
1749
- new_resolved = info.resolve_package(h).inject(Hash.new) do |osdep_h, (handler, status, list)|
1750
- osdep_h[handler.name] = [status, list]
1751
- osdep_h
1752
- end
1753
- if old_resolved != new_resolved
1754
- Autoproj.warn("osdeps definition for #{h}, previously defined in #{old} overridden by #{new}")
1755
- end
1756
- end
1757
- v2
1758
- end
1759
- @sources = sources.merge(info.sources)
1760
- @all_definitions = all_definitions.merge(info.all_definitions) do |package_name, all_defs, new_all_defs|
1761
- all_defs = all_defs.dup
1762
- new_all_defs = new_all_defs.dup
1763
- new_all_defs.delete_if do |files, data|
1764
- if entry = all_defs.find { |_, d| d == data }
1765
- entry[0] |= files
1766
- end
1767
- end
1768
- all_defs.concat(new_all_defs)
1769
- end
1770
- end
1771
-
1772
- # Perform some sanity checks on the given osdeps definitions
1773
- def self.verify_definitions(hash, path = [])
1774
- hash.each do |key, value|
1775
- if value && !key.kind_of?(String)
1776
- raise ArgumentError, "invalid osdeps definition: found an #{key.class} as a key in #{path.join("/")}. Don't forget to put quotes around numbers"
1777
- elsif !value && (key.kind_of?(Hash) || key.kind_of?(Array))
1778
- verify_definitions(key)
1779
- end
1780
- next if !value
1781
-
1782
- if value.kind_of?(Array) || value.kind_of?(Hash)
1783
- verify_definitions(value, (path + [key]))
1784
- else
1785
- if !value.kind_of?(String)
1786
- raise ArgumentError, "invalid osdeps definition: found an #{value.class} as a value in #{path.join("/")}. Don't forget to put quotes around numbers"
1787
- end
1788
- end
1789
- end
1790
- end
1791
-
1792
- # Returns true if it is possible to install packages for the operating
1793
- # system on which we are installed
1794
- def self.supported_operating_system?
1795
- if @supported_operating_system.nil?
1796
- os_names, _ = operating_system
1797
- @supported_operating_system =
1798
- if !os_names then false
1799
- else
1800
- os_names.any? { |os_name| OS_PACKAGE_HANDLERS.has_key?(os_name) }
1801
- end
1802
- end
1803
- return @supported_operating_system
1804
- end
1805
-
1806
- # Used mainly during testing to bypass the operating system
1807
- # autodetection
1808
- def self.operating_system=(values)
1809
- @supported_operating_system = nil
1810
- @operating_system = values
1811
- end
1812
-
1813
- def self.guess_operating_system
1814
- if File.exists?('/etc/debian_version')
1815
- versions = [File.read('/etc/debian_version').strip]
1816
- if versions.first =~ /sid/
1817
- versions = ["unstable", "sid"]
1818
- end
1819
- [['debian'], versions]
1820
- elsif File.exists?('/etc/redhat-release')
1821
- release_string = File.read('/etc/redhat-release').strip
1822
- release_string =~ /(.*) release ([\d.]+)/
1823
- name = $1.downcase
1824
- version = $2
1825
- if name =~ /Red Hat Entreprise/
1826
- name = 'rhel'
1827
- end
1828
- [[name], [version]]
1829
- elsif File.exists?('/etc/gentoo-release')
1830
- release_string = File.read('/etc/gentoo-release').strip
1831
- release_string =~ /^.*([^\s]+)$/
1832
- version = $1
1833
- [['gentoo'], [version]]
1834
- elsif File.exists?('/etc/arch-release')
1835
- [['arch'], []]
1836
- elsif Autobuild.macos?
1837
- version=`sw_vers | head -2 | tail -1`.split(":")[1]
1838
- manager =
1839
- if ENV['AUTOPROJ_MACOSX_PACKAGE_MANAGER']
1840
- ENV['AUTOPROJ_MACOSX_PACKAGE_MANAGER']
1841
- else 'macos-brew'
1842
- end
1843
- if !OS_PACKAGE_HANDLERS.include?(manager)
1844
- known_managers = OS_PACKAGE_HANDLERS.keys.grep(/^macos/)
1845
- raise ArgumentError, "#{manager} is not a known MacOSX package manager. Known package managers are #{known_managers.join(", ")}"
1846
- end
1847
-
1848
- managers =
1849
- if manager == 'macos-port'
1850
- [manager, 'port']
1851
- else [manager]
1852
- end
1853
- [[*managers, 'darwin'], [version.strip]]
1854
- elsif Autobuild.windows?
1855
- [['windows'], []]
1856
- elsif File.exists?('/etc/SuSE-release')
1857
- version = File.read('/etc/SuSE-release').strip
1858
- version =~/.*VERSION\s+=\s+([^\s]+)/
1859
- version = $1
1860
- [['opensuse'], [version.strip]]
1861
- elsif Autobuild.freebsd?
1862
- version = `uname -r`.strip.split("-")[0]
1863
- [['freebsd'],[version]]
1864
- end
1865
- end
1866
-
1867
- def self.ensure_derivatives_refer_to_their_parents(names)
1868
- names = names.dup
1869
- version_files = Hash[
1870
- '/etc/debian_version' => 'debian',
1871
- '/etc/redhat-release' => 'fedora',
1872
- '/etc/gentoo-release' => 'gentoo',
1873
- '/etc/arch-release' => 'arch',
1874
- '/etc/SuSE-release' => 'opensuse']
1875
- version_files.each do |file, name|
1876
- if File.exists?(file) && !names.include?(name)
1877
- names << name
1878
- end
1879
- end
1880
- names
1881
- end
1882
-
1883
- def self.normalize_os_representation(names, versions)
1884
- # Normalize the names to lowercase
1885
- names = names.map(&:downcase)
1886
- versions = versions.map(&:downcase)
1887
- if !versions.include?('default')
1888
- versions += ['default']
1889
- end
1890
- return names, versions
1891
- end
1892
-
1893
- # Autodetects the operating system name and version
1894
- #
1895
- # +osname+ is the operating system name, all in lowercase (e.g. ubuntu,
1896
- # arch, gentoo, debian)
1897
- #
1898
- # +versions+ is a set of names that describe the OS version. It includes
1899
- # both the version number (as a string) and/or the codename if there is
1900
- # one.
1901
- #
1902
- # Examples: ['debian', ['sid', 'unstable']] or ['ubuntu', ['lucid lynx', '10.04']]
1903
- def self.operating_system(options = Hash.new)
1904
- # Validate the options. We check on the availability of
1905
- # validate_options as to not break autoproj_bootstrap (in which
1906
- # validate_options is not available)
1907
- options = validate_options options, force: false, config: Autoproj.config
1908
- config = options.fetch(:config)
1909
-
1910
- if user_os = ENV['AUTOPROJ_OS']
1911
- @operating_system =
1912
- if user_os.empty? then false
1913
- else
1914
- names, versions = user_os.split(':')
1915
- normalize_os_representation(names.split(','), versions.split(','))
1916
- end
1917
- return @operating_system
1918
- end
1919
-
1920
-
1921
- if options[:force]
1922
- @operating_system = nil
1923
- elsif !@operating_system.nil? # @operating_system can be set to false to simulate an unknown OS
1924
- return @operating_system
1925
- elsif config.has_value_for?('operating_system')
1926
- os = config.get('operating_system')
1927
- if os.respond_to?(:to_ary)
1928
- if os[0].respond_to?(:to_ary) && os[0].all? { |s| s.respond_to?(:to_str) } &&
1929
- os[1].respond_to?(:to_ary) && os[1].all? { |s| s.respond_to?(:to_str) }
1930
- @operating_system = os
1931
- return os
1932
- end
1933
- end
1934
- @operating_system = nil # Invalid OS format in the configuration file
1935
- end
1936
-
1937
- Autobuild.progress :operating_system_autodetection, "autodetecting the operating system"
1938
- names, versions = os_from_os_release
1939
-
1940
- if !names
1941
- names, versions = guess_operating_system
1942
- end
1943
-
1944
- # on Debian, they refuse to put enough information to detect
1945
- # 'unstable' reliably. So, we use the heuristic method for it
1946
- if names[0] == "debian"
1947
- # check if we actually got a debian with the "unstable" (sid)
1948
- # flavour. it seems that "/etc/debian_version" does not contain
1949
- # "sid" (but "8.0" for example) during the feature freeze
1950
- # phase...
1951
- if File.exists?('/etc/debian_version')
1952
- debian_versions = [File.read('/etc/debian_version').strip]
1953
- if debian_versions.first =~ /sid/
1954
- versions = ["unstable", "sid"]
1955
- end
1956
- end
1957
- # otherwise "versions" contains the result it previously had
1958
- end
1959
- return if !names
1960
-
1961
- names = ensure_derivatives_refer_to_their_parents(names)
1962
- names, versions = normalize_os_representation(names, versions)
1963
-
1964
- @operating_system = [names, versions]
1965
- config.set('operating_system', @operating_system, true)
1966
- Autobuild.progress :operating_system_autodetection, "operating system: #{(names - ['default']).join(",")} - #{(versions - ['default']).join(",")}"
1967
- @operating_system
1968
- ensure
1969
- Autobuild.progress_done :operating_system_autodetection
1970
- end
1971
-
1972
- def self.os_from_os_release(filename = '/etc/os-release')
1973
- return if !File.exists?(filename)
1974
-
1975
- fields = Hash.new
1976
- File.readlines(filename).each do |line|
1977
- line = line.strip
1978
- if line.strip =~ /^(\w+)=(?:["'])?([^"']+)(?:["'])?$/
1979
- fields[$1] = $2
1980
- elsif !line.empty?
1981
- Autoproj.warn "could not parse line '#{line.inspect}' in /etc/os-release"
1982
- end
1983
- end
1984
-
1985
- names = []
1986
- versions = []
1987
- names << fields['ID'] << fields['ID_LIKE']
1988
- versions << fields['VERSION_ID']
1989
- version = fields['VERSION'] || ''
1990
- versions.concat(version.gsub(/[^\w.]/, ' ').split(' '))
1991
- return names.compact.uniq, versions.compact.uniq
1992
- end
1993
-
1994
- def self.os_from_lsb
1995
- if !Autobuild.find_in_path('lsb_release')
1996
- return
1997
- end
1998
-
1999
- distributor = [`lsb_release -i -s`.strip.downcase]
2000
- codename = `lsb_release -c -s`.strip.downcase
2001
- version = `lsb_release -r -s`.strip.downcase
2002
-
2003
- return [distributor, [codename, version]]
2004
- end
2005
-
2006
- class InvalidRecursiveStatement < Autobuild::Exception; end
2007
-
2008
- # Return the path to the osdeps name for a given package name while
2009
- # accounting for package aliases
2010
- #
2011
- # returns an array contain the path starting with name and
2012
- # ending at the resolved name
2013
- def self.resolve_name(name)
2014
- path = [ name ]
2015
- while OSDependencies.aliases.has_key?(name)
2016
- name = OSDependencies.aliases[name]
2017
- path << name
2018
- end
2019
- path
2020
- end
2021
-
2022
- # Return the list of packages that should be installed for +name+
2023
- #
2024
- # The following two simple return values are possible:
2025
- #
2026
- # nil:: +name+ has no definition
2027
- # []:: +name+ has no definition on this OS and/or for this specific OS
2028
- # version
2029
- #
2030
- # In all other cases, the method returns an array of triples:
2031
- #
2032
- # [package_handler, status, package_list]
2033
- #
2034
- # where status is FOUND_PACKAGES if +package_list+ is the list of
2035
- # packages that should be installed with +package_handler+ for +name+,
2036
- # and FOUND_NONEXISTENT if the nonexistent keyword is used for this OS
2037
- # name and version. The package list might be empty even if status ==
2038
- # FOUND_PACKAGES, for instance if the ignore keyword is used.
2039
- def resolve_package(name)
2040
- path = OSDependencies.resolve_name(name)
2041
- name = path.last
2042
-
2043
- os_names, os_versions = OSDependencies.operating_system
2044
- os_names = os_names.dup
2045
- os_names << 'default'
2046
-
2047
- dep_def = definitions[name]
2048
- if !dep_def
2049
- return nil
2050
- end
2051
-
2052
- # Partition the found definition in all entries that are interesting
2053
- # for us: toplevel os-independent package managers, os-dependent
2054
- # package managers and os-independent package managers selected by
2055
- # OS or version
2056
- if !os_names
2057
- os_names = ['default']
2058
- os_versions = ['default']
2059
- end
8
+ require 'optparse'
9
+ require 'fileutils'
10
+ require 'yaml'
2060
11
 
2061
- package_handler_names = package_handlers.keys
12
+ module Autoproj
13
+ module Ops
14
+ # This class contains the functionality necessary to install autoproj in a
15
+ # clean root
16
+ #
17
+ # It can be required standalone (i.e. does not depend on anything else than
18
+ # ruby and the ruby standard library)
19
+ class Install
20
+ # The directory in which to install autoproj
21
+ attr_reader :root_dir
22
+ # Content of the Gemfile generated to install autoproj itself
23
+ attr_accessor :gemfile
24
+
25
+ def initialize(root_dir)
26
+ @root_dir = root_dir
27
+ @gemfile = default_gemfile_contents
28
+ @private_bundler = false
29
+ @private_autoproj = false
30
+ @private_gems = false
31
+ end
32
+
33
+ def dot_autoproj; File.join(root_dir, '.autoproj') end
34
+ def bin_dir; File.join(dot_autoproj, 'bin') end
35
+ def bundler_install_dir; File.join(dot_autoproj, 'bundler') end
36
+ def autoproj_install_dir; File.join(dot_autoproj, 'autoproj') end
37
+ # The path to the gemfile used to install autoproj
38
+ def autoproj_gemfile_path; File.join(autoproj_install_dir, 'Gemfile') end
39
+ def autoproj_config_path; File.join(dot_autoproj, 'config.yml') end
40
+
41
+ # Whether bundler should be installed locally in {#dot_autoproj}
42
+ def private_bundler?; @private_bundler end
43
+ # Whether autoproj should be installed locally in {#dot_autoproj}
44
+ def private_autoproj?; @private_autoproj end
45
+ # Whether bundler should be installed locally in the workspace
46
+ # prefix directory
47
+ def private_gems?; @private_gems end
2062
48
 
2063
- result = []
2064
- found, pkg = partition_osdep_entry(name, dep_def, nil, (package_handler_names - os_package_handler.names), os_names, os_versions)
2065
- if found
2066
- result << [os_package_handler, found, pkg]
2067
- end
49
+ def guess_gem_program
50
+ ruby_bin = RbConfig::CONFIG['RUBY_INSTALL_NAME']
51
+ ruby_bindir = RbConfig::CONFIG['bindir']
2068
52
 
2069
- # NOTE: package_handlers might contain the same handler multiple
2070
- # times (when a package manager has multiple names). That's why we
2071
- # do a to_set.each
2072
- package_handlers.each_value.to_set.each do |handler|
2073
- found, pkg = partition_osdep_entry(name, dep_def, handler.names, [], os_names, os_versions)
2074
- if found
2075
- result << [handler, found, pkg]
53
+ candidates = ['gem']
54
+ if ruby_bin =~ /^ruby(.+)$/
55
+ candidates << "gem#{$1}"
2076
56
  end
2077
- end
2078
57
 
2079
- # Recursive resolutions
2080
- found, pkg = partition_osdep_entry(name, dep_def, ['osdep'], [], os_names, os_versions)
2081
- if found
2082
- pkg.each do |pkg_name|
2083
- resolved = resolve_package(pkg_name)
2084
- if !resolved
2085
- raise InvalidRecursiveStatement, "osdep #{pkg_name} does not exist. It is referred to by #{name}."
58
+ candidates.each do |gem_name|
59
+ if File.file?(gem_full_path = File.join(ruby_bindir, gem_name))
60
+ return gem_full_path
2086
61
  end
2087
- result.concat(resolved)
2088
62
  end
63
+ raise ArgumentError, "cannot find a gem program (tried #{candidates.sort.join(", ")} in #{ruby_bindir})"
2089
64
  end
2090
65
 
2091
- result.map do |handler, status, entries|
2092
- if handler.respond_to?(:parse_package_entry)
2093
- [handler, status, entries.map { |s| handler.parse_package_entry(s) }]
2094
- else
2095
- [handler, status, entries]
2096
- end
66
+ # The content of the default {#gemfile}
67
+ #
68
+ # @param [String] autoproj_version a constraint on the autoproj version
69
+ # that should be used
70
+ # @return [String]
71
+ def default_gemfile_contents(autoproj_version = ">= 0")
72
+ ["source \"https://rubygems.org\"",
73
+ "gem \"autoproj\", \"#{autoproj_version}\""].join("\n")
2097
74
  end
2098
- end
2099
75
 
2100
- # Value returned by #resolve_package and #partition_osdep_entry in
2101
- # the status field. See the documentation of these methods for more
2102
- # information
2103
- FOUND_PACKAGES = 0
2104
- # Value returned by #resolve_package and #partition_osdep_entry in
2105
- # the status field. See the documentation of these methods for more
2106
- # information
2107
- FOUND_NONEXISTENT = 1
2108
-
2109
- # Helper method that parses the osdep definition to split between the
2110
- # parts needed for this OS and specific package handlers.
2111
- #
2112
- # +osdep_name+ is the name of the osdep. It is used to resolve explicit
2113
- # mentions of a package handler, i.e. so that:
2114
- #
2115
- # pkg: gem
2116
- #
2117
- # is resolved as the 'pkg' package to be installed by the 'gem' handler
2118
- #
2119
- # +dep_def+ is the content to parse. It can be a string, array or hash
2120
- #
2121
- # +handler_names+ is a list of entries that we are looking for. If it is
2122
- # not nil, only entries that explicitely refer to +handler_names+ will
2123
- # be browsed, i.e. in:
2124
- #
2125
- # pkg:
2126
- # - test: 1
2127
- # - [a, list, of, packages]
2128
- #
2129
- # partition_osdep_entry('osdep_name', data, ['test'], [])
2130
- #
2131
- # will ignore the toplevel list of packages, while
2132
- #
2133
- # partition_osdep_entry('osdep_name', data, nil, [])
2134
- #
2135
- # will return it.
2136
- #
2137
- # +excluded+ is a list of branches that should be ignored during
2138
- # parsing. It is used to e.g. ignore 'gem' when browsing for the main OS
2139
- # package list. For instance, in
2140
- #
2141
- # pkg:
2142
- # - test
2143
- # - [a, list, of, packages]
2144
- #
2145
- # partition_osdep_entry('osdep_name', data, nil, ['test'])
2146
- #
2147
- # the returned value will only include the list of packages (and not
2148
- # 'test')
2149
- #
2150
- # The rest of the arguments are array of strings that contain list of
2151
- # keys to browse for (usually, the OS names and version)
2152
- #
2153
- # The return value is either nil if no packages were found, or a pair
2154
- # [status, package_list] where status is FOUND_NONEXISTENT if the
2155
- # nonexistent keyword was found, and FOUND_PACKAGES if either packages
2156
- # or the ignore keyword were found.
2157
- #
2158
- def partition_osdep_entry(osdep_name, dep_def, handler_names, excluded, *keys)
2159
- keys, *additional_keys = *keys
2160
- keys ||= []
2161
- found = false
2162
- nonexistent = false
2163
- result = []
2164
- found_keys = Hash.new
2165
- Array(dep_def).each do |names, values|
2166
- if !values
2167
- # Raw array of packages. Possible only if we are not at toplevel
2168
- # (i.e. if we already have a handler)
2169
- if names == 'ignore'
2170
- found = true if !handler_names
2171
- elsif names == 'nonexistent'
2172
- nonexistent = true if !handler_names
2173
- elsif !handler_names && names.kind_of?(Array)
2174
- result.concat(result)
2175
- found = true
2176
- elsif names.respond_to?(:to_str)
2177
- if excluded.include?(names)
2178
- elsif handler_names && handler_names.include?(names)
2179
- result << osdep_name
2180
- found = true
2181
- elsif !handler_names
2182
- result << names
2183
- found = true
2184
- end
2185
- elsif names.respond_to?(:to_hash)
2186
- rec_found, rec_result = partition_osdep_entry(osdep_name, names, handler_names, excluded, keys, *additional_keys)
2187
- if rec_found == FOUND_NONEXISTENT then nonexistent = true
2188
- elsif rec_found == FOUND_PACKAGES then found = true
2189
- end
2190
- result.concat(rec_result)
76
+ # Parse the provided command line options and returns the non-options
77
+ def parse_options(args = ARGV)
78
+ options = OptionParser.new do |opt|
79
+ opt.on '--private-bundler', 'install bundler locally in the workspace' do
80
+ @private_bundler = true
2191
81
  end
2192
- else
2193
- if names.respond_to?(:to_str) # names could be an array already
2194
- names = names.split(',')
2195
- end
2196
-
2197
- if handler_names
2198
- if matching_name = handler_names.find { |k| names.any? { |name_tag| k == name_tag.downcase } }
2199
- rec_found, rec_result = partition_osdep_entry(osdep_name, values, nil, excluded, keys, *additional_keys)
2200
- if rec_found == FOUND_NONEXISTENT then nonexistent = true
2201
- elsif rec_found == FOUND_PACKAGES then found = true
2202
- end
2203
- result.concat(rec_result)
2204
- end
82
+ opt.on '--private-autoproj', 'install autoproj locally in the workspace' do
83
+ @private_autoproj = true
2205
84
  end
2206
-
2207
- matching_name = keys.find { |k| names.any? { |name_tag| k == name_tag.downcase } }
2208
- if matching_name
2209
- rec_found, rec_result = partition_osdep_entry(osdep_name, values, handler_names, excluded, *additional_keys)
2210
- # We only consider the first highest-priority entry,
2211
- # regardless of whether it has some packages for us or
2212
- # not
2213
- idx = keys.index(matching_name)
2214
- if !rec_found
2215
- if !found_keys.has_key?(idx)
2216
- found_keys[idx] = nil
2217
- end
2218
- else
2219
- found_keys[idx] ||= [0, []]
2220
- found_keys[idx][0] += rec_found
2221
- found_keys[idx][1].concat(rec_result)
2222
- end
85
+ opt.on '--private-gems', 'install gems locally in the prefix directory' do
86
+ @private_gems = true
2223
87
  end
2224
- end
2225
- end
2226
- first_entry = found_keys.keys.sort.first
2227
- found_keys = found_keys[first_entry]
2228
- if found_keys
2229
- if found_keys[0] > 0
2230
- nonexistent = true
2231
- else
2232
- found = true
2233
- end
2234
- result.concat(found_keys[1])
2235
- end
2236
-
2237
- found =
2238
- if nonexistent then FOUND_NONEXISTENT
2239
- elsif found then FOUND_PACKAGES
2240
- else false
2241
- end
2242
-
2243
- return found, result
2244
- end
2245
-
2246
- # Resolves the given OS dependencies into the actual packages that need
2247
- # to be installed on this particular OS.
2248
- #
2249
- # @param [Array<String>] dependencies the list of osdep names that should be resolved
2250
- # @return [Array<#install,Array<String>>] the set of packages, grouped
2251
- # by the package handlers that should be used to install them
2252
- #
2253
- # @raise MissingOSDep if some packages can't be found or if the
2254
- # nonexistent keyword was found for some of them
2255
- def resolve_os_dependencies(dependencies)
2256
- all_packages = []
2257
- dependencies.each do |name|
2258
- result = resolve_package(name)
2259
- if !result
2260
- path = OSDependencies.resolve_name(name)
2261
- raise MissingOSDep.new, "there is no osdeps definition for #{path.last} (search tree: #{path.join("->")})"
2262
- end
2263
-
2264
- if result.empty?
2265
- if OSDependencies.supported_operating_system?
2266
- os_names, os_versions = OSDependencies.operating_system
2267
- raise MissingOSDep.new, "there is an osdeps definition for #{name}, but not for this operating system and version (resp. #{os_names.join(", ")} and #{os_versions.join(", ")})"
88
+ opt.on '--private', 'whether bundler, autoproj and the workspace gems should be installed locally in the workspace' do
89
+ @private_bundler = true
90
+ @private_autoproj = true
91
+ @private_gems = true
2268
92
  end
2269
- result = [[os_package_handler, FOUND_PACKAGES, [name]]]
2270
- end
2271
-
2272
- result.each do |handler, status, packages|
2273
- if status == FOUND_NONEXISTENT
2274
- raise MissingOSDep.new, "there is an osdep definition for #{name}, and it explicitely states that this package does not exist on your OS"
93
+ opt.on '--version=VERSION_CONSTRAINT', String, 'use the provided string as a version constraint for autoproj' do |version|
94
+ @gemfile = default_gemfile_contents(version)
2275
95
  end
2276
- if entry = all_packages.find { |h, _| h == handler }
2277
- entry[1].concat(packages)
2278
- else
2279
- all_packages << [handler, packages]
96
+ opt.on '--gemfile=PATH', String, 'use the given Gemfile to install autoproj instead of the default' do |path|
97
+ @gemfile = File.read(path)
2280
98
  end
2281
99
  end
100
+ options.parse(ARGV)
2282
101
  end
2283
102
 
2284
- all_packages.delete_if do |handler, pkg|
2285
- pkg.empty?
2286
- end
2287
- return all_packages
2288
- end
2289
-
2290
-
2291
- # Returns true if +name+ is an acceptable OS package for this OS and
2292
- # version
2293
- def has?(name)
2294
- status = availability_of(name)
2295
- status == AVAILABLE || status == IGNORE
2296
- end
2297
-
2298
- # Value returned by #availability_of if the required package has no
2299
- # definition
2300
- NO_PACKAGE = 0
2301
- # Value returned by #availability_of if the required package has
2302
- # definitions, but not for this OS name or version
2303
- WRONG_OS = 1
2304
- # Value returned by #availability_of if the required package has
2305
- # definitions, but the local OS is unknown
2306
- UNKNOWN_OS = 2
2307
- # Value returned by #availability_of if the required package has
2308
- # definitions, but the nonexistent keyword was used for this OS
2309
- NONEXISTENT = 3
2310
- # Value returned by #availability_of if the required package is
2311
- # available
2312
- AVAILABLE = 4
2313
- # Value returned by #availability_of if the required package is
2314
- # available, but no package needs to be installed to have it
2315
- IGNORE = 5
2316
-
2317
- # If +name+ is an osdeps that is available for this operating system,
2318
- # returns AVAILABLE. Otherwise, returns one of:
2319
- #
2320
- # NO_PACKAGE:: the package has no definitions
2321
- # WRONG_OS:: the package has a definition, but not for this OS
2322
- # UNKNOWN_OS:: the package has a definition, but the local OS is unknown
2323
- # NONEXISTENT:: the package has a definition, but the 'nonexistent'
2324
- # keyword was found for this OS
2325
- # AVAILABLE:: the package is available for this OS
2326
- # IGNORE:: the package is available for this OS, but no packages need to
2327
- # be installed for it
2328
- def availability_of(name)
2329
- resolved = resolve_package(name)
2330
- if !resolved
2331
- return NO_PACKAGE
2332
- end
2333
-
2334
- if resolved.empty?
2335
- if !OSDependencies.operating_system
2336
- return UNKNOWN_OS
2337
- elsif !OSDependencies.supported_operating_system?
2338
- return AVAILABLE
2339
- else return WRONG_OS
2340
- end
2341
- end
2342
-
2343
- resolved = resolved.delete_if { |_, status, list| status == FOUND_PACKAGES && list.empty? }
2344
- failed = resolved.find_all do |handler, status, list|
2345
- status == FOUND_NONEXISTENT
2346
- end
2347
- if failed.empty?
2348
- if resolved.empty?
2349
- return IGNORE
2350
- else
2351
- return AVAILABLE
2352
- end
2353
- else
2354
- return NONEXISTENT
2355
- end
2356
- end
2357
-
2358
- HANDLE_ALL = 'all'
2359
- HANDLE_RUBY = 'ruby'
2360
- HANDLE_OS = 'os'
2361
- HANDLE_NONE = 'none'
2362
-
2363
- def self.osdeps_mode_option_unsupported_os(config = Autoproj.config)
2364
- long_doc =<<-EOT
2365
- The software packages that autoproj will have to build may require other
2366
- prepackaged softwares (a.k.a. OS dependencies) to be installed (RubyGems
2367
- packages, packages from your operating system/distribution, ...). Autoproj is
2368
- usually able to install those automatically, but unfortunately your operating
2369
- system is not (yet) supported by autoproj's osdeps mechanism, it can only offer
2370
- you some limited support.
2371
-
2372
- Some package handlers are cross-platform, and are therefore supported. However,
2373
- you will have to install the kind of OS dependencies (so-called OS packages)
2374
-
2375
- This option is meant to allow you to control autoproj's behaviour while handling
2376
- OS dependencies.
2377
-
2378
- * if you say "all", all OS-independent packages are going to be installed.
2379
- * if you say "gem", the RubyGem packages will be installed.
2380
- * if you say "pip", the Pythin PIP packages will be installed.
2381
- * if you say "none", autoproj will not do anything related to the OS
2382
- dependencies.
2383
-
2384
- As any configuration value, the mode can be changed anytime by calling
2385
- autoproj reconfigure
2386
-
2387
- Finally, the "autoproj osdeps" command will give you the necessary information
2388
- about the OS packages that you will need to install manually.
2389
-
2390
- So, what do you want ? (all, none or a comma-separated list of: gem pip)
2391
- EOT
2392
- message = [ "Which prepackaged software (a.k.a. 'osdeps') should autoproj install automatically (all, none or a comma-separated list of: gem pip) ?", long_doc.strip ]
2393
-
2394
- config.declare 'osdeps_mode', 'string',
2395
- :default => 'ruby',
2396
- :doc => message,
2397
- :lowercase => true
2398
- end
2399
-
2400
- def self.osdeps_mode_option_supported_os(config = Autoproj.config)
2401
- long_doc =<<-EOT
2402
- The software packages that autoproj will have to build may require other
2403
- prepackaged softwares (a.k.a. OS dependencies) to be installed (RubyGems
2404
- packages, packages from your operating system/distribution, ...). Autoproj
2405
- is able to install those automatically for you.
2406
-
2407
- Advanced users may want to control this behaviour. Additionally, the
2408
- installation of some packages require administration rights, which you may
2409
- not have. This option is meant to allow you to control autoproj's behaviour
2410
- while handling OS dependencies.
2411
-
2412
- * if you say "all", it will install all packages automatically.
2413
- This requires root access thru 'sudo'
2414
- * if you say "pip", only the Ruby packages will be installed.
2415
- Installing these packages does not require root access.
2416
- * if you say "gem", only the Ruby packages will be installed.
2417
- Installing these packages does not require root access.
2418
- * if you say "os", only the OS-provided packages will be installed.
2419
- Installing these packages requires root access.
2420
- * if you say "none", autoproj will not do anything related to the
2421
- OS dependencies.
103
+ def install_bundler
104
+ gem_program = guess_gem_program
105
+ puts "Detected 'gem' to be #{gem_program}"
2422
106
 
2423
- Finally, you can provide a comma-separated list of pip gem and os.
107
+ result = system(
108
+ Hash['GEM_PATH' => nil,
109
+ 'GEM_HOME' => bundler_install_dir],
110
+ gem_program, 'install', '--no-document', '--no-user-install', '--no-format-executable',
111
+ "--bindir=#{File.join(bundler_install_dir, 'bin')}", 'bundler')
2424
112
 
2425
- As any configuration value, the mode can be changed anytime by calling
2426
- autoproj reconfigure
2427
-
2428
- Finally, the "autoproj osdeps" command will give you the necessary information
2429
- about the OS packages that you will need to install manually.
2430
-
2431
- So, what do you want ? (all, none or a comma-separated list of: os gem pip)
2432
- EOT
2433
- message = [ "Which prepackaged software (a.k.a. 'osdeps') should autoproj install automatically (all, none or a comma-separated list of: os gem pip) ?", long_doc.strip ]
2434
-
2435
- config.declare 'osdeps_mode', 'string',
2436
- :default => 'all',
2437
- :doc => message,
2438
- :lowercase => true
2439
- end
2440
-
2441
- def self.define_osdeps_mode_option(config = Autoproj.config)
2442
- if supported_operating_system?
2443
- osdeps_mode_option_supported_os(config)
2444
- else
2445
- osdeps_mode_option_unsupported_os(config)
2446
- end
2447
- end
2448
-
2449
- def self.osdeps_mode_string_to_value(string)
2450
- string = string.to_s.downcase.split(',')
2451
- modes = []
2452
- string.map do |str|
2453
- case str
2454
- when 'all' then modes.concat(['os', 'gem', 'pip'])
2455
- when 'ruby' then modes << 'gem'
2456
- when 'gem' then modes << 'gem'
2457
- when 'pip' then modes << 'pip'
2458
- when 'os' then modes << 'os'
2459
- when 'none' then
2460
- else raise ArgumentError, "#{str} is not a known package handler"
113
+ if !result
114
+ STDERR.puts "FATAL: failed to install bundler in #{dot_autoproj}"
115
+ exit 1
2461
116
  end
117
+ File.join(bin_dir, 'bundler')
2462
118
  end
2463
- modes
2464
- end
2465
-
2466
- # If set to true (the default), #install will try to remove the list of
2467
- # already uptodate packages from the installed packages. Set to false to
2468
- # install all packages regardless of their status
2469
- attr_writer :filter_uptodate_packages
2470
-
2471
- # If set to true (the default), #install will try to remove the list of
2472
- # already uptodate packages from the installed packages. Use
2473
- # #filter_uptodate_packages= to set it to false to install all packages
2474
- # regardless of their status
2475
- def filter_uptodate_packages?
2476
- !!@filter_uptodate_packages
2477
- end
2478
-
2479
- # Override the osdeps mode
2480
- def osdeps_mode=(value)
2481
- @osdeps_mode = OSDependencies.osdeps_mode_string_to_value(value)
2482
- end
2483
-
2484
- # Returns the osdeps mode chosen by the user
2485
- def osdeps_mode
2486
- # This has two uses. It caches the value extracted from the
2487
- # AUTOPROJ_OSDEPS_MODE and/or configuration file. Moreover, it
2488
- # allows to override the osdeps mode by using
2489
- # OSDependencies#osdeps_mode=
2490
- if @osdeps_mode
2491
- return @osdeps_mode
2492
- end
2493
-
2494
- @osdeps_mode = OSDependencies.osdeps_mode
2495
- end
2496
-
2497
- def self.osdeps_mode(config = Autoproj.config)
2498
- while true
2499
- mode =
2500
- if !config.has_value_for?('osdeps_mode') &&
2501
- mode_name = ENV['AUTOPROJ_OSDEPS_MODE']
2502
- begin OSDependencies.osdeps_mode_string_to_value(mode_name)
2503
- rescue ArgumentError
2504
- Autoproj.warn "invalid osdeps mode given through AUTOPROJ_OSDEPS_MODE (#{mode})"
2505
- nil
2506
- end
2507
- else
2508
- mode_name = config.get('osdeps_mode')
2509
- begin OSDependencies.osdeps_mode_string_to_value(mode_name)
2510
- rescue ArgumentError
2511
- Autoproj.warn "invalid osdeps mode stored in configuration file"
2512
- nil
2513
- end
2514
- end
2515
119
 
2516
- if mode
2517
- @osdeps_mode = mode
2518
- config.set('osdeps_mode', mode_name, true)
2519
- return mode
120
+ def save_env_sh
121
+ env = Autobuild::Environment.new
122
+ path = []
123
+ if private_bundler?
124
+ env.push_path 'PATH', File.join(bundler_install_dir, 'bin')
125
+ env.push_path 'GEM_PATH', bundler_install_dir
2520
126
  end
2521
-
2522
- # Invalid configuration values. Retry
2523
- config.reset('osdeps_mode')
2524
- ENV['AUTOPROJ_OSDEPS_MODE'] = nil
2525
- end
2526
- end
2527
-
2528
- # The set of packages that have already been installed
2529
- attr_reader :installed_packages
2530
-
2531
- # Set up the registered package handlers according to the specified osdeps mode
2532
- #
2533
- # It enables/disables package handlers based on either the value
2534
- # returned by {#osdeps_mode} or the value passed as option (the latter
2535
- # takes precedence). Moreover, sets the handler's silent flag using
2536
- # {#silent?}
2537
- #
2538
- # @option options [Array<String>] the package handlers that should be
2539
- # enabled. The default value is returned by {#osdeps_mode}
2540
- # @return [Array<PackageManagers::Manager>] the set of enabled package
2541
- # managers
2542
- def setup_package_handlers(options = Hash.new)
2543
- options =
2544
- if Kernel.respond_to?(:validate_options)
2545
- Kernel.validate_options options,
2546
- osdeps_mode: osdeps_mode
2547
- else
2548
- options = options.dup
2549
- options[:osdeps_mode] ||= osdeps_mode
2550
- options
127
+ env.push_path 'PATH', File.join(autoproj_install_dir, 'bin')
128
+ env.inherit 'PATH'
129
+ if private_autoproj?
130
+ env.push_path 'GEM_PATH', autoproj_install_dir
2551
131
  end
2552
132
 
2553
- os_package_handler.enabled = false
2554
- package_handlers.each_value do |handler|
2555
- handler.enabled = false
2556
- end
2557
- options[:osdeps_mode].each do |m|
2558
- if m == 'os'
2559
- os_package_handler.enabled = true
2560
- elsif pkg = package_handlers[m]
2561
- pkg.enabled = true
2562
- else
2563
- Autoproj.warn "osdep handler #{m.inspect} has no handler, available handlers are #{package_handlers.keys.map(&:inspect).sort.join(", ")}"
133
+ # Generate environment files right now, we can at least use bundler
134
+ File.open(File.join(dot_autoproj, 'env.sh'), 'w') do |io|
135
+ env.export_env_sh(io)
2564
136
  end
2565
- end
2566
- os_package_handler.silent = self.silent?
2567
- package_handlers.each_value do |v|
2568
- v.silent = self.silent?
2569
- end
2570
137
 
2571
- enabled_handlers = []
2572
- if os_package_handler.enabled?
2573
- enabled_handlers << os_package_handler
2574
- end
2575
- package_handlers.each_value do |v|
2576
- if v.enabled?
2577
- enabled_handlers << v
138
+ File.open(File.join(root_dir, 'env.sh'), 'w') do |io|
139
+ io.write <<-EOSHELL
140
+ source "#{File.join(dot_autoproj, 'env.sh')}"
141
+ export AUTOPROJ_CURRENT_ROOT=#{root_dir}
142
+ EOSHELL
2578
143
  end
2579
144
  end
2580
- enabled_handlers
2581
- end
2582
-
2583
- # Requests that packages that are handled within the autoproj project
2584
- # (i.e. gems) are restored to pristine condition
2585
- #
2586
- # This is usually called as a rebuild step to make sure that all these
2587
- # packages are updated to whatever required the rebuild
2588
- def pristine(packages, options = Hash.new)
2589
- install(packages, options.merge(install_only: true))
2590
- packages = resolve_os_dependencies(packages)
2591
145
 
2592
- _, other_packages =
2593
- packages.partition { |handler, list| handler == os_package_handler }
2594
- other_packages.each do |handler, list|
2595
- if handler.respond_to?(:pristine)
2596
- handler.pristine(list)
146
+ def save_gemfile
147
+ FileUtils.mkdir_p File.dirname(autoproj_gemfile_path)
148
+ File.open(autoproj_gemfile_path, 'w') do |io|
149
+ io.write gemfile
2597
150
  end
2598
151
  end
2599
- end
2600
-
2601
- # Requests the installation of the given set of packages
2602
- def install(packages, options = Hash.new)
2603
- # Remove the set of packages that have already been installed
2604
- packages = packages.to_set - installed_packages
2605
- return false if packages.empty?
2606
-
2607
- filter_options, options =
2608
- filter_options options, install_only: !Autobuild.do_update
2609
- setup_package_handlers(options)
2610
-
2611
- packages = resolve_os_dependencies(packages)
2612
-
2613
- needs_filter = (filter_uptodate_packages? || filter_options[:install_only])
2614
- packages = packages.map do |handler, list|
2615
- if needs_filter && handler.respond_to?(:filter_uptodate_packages)
2616
- list = handler.filter_uptodate_packages(list, filter_options)
152
+
153
+ def install_autoproj(bundler)
154
+ # Force bundler to update. If the user does not want this, let him specify a
155
+ # Gemfile with tighter version constraints
156
+ lockfile = File.join(File.dirname(autoproj_gemfile_path), 'Gemfile.lock')
157
+ if File.exist?(lockfile)
158
+ FileUtils.rm lockfile
2617
159
  end
2618
160
 
2619
- if !list.empty?
2620
- [handler, list]
2621
- end
2622
- end.compact
2623
- return false if packages.empty?
161
+ env = Hash['BUNDLE_GEMFILE' => nil, 'RUBYLIB' => nil]
162
+ opts = Array.new
2624
163
 
2625
- # Install OS packages first, as the other package handlers might
2626
- # depend on OS packages
2627
- os_packages, other_packages = packages.partition { |handler, list| handler == os_package_handler }
2628
- [os_packages, other_packages].each do |packages|
2629
- packages.each do |handler, list|
2630
- handler.install(list)
2631
- @installed_packages |= list.to_set
164
+ if private_autoproj?
165
+ env = Hash['GEM_PATH' => bundler_install_dir,
166
+ 'GEM_HOME' => nil]
167
+ opts << "--clean" << "--path=#{autoproj_install_dir}"
2632
168
  end
2633
- end
2634
- true
2635
- end
2636
169
 
2637
- def reinstall(options = Hash.new)
2638
- # We also reinstall the osdeps that provide the
2639
- # functionality
2640
- managers = setup_package_handlers(options)
2641
- managers.each do |mng|
2642
- if mng.enabled? && mng.respond_to?(:reinstall)
2643
- mng.reinstall
170
+ result = system(env,
171
+ bundler, 'install',
172
+ "--gemfile=#{autoproj_gemfile_path}",
173
+ "--binstubs=#{File.join(autoproj_install_dir, 'bin')}",
174
+ *opts)
175
+ if !result
176
+ STDERR.puts "FATAL: failed to install autoproj in #{dot_autoproj}"
177
+ exit 1
2644
178
  end
2645
179
  end
2646
- end
2647
- end
2648
- end
2649
-
2650
-
2651
- module Autobuild
2652
- class << self
2653
- # Configure the programs used by different packages
2654
- attr_reader :programs
2655
- # A cache of entries in programs to their resolved full path
2656
- #
2657
- # @return [{String=>[String,String,String]}] the triplet (full path,
2658
- # tool name, value of ENV['PATH']). The last two values are used to
2659
- # invalidate the cache when needed
2660
- #
2661
- # @see tool_in_path
2662
- attr_reader :programs_in_path
2663
-
2664
- # Get a given program, using its name as default value. For
2665
- # instance
2666
- # tool('automake')
2667
- # will return 'automake' unless the autobuild script defined
2668
- # another automake program in Autobuild.programs by doing
2669
- # Autobuild.programs['automake'] = 'automake1.9'
2670
- def tool(name)
2671
- programs[name.to_sym] || programs[name.to_s] || name.to_s
2672
- end
2673
-
2674
- def find_in_path(file)
2675
- path = ENV['PATH'].split(File::PATH_SEPARATOR).
2676
- find { |dir| File.exist?(File.join(dir, file)) }
2677
- if path
2678
- return File.join(path, file)
2679
- end
2680
- end
2681
180
 
2682
- # Resolves the absolute path to a given tool
2683
- def tool_in_path(name)
2684
- path, path_name, path_env = programs_in_path[name]
2685
- current = tool(name)
2686
- if path_env != ENV['PATH'] || path_name != current
2687
- # Delete the current entry given that it is invalid
2688
- programs_in_path.delete(name)
2689
- if current[0, 1] == "/"
2690
- # This is already a full path
2691
- path = current
181
+ def update_configuration
182
+ if File.exist?(autoproj_config_path)
183
+ config = YAML.load(File.read(autoproj_config_path)) || Hash.new
2692
184
  else
2693
- path = find_in_path(current)
185
+ config = Hash.new
2694
186
  end
2695
-
2696
- if !path
2697
- raise ArgumentError, "tool #{name}, set to #{current}, can not be found in PATH=#{path_env}"
187
+ config['private_bundler'] = private_bundler?
188
+ config['private_autoproj'] = private_autoproj?
189
+ config['private_gems'] = private_gems?
190
+ File.open(autoproj_config_path, 'w') do |io|
191
+ YAML.dump(config, io)
2698
192
  end
193
+ end
2699
194
 
2700
- # Verify that the new value is a file and is executable
2701
- if !File.file?(path)
2702
- raise ArgumentError, "tool #{name} is set to #{current}, but this resolves to #{path} which is not a file"
2703
- elsif !File.executable?(path)
2704
- raise ArgumentError, "tool #{name} is set to #{current}, but this resolves to #{path} which is not executable"
195
+ def install
196
+ if private_bundler?
197
+ puts "Installing bundler in #{bundler_install_dir}"
198
+ bundler = install_bundler
2705
199
  end
2706
- programs_in_path[name] = [path, current, ENV['PATH']]
200
+ save_gemfile
201
+ puts "Installing autoproj in #{dot_autoproj}"
202
+ install_autoproj(bundler || 'bundler')
2707
203
  end
2708
204
 
2709
- return path
2710
- end
2711
- end
2712
-
2713
- @programs = Hash.new
2714
- @programs_in_path = Hash.new
2715
- end
2716
-
2717
-
2718
- module Autoproj
2719
- # OS-independent creation of symbolic links. Note that on windows, it only
2720
- # works for directories
2721
- def self.create_symlink(from, to)
2722
- if Autobuild.windows?
2723
- Dir.create_junction(to, from)
2724
- else
2725
- FileUtils.ln_sf from, to
2726
- end
2727
- end
2728
-
2729
- # Returns true if +path+ is part of an autoproj installation
2730
- def self.in_autoproj_installation?(path)
2731
- root_dir(File.expand_path(path))
2732
- true
2733
- rescue UserError
2734
- false
2735
- end
2736
-
2737
- # Forcefully sets the root directory
2738
- #
2739
- # This is mostly useful during bootstrapping (i.e. when the search would
2740
- # fail)
2741
- def self.root_dir=(dir)
2742
- if @workspace && dir != @workspace.root_dir
2743
- raise WorkspaceAlreadyCreated, "cannot switch global root directory after a workspace object got created"
2744
- end
2745
- @root_dir = dir
2746
- end
2747
-
2748
- # Returns the root directory of the current autoproj installation.
2749
- #
2750
- # If the current directory is not in an autoproj installation,
2751
- # raises UserError.
2752
- def self.root_dir(dir = Dir.pwd)
2753
- if @root_dir
2754
- return @root_dir
2755
- end
2756
- path = Workspace.find_root_dir(dir)
2757
- if !path
2758
- raise UserError, "not in a Autoproj installation"
2759
- end
2760
- path
2761
- end
2762
-
2763
- # @deprecated use workspace.config_dir instead
2764
- def self.config_dir
2765
- Autoproj.warn "#{__method__} is deprecated, use workspace.config_dir instead"
2766
- caller.each { |l| Autoproj.warn " #{l}" }
2767
- workspace.config_dir
2768
- end
2769
- # @deprecated use workspace.overrides_dir instead
2770
- def self.overrides_dir
2771
- Autoproj.warn "#{__method__} is deprecated, use workspace.overrides_dir instead"
2772
- caller.each { |l| Autoproj.warn " #{l}" }
2773
- workspace.overrides_dir
2774
- end
2775
- # @deprecated use Autobuild.find_in_path instead
2776
- #
2777
- # Warning: the autobuild method returns nil (instead of raising) if the
2778
- # argument cannot be found
2779
- def self.find_in_path(name)
2780
- Autoproj.warn "#{__method__} is deprecated, use Autobuild.find_in_path instead"
2781
- caller.each { |l| Autoproj.warn " #{l}" }
2782
- if path = Autobuild.find_in_path(name)
2783
- return path
2784
- else raise ArgumentError, "cannot find #{name} in PATH (#{ENV['PATH']})"
2785
- end
2786
- end
2787
- # @deprecated use workspace.prefix_dir instead
2788
- def self.prefix
2789
- Autoproj.warn_deprecated(__method__, 'workspace.prefix_dir')
2790
- workspace.prefix_dir
2791
- end
2792
- # @deprecated use workspace.prefix_dir= instead
2793
- def self.prefix=(path)
2794
- Autoproj.warn_deprecated(__method__, 'workspace.prefix_dir=')
2795
- workspace.prefix_dir = path
2796
- end
2797
- # @deprecated use workspace.prefix_dir instead
2798
- def self.build_dir
2799
- Autoproj.warn_deprecated(__method__, 'workspace.prefix_dir')
2800
- workspace.prefix_dir
2801
- end
2802
- # @deprecated compute the full path with File.join(config_dir, file)
2803
- # directly instead
2804
- def self.config_file(file)
2805
- Autoproj.warn_deprecated(__method__, 'compute the full path with File.join(config_dir, ...) instead')
2806
- File.join(config_dir, file)
2807
- end
2808
- # @deprecated use workspace.remotes_dir instead
2809
- def self.remotes_dir
2810
- Autoproj.warn_deprecated(__method__, 'use workspace.remotes_dir instead')
2811
- workspace.remotes_dir
2812
- end
2813
- # @deprecated use workspace.load or add a separate Loader object to your class
2814
- def self.load(package_set, *path)
2815
- Autoproj.warn_deprecated(
2816
- __method__,
2817
- 'use workspace.load or add a separate Loader object to your class')
2818
- workspace.load(package_set, *path)
2819
- end
2820
- # @deprecated use workspace.load_if_present or add a separate Loader object to your class
2821
- def self.load_if_present(package_set, *path)
2822
- Autoproj.warn_deprecated(
2823
- __method__,
2824
- 'use workspace.load_if_present or add a separate Loader object to your class')
2825
- workspace.load_if_present(package_set, *path)
2826
- end
2827
-
2828
- # Run the provided command as user
2829
- def self.run_as_user(*args)
2830
- if !system(*args)
2831
- raise "failed to run #{args.join(" ")}"
2832
- end
2833
- end
2834
- # Run the provided command as root, using sudo to gain root access
2835
- def self.run_as_root(*args)
2836
- if !system(Autobuild.tool_in_path('sudo'), *args)
2837
- raise "failed to run #{args.join(" ")} as root"
2838
- end
2839
- end
2840
-
2841
- # Look into +dir+, searching for shared libraries. For each library, display
2842
- # a warning message if this library has undefined symbols.
2843
- def self.validate_solib_dependencies(dir, exclude_paths = [])
2844
- Find.find(File.expand_path(dir)) do |name|
2845
- next unless name =~ /\.so$/
2846
- next if exclude_paths.find { |p| name =~ p }
2847
-
2848
- output = `ldd -r #{name} 2>&1`
2849
- if output =~ /undefined symbol/
2850
- Autoproj.message(" WARN: #{name} has undefined symbols", :magenta)
205
+ # Actually perform the install
206
+ def run
207
+ install
208
+ ENV['BUNDLE_GEMFILE'] = autoproj_gemfile_path
209
+ require 'bundler'
210
+ Bundler.setup
211
+ require 'autobuild'
212
+ save_env_sh
213
+ update_configuration
2851
214
  end
2852
215
  end
2853
216
  end
@@ -2855,360 +218,6 @@ end
2855
218
 
2856
219
 
2857
220
 
2858
- DEFS = <<EODEFS
2859
- ---
2860
- none: ignore
2861
- ruby19:
2862
- debian:
2863
- - ruby1.9.1
2864
- - ruby1.9.1-dev
2865
- - rubygems1.9.1
2866
- - rake
2867
- - rubygems-integration
2868
- ubuntu:
2869
- '12.04':
2870
- - ruby1.9.1
2871
- - ruby1.9.1-dev
2872
- - rubygems1.9.1
2873
- - ri1.9.1
2874
- - libopenssl-ruby1.9.1
2875
- - rake
2876
- default:
2877
- - ruby1.9.1
2878
- - ruby1.9.1-dev
2879
- - rubygems1.9.1
2880
- - ri1.9.1
2881
- - libopenssl-ruby1.9.1
2882
- - rake
2883
- - rubygems-integration
2884
- gentoo:
2885
- - dev-lang/ruby:1.9
2886
- - rake
2887
- fedora:
2888
- '17':
2889
- - ruby
2890
- - rubygems
2891
- macos-port:
2892
- - ruby19
2893
- - rake
2894
- macos-brew:
2895
- - gem: rake
2896
- opensuse: ruby19-devel
2897
- default: ignore
2898
- ruby20:
2899
- debian:
2900
- - ruby2.0
2901
- - ruby2.0-dev
2902
- - rake
2903
- - rubygems-integration
2904
- ubuntu:
2905
- 13.10,14.04:
2906
- - ruby2.0
2907
- - ruby2.0-dev
2908
- - rake
2909
- - rubygems-integration
2910
- fedora:
2911
- '20':
2912
- - ruby
2913
- - ruby-devel
2914
- - rubygem-rake
2915
- opensuse: ruby20-devel
2916
- macos-brew:
2917
- - gem: rake
2918
- default: ignore
2919
- ruby21:
2920
- debian:
2921
- - ruby2.1
2922
- - ruby2.1-dev
2923
- - rake
2924
- - rubygems-integration
2925
- ubuntu:
2926
- '14.10':
2927
- - ruby2.1
2928
- - ruby2.1-dev
2929
- - rake
2930
- - rubygems-integration
2931
- default: ignore
2932
- fedora: ruby-devel
2933
- macos-brew:
2934
- - gem: rake
2935
- default: ignore
2936
- build-essential:
2937
- debian,ubuntu: build-essential
2938
- gentoo: ignore
2939
- arch: base-devel
2940
- fedora:
2941
- - gcc-c++
2942
- - make
2943
- - glibc-devel
2944
- darwin: ignore
2945
- opensuse:
2946
- - "@devel_C_C++"
2947
- - gcc-c++
2948
- default: clang
2949
- autobuild:
2950
- - gem: autobuild
2951
- - osdep: readline
2952
- autoproj:
2953
- - gem: autoproj
2954
- - osdep: readline
2955
- readline:
2956
- debian,ubuntu: libreadline-dev
2957
- fedora: readline-devel
2958
- opensuse: readline-devel
2959
- arch: core/readline
2960
- macos-brew: readline
2961
- default: ignore
2962
- git:
2963
- debian:
2964
- lenny: git
2965
- default: git-core
2966
- ubuntu: git-core
2967
- gentoo: dev-vcs/git
2968
- arch: git
2969
- fedora: git
2970
- macos-port: git
2971
- macos-brew: git
2972
- opensuse: git
2973
- freebsd: git
2974
- hg:
2975
- debian,ubuntu: mercurial
2976
- gentoo: dev-vcs/mercurial
2977
- arch: mercurial
2978
- fedora: mercurial
2979
- darwin: mercurial
2980
- opensuse: mercurial
2981
- freebsd: mercurial
2982
- svn:
2983
- debian,ubuntu: subversion
2984
- gentoo: dev-util/subversion
2985
- arch: subversion
2986
- fedora: subversion
2987
- darwin: subversion
2988
- opensuse: subversion
2989
- freebsd: subversion
2990
- cmake:
2991
- debian,ubuntu: cmake
2992
- gentoo: dev-util/cmake
2993
- arch: cmake
2994
- fedora: cmake
2995
- darwin: cmake
2996
- opensuse: cmake
2997
- freebsd: cmake
2998
- autotools:
2999
- debian,ubuntu:
3000
- - automake
3001
- - autoconf
3002
- gentoo:
3003
- - sys-devel/automake
3004
- - sys-devel/autoconf
3005
- arch:
3006
- - automake
3007
- - autoconf
3008
- fedora:
3009
- - automake
3010
- - autoconf
3011
- darwin:
3012
- - automake
3013
- - autoconf
3014
- opensuse:
3015
- - automake
3016
- - autoconf
3017
- freebsd:
3018
- - automake
3019
- - autoconf
3020
- archive:
3021
- debian,ubuntu:
3022
- - tar
3023
- - unzip
3024
- gentoo:
3025
- - app-arch/tar
3026
- - app-arch/unzip
3027
- arch:
3028
- - tar
3029
- - unzip
3030
- fedora:
3031
- - tar
3032
- - unzip
3033
- macos-port:
3034
- - gnutar
3035
- - unzip
3036
- macos-brew:
3037
- - gnu-tar
3038
- opensuse:
3039
- - tar
3040
- - unzip
3041
- default: ignore
3042
- cvs:
3043
- debian,ubuntu: cvs
3044
- fedora: cvs
3045
- darwin: cvs
3046
- arch: cvs
3047
- opensuse: cvs
3048
- freebsd: cvs
3049
- pip:
3050
- debian,ubuntu: python-pip
3051
- arch: python2-pip
3052
- opensuse: python-pip
3053
- fedora: python-pip
3054
- freebsd: pip
3055
- sudo:
3056
- default: sudo
3057
-
3058
- EODEFS
3059
-
3060
- # Override Autoproj.root_dir
3061
- module Autoproj
3062
- def self.root_dir
3063
- @root_dir
3064
- end
3065
- @root_dir = Dir.pwd
3066
- end
3067
-
3068
- if File.directory?(File.join(Autoproj.root_dir, 'autoproj'))
3069
- STDERR.puts "there is already an autoproj/ directory here, cannot bootstrap"
3070
- STDERR.puts "Either delete it and attempt bootstrapping again, or source env.sh"
3071
- STDERR.puts "and use the usual autoproj workflow"
3072
- exit 1
3073
- end
3074
-
3075
- if defined? Encoding # This is a 1.9-only thing
3076
- Encoding.default_internal = Encoding::UTF_8
3077
- Encoding.default_external = Encoding::UTF_8
3078
- end
3079
-
3080
- if ENV['AUTOPROJ_CURRENT_ROOT'] && ENV['AUTOPROJ_CURRENT_ROOT'] != Dir.pwd
3081
- STDERR.puts "the env.sh from #{ENV['AUTOPROJ_CURRENT_ROOT']} seem to already be sourced"
3082
- STDERR.puts "start a new shell and try to bootstrap again"
3083
- exit 1
3084
- end
3085
-
3086
- require 'set'
3087
- curdir_entries = Dir.entries('.').to_set - [".", "..", "autoproj_bootstrap", ".gems", 'env.sh'].to_set
3088
- if !curdir_entries.empty? && ENV['AUTOPROJ_BOOTSTRAP_IGNORE_NONEMPTY_DIR'] != '1'
3089
- while true
3090
- print "The current directory is not empty, continue bootstrapping anyway ? [yes] "
3091
- STDOUT.flush
3092
- answer = STDIN.readline.chomp
3093
- if answer == "no"
3094
- exit
3095
- elsif answer == "" || answer == "yes"
3096
- # Set the environment variable since we might restart the
3097
- # autoproj_bootstrap script and -- anyway -- will run "autoproj
3098
- # bootstrap" later on
3099
- break
3100
- else
3101
- STDOUT.puts "invalid answer. Please answer 'yes' or 'no'"
3102
- STDOUT.flush
3103
- end
3104
- end
3105
- end
3106
-
3107
- # While in here, we don't have utilrb
3108
- module Kernel
3109
- def filter_options(options, defaults)
3110
- options = options.dup
3111
- filtered = Hash.new
3112
- defaults.each do |k, v|
3113
- filtered[k] =
3114
- if options.has_key?(k) then options.delete(k)
3115
- else v
3116
- end
3117
- end
3118
- return filtered, options
3119
- end
3120
- def validate_options(options, defaults)
3121
- defaults.merge(options)
3122
- end
3123
- end
3124
-
3125
- # Environment is clean, so just mark it as so unconditionally
3126
- ENV['AUTOPROJ_BOOTSTRAP_IGNORE_NONEMPTY_DIR'] = '1'
3127
-
3128
- gem_home = ENV['AUTOPROJ_GEM_HOME'] || File.join(Dir.pwd, '.gems')
3129
- gem_path = ([gem_home] + Gem.default_path).join(":")
3130
- Gem.paths = Hash['GEM_HOME' => gem_home, 'GEM_PATH' => gem_path]
3131
-
3132
- ENV['GEM_HOME'] = gem_home
3133
- ENV['GEM_PATH'] = gem_path
3134
- ENV['PATH'] = "#{ENV['GEM_HOME']}/bin:#{ENV['PATH']}"
3135
-
3136
- Autoproj::OSDependencies.define_osdeps_mode_option
3137
- osdeps_mode = Autoproj::OSDependencies.osdeps_mode.join(",")
3138
- ENV['AUTOPROJ_OSDEPS_MODE'] = osdeps_mode
3139
-
3140
- # First thing we do is install a proper ruby environment. We make sure that we
3141
- # aren't installing any gems for now (as we need to choose the right gem
3142
- # binary) by setting Autobuild.programs['gem'] to nil
3143
- Autobuild.programs['gem'] = nil
3144
- Autoproj::OSDependencies.autodetect_ruby
3145
- Autoproj::OSDependencies.autodetect_ruby_program
3146
-
3147
- osdeps_management =
3148
- if ENV['AUTOPROJ_DEFAULT_OSDEPS']
3149
- Autoproj::OSDependencies.load(ENV['AUTOPROJ_DEFAULT_OSDEPS'])
3150
- else
3151
- Autoproj::OSDependencies.new(YAML.load(DEFS))
3152
- end
3153
- osdeps_management.silent = false
3154
- Autoproj::PackageManagers::GemManager.gem_home = gem_home
3155
- Autoproj::PackageManagers::GemManager.use_cache_dir
3156
-
3157
- begin
3158
- STDERR.puts "autoproj: installing a proper Ruby environment (this can take a long time)"
3159
- osdeps_management.install(['ruby'])
3160
- rescue Autoproj::ConfigError => e
3161
- STDERR.puts "failed: #{e.message}"
3162
- exit(1)
3163
- end
3164
-
3165
- # Now try to find out the name of the gem binary
3166
- PACKAGES = ['build-essential', 'sudo']
3167
-
3168
- STDERR.puts "autoproj: installing autoproj and its dependencies (this can take a long time)"
3169
- # First install the dependencies of autoproj, as we don't want them to be
3170
- # affected by the prerelease flag
3171
- begin
3172
- if !PACKAGES.empty?
3173
- osdeps_management.install(PACKAGES)
3174
- end
3175
- rescue Autoproj::ConfigError => e
3176
- STDERR.puts "failed: #{e.message}"
3177
- exit(1)
3178
- end
3179
-
3180
- File.open('env.sh', 'w') do |io|
3181
- io.write <<-EOSHELL
3182
- export RUBYOPT=-rubygems
3183
- export GEM_PATH=#{gem_path}
3184
- export GEM_HOME=#{gem_home}
3185
- export PATH=$GEM_HOME/bin:$PATH
3186
- EOSHELL
3187
- end
3188
-
3189
- # If the user specifies "dev" on the command line, install the prerelease
3190
- # version of autoproj. If it is "localdev", expect him to install autoproj and
3191
- # run autoproj bootstrap manually.
3192
- if ARGV.first != "localdev"
3193
- if ARGV.first == "dev"
3194
- ENV['AUTOPROJ_USE_PRERELEASE'] = '1'
3195
- ARGV.shift
3196
- end
3197
-
3198
- Autoproj::PackageManagers::GemManager.with_prerelease =
3199
- (ENV['AUTOPROJ_USE_PRERELEASE'] == '1')
3200
- begin
3201
- osdeps_management.install(['autobuild'])
3202
- osdeps_management.install(['autoproj'])
3203
- rescue Autoproj::ConfigError => e
3204
- STDERR.puts "failed: #{e.message}"
3205
- exit(1)
3206
- end
3207
- Autoproj::PackageManagers::GemManager.with_prerelease = false
3208
-
3209
- if !system('autoproj', 'bootstrap', *ARGV)
3210
- STDERR.puts "ERROR: failed to run autoproj bootstrap #{ARGV.join(", ")}"
3211
- exit 1
3212
- end
3213
- end
3214
-
221
+ ops = Autoproj::Ops::Install.new(Dir.pwd)
222
+ ops.parse_options(ARGV)
223
+ ops.run