Class: Autoproj::OSPackageInstaller

Inherits:
Object
  • Object
show all
Defined in:
lib/autoproj/os_package_installer.rb

Constant Summary collapse

PACKAGE_MANAGERS =
Hash[
   "apt-dpkg" => PackageManagers::AptDpkgManager,
   "gem" => PackageManagers::BundlerManager,
   "emerge" => PackageManagers::EmergeManager,
   "pacman" => PackageManagers::PacmanManager,
   "brew" => PackageManagers::HomebrewManager,
   "yum" => PackageManagers::YumManager,
   "macports" => PackageManagers::PortManager,
   "zypper" => PackageManagers::ZypperManager,
   "pip" => PackageManagers::PipManager,
   "pkg" => PackageManagers::PkgManager
]
HANDLE_ALL =
"all"
HANDLE_RUBY =
"ruby"
HANDLE_OS =
"os"
HANDLE_NONE =
"none"

Class Attribute Summary collapse

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(ws, os_package_resolver, package_managers: PACKAGE_MANAGERS) ⇒ OSPackageInstaller

Returns a new instance of OSPackageInstaller.



51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/autoproj/os_package_installer.rb', line 51

def initialize(ws, os_package_resolver, package_managers: PACKAGE_MANAGERS)
    @ws = ws
    @os_package_resolver = os_package_resolver
    @os_package_manager  = nil
    @installed_resolved_packages = Hash.new { |h, k| h[k] = Set.new }
    @silent = true
    @filter_uptodate_packages = true
    @osdeps_mode = nil

    @package_managers = Hash.new
    package_managers.each do |name, klass|
        @package_managers[name] = klass.new(ws)
    end
end

Class Attribute Details

.force_osdepsObject

Returns the value of attribute force_osdeps.



48
49
50
# File 'lib/autoproj/os_package_installer.rb', line 48

def force_osdeps
  @force_osdeps
end

Instance Attribute Details

#filter_uptodate_packages=(value) ⇒ Object (writeonly)

If set to true (the default), #install will try to remove the list of already uptodate packages from the installed packages. Set to false to install all packages regardless of their status



208
209
210
# File 'lib/autoproj/os_package_installer.rb', line 208

def filter_uptodate_packages=(value)
  @filter_uptodate_packages = value
end

#installed_resolved_packagesObject (readonly)

The set of resolved packages that have already been installed



39
40
41
# File 'lib/autoproj/os_package_installer.rb', line 39

def installed_resolved_packages
  @installed_resolved_packages
end

#os_package_resolverObject (readonly)

Returns the value of attribute os_package_resolver.



36
37
38
# File 'lib/autoproj/os_package_installer.rb', line 36

def os_package_resolver
  @os_package_resolver
end

#package_managersObject (readonly)

Returns the set of package managers



77
78
79
# File 'lib/autoproj/os_package_installer.rb', line 77

def package_managers
  @package_managers
end

#silent=(value) ⇒ Object (writeonly)

Sets the attribute silent

Parameters:

  • value

    the value to set the attribute silent to.



41
42
43
# File 'lib/autoproj/os_package_installer.rb', line 41

def silent=(value)
  @silent = value
end

#wsObject (readonly)

Returns the value of attribute ws.



21
22
23
# File 'lib/autoproj/os_package_installer.rb', line 21

def ws
  @ws
end

Instance Method Details

#configure_managerObject



201
202
203
# File 'lib/autoproj/os_package_installer.rb', line 201

def configure_manager
    os_package_manager.configure_manager if osdeps_mode.include?("os")
end

#define_osdeps_mode_optionObject



170
171
172
173
174
175
176
# File 'lib/autoproj/os_package_installer.rb', line 170

def define_osdeps_mode_option
    if os_package_resolver.supported_operating_system?
        osdeps_mode_option_supported_os(ws.config)
    else
        osdeps_mode_option_unsupported_os(ws.config)
    end
end

#each_manager(&block) ⇒ Object



79
80
81
# File 'lib/autoproj/os_package_installer.rb', line 79

def each_manager(&block)
    package_managers.each_value(&block)
end

#each_manager_with_name(&block) ⇒ Object



83
84
85
# File 'lib/autoproj/os_package_installer.rb', line 83

def each_manager_with_name(&block)
    package_managers.each(&block)
end

#filter_uptodate_packages?Boolean

If set to true (the default), #install will try to remove the list of already uptodate packages from the installed packages. Use #filter_uptodate_packages= to set it to false to install all packages regardless of their status

Returns:

  • (Boolean)


214
215
216
# File 'lib/autoproj/os_package_installer.rb', line 214

