Class: Autoproj::PackageManagers::GemManager

Inherits:
Manager
  • Object
show all
Defined in:
lib/autoproj/package_managers/gem_manager.rb

Overview

Package manager interface for the RubyGems system

Class Attribute Summary collapse

Instance Attribute Summary collapse

Attributes inherited from Manager

#enabled, #silent, #ws

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Manager

#call_while_empty?, #configure_manager, #enabled?, #os_dependencies, #silent?, #strict?

Constructor Details

#initialize(ws) ⇒ GemManager

Returns a new instance of GemManager.



105
106
107
108
# File 'lib/autoproj/package_managers/gem_manager.rb', line 105

def initialize(ws)
    super(ws)
    @installed_gems = Set.new
end

Class Attribute Details

.gem_homeObject

Returns the value of attribute gem_home.



91
92
93
# File 'lib/autoproj/package_managers/gem_manager.rb', line 91

def gem_home
  @gem_home
end

.with_docObject

Returns the value of attribute with_doc.



7
8
9
# File 'lib/autoproj/package_managers/gem_manager.rb', line 7

def with_doc
  @with_doc
end

.with_prerelease(*value) ⇒ Object



12
13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/autoproj/package_managers/gem_manager.rb', line 12

def self.with_prerelease(*value)
    if value.empty?
        @with_prerelease
    else
        begin
            saved_flag = @with_prerelease
            @with_prerelease = value.first
            yield
        ensure
            @with_prerelease = saved_flag
        end
    end
end

Instance Attribute Details

#gem_fetcherObject



117
118
119
120
121
122
123
# File 'lib/autoproj/package_managers/gem_manager.rb', line 117

def gem_fetcher
    unless @gem_fetcher
        Autoproj.message "  looking for RubyGems updates"
        @gem_fetcher = Gem::SpecFetcher.fetcher
    end
    @gem_fetcher
end

#installed_gemsObject (readonly)

The set of gems installed during this autoproj session



115
116
117
# File 'lib/autoproj/package_managers/gem_manager.rb', line 115

def installed_gems
  @installed_gems
end

Class Method Details

.cache_dirObject

A global cache directory that should be used to avoid re-downloading gems



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

def self.cache_dir
    return unless (dir = ENV["AUTOBUILD_CACHE_DIR"])

    dir = File.join(dir, "gems")
    FileUtils.mkdir_p dir
    dir
end

.default_install_optionsObject

Returns the set of default options that are added to gem

By default, we add –no-user-install to un-break distributions like Arch that set –user-install by default (thus disabling the role of GEM_HOME)



101
102
103
# File 'lib/autoproj/package_managers/gem_manager.rb', line 101

def self.default_install_options
    @default_install_options ||= ["--no-user-install", "--no-format-executable"]
end

.use_cache_dirObject



76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/autoproj/package_managers/gem_manager.rb', line 76

def self.use_cache_dir
    # If there is a cache directory, make sure .gems/cache points to
    # it (there are no programmatic ways to override this)
    return unless (cache = cache_dir)

    gem_cache_dir = File.join(gem_home, "cache")
    if !File.symlink?(gem_cache_dir) || File.readlink(gem_cache_dir) != cache
        FileUtils.mkdir_p gem_home
        FileUtils.rm_rf gem_cache_dir
        Autoproj.create_symlink(cache, gem_cache_dir)
    end
end

Instance Method Details

#build_gem_cmdlines(gems) ⇒ Object



144
145
146
147
148
149
150
151
152
153
# File 'lib/autoproj/package_managers/gem_manager.rb', line 144

def build_gem_cmdlines(gems)
    with_version, without_version = gems.partition { |name, v| v }

    cmdlines = []
    cmdlines << without_version.flatten unless without_version.empty?
    with_version.each do |name, v|
        cmdlines << [name, "-v", v]
    end
    cmdlines
end

#filter_uptodate_packages(gems, options = Hash.new) ⇒ Object

Returns the set of RubyGem packages in packages that are not already installed, or that can be upgraded



