autoproj 2.0.0.b7 → 2.0.0.rc1
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/.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
|