Class: Gel::Package::Installer::GemInstaller

Inherits:
Object
  • Object
show all
Defined in:
lib/gel/package/installer.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(spec, store) ⇒ GemInstaller



27
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
# File 'lib/gel/package/installer.rb', line 27

def initialize(spec, store)
  @spec = spec
  @root_store = store

  if store.is_a?(Gel::MultiStore)
    store = store[spec.architecture, spec.extensions.any?]
  end
  @store = store

  raise "gem already installed" if store.gem?(spec.name, spec.version)

  @config = Gel::Environment.config

  @root = store.gem_root(spec.name, spec.version)
  FileUtils.rm_rf(@root) if @root && Dir.exist?(@root)

  if spec.extensions.any?
    @build_path = store.extension_path(spec.name, spec.version)
    FileUtils.rm_rf(@build_path) if @build_path && Dir.exist?(@build_path)
  else
    @build_path = nil
  end

  @files = {}
  @installed_files = []
  spec.require_paths.each { |reqp| @files[reqp] = [] }
end

Instance Attribute Details

#build_pathObject (readonly)

Returns the value of attribute build_path.



25
26
27
# File 'lib/gel/package/installer.rb', line 25

def build_path
  @build_path
end

#rootObject (readonly)

Returns the value of attribute root.



25
26
27
# File 'lib/gel/package/installer.rb', line 25

def root
  @root
end

#specObject (readonly)

Returns the value of attribute spec.



25
26
27
# File 'lib/gel/package/installer.rb', line 25

def spec
  @spec
end

#storeObject (readonly)

Returns the value of attribute store.



25
26
27
# File 'lib/gel/package/installer.rb', line 25

def store
  @store
end

Instance Method Details

#abort!Object



55
56
57
58
59
60
# File 'lib/gel/package/installer.rb', line 55

def abort!
  $stderr.puts "FileUtils.rm_rf(#{root.inspect})" if root
  $stderr.puts "FileUtils.rm_rf(#{build_path.inspect})" if build_path
  #FileUtils.rm_rf(root) if root
  #FileUtils.rm_rf(build_path) if build_path
end

#build_command(work_dir, log, *command, rake: false, **options) ⇒ Object



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/gel/package/installer.rb', line 135

def build_command(work_dir, log, *command, rake: false, **options)
  env = build_environment(rake: rake)
  env.merge!(command.shift) if command.first.is_a?(Hash)

  pid = spawn(
    env,
    *command,
    chdir: work_dir,
    in: IO::NULL,
    [:out, :err] => log,
    **options,
  )

  _, status = Process.waitpid2(pid)
  status
end

#build_environment(rake: false) ⇒ Object



124
125
126
127
128
129
130
131
132
133
# File 'lib/gel/package/installer.rb', line 124

def build_environment(rake: false)
  gemfile, lockfile = gemfile_and_lockfile(rake: rake)

  {
    "RUBYLIB" => Gel::Environment.modified_rubylib,
    "GEL_STORE" => File.expand_path(@root_store.root),
    "GEL_GEMFILE" => gemfile,
    "GEL_LOCKFILE" => lockfile,
  }
end

#compileObject



202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/gel/package/installer.rb', line 202

def compile
  if spec.extensions.any?
    spec.extensions.each do |ext|
      case File.basename(ext)
      when /extconf/i
        compile_extconf ext, build_path
      when /mkrf_conf/i, /rakefile/i
        compile_rakefile ext, build_path
      else
        raise "Don't know how to build #{ext.inspect} yet"
      end
    end
  end
end

#compile_extconf(ext, install_dir) ⇒ Object



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/gel/package/installer.rb', line 152

def compile_extconf(ext, install_dir)
  with_build_environment(ext, install_dir) do |work_dir, short_install_dir, local_config_path, log|
    status = build_command(
      work_dir, log,
      { "MAKEFLAGS" => "-j3" },
      RbConfig.ruby,
      "-r", local_config_path,
      File.basename(ext),
      *Shellwords.shellsplit(@config[:build, @spec.name] || ""),
    )
    raise "extconf exited with #{status.exitstatus}" unless status.success?

    _status = build_command(work_dir, log, "make", "clean", "DESTDIR=")
    # Ignore exit status

    status = build_command(work_dir, log, "make", "-j3", "DESTDIR=")
    raise "make exited with #{status.exitstatus}" unless status.success?

    status = build_command(work_dir, log, "make", "install", "DESTDIR=")
    raise "make install exited with #{status.exitstatus}" unless status.success?
  end
end

#compile_rakefile(ext, install_dir) ⇒ Object



175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/gel/package/installer.rb', line 175

def compile_rakefile(ext, install_dir)
  with_build_environment(ext, install_dir) do |work_dir, short_install_dir, local_config_path, log|
    if File.basename(ext) =~ /mkrf_conf/i
      status = build_command(
        work_dir, log,
        RbConfig.ruby,
        "-r", local_config_path,
        File.basename(ext),
        rake: true,
      )
      raise "mkrf_conf exited with #{status.exitstatus}" unless status.success?
    end

    status = build_command(
      work_dir, log,
      { "RUBYARCHDIR" => short_install_dir, "RUBYLIBDIR" => short_install_dir },
      RbConfig.ruby,
      "-r", File.expand_path("../command", __dir__),
      "-e", "Gel::Command.run(ARGV)",
      "--",
      "exec",
      "rake",
      rake: true,
    )
  end