def filter_uptodate_packages?
    !!@filter_uptodate_packages
end

#install(osdep_packages, all: nil, install_only: false, run_package_managers_without_packages: false, **options) ⇒ Object

Requests the installation of the given set of packages



424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
# File 'lib/autoproj/os_package_installer.rb', line 424

def install(
    osdep_packages, all: nil, install_only: false,
    run_package_managers_without_packages: false, **options
)
    setup_package_managers(**options)
    partitioned_packages =
        resolve_and_partition_osdep_packages(osdep_packages, all)

    # Install OS packages first, as the other package handlers might
    # depend on OS packages
    if (os_packages = partitioned_packages.delete(os_package_manager))
        install_manager_packages(
            os_package_manager, os_packages,
            install_only: install_only,
            run_package_managers_without_packages:
                run_package_managers_without_packages
        )
    end
    partitioned_packages.each do |manager, package_list|
        install_manager_packages(
            manager, package_list,
            install_only: install_only,
            run_package_managers_without_packages:
                run_package_managers_without_packages
        )
    end
end

#install_manager_packages(manager, package_list, install_only: false, run_package_managers_without_packages: false) ⇒ Object



452
453
454
455
456
457
458
459
460
461
462
463
# File 'lib/autoproj/os_package_installer.rb', line 452

def install_manager_packages(manager, package_list, install_only: false, run_package_managers_without_packages: false)
    list = package_list.to_set - installed_resolved_packages[manager]

    if !list.empty? || run_package_managers_without_packages
        manager.install(
            list.to_a,
            filter_uptodate_packages: filter_uptodate_packages?,
            install_only: install_only
        )
        installed_resolved_packages[manager].merge(list)
    end
end

#os_package_managerObject

Returns the package manager object for the current OS



67
68
69
70
71
72
73
74
# File 'lib/autoproj/os_package_installer.rb', line 67

def os_package_manager
    unless @os_package_manager
        name = os_package_resolver.os_package_manager
        @os_package_manager = package_managers[name] ||
                              PackageManagers::UnknownOSManager.new(ws)
    end
    @os_package_manager
end

#osdeps_modeObject

Returns the osdeps mode chosen by the user



224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
# File 'lib/autoproj/os_package_installer.rb', line 224

def osdeps_mode
    # This has two uses. It caches the value extracted from the
    # AUTOPROJ_OSDEPS_MODE and/or configuration file. Moreover, it
    # allows to override the osdeps mode by using
    # OSPackageInstaller#osdeps_mode=
    return @osdeps_mode if @osdeps_mode

    config = ws.config
    loop do
        mode =
            if !config.has_value_for?("osdeps_mode") &&
               (mode_name = ENV["AUTOPROJ_OSDEPS_MODE"])
                begin osdeps_mode_string_to_value(mode_name)
                rescue ArgumentError
                    Autoproj.warn "invalid osdeps mode given through "\
                                  "AUTOPROJ_OSDEPS_MODE (#{mode})"
                    nil
                end
            else
                mode_name = config.get("osdeps_mode")
                begin osdeps_mode_string_to_value(mode_name)
                rescue ArgumentError
                    Autoproj.warn "invalid osdeps mode stored "\
                                  "in configuration file"
                    nil
                end
            end

        if mode
            @osdeps_mode = mode
            config.set("osdeps_mode", mode_name, true)
            return mode
        end

        # Invalid configuration values. Retry
        config.reset("osdeps_mode")
        ENV["AUTOPROJ_OSDEPS_MODE"] = nil
    end
end

#osdeps_mode=(value) ⇒ Object

Override the osdeps mode



219
220
221
# File 'lib/autoproj/os_package_installer.rb', line 219

def osdeps_mode=(value)
    @osdeps_mode = osdeps_mode_string_to_value(value)
end

#osdeps_mode_option_supported_os(config) ⇒ Object



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/autoproj/os_package_installer.rb', line 129

def osdeps_mode_option_supported_os(config)
    long_doc = <<-EOT
The software packages that autoproj will have to build may require other
prepackaged softwares (a.k.a. OS dependencies) to be installed (RubyGems
packages, packages from your operating system/distribution, ...). Autoproj
is able to install those automatically for you.

Advanced users may want to control this behaviour. Additionally, the
installation of some packages require administration rights, which you may
not have. This option is meant to allow you to control autoproj's behaviour
while handling OS dependencies.

* if you say "all", it will install all packages automatically.
  This requires root access thru 'sudo'
* if you say "pip", only the Python packages will be installed.
  Installing these packages does not require root access.
