From 310d77d4b05d1aa166616ca323ebcc7f67bc7378 Mon Sep 17 00:00:00 2001 From: drbrain Date: Tue, 30 Jul 2013 22:10:21 +0000 Subject: * lib/rubygems: Import RubyGems from master as of commit 523551c * test/rubygems: ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@42257 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- lib/rubygems/ext/builder.rb | 118 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) (limited to 'lib/rubygems/ext') diff --git a/lib/rubygems/ext/builder.rb b/lib/rubygems/ext/builder.rb index ee390578ae..8c05723573 100644 --- a/lib/rubygems/ext/builder.rb +++ b/lib/rubygems/ext/builder.rb @@ -4,8 +4,23 @@ # See LICENSE.txt for permissions. #++ +require 'rubygems/user_interaction' +require 'thread' + class Gem::Ext::Builder + include Gem::UserInteraction + + ## + # 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. + + CHDIR_MUTEX = Mutex.new # :nodoc: + + attr_accessor :build_args # :nodoc: + def self.class_name name =~ /Ext::(.*)Builder/ $1.downcase @@ -63,5 +78,108 @@ class Gem::Ext::Builder end end + ## + # Creates a new extension builder for +spec+ using the given +build_args+. + # The gem for +spec+ is unpacked in +gem_dir+. + + def initialize spec, build_args + @spec = spec + @build_args = build_args + @gem_dir = spec.gem_dir + + @ran_rake = nil + end + + ## + # Chooses the extension builder class for +extension+ + + 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) + + message = "No builder for extension '#{extension}'" + build_error extension_dir, message + end + end + + ## + # Logs the build +output+ in +build_dir+, then raises ExtensionBuildError. + + def build_error build_dir, output, backtrace = nil # :nodoc: + gem_make_out = File.join build_dir, 'gem_make.out' + + open gem_make_out, 'wb' do |io| io.puts output end + + message = <<-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::Installer::ExtensionBuildError, message, backtrace + end + + def build_extension extension, dest_path # :nodoc: + results = [] + + extension ||= '' # I wish I knew why this line existed + extension_dir = File.join @gem_dir, File.dirname(extension) + + builder = builder_for extension + + begin + FileUtils.mkdir_p dest_path + + CHDIR_MUTEX.synchronize do + Dir.chdir extension_dir do + results = builder.build(extension, @gem_dir, dest_path, + results, @build_args) + + say results.join("\n") if Gem.configuration.really_verbose + end + end + rescue + build_error extension_dir, results.join("\n"), $@ + end + end + + ## + # Builds extensions. Valid types of extensions are extconf.rb files, + # configure scripts and rakefiles or mkrf_conf files. + + 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 = File.join @gem_dir, @spec.require_paths.first + + @ran_rake = false # only run rake once + + @spec.extensions.each do |extension| + break if @ran_rake + + build_extension extension, dest_path + end + end + end -- cgit v1.2.3