Class: Gem::Ext::Builder
Relationships & Source Files | |
Extension / Inclusion / Inheritance Descendants | |
Subclasses:
|
|
Super Chains via Extension / Inclusion / Inheritance | |
Instance Chain:
|
|
Inherits: | Object |
Defined in: | lib/rubygems/ext/builder.rb |
Constant Summary
-
CHDIR_MUTEX =
Internal use only
The builder shells-out to run various commands after changing the directory. This means multiple installations cannot be allowed to build extensions in parallel as they may change each other’s directories leading to broken extensions or failed installations.
Mutex.new
Class Method Summary
- .class_name
- .make(dest_path, results)
-
.new(spec, build_args = spec.build_args) ⇒ Builder
constructor
Creates a new extension builder for
spec
. - .redirector
- .run(command, results, command_name = nil)
Instance Attribute Summary
- #build_args rw Internal use only
::Gem::DefaultUserInteraction
- Included
Instance Method Summary
-
#build_extensions
Builds extensions.
-
#build_error(build_dir, output, backtrace = nil)
Internal use only
Logs the build
output
inbuild_dir
, then raisesBuildError
. - #build_extension(extension, dest_path) Internal use only
-
#builder_for(extension)
Internal use only
Chooses the extension builder class for
extension
-
#write_gem_make_out(output)
Internal use only
Writes
output
to gem_make.out in the extension install directory.
::Gem::UserInteraction
- Included
#alert | Displays an alert |
#alert_error | Displays an error |
#alert_warning | Displays a warning |
#ask | Asks a |
#ask_for_password | Asks for a password with a |
#ask_yes_no | Asks a yes or no |
#choose_from_list | Asks the user to answer |
#say | Displays the given |
#terminate_interaction | Terminates the RubyGems process with the given |
#verbose | Calls |
::Gem::DefaultUserInteraction
- Included
::Gem::Text
- Included
#clean_text | Remove any non-printable characters and make the text suitable for printing. |
#format_text | Wraps |
#levenshtein_distance | This code is based directly on the |
#truncate_text, #min3 |
Constructor Details
.new(spec, build_args = spec.build_args) ⇒ Builder
Creates a new extension builder for spec
. If the spec
does not yet have build arguments, saved, set #build_args which is an ARGV-style array.
# File 'lib/rubygems/ext/builder.rb', line 108
def initialize(spec, build_args = spec.build_args) @spec = spec @build_args = build_args @gem_dir = spec.full_gem_path @ran_rake = nil end
Class Method Details
.class_name
[ GitHub ]# File 'lib/rubygems/ext/builder.rb', line 24
def self.class_name name =~ /Ext::(.*)Builder/ $1.downcase end
.make(dest_path, results)
[ GitHub ]# File 'lib/rubygems/ext/builder.rb', line 29
def self.make(dest_path, results) unless File.exist? 'Makefile' raise Gem::InstallError, 'Makefile not found' end # try to find make program from Ruby configure arguments first RbConfig::CONFIG['configure_args'] =~ /with-make-prog\=(\w+)/ make_program = ENV['MAKE'] || ENV['make'] || $1 unless make_program make_program = (/mswin/ =~ RUBY_PLATFORM) ? 'nmake' : 'make' end destdir = '"DESTDIR=%s"' % ENV['DESTDIR'] ['clean', '', 'install'].each do |target| # Pass DESTDIR via command line to override what's in MAKEFLAGS cmd = [ make_program, destdir, target ].join(' ').rstrip begin run(cmd, results, "make #{target}".rstrip) rescue Gem::InstallError raise unless target == 'clean' # ignore clean failure end end end
.redirector
[ GitHub ]# File 'lib/rubygems/ext/builder.rb', line 58
def self.redirector warn "#{caller[0]}: Use IO.popen(..., err: [:child, :out])" '2>&1' end
.run(command, results, command_name = nil)
[ GitHub ]# File 'lib/rubygems/ext/builder.rb', line 63
def self.run(command, results, command_name = nil) verbose = Gem.configuration.really_verbose begin rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], nil if verbose puts("current directory: #{Dir.pwd}") p(command) end results << "current directory: #{Dir.pwd}" results << (command.respond_to?(:shelljoin) ? command.shelljoin : command) redirections = verbose ? {} : {err: [:child, :out]} IO.popen(command, "r", redirections) do |io| if verbose IO.copy_stream(io, $stdout) else results << io.read end end rescue => error raise Gem::InstallError, "#{command_name || class_name} failed#{error.}" ensure ENV['RUBYGEMS_GEMDEPS'] = rubygems_gemdeps end unless $?.success? results << "Building has failed. See above output for more information on the failure." if verbose exit_reason = if $?.exited? ", exit code #{$?.exitstatus}" elsif $?.signaled? ", uncaught signal #{$?.termsig}" end raise Gem::InstallError, "#{command_name || class_name} failed#{exit_reason}" end end
Instance Attribute Details
#build_args (rw)
# File 'lib/rubygems/ext/builder.rb', line 22
attr_accessor :build_args # :nodoc:
Instance Method Details
#build_error(build_dir, output, backtrace = nil)
Logs the build output
in build_dir
, then raises BuildError
.
# File 'lib/rubygems/ext/builder.rb', line 141
def build_error(build_dir, output, backtrace = nil) # :nodoc: gem_make_out = write_gem_make_out output = <<-EOF ERROR: Failed to build gem native extension. #{output} Gem files will remain installed in #{@gem_dir} for inspection. Results logged to #{gem_make_out} EOF raise Gem::Ext::BuildError, , backtrace end
#build_extension(extension, dest_path)
# File 'lib/rubygems/ext/builder.rb', line 156
def build_extension(extension, dest_path) # :nodoc: results = [] # FIXME: Determine if this line is necessary and, if so, why. # Notes: # 1. As far as I can tell, this method is only called by build_extensions. # 2. The existence of this line implies extension is, or previously was, # sometimes false or nil. # 3. #1 and #2 combined suggests, but does not confirm, that # @specs.extensions sometimes contained false or nil values. # 4. Nothing seems to explicitly handle extension being empty, # which makes me wonder both what it should do and what it does. # # - @duckinator extension ||= '' # I wish I knew why this line existed extension_dir = File. File.join(@gem_dir, File.dirname(extension)) lib_dir = File.join @spec.full_gem_path, @spec.raw_require_paths.first builder = builder_for extension begin FileUtils.mkdir_p dest_path CHDIR_MUTEX.synchronize do pwd = Dir.getwd Dir.chdir extension_dir begin results = builder.build(extension, dest_path, results, @build_args, lib_dir) verbose { results.join("\n") } ensure begin Dir.chdir pwd rescue SystemCallError Dir.chdir dest_path end end end write_gem_make_out results.join "\n" rescue => e results << e. build_error extension_dir, results.join("\n"), $@ end end
#build_extensions
Builds extensions. Valid types of extensions are extconf.rb files, configure scripts and rakefiles or mkrf_conf files.
# File 'lib/rubygems/ext/builder.rb', line 209
def build_extensions return if @spec.extensions.empty? if @build_args.empty? say "Building native extensions. This could take a while..." else say "Building native extensions with: '#{@build_args.join ' '}'" say "This could take a while..." end dest_path = @spec.extension_dir FileUtils.rm_f @spec.gem_build_complete_path # FIXME: action at a distance: @ran_rake modified deep in build_extension(). - @duckinator @ran_rake = false # only run rake once @spec.extensions.each do |extension| break if @ran_rake build_extension extension, dest_path end FileUtils.touch @spec.gem_build_complete_path end
#builder_for(extension)
Chooses the extension builder class for extension
# File 'lib/rubygems/ext/builder.rb', line 119
def builder_for(extension) # :nodoc: case extension when /extconf/ then Gem::Ext::ExtConfBuilder when /configure/ then Gem::Ext::ConfigureBuilder when /rakefile/i, /mkrf_conf/i then @ran_rake = true Gem::Ext::RakeBuilder when /CMakeLists.txt/ then Gem::Ext::CmakeBuilder else extension_dir = File.join @gem_dir, File.dirname(extension) = "No builder for extension '#{extension}'" build_error extension_dir, end end
#write_gem_make_out(output)
Writes output
to gem_make.out in the extension install directory.
# File 'lib/rubygems/ext/builder.rb', line 238
def write_gem_make_out(output) # :nodoc: destination = File.join @spec.extension_dir, 'gem_make.out' FileUtils.mkdir_p @spec.extension_dir File.open destination, 'wb' do |io| io.puts output end destination end