* if you say "gem", only the Ruby packages will be installed.
  Installing these packages does not require root access.
* if you say "os", only the OS-provided packages will be installed.
  Installing these packages requires root access.
* if you say "none", autoproj will not do anything related to the
  OS dependencies.

Finally, you can provide a comma-separated list of pip gem and os.

As any configuration value, the mode can be changed anytime by calling
  autoproj reconfigure

Finally, the "autoproj osdeps" command will give you the necessary information
about the OS packages that you will need to install manually.

So, what do you want ? (all, none or a comma-separated list of: os gem pip)
    EOT
    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]

    config.declare "osdeps_mode", "string",
                   default: "all",
                   doc: message,
                   lowercase: true
end

#osdeps_mode_option_unsupported_os(config) ⇒ Object



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/autoproj/os_package_installer.rb', line 92

def osdeps_mode_option_unsupported_os(config)
    long_doc = <<-EOT
The software packages that autoproj will have to build may require other
prepackaged softwares (a.k.a. OS dependencies) to be installed (RubyGems
packages, packages from your operating system/distribution, ...). Autoproj is
usually able to install those automatically, but unfortunately your operating
system is not (yet) supported by autoproj's osdeps mechanism, it can only offer
you some limited support.

Some package handlers are cross-platform, and are therefore supported.  However,
you will have to install the kind of OS dependencies (so-called OS packages)

This option is meant to allow you to control autoproj's behaviour while handling
OS dependencies.

* if you say "all", all OS-independent packages are going to be installed.
* if you say "gem", the RubyGem packages will be installed.
* if you say "pip", the Python PIP packages will be installed.
* if you say "none", autoproj will not do anything related to the OS
  dependencies.

As any configuration value, the mode can be changed anytime by calling
  autoproj reconfigure

Finally, the "autoproj osdeps" command will give you the necessary information
about the OS packages that you will need to install manually.

So, what do you want ? (all, none or a comma-separated list of: gem pip)
    EOT
    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]

    config.declare "osdeps_mode", "string",
                   default: "ruby",
                   doc: message,
                   lowercase: true
end

#osdeps_mode_string_to_value(string) ⇒ Object



178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/autoproj/os_package_installer.rb', line 178

def osdeps_mode_string_to_value(string)
    user_modes = string.to_s.downcase.split(",")
    modes = []
    user_modes.each do |str|
        case str
        when "all"  then modes.concat(%w[os gem pip])
        when "ruby" then modes << "gem"
        when "gem"  then modes << "gem"
        when "pip"  then modes << "pip"
        when "os"   then modes << "os"
        when "none" then
            # noop
        else
            if package_managers.key?(str)
                modes << str
            else
                raise ArgumentError, "#{str} is not a known package handler, known handlers are #{package_managers.keys.sort.join(', ')}"
            end
        end
    end
    modes
end

#pristine(packages, options = Hash.new) ⇒ Object

Requests that packages that are handled within the autoproj project (i.e. gems) are restored to pristine condition

This is usually called as a rebuild step to make sure that all these packages are updated to whatever required the rebuild



309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
# File 'lib/autoproj/os_package_installer.rb', line 309

def pristine(packages, options = Hash.new)
    install(packages, options.merge(install_only: true))
    packages = os_package_resolver.resolve_os_packages(packages)

    packages = packages.map do |handler_name, list|
        unless (manager = package_managers[handler_name])
            raise ArgumentError, "no package manager called #{handler_name} found"
        end

        [manager, list]
    end

    _, other_packages =
        packages.partition { |handler, list| handler == os_package_manager }
    other_packages.each do |handler, list|
        handler.pristine(list) if handler.respond_to?(:pristine)
    end
end

#resolve_and_partition_osdep_packages(osdep_packages, all_osdep_packages = nil) ⇒ Hash<PackageManagers::Manager,Array<(String, String)>] the resolved and partitioned osdeps

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Resolves and partitions osdep packages into the various package managers. The returned hash will have one entry per package manager that has a package, with additional entries for package managers that have an empty list but for which PackageManagers::Manager#call_while_empty? returns true

Parameters:

  • osdep_packages (Array<String>)

    the list of osdeps to install

Returns:

  • (Hash<PackageManagers::Manager,Array<(String, String)>] the resolved and partitioned osdeps)

    Hash<PackageManagers::Manager,Array<(String, String)>] the resolved and partitioned osdeps

Raises:

  • (InternalError)

    if all_osdep_packages is not provided (is nil) and some strict package managers need to be called to handle osdep_packages

  • (ArgumentError)

    if a manager name cannot be resolved



