1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 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 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 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 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 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 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
|
require 'rbconfig'
require 'rake/tasklib'
module Mkrf
# +Generator+ is concerned with taking configuration for an extension
# and writing a +Rakefile+ to the local filesystem which will later be
# used to build the extension.
#
# You will typically only create one +Generator+ per <tt>extconf.rb</tt>
# file, which in turn will generate a Rakefile for building one extension
# module.
#
# = Usage
#
# In the most basic usage, +Generator+ simply takes the name of the library
# to compile:
#
# require 'mkrf'
# Mkrf::Generator.new('libtrivial')
#
# Configuration of the build can be passed to the +Generator+ constructor
# as a block:
#
# Mkrf::Generator.new('libxml') do |g|
# g.include_library('socket','socket')
# g.include_header('libxml/xmlversion.h',
# '/opt/include/libxml2',
# '/usr/local/include/libxml2',
# '/usr/include/libxml2')
# end
#
# It is also possible to specify the library paths in
# include_library
# Mkrf::Generator.new('libxml') do |g|
# g.include_library('socket','socket', '/usr/local/lib/libxml')
# end
#
class Generator
include Rake
CONFIG = RbConfig::CONFIG
# Any extra code, given as a string, to be appended to the Rakefile.
attr_accessor :additional_code
# You may append to these attributes directly in your Generator.new block,
# for example: <tt>g.objects << ' ../common/foo.o ../common/bar.so -lmystuff'</tt> or
# <tt>g.cflags << ' -ansi -Wall'</tt>
#
# Note the extra space at the beginning of those strings.
attr_accessor :cflags
# +objects+ is for adding _additional_ object files to the link-edit command -- outside
# of the ones that correspond to the source files.
attr_accessor :objects
# Any additional options you'd like appended to your system-specific linker command
# (which is used to build the shared library).
attr_accessor :ldshared
# Create a +Generator+ object which writes a Rakefile to the current directory of the local
# filesystem.
#
# Params:
# * +extension_name+ -- the name of the extension
# * +source_patterns+ -- an array of patterns describing source files to be compiled. ["*.c"] is the default.
def initialize(extension_name, source_patterns = ["*.c"], availability_options = {})
@sources = source_patterns
@extension_name = extension_name + ".#{CONFIG['DLEXT']}"
@available = Mkrf::Availability.new(availability_options)
@defines = []
if @sources[0] =~ /cpp/
@cc = 'g++' # should be in CONFIG['C++'] but is not.
@source_extension = 'cpp'
else
@cc = CONFIG['CC']
@source_extension = 'c'
end
@objects = ''
@ldshared = ''
@cflags = "#{CONFIG['CCDLFLAGS']} #{CONFIG['CFLAGS']} #{CONFIG['ARCH_FLAG']}"
yield self if block_given?
write_rakefile
end
# An array of the source patterns as single quoted strings
def sources
@sources.collect {|s| "'#{s}'"}
end
# Add a define to the compile string. Example:
#
# Mkrf::Generator.new('my_library') do |g|
# g.add_define('HAVE_PTHREADS')
# end
#
# Params:
# * +defn+ -- string to add to compile time defines
def add_define(defn)
@available.defines << defn
end
# Include a library in the compile. Returns +false+ if the
# library is not available. Returns non-false otherwise.
# Parameters are the same as Mkrf::Availability#include_library
def include_library(*args)
@available.include_library(*args)
end
# Include a header in the compile. Returns +false+ if the header is not
# available, returns non-false otherwise. As a side effect, a compile
# time define occurs as +HAVE_+ appended with the name of the header in
# upper and scored case.
# Parameters are the same as Mkrf::Availability#include_header
def include_header(*args)
@available.include_header(*args)
end
# Returns +true+ if the function is able to be called based on libraries and
# headers currently loaded. Returns +false+ otherwise.
#
# Params:
# * <tt>function</tt> -- the function to check for
def has_function?(function)
@available.has_function? function
end
# Returns mkrf's logger instance. You can use this to set logging levels.
#
# Mkrf::Generator.new('libsomethin') do |g|
# g.logger.level = Logger::WARN
# end
#
def logger
@available.logger
end
# Logs a fatal error and exits with a non-zero code (defaults to 1)
def abort!(str, code = 1)
logger.fatal str
exit code
end
def write_rakefile(filename = "Rakefile") # :nodoc:
File.open(filename, "w+") do |f|
f.puts rakefile_contents
end
logger.info "Rakefile written"
end
def defines_compile_string # :nodoc:
@available.defines.collect {|define| "-D#{define}"}.join(' ')
end
def library_path(path) # :nodoc:
if RUBY_PLATFORM =~ /mswin/
"-libpath:#{path}"
else
"-L#{path}"
end
end
def rakefile_contents # :nodoc:
objext = CONFIG['OBJEXT']
<<-END_RAKEFILE
# Generated by mkrf
require 'rake/clean'
CLEAN.include('*.#{objext}')
CLOBBER.include('#{@extension_name}', 'mkrf.log')
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"]}"
LIBRUBYARG_SHARED = "#{CONFIG['LIBRUBYARG_SHARED']}"
task :default => ['#{@extension_name}']
rule '.#{objext}' => '.#{@source_extension}' do |t|
sh "\#{CC} \#{CFLAGS} \#{INCLUDES} -c \#{t.source}"
end
desc "Build this extension"
file '#{@extension_name}' => OBJ do
sh "\#{LDSHARED} \#{LIBPATH} #{@available.ld_outfile(@extension_name)} \#{OBJ} \#{ADDITIONAL_OBJECTS} \#{LIBS} \#{LIBRUBYARG_SHARED}"
end
desc "Install this extension"
task :install => '#{@extension_name}' do
makedirs "\#{RUBYARCHDIR}"
install "#{@extension_name}", "\#{RUBYARCHDIR}"
end
#{additional_code}
END_RAKEFILE
end
end
end
|