# -*- coding: us-ascii -*-
# frozen-string-literal: false
# module to create Makefile for extension modules
# invoke like: ruby -r mkmf extconf.rb
# Wraps a string in escaped quotes if it contains whitespace.
/\s/ =~ self ? "\"#{self}\"" : "#{self}"
# Escape whitespaces for Makefile.
# Generates a string used as cpp macro name.
strip.upcase.tr_s("^A-Z0-9_*", "_").tr_s("*", "P")
/\)\z/ =~ self ? dup : "#{self}()"
# Wraps all strings in escaped quotes if they contain whitespace.
# mkmf.rb is used by Ruby C extensions to generate a Makefile which will
# correctly compile and link the C extension to Ruby and a third-party
#### defer until this module become global-state free.
# def initialize(*args, rbconfig: RbConfig, **rest)
# init_mkmf(rbconfig::MAKEFILE_CONFIG, rbconfig::CONFIG)
# The makefile configuration using the defaults from when Ruby was built.
CONFIG = RbConfig::MAKEFILE_CONFIG
ORIG_LIBPATH = ENV['LIB']
# Extensions for files compiled with a C compiler
# Extensions for files complied with a C++ compiler
CXX_EXT = %w[cc mm cxx cpp]
unless File.exist?(File.join(*File.split(__FILE__).tap {|d, b| b.swapcase}))
# Extensions for source files
SRC_EXT = C_EXT + CXX_EXT
# Extensions for header files
$config_h = '$(arch_hdrdir)/ruby/config.h'
$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
$mingw = /mingw/ =~ RUBY_PLATFORM
$cygwin = /cygwin/ =~ RUBY_PLATFORM
$netbsd = /netbsd/ =~ RUBY_PLATFORM
$haiku = /haiku/ =~ RUBY_PLATFORM
$solaris = /solaris/ =~ RUBY_PLATFORM
$universal = /universal/ =~ 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
module_function :config_string
Regexp.new('\$(?:\('+dir+'\)|\{'+dir+'\})(?:\$(?:\(target_prefix\)|\{target_prefix\}))?')
def relative_from(path, base)
dir = File.join(path, "")
if File.expand_path(dir) == File.expand_path(dir, base)
[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('vendorarchdir'), "$(RUBYARCHDIR)"],
[dir_re('rubyhdrdir'), "$(RUBYHDRDIR)"],
[dir_re('sitehdrdir'), "$(SITEHDRDIR)"],
[dir_re('vendorhdrdir'), "$(VENDORHDRDIR)"],
[dir_re('bindir'), "$(BINDIR)"],
def install_dirs(target_prefix = nil)
['BINDIR', '$(extout)/bin'],
['RUBYCOMMONDIR', '$(extout)/common'],
['RUBYLIBDIR', '$(RUBYCOMMONDIR)$(target_prefix)'],
['RUBYARCHDIR', '$(extout)/$(arch)$(target_prefix)'],
['HDRDIR', '$(extout)/include/ruby$(target_prefix)'],
['ARCHHDRDIR', '$(extout)/include/$(arch)/ruby$(target_prefix)'],
['extout_prefix', "#$extout_prefix"],
['RUBYCOMMONDIR', '$(rubylibdir)'],
['RUBYLIBDIR', '$(rubylibdir)$(target_prefix)'],
['RUBYARCHDIR', '$(archdir)$(target_prefix)'],
['HDRDIR', '$(rubyhdrdir)/ruby$(target_prefix)'],
['ARCHHDRDIR', '$(rubyhdrdir)/$(arch)/ruby$(target_prefix)'],
elsif $configure_args.has_key?('--vendor')
['RUBYCOMMONDIR', '$(vendordir)$(target_prefix)'],
['RUBYLIBDIR', '$(vendorlibdir)$(target_prefix)'],
['RUBYARCHDIR', '$(vendorarchdir)$(target_prefix)'],
['HDRDIR', '$(rubyhdrdir)/ruby$(target_prefix)'],
['ARCHHDRDIR', '$(rubyhdrdir)/$(arch)/ruby$(target_prefix)'],
['RUBYCOMMONDIR', '$(sitedir)$(target_prefix)'],
['RUBYLIBDIR', '$(sitelibdir)$(target_prefix)'],
['RUBYARCHDIR', '$(sitearchdir)$(target_prefix)'],
['HDRDIR', '$(rubyhdrdir)/ruby$(target_prefix)'],
['ARCHHDRDIR', '$(rubyhdrdir)/$(arch)/ruby$(target_prefix)'],
dirs << ['target_prefix', (target_prefix ? "/#{target_prefix}" : "")]
def map_dir(dir, map = nil)
map.inject(dir) {|d, (orig, new)| d.gsub(orig, new)}
topdir = File.dirname(File.dirname(__FILE__))
path = File.expand_path($0)
until (dir = File.dirname(path)) == path
if File.identical?(dir, topdir)
$extmk = true if %r"\A(?:ext|enc|tool|test)\z" =~ File.basename(path)
if not $extmk and File.exist?(($hdrdir = RbConfig::CONFIG["rubyhdrdir"]) + "/ruby/ruby.h")
$arch_hdrdir = RbConfig::CONFIG["rubyarchhdrdir"]
elsif File.exist?(($hdrdir = ($top_srcdir ||= topdir) + "/include") + "/ruby.h")
$topdir ||= RbConfig::CONFIG["topdir"]
$arch_hdrdir = "$(extout)/include/$(arch)"
abort "mkmf.rb can't find header files for ruby at #{$hdrdir}/ruby.h"
CONFTEST = "conftest".freeze
CONFTEST_C = "#{CONFTEST}.c"
OUTFLAG = CONFIG['OUTFLAG']
COUTFLAG = CONFIG['COUTFLAG']
CSRCFLAG = CONFIG['CSRCFLAG']
CPPOUTFILE = config_string('CPPOUTFILE') {|str| str.sub(/\bconftest\b/, CONFTEST)}
opt = (Hash === files.last ? [files.pop] : [])
FileUtils.rm_f(Dir[*files.flatten], *opt)
opt = (Hash === files.last ? [files.pop] : [])
FileUtils.rm_rf(Dir[*files.flatten], *opt)
# 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}
strs.map {|s| s.split(/\s+(?=-|\z)/)}.flatten
libs.inject([]) do |x, y|
y = y.inject([]) {|ary, e| ary.last == e ? ary : ary << e}
y.each_with_index do |v, yi|
x[(xi+1)..-1] = merge_libs(y[(yi+1)..-1], x[(xi+1)..-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, 'wb')
@log and not @log.closed?
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
log.print(open {yield @log})
@log.close if @log and not @log.closed?
File::open(tmplog) {|t| FileUtils.copy_stream(t, log)} if File.exist?(tmplog)
@log, @logfile, @orgout, @orgerr = log, *save
# used only if native compiling
if libpathenv = config_string("LIBPATHENV")
pathenv = ENV[libpathenv]
libpath = RbConfig.expand($DEFLIBPATH.join(File::PATH_SEPARATOR))
{libpathenv => [libpath, pathenv].compact.join(File::PATH_SEPARATOR)}
def xsystem command, opts = nil
varpat = /\$\((\w+)\)|\$\{(\w+)\}/
vars = Hash.new {|h, k| h[k] = ENV[k]}
nil while command.gsub!(varpat) {vars[$1||$2]}
if opts and opts[:werror]
Logging.postpone do |log|
output = IO.popen(libpath_env, command, &:read)
result = ($?.success? and File.zero?(log.path))
system(libpath_env, command)
def xpopen command, *mode, &block
IO.popen(libpath_env, command, *mode, &block)
def log_src(src, heading="checked program was")
fmt = "%#{src.size.to_s.size}d: %s"
src.each_with_index {|line, no| Logging::message fmt, no+1, line}
src = "#{COMMON_HEADERS}\n#{src}"
src = yield(src) if block_given?
src.gsub!(/\A\n+|^\n+$/, '')
src.sub!(/[^\n]\z/, "\\&\n")
open(CONFTEST_C, "wb") do |cfile|
unless defined? $have_devel
$have_devel = try_link(MAIN_DOES_NOTHING)
def try_do(src, command, *opts, &b)
The compiler failed to generate an executable file.
You have to install development tools first.
src = create_tmpsrc(src, &b)
def link_command(ldflags, opt="", libpath=$DEFLIBPATH|$LIBPATH)
librubyarg = $extmk ? $LIBRUBYARG_STATIC : "$(LIBRUBYARG)"
conf = RbConfig::CONFIG.merge('hdrdir' => $hdrdir.quote,
'src' => "#{CONFTEST_C}",
'arch_hdrdir' => $arch_hdrdir.quote,
'top_srcdir' => $top_srcdir.quote,
'INCFLAGS' => "#$INCFLAGS",
'CPPFLAGS' => "#$CPPFLAGS",
'ARCH_FLAG' => "#$ARCH_FLAG",
'LDFLAGS' => "#$LDFLAGS #{ldflags}",
'LOCAL_LIBS' => "#$LOCAL_LIBS #$libs",
'LIBS' => "#{librubyarg} #{opt} #$LIBS")
conf['LIBPATH'] = libpathflag(libpath.map {|s| RbConfig::expand(s.dup, conf)})
RbConfig::expand(TRY_LINK.dup, conf)
conf = RbConfig::CONFIG.merge('hdrdir' => $hdrdir.quote, 'srcdir' => $srcdir.quote,
'arch_hdrdir' => $arch_hdrdir.quote,
'top_srcdir' => $top_srcdir.quote)
RbConfig::expand("$(CC) #$INCFLAGS #$CPPFLAGS #$CFLAGS #$ARCH_FLAG #{opt} -c #{CONFTEST_C}",
def cpp_command(outfile, opt="")
conf = RbConfig::CONFIG.merge('hdrdir' => $hdrdir.quote, 'srcdir' => $srcdir.quote,
'arch_hdrdir' => $arch_hdrdir.quote,
'top_srcdir' => $top_srcdir.quote)
if $universal and (arch_flag = conf['ARCH_FLAG']) and !arch_flag.empty?
conf['ARCH_FLAG'] = arch_flag.gsub(/(?:\G|\s)-arch\s+\S+/, '')