364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
# File 'lib/autoproj/os_package_installer.rb', line 364

def resolve_and_partition_osdep_packages(osdep_packages, all_osdep_packages = nil)
    packages = os_package_resolver.resolve_os_packages(osdep_packages)
    packages = resolve_package_managers_in_mapping(packages)
    all_packages = os_package_resolver.resolve_os_packages(all_osdep_packages || Array.new)
    all_packages = resolve_package_managers_in_mapping(all_packages)

    partitioned_packages = Hash.new
    package_managers.each do |manager_name, manager|
        manager_selected = packages.fetch(manager, Set.new).to_set
        manager_all      = all_packages.fetch(manager, Set.new).to_set

        next if manager_selected.empty? && !manager.call_while_empty?

        # If the manager is strict, we need to bypass it if we did not
        # get the complete list of osdep packages
        if manager.strict? && !all_osdep_packages
            unless manager_selected.empty?
                raise InternalError, "requesting to install the osdeps #{partitioned_packages[manager].to_a.sort.join(', ')} through #{manager_name} but the complete list of osdep packages managed by this manager was not provided. This would break the workspace"
            end

            next
        end

        if manager.strict?
            manager_packages = manager_all | manager_selected
        else
            manager_packages = manager_selected
        end

        partitioned_packages[manager] = manager_packages
    end
    resolve_managers_dependencies(partitioned_packages)
end

#resolve_managers_dependencies(partitioned_packages) ⇒ Object



398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
# File 'lib/autoproj/os_package_installer.rb', line 398

def resolve_managers_dependencies(partitioned_packages)
    partitioned_packages.clone.each do |manager, packages|
        # Skip if the manager is not being used
        next if packages&.empty?

        manager_dependencies = os_package_resolver.resolve_os_packages(manager.os_dependencies)
        manager_dependencies = resolve_package_managers_in_mapping(manager_dependencies)
        manager_dependencies.each_key do |nested_manager|
            deps = manager_dependencies.fetch(nested_manager, Set.new).to_set
            next if deps.empty?

            unless partitioned_packages[nested_manager]
                partitioned_packages[nested_manager] = Set.new
                enable_recursion = true
            end

            partitioned_packages[nested_manager] += deps
            if enable_recursion
                partitioned_packages = resolve_managers_dependencies(partitioned_packages)
            end
        end
    end
    partitioned_packages
end

#resolve_package_managers_in_mapping(mapping) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Resolves the package managers from their name in a manager-to-package list mapping.

This is a helper method to resolve the value returned by Autoproj::OSPackageResolver#resolve_os_packages

Raises:

  • (ArgumentError)

    if a manager name cannot be resolved



337
338
339
340
341
342
343
344
345
346
347
# File 'lib/autoproj/os_package_installer.rb', line 337

def resolve_package_managers_in_mapping(mapping)
    resolved = Hash.new
    mapping.each do |manager_name, package_list|
        unless (manager = package_managers[manager_name])
            raise ArgumentError, "no package manager called #{handler_name} found"
        end

        resolved[manager] = package_list
    end
    resolved
end

#setup_package_managers(osdeps_mode: self.osdeps_mode) ⇒ Array<PackageManagers::Manager>

Set up the registered package handlers according to the specified osdeps mode

It enables/disables package handlers based on either the value returned by #osdeps_mode or the value passed as option (the latter takes precedence). Moreover, sets the handler’s silent flag using #silent?

Parameters:

  • options (Hash)

    a customizable set of options

Returns:



275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
# File 'lib/autoproj/os_package_installer.rb', line 275

def setup_package_managers(osdeps_mode: self.osdeps_mode)
    os_package_manager.enabled = false
    package_managers.each_value do |handler|
        handler.enabled = false
    end
    osdeps_mode.each do |m|
        if m == "os"
            os_package_manager.enabled = true
        elsif (pkg = package_managers[m])
            pkg.enabled = true
        else
            available = package_managers.keys.map(&:inspect).sort.join(", ")
            Autoproj.warn "osdep handler #{m.inspect} found in osdep_mode "\
                          "has no handler, available handlers are #{available}"
        end
    end
    os_package_manager.silent = silent?
    package_managers.each_value do |v|
        v.silent = silent?
    end

    enabled_handlers = []
    enabled_handlers << os_package_manager if os_package_manager.enabled?
    package_managers.each_value do |v|
        enabled_handlers << v if v.enabled?
    end
    enabled_handlers
end

#silent?Boolean

Returns:

  • (Boolean)


43
44
45
# File 'lib/autoproj/os_package_installer.rb', line 43

def silent?
    @silent
end