# module to create Makefile for extension modules
# invoke like: ruby -r mkmf extconf.rb
CONFIG = Config::MAKEFILE_CONFIG
ORIG_LIBPATH = ENV['LIB']
if /mswin|bccwin|mingw|msdosdjgpp|human|os2/ !~ CONFIG['build_os']
SRC_EXT = %w[c m].concat(CXX_EXT)
$static = $config_h = nil
$default_static = $static
unless defined? $configure_args
args = CONFIG["configure_args"]
args << " " << ENV["CONFIGURE_ARGS"]
for arg in Shellwords::shellwords(args)
arg, val = arg.split('=', 2)
if arg.sub!(/^(?!--)/, '--')
next if /^--(?:top|topsrc|src|cur)dir$/ =~ arg
$configure_args[arg] = val || true
arg, val = arg.split('=', 2)
if arg.sub!(/^(?!--)/, '--')
$configure_args[arg] = val || true
$libdir = CONFIG["libdir"]
$rubylibdir = CONFIG["rubylibdir"]
$archdir = CONFIG["archdir"]
$sitedir = CONFIG["sitedir"]
$sitelibdir = CONFIG["sitelibdir"]
$sitearchdir = CONFIG["sitearchdir"]
$vendordir = CONFIG["vendordir"]
$vendorlibdir = CONFIG["vendorlibdir"]
$vendorarchdir = CONFIG["vendorarchdir"]
$mswin = /mswin/ =~ RUBY_PLATFORM
$bccwin = /bccwin/ =~ RUBY_PLATFORM
$mingw = /mingw/ =~ RUBY_PLATFORM
$cygwin = /cygwin/ =~ RUBY_PLATFORM
$human = /human/ =~ RUBY_PLATFORM
$netbsd = /netbsd/ =~ RUBY_PLATFORM
$os2 = /os2/ =~ RUBY_PLATFORM
$beos = /beos/ =~ RUBY_PLATFORM
$solaris = /solaris/ =~ RUBY_PLATFORM
$dest_prefix_pattern = (File::PATH_SEPARATOR == ';' ? /\A([[:alpha:]]:)?/ : /\A/)
def config_string(key, config = CONFIG)
s = config[key] and !s.empty? and block_given? ? yield(s) : s
Regexp.new('\$(?:\('+dir+'\)|\{'+dir+'\})(?:\$(?:\(target_prefix\)|\{target_prefix\}))?')
[dir_re('commondir'), "$(RUBYCOMMONDIR)"],
[dir_re('sitedir'), "$(RUBYCOMMONDIR)"],
[dir_re('vendordir'), "$(RUBYCOMMONDIR)"],
[dir_re('rubylibdir'), "$(RUBYLIBDIR)"],
[dir_re('archdir'), "$(RUBYARCHDIR)"],
[dir_re('sitelibdir'), "$(RUBYLIBDIR)"],
[dir_re('vendorlibdir'), "$(RUBYLIBDIR)"],
[dir_re('sitearchdir'), "$(RUBYARCHDIR)"],
[dir_re('bindir'), "$(BINDIR)"],
[dir_re('vendorarchdir'), "$(RUBYARCHDIR)"],
def install_dirs(target_prefix = nil)
['BINDIR', '$(extout)/bin'],
['RUBYCOMMONDIR', '$(extout)/common'],
['RUBYLIBDIR', '$(RUBYCOMMONDIR)$(target_prefix)'],
['RUBYARCHDIR', '$(extout)/$(arch)$(target_prefix)'],
['extout_prefix', "#$extout_prefix"],
['RUBYCOMMONDIR', '$(rubylibdir)'],
['RUBYLIBDIR', '$(rubylibdir)$(target_prefix)'],
['RUBYARCHDIR', '$(archdir)$(target_prefix)'],
elsif $configure_args.has_key?('--vendor')
['RUBYCOMMONDIR', '$(vendordir)$(target_prefix)'],
['RUBYLIBDIR', '$(vendorlibdir)$(target_prefix)'],
['RUBYARCHDIR', '$(vendorarchdir)$(target_prefix)'],
['RUBYCOMMONDIR', '$(sitedir)$(target_prefix)'],
['RUBYLIBDIR', '$(sitelibdir)$(target_prefix)'],
['RUBYARCHDIR', '$(sitearchdir)$(target_prefix)'],
dirs << ['target_prefix', (target_prefix ? "/#{target_prefix}" : "")]
def map_dir(dir, map = nil)
map.inject(dir) {|dir, (orig, new)| dir.gsub(orig, new)}
topdir = File.dirname(libdir = File.dirname(__FILE__))
extdir = File.expand_path("ext", topdir)
$extmk = File.expand_path($0)[0, extdir.size+1] == extdir+"/"
if not $extmk and File.exist?(($hdrdir = Config::CONFIG["archdir"]) + "/ruby.h")
elsif File.exist?(($hdrdir = ($top_srcdir ||= topdir)) + "/ruby.h") and
File.exist?(($topdir ||= Config::CONFIG["topdir"]) + "/config.h")
abort "mkmf.rb can't find header files for ruby at #{$hdrdir}/ruby.h"
OUTFLAG = CONFIG['OUTFLAG']
CPPOUTFILE = CONFIG['CPPOUTFILE']
CONFTEST_C = "conftest.c"
# Wraps a string in escaped quotes if it contains whitespace.
/\s/ =~ self ? "\"#{self}\"" : "#{self}"
# Generates a string used as cpp macro name.
strip.upcase.tr_s("^A-Z0-9_", "_")
# Wraps all strings in escaped quotes if they contain whitespace.
FileUtils.rm_f(Dir[files.join("\0")])
# Returns time stamp of the +target+ file if it exists and is newer
# than or equal to all of +times+.
def modified?(target, times)
(t = File.mtime(target)) rescue return nil
Array === times or times = [times]
t if times.all? {|n| n <= t}
libs.inject([]) do |x, y|
y = y.inject([]) {|ary, e| ary.last == e ? ary : ary << e}
y.each_with_index do |v, yi|
xi = [x.index(v), xn].max()
xn, yn = xi + (yi - yn + 1), yi + 1
x.concat(y[yn..-1] || [])
# This is a custom logging module. It generates an mkmf.log file when you
# run your extconf.rb script. This can be useful for debugging unexpected
# This module and its associated methods are meant for internal use only.
@log ||= File::open(@logfile, 'w')
@log ||= File::open(@logfile, 'w')
if @log and not @log.closed?
tmplog = "mkmftmp#{@postpone += 1}.log"
log, *save = @log, @logfile, @orgout, @orgerr
@log, @logfile, @orgout, @orgerr = nil, tmplog, log, log
File::open(tmplog) {|t| FileUtils.copy_stream(t, log)}
@log, @logfile, @orgout, @orgerr = log, *save
varpat = /\$\((\w+)\)|\$\{(\w+)\}/
vars = Hash.new {|h, k| h[k] = ''; ENV[k]}
nil while command.gsub!(varpat) {vars[$1||$2]}
def xpopen command, *mode, &block
IO.popen(command, *mode, &block)
fmt = "%#{src.size.to_s.size}d: %s"
src.each_with_index {|line, no| Logging::message fmt, no+1, line}
src = yield(src) if block_given?
src = src.gsub(/[ \t]+$/, '').gsub(/\A\n+|^\n+$/, '').sub(/[^\n]\z/, "\\&\n")
open(CONFTEST_C, "wb") do |cfile|
def try_do(src, command, &b)
src = create_tmpsrc(src, &b)
def link_command(ldflags, opt="", libpath=$DEFLIBPATH|$LIBPATH)
conf = Config::CONFIG.merge('hdrdir' => $hdrdir.quote,
'ARCH_FLAG' => "#$ARCH_FLAG",
'LDFLAGS' => "#$LDFLAGS #{ldflags}",
'LIBPATH' => libpathflag(libpath),
'LOCAL_LIBS' => "#$LOCAL_LIBS #$libs",
'LIBS' => "#$LIBRUBYARG_STATIC #{opt} #$LIBS")
Config::expand(TRY_LINK.dup, conf)
conf = Config::CONFIG.merge('hdrdir' => $hdrdir.quote, 'srcdir' => $srcdir.quote)
Config::expand("$(CC) #$INCFLAGS #$CPPFLAGS #$CFLAGS #$ARCH_FLAG #{opt} -c #{CONFTEST_C}",
def cpp_command(outfile, opt="")
conf = Config::CONFIG.merge('hdrdir' => $hdrdir.quote, 'srcdir' => $srcdir.quote)
Config::expand("$(CPP) #$INCFLAGS #$CPPFLAGS #$CFLAGS #{opt} #{CONFTEST_C} #{outfile}",
def libpathflag(libpath=$DEFLIBPATH|$LIBPATH)
def try_link0(src, opt="", &b)
try_do(src, link_command("", opt), &b)
def try_link(src, opt="", &b)
rm_f "conftest*", "c0x32*"
def try_compile(src, opt="", &b)
try_do(src, cc_command(opt), &b)
def try_cpp(src, opt="", &b)
try_do(src, cpp_command(CPPOUTFILE, opt), &b)
header = [header] unless header.kind_of? Array
header.map {|h| "#include <#{h}>\n"}.join
$CPPFLAGS = cppflags unless ret
$CFLAGS = cflags unless ret
$LDFLAGS = ldflags unless ret
def try_static_assert(expr, headers = nil, opt = "", &b)
headers = cpp_include(headers)
try_compile(<<SRC, opt, &b)
int conftest_const[(#{expr}) ? 1 : -1];
def try_constant(const, headers = nil, opt = "", &b)
includes = cpp_include(headers)
if try_static_assert("#{const} > 0", headers, opt)
elsif try_static_assert("#{const} < 0", headers, opt)
elsif try_static_assert("#{const} == 0", headers, opt)
until try_static_assert("#{const} <= #{upper}", headers, opt)
mid = (upper + lower) / 2
if try_static_assert("#{const} > #{mid}", headers, opt)
src = %{#{COMMON_HEADERS}
int conftest_const = (int)(#{const});
int main() {printf("%d\\n", conftest_const); return 0;}
if try_link0(src, opt, &b)
xpopen("./conftest") do |f|
def try_func(func, libs, headers = nil, &b)
headers = cpp_include(headers)
try_link(<<"SRC", libs, &b) or try_link(<<"SRC", libs, &b)
int t() { void ((*volatile p)()); p = (void ((*)()))#{func}; return 0; }
int t() { #{func}(); return 0; }
def try_var(var, headers = nil, &b)
headers = cpp_include(headers)
int t() { const volatile void *volatile p; p = &(&#{var})[0]; return 0; }
def egrep_cpp(pat, src, opt = "", &b)
src = create_tmpsrc(src, &b)
xpopen(cpp_command('', opt)) do |f|
puts(" ruby -ne 'print if #{pat.inspect}'")