autoproj 2.0.0.rc3 → 2.0.0.rc4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Rakefile +9 -29
- data/bin/autoproj_bootstrap +159 -3150
- data/bin/autoproj_bootstrap.in +4 -256
- data/bin/autoproj_install +225 -0
- data/bin/autoproj_install.in +14 -0
- data/lib/autoproj.rb +2 -1
- data/lib/autoproj/autobuild.rb +2 -2
- data/lib/autoproj/cli/bootstrap.rb +0 -39
- data/lib/autoproj/cli/build.rb +0 -3
- data/lib/autoproj/cli/main.rb +13 -1
- data/lib/autoproj/cli/osdeps.rb +1 -1
- data/lib/autoproj/cli/show.rb +1 -1
- data/lib/autoproj/cli/update.rb +4 -4
- data/lib/autoproj/cli/upgrade.rb +71 -0
- data/lib/autoproj/configuration.rb +18 -1
- data/lib/autoproj/exceptions.rb +7 -0
- data/lib/autoproj/installation_manifest.rb +23 -12
- data/lib/autoproj/manifest.rb +22 -48
- data/lib/autoproj/ops/build.rb +2 -2
- data/lib/autoproj/ops/configuration.rb +1 -1
- data/lib/autoproj/ops/import.rb +1 -1
- data/lib/autoproj/ops/install.rb +211 -0
- data/lib/autoproj/ops/main_config_switcher.rb +1 -5
- data/lib/autoproj/os_package_installer.rb +348 -0
- data/lib/autoproj/{osdeps.rb → os_package_resolver.rb} +56 -392
- data/lib/autoproj/package_managers/apt_dpkg_manager.rb +2 -2
- data/lib/autoproj/package_managers/bundler_manager.rb +179 -0
- data/lib/autoproj/package_managers/emerge_manager.rb +2 -2
- data/lib/autoproj/package_managers/gem_manager.rb +7 -6
- data/lib/autoproj/package_managers/homebrew_manager.rb +2 -2
- data/lib/autoproj/package_managers/manager.rb +5 -6
- data/lib/autoproj/package_managers/pacman_manager.rb +2 -2
- data/lib/autoproj/package_managers/pip_manager.rb +8 -8
- data/lib/autoproj/package_managers/pkg_manager.rb +2 -2
- data/lib/autoproj/package_managers/port_manager.rb +2 -2
- data/lib/autoproj/package_managers/shell_script_manager.rb +4 -4
- data/lib/autoproj/package_managers/unknown_os_manager.rb +2 -2
- data/lib/autoproj/package_managers/yum_manager.rb +2 -2
- data/lib/autoproj/package_managers/zypper_manager.rb +2 -2
- data/lib/autoproj/package_set.rb +10 -10
- data/lib/autoproj/reporter.rb +3 -2
- data/lib/autoproj/system.rb +1 -4
- data/lib/autoproj/version.rb +1 -1
- data/lib/autoproj/workspace.rb +155 -32
- metadata +9 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bbe30e7cd487de7bf07f9cf6b19d1766caa87aa4
|
4
|
+
data.tar.gz: e88e2b24c5e121542212cb253296b7c73fa01681
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
16
|
-
|
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
|
-
[
|
19
|
+
[autoproj_ops_install].each do |text|
|
37
20
|
text.gsub! /\\/, '\\\\\\\\'
|
38
21
|
end
|
39
22
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
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'
|
data/bin/autoproj_bootstrap
CHANGED
@@ -5,2849 +5,212 @@ if RUBY_VERSION < "1.9.2"
|
|
5
5
|
exit 1
|
6
6
|
end
|
7
7
|
|
8
|
-
require '
|
9
|
-
|
10
|
-
|
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
|
-
|
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
|
-
|
2064
|
-
|
2065
|
-
|
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
|
-
|
2070
|
-
|
2071
|
-
|
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
|
-
|
2080
|
-
|
2081
|
-
|
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
|
-
|
2092
|
-
|
2093
|
-
|
2094
|
-
|
2095
|
-
|
2096
|
-
|
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
|
-
|
2101
|
-
|
2102
|
-
|
2103
|
-
|
2104
|
-
|
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
|
-
|
2193
|
-
|
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
|
-
|
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
|
-
|
2225
|
-
|
2226
|
-
|
2227
|
-
|
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
|
-
|
2270
|
-
|
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
|
-
|
2277
|
-
|
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
|
-
|
2285
|
-
|
2286
|
-
|
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
|
-
|
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
|
-
|
2426
|
-
|
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
|
-
|
2517
|
-
|
2518
|
-
|
2519
|
-
|
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
|
-
|
2523
|
-
|
2524
|
-
|
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
|
-
|
2554
|
-
|
2555
|
-
|
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
|
-
|
2572
|
-
|
2573
|
-
|
2574
|
-
|
2575
|
-
|
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
|
-
|
2593
|
-
|
2594
|
-
|
2595
|
-
|
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
|
-
|
2600
|
-
|
2601
|
-
|
2602
|
-
|
2603
|
-
|
2604
|
-
|
2605
|
-
|
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
|
-
|
2620
|
-
|
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
|
-
|
2626
|
-
|
2627
|
-
|
2628
|
-
|
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
|
-
|
2638
|
-
|
2639
|
-
|
2640
|
-
|
2641
|
-
|
2642
|
-
if
|
2643
|
-
|
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
|
-
|
2683
|
-
|
2684
|
-
|
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
|
-
|
185
|
+
config = Hash.new
|
2694
186
|
end
|
2695
|
-
|
2696
|
-
|
2697
|
-
|
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
|
-
|
2701
|
-
if
|
2702
|
-
|
2703
|
-
|
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
|
-
|
200
|
+
save_gemfile
|
201
|
+
puts "Installing autoproj in #{dot_autoproj}"
|
202
|
+
install_autoproj(bundler || 'bundler')
|
2707
203
|
end
|
2708
204
|
|
2709
|
-
|
2710
|
-
|
2711
|
-
|
2712
|
-
|
2713
|
-
|
2714
|
-
|
2715
|
-
|
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
|
-
|
2859
|
-
|
2860
|
-
|
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
|