autoproj 2.0.0.rc3 → 2.0.0.rc4

Sign up to get free protection for your applications and to get access to all the features.
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