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