misc/monkeypatches.rb
author Mahlon E. Smith <mahlon@martini.nu>
Tue, 14 Oct 2008 16:11:19 +0000
branchmahlon-misc
changeset 9 4c51ebe6e9b6
parent 1 09d0d209d06d
child 10 b1426511fb64
permissions -rw-r--r--
* Add a mkrf monkeypatch so BSD build flags are generated correctly. * Fix typos!

### Monkeypatch to work around the broken Rakefile generated by Mkrf <= 0.2.3

# This fixes:
# * Some weird unnecessary string interpolation
# * The :install task doesn't work unless you have RUBYARCHDIR in your environment, instead of
#   falling back to CONFIG['sitearchdir'].
# * The :install task created the target install directory every time instead of just
#   declaring a dependency on a directory task

require 'mkrf/generator'

RAKEFILE_TEMPLATE = %q{
	# Generated by monkeypatched mkrf
	require 'rake/clean'
	require 'rbconfig'

	include Config

	SRC = FileList[#{sources.join(',')}]
	OBJ = SRC.ext('#{objext}')
	CC = '#{@cc}'

	ADDITIONAL_OBJECTS = '#{objects}'

	LDSHARED = "#{@available.ldshared_string} #{ldshared}"

	LIBPATH =  "#{library_path(CONFIG['libdir'])} #{@available.library_paths_compile_string}"

	INCLUDES = "#{@available.includes_compile_string}"

	LIBS = "#{@available.library_compile_string}"

	CFLAGS = "#{cflags} #{defines_compile_string}"

	RUBYARCHDIR = ENV["RUBYARCHDIR"] || CONFIG['sitearchdir']
	LIBRUBYARG_SHARED = "#{CONFIG['LIBRUBYARG_SHARED']}"
	EXT = '#{@extension_name}'

	CLEAN.include( EXT, '*.#{objext}' )
	CLOBBER.include( 'mkrf.log' )

	task :default => EXT

	rule '.#{objext}' => '.#{@source_extension}' do |t|
	  sh "\#{CC} \#{CFLAGS} \#{INCLUDES} -c \#{t.source}"
	end

	desc "Build this extension"
	file EXT => OBJ do
	  sh "\#{LDSHARED} \#{LIBPATH} #{@available.ld_outfile(@extension_name)} \#{OBJ} \#{ADDITIONAL_OBJECTS} \#{LIBS} \#{LIBRUBYARG_SHARED}"
	end


	directory RUBYARCHDIR

	desc "Install this extension"
	task :install => [EXT, RUBYARCHDIR] do
	  install EXT, RUBYARCHDIR, :verbose => true
	end

	#{additional_code}
}.gsub( /^\t/m, '' )



module Mkrf

	class Availability

		# Create a new Availability instance.
		#
		# Valid keys for the options hash include:
		# * <tt>:loaded_libs</tt> -- libraries to load by default
		# * <tt>:library_paths</tt> -- libraries paths to include by default
		# * <tt>:headers</tt> -- headers to load by default
		# * <tt>:compiler</tt> -- which compiler to use when determining availability
		# * <tt>:includes</tt> -- directories that should be searched for include files
		def initialize(options = {})      
			@loaded_libs = options[:loaded_libs] || []
			@loaded_libs.flatten!

			@library_paths = [(options[:library_paths] || [])].flatten
			# Not sure what COMMON_HEADERS looks like when populated
			@headers = options[:headers] || [] # Config::CONFIG["COMMON_HEADERS"]
			@compiler = options[:compiler] || Config::CONFIG["CC"]
			@includes = [(options[:includes] || DEFAULT_INCLUDES)].flatten
			@logger = Logger.new('mkrf.log')
			@defines = []
		end


		def can_link?( function_body )
			silence_command_line do
				create_source(function_body)
				cmd = link_command()
				@logger.debug "Running link command: #{cmd}"
				system( cmd )
			end
		ensure
			FileUtils.rm_f TEMP_SOURCE_FILE
			FileUtils.rm_f TEMP_EXECUTABLE
		end


		# Add the LIBRUBYARG_SHARED setting to the library paths for non-windows boxen. This is
		# necessary if Ruby is installed in a directory that isn't in the default library
		# search path (e.g., on FreeBSD where Ruby is /usr/local/bin/ruby).
		def library_paths_compile_string
			if RUBY_PLATFORM =~ /mswin/
				@library_paths.collect {|l| "/libpath:#{l}"}.join(' ')
			else
				Config::CONFIG['LIBRUBYARG_SHARED'] + @library_paths.collect {|l| "-L#{l}"}.join(' ')
			end
		end


		# Separate includes with a newline instead of a literal '\n'
		def header_include_string
			@headers.collect {|header| "#include <#{header}>"}.join( "\n" )
		end


		# Log the created source to the logfile
		def create_source( src )
			@logger.debug "Creating source file:\n#{src}"
			File.open( TEMP_SOURCE_FILE, "w+" ) do |f|
				f.write( src )
			end
		end

		# Prepend the LIBS string directly to the @loaded_libs, as not all arguments in
		# it 
		def library_compile_string
			added_libs = nil
			if RUBY_PLATFORM =~ /mswin/
				added_libs = @loaded_libs.join(' ')
			else
				added_libs = @loaded_libs.collect {|l| "-l#{l}"}.join(' ')
			end
			
			return Config::CONFIG["LIBS"] + ' ' + added_libs
		end


		# Redirect to the mkrf log instead of /dev/null
		def silence_stream( stream )
			old_stream = stream.dup
			stream.reopen( @logger.instance_variable_get(:@logdev).dev )
			stream.sync = true
			yield
		ensure
			stream.reopen( old_stream )
		end
	end

	class Generator

		def rakefile_contents # :nodoc:
			objext = CONFIG['OBJEXT']
			return interpolate( RAKEFILE_TEMPLATE, binding() )
		end


		### Interpolate any '#{...}' placeholders in the string within the given
		### +scope+ (a Binding object).
	    def interpolate( string, scope )
	        unless scope.is_a?( Binding )
	            raise TypeError, "Argument to interpolate must be a Binding, not "\
	                "a #{scope.class.name}"
	        end

			# $stderr.puts ">>> Interpolating '#{self}'..."

	        copy = string.gsub( /"/, %q:\": )
	        eval( '"' + copy + '"', scope )
		rescue Exception => err
			nicetrace = err.backtrace.find_all {|frame|
				/in `(interpolate|eval)'/i !~ frame
			}
			Kernel.raise( err, err.message, nicetrace )
	    end

	end # class Generator

end # module Mkrf