end

#compile_ready?Boolean



66
67
68
# File 'lib/gel/package/installer.rb', line 66

def compile_ready?
  true
end

#file(filename, io, source_mode) ⇒ Object



242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
# File 'lib/gel/package/installer.rb', line 242

def file(filename, io, source_mode)
  target = File.expand_path(filename, root)
  raise "invalid filename #{target.inspect} outside #{(root + "/").inspect}" unless target.start_with?("#{root}/")
  return if @installed_files.include?(target)
  @installed_files << target
  spec.require_paths.each do |reqp|
    prefix = "#{root}/#{reqp}/"
    if target.start_with?(prefix)
      @files[reqp] << target[prefix.size..-1]
    end
  end
  raise "won't overwrite #{target}" if File.exist?(target)
  FileUtils.mkdir_p(File.dirname(target))
  mode = 0444
  mode |= source_mode & 0200
  mode |= 0111 if source_mode & 0111 != 0
  if exe = spec.executables.find { |e| filename == "#{spec.bindir}/#{e}" }
    mode |= 0111
    @root_store.stub_set.add(File.basename(@store.root), [exe])
  end
  File.open(target, "wb", mode) do |f|
    f.write io.read
  end
end

#gemfile_and_lockfile(rake: false) ⇒ Object



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
# File 'lib/gel/package/installer.rb', line 94

def gemfile_and_lockfile(rake: false)
  @gemfile ||=
    begin
      gemfile = Tempfile.new(["#{spec.name}.gemfile", ".rb"])
      gemfile.puts "source :local"
      spec.runtime_dependencies.each do |(name, operator_pairs)|
        arguments = [name, *operator_pairs.map { |op, ver| "#{op} #{ver}" }]
        gemfile.puts "gem #{arguments.map(&:inspect).join(", ")}"
      end
      if rake
        gemfile.puts "gem 'rake'" unless spec.runtime_dependencies.any? { |name, *| name == "rake" }
      end
      gemfile.close

      gemfile
    end

  @lockfile ||=
    begin
      lockfile = Tempfile.new(["#{spec.name}.lockfile", ".lock"])
      lockfile.close

      Gel::Environment.lock(store: @root_store, output: nil, gemfile: Gel::GemfileParser.parse(File.read(gemfile.path), gemfile.path, 1), lockfile: lockfile.path)

      lockfile
    end

  [@gemfile.path, @lockfile.path]
end

#installObject



217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# File 'lib/gel/package/installer.rb', line 217

def install
  loadable_file_types = ["rb", RbConfig::CONFIG["DLEXT"], RbConfig::CONFIG["DLEXT2"]].compact.reject(&:empty?)
  loadable_file_types_re = /\.#{Regexp.union loadable_file_types}\z/
  loadable_file_types_pattern = "*.{#{loadable_file_types.join(",")}}"

  store.add_gem(spec.name, spec.version, spec.bindir, spec.executables, spec.require_paths, spec.runtime_dependencies, spec.extensions.any?) do
    is_first = true
    spec.require_paths.each do |reqp|
      location = is_first ? spec.version : [spec.version, reqp]
      store.add_lib(spec.name, location, @files[reqp].map { |s| s.sub(loadable_file_types_re, "") })
      is_first = false
    end

    if build_path
      files = Dir["#{build_path}/**/#{loadable_file_types_pattern}"].map do |file|
        file[build_path.size + 1..-1]
      end.map do |file|
        file.sub(loadable_file_types_re, "")
      end

      store.add_lib(spec.name, [spec.version, Gel::StoreGem::EXTENSION_SUBDIR_TOKEN], files)
    end
  end
end

#needs_compile?Boolean



62
63
64
# File 'lib/gel/package/installer.rb', line 62

def needs_compile?
  !!@build_path
end

#with_build_environment(ext, install_dir) ⇒ Object



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/gel/package/installer.rb', line 70

def with_build_environment(ext, install_dir)
  work_dir = File.expand_path(File.dirname(ext), root)

  FileUtils.mkdir_p(install_dir)
  short_install_dir = Pathname.new(install_dir).relative_path_from(Pathname.new(work_dir)).to_s

  local_config = Tempfile.new(["config", ".rb"])
  local_config.write("    require \"rbconfig\"\n\n    RbConfig::MAKEFILE_CONFIG[\"sitearchdir\"] =\n      RbConfig::MAKEFILE_CONFIG[\"sitelibdir\"] =\n      RbConfig::CONFIG[\"sitearchdir\"] =\n      RbConfig::CONFIG[\"sitelibdir\"] = \#{short_install_dir.dump}.freeze\n  RUBY\n  local_config.close\n\n  File.open(\"\#{install_dir}/build.log\", \"w\") do |log|\n    yield work_dir, short_install_dir, local_config.path, log\n  end\nensure\n  local_config.unlink if local_config\nend\n")