200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
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
# File 'lib/autoproj/package_managers/gem_manager.rb', line 200

def filter_uptodate_packages(gems, options = Hash.new)
    options = validate_options options,
                               install_only: !Autobuild.do_update

    # Don't install gems that are already there ...
    gems = gems.dup
    gems.delete_if do |name, version|
        next(true) if installed_gems.include?(name)

        version_requirements = Gem::Requirement.new(version || ">= 0")
        installed =
            if Gem::Specification.respond_to?(:find_by_name)
                begin
                    [Gem::Specification.find_by_name(name, version_requirements)]
                rescue Gem::LoadError
                    []
                end
            else
                Gem.source_index.find_name(name, version_requirements)
            end

        if !installed.empty? && !options[:install_only]
            # Look if we can update the package ...
            dep = Gem::Dependency.new(name, version_requirements)
            available =
                if gem_fetcher.respond_to?(:find_matching)
                    non_prerelease = gem_fetcher.find_matching(dep, true, true).map(&:first)
                    if GemManager.with_prerelease
                        prerelease = gem_fetcher.find_matching(dep, false, true, true).map(&:first)
                    else
                        prerelease = Array.new
                    end
                    (non_prerelease + prerelease)
                        .map { |n, v, _| [n, v] }

                else # Post RubyGems-2.0
                    type = if GemManager.with_prerelease then :complete
                           else
                               :released
                           end

                    gem_fetcher.detect(type) do |tuple|
                        tuple.name == name && dep.match?(tuple)
                    end.map { |tuple, _| [tuple.name, tuple.version] }
                end
            installed_version = installed.map(&:version).max
            available_version = available.map { |_, v| v }.max
            unless available_version
                if version
                    raise ConfigError.new, "cannot find any gem with the name '#{name}' and version #{version}"
                else
                    raise ConfigError.new, "cannot find any gem with the name '#{name}'"
                end
            end
            needs_update = (available_version > installed_version)
            !needs_update
        else
            !installed.empty?
        end
    end
    gems
end

#gems_interaction(gems, cmdlines) ⇒ Object



271
272
273
274
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
303
304
# File 'lib/autoproj/package_managers/gem_manager.rb', line 271

def gems_interaction(gems, cmdlines)
    if OSPackageInstaller.force_osdeps
        return true
    elsif enabled?
        return true
    elsif silent?
        return false
    end

    # We're not supposed to install rubygem packages but silent is not
    # set, so display information about them anyway
    puts <<-EOMSG
      #{Autoproj.color('The build process and/or the packages require some Ruby Gems to be installed', :bold)}
      #{Autoproj.color('and you required autoproj to not do it itself', :bold)}
        You can use the --all or --ruby options to autoproj osdeps to install these
        packages anyway, and/or change to the osdeps handling mode by running an
        autoproj operation with the --reconfigure option as for instance
        autoproj build --reconfigure
      #{'  '}
        The following command line can be used to install them manually
      #{'  '}
          #{cmdlines.map { |c| c.join(' ') }.join("\n      ")}
      #{'  '}
        Autoproj expects these Gems to be installed in #{GemManager.gem_home} This can
        be overridden by setting the AUTOPROJ_GEM_HOME environment variable manually

    EOMSG
    print "    #{Autoproj.color('Press ENTER to continue ', :bold)}"

    STDOUT.flush
    STDIN.readline
    puts
    false
end

#guess_gem_programObject

Raises:

  • (ArgumentError)


125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/autoproj/package_managers/gem_manager.rb', line 125

def guess_gem_program
    return Autobuild.programs["gem"] if Autobuild.programs["gem"]

    ruby_bin = RbConfig::CONFIG["RUBY_INSTALL_NAME"]
    ruby_bindir = RbConfig::CONFIG["bindir"]

    candidates = ["gem"]
    candidates << "gem#{$1}" if ruby_bin =~ /^ruby(.+)$/

    candidates.each do |gem_name|
        if File.file?(gem_full_path = File.join(ruby_bindir, gem_name))
            Autobuild.programs["gem"] = gem_full_path
            return
        end
    end

    raise ArgumentError, "cannot find a gem program (tried #{candidates.sort.join(', ')} in #{ruby_bindir})"
