Cleaned up bsdjail extension code a bit more and made the extconf check for
the actual headers being included.
### 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|
$stderr.puts " building \#{t.name} from \#{t.source}"
sh "\#{CC} \#{CFLAGS} \#{INCLUDES} -c \#{t.source}"
end
desc "Build this extension"
file EXT => OBJ do
$stderr.puts " linking \#{OBJ.join(', ')} into \#{EXT}"
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