end

#initialize_environmentObject

Filters all paths that come from other autoproj installations out of GEM_PATH



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/autoproj/package_managers/gem_manager.rb', line 28

def initialize_environment
    env = ws.env
    env.original_env["GEM_PATH"] =
        (env["GEM_PATH"] || "").split(File::PATH_SEPARATOR).find_all do |p|
            !Autoproj.in_autoproj_installation?(p)
        end.join(File::PATH_SEPARATOR)
    env.inherit "GEM_PATH"
    env.init_from_env "GEM_PATH"

    orig_gem_path = env.original_env["GEM_PATH"].split(File::PATH_SEPARATOR)
    env.system_env["GEM_PATH"] = Gem.default_path
    env.original_env["GEM_PATH"] = orig_gem_path.join(File::PATH_SEPARATOR)

    ws.config.each_reused_autoproj_installation do |p|
        p_gems = File.join(p, ".gems")
        if File.directory?(p_gems)
            env.push_path "GEM_PATH", p_gems
            env.push_path "PATH", File.join(p_gems, "bin")
        end
    end

    @gem_home = (ENV["AUTOPROJ_GEM_HOME"] || File.join(ws.root_dir, ".gems"))
    env.push_path "GEM_PATH", gem_home
    env.set "GEM_HOME", gem_home
    env.push_path "PATH", "#{gem_home}/bin"

    # Now, reset the directories in our own RubyGems instance
    Gem.paths = env.resolved_env

    use_cache_dir
end

#install(gems) ⇒ Object



172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/autoproj/package_managers/gem_manager.rb', line 172

def install(gems)
    guess_gem_program

    base_cmdline = [Autobuild.tool_in_path("ruby", env: ws.env), "-S", Autobuild.tool("gem"), "install", *GemManager.default_install_options]
    base_cmdline << "--no-rdoc" << "--no-ri" unless GemManager.with_doc

    base_cmdline << "--prerelease" if GemManager.with_prerelease

    cmdlines = build_gem_cmdlines(gems).map do |line|
        base_cmdline + line
    end
    if gems_interaction(gems, cmdlines)
        Autoproj.message "  installing/updating RubyGems dependencies: #{gems.map { |g| g.join(' ') }.sort.join(', ')}"

        cmdlines.each do |c|
            Autobuild::Subprocess.run "autoproj", "osdeps", *c,
                                      env: Hash["GEM_HOME" => Gem.paths.home,
                                                "GEM_PATH" => Gem.paths.path.join(":")]
        end
        gems.each do |name, v|
            installed_gems << name
        end
        true
    end
end

#parse_package_entry(entry) ⇒ Object



263
264
265
266
267
268
269
# File 'lib/autoproj/package_managers/gem_manager.rb', line 263

def parse_package_entry(entry)
    if entry =~ /^([^><=~]*)([><=~]+.*)$/
        [$1.strip, $2.strip]
    else
        [entry]
    end
end

#pristine(gems) ⇒ Object



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/autoproj/package_managers/gem_manager.rb', line 155

def pristine(gems)
    guess_gem_program
    base_cmdline = [Autobuild.tool_in_path("ruby", env: ws.env), "-S", Autobuild.tool("gem")]
    cmdlines = [
        [*base_cmdline, "clean"]
    ]
    cmdlines += build_gem_cmdlines(gems).map do |line|
        base_cmdline + ["pristine", "--extensions"] + line
    end
    if gems_interaction(gems, cmdlines)
        Autoproj.message "  restoring RubyGems: #{gems.map { |g| g.join(' ') }.sort.join(', ')}"
        cmdlines.each do |c|
            Autobuild::Subprocess.run "autoproj", "osdeps", *c
        end
    end
end