module MakeMakefile

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 library.

Constants

ASSEMBLE_C

Command which will translate C files to assembler sources in the generated Makefile

ASSEMBLE_CXX

Command which will translate C++ files to assembler sources in the generated Makefile

CLEANINGS

Makefile rules that will clean the extension build directory

COMMON_HEADERS

Common headers for Ruby C extensions

COMMON_LIBS

Common libraries for Ruby C extensions

COMPILE_C

Command which will compile C files in the generated Makefile

COMPILE_CXX

Command which will compile C++ files in the generated Makefile

COMPILE_RULES

make compile rules

CONFIG

The makefile configuration using the defaults from when Ruby was built.

CXX_EXT

Extensions for files compiled with a C++ compiler

C_EXT

Extensions for files compiled with a C compiler

EXPORT_PREFIX

The prefix added to exported symbols automatically

HDR_EXT

Extensions for header files

LANGUAGE

The language that this module is for

LIBARG

Argument which will add a library to the linker

LIBPATHFLAG

Argument which will add a library path to the linker

Command which will link a shared library

MAIN_DOES_NOTHING

A C main function which does no work

ORIG_LIBPATH

The saved original value of LIB environment variable

RPATHFLAG

Argument which will add a runtime library path to the linker

RULE_SUBST

Substitution in rules for NMake

RbConfig

The RbConfig for the target platform where the built extension runs.

SRC_EXT

Extensions for source files

Command which will compile a program in order to test linking a library

UNIVERSAL_INTS

The type names for convertible_int

Public Class Methods

[] (name)

Retrieves the module for name language.

# File lib/mkmf.rb, line 2961
def self.[](name)
  @lang.fetch(name)
end
[]= (name, mod)

Defines the module for name language.

# File lib/mkmf.rb, line 2967
def self.[]=(name, mod)
  @lang[name] = mod
end
rm_f (*files)

Removes files.

# File lib/mkmf.rb, line 270
def rm_f(*files)
  opt = (Hash === files.last ? [files.pop] : [])
  FileUtils.rm_f(Dir[*files.flatten], *opt)
end
rm_rf (*files)

Removes files recursively.

# File lib/mkmf.rb, line 277
def rm_rf(*files)
  opt = (Hash === files.last ? [files.pop] : [])
  FileUtils.rm_rf(Dir[*files.flatten], *opt)
end

Public Instance Methods

append_cflags (flags, **opts)

Check whether each given C compiler flag is acceptable and append it to $CFLAGS if so.

flags

a C compiler flag as a String or an Array of them

# File lib/mkmf.rb, line 1106
def append_cflags(flags, **opts)
  Array(flags).each do |flag|
    if checking_for("whether #{flag} is accepted as CFLAGS") {
         try_cflags(flag, **opts)
       }
      $CFLAGS << " " << flag
    end
  end
end
append_ldflags (flags, **opts)

Check whether each given linker flag is acceptable and append it to $LDFLAGS if so.

flags

a linker flag as a String or an Array of them

# File lib/mkmf.rb, line 762
def append_ldflags(flags, **opts)
  Array(flags).each do |flag|
    if checking_for("whether #{flag} is accepted as LDFLAGS") {
         try_ldflags(flag, **opts)
       }
      $LDFLAGS << " " << flag
    end
  end
end
check_signedness (type, headers = nil, opts = nil, &b)

Returns the signedness of the given type. You may optionally specify additional headers to search in for the type.

If the type is found and is a numeric type, a macro is passed as a preprocessor constant to the compiler using the type name, in uppercase, prepended with SIGNEDNESS_OF_, followed by the type name, followed by =X where “X” is positive integer if the type is unsigned and a negative integer if the type is signed.

For example, if size_t is defined as unsigned, then check_signedness('size_t') would return +1 and the SIGNEDNESS_OF_SIZE_T=+1 preprocessor macro would be passed to the compiler. The SIGNEDNESS_OF_INT=-1 macro would be set for check_signedness('int')

# File lib/mkmf.rb, line 1509
def check_signedness(type, headers = nil, opts = nil, &b)
  typedef, member, prelude = typedef_expr(type, headers)
  signed = nil
  checking_for("signedness of #{type}", STRING_OR_FAILED_FORMAT) do
    signed = try_signedness(typedef, member, [prelude], opts, &b) or next nil
    $defs.push("-DSIGNEDNESS_OF_%s=%+d" % [type.tr_cpp, signed])
    signed < 0 ? "signed" : "unsigned"
  end
  signed
end
check_sizeof (type, headers = nil, opts = "", &b)

Returns the size of the given type. You may optionally specify additional headers to search in for the type.

If found, a macro is passed as a preprocessor constant to the compiler using the type name, in uppercase, prepended with SIZEOF_, followed by the type name, followed by =X where “X” is the actual size.

For example, if check_sizeof('mystruct') returned 12, then the SIZEOF_MYSTRUCT=12 preprocessor macro would be passed to the compiler.

# File lib/mkmf.rb, line 1480
def check_sizeof(type, headers = nil, opts = "", &b)
  typedef, member, prelude = typedef_expr(type, headers)
  prelude << "#{typedef} *rbcv_ptr_;\n"
  prelude = [prelude]
  expr = "sizeof((*rbcv_ptr_)#{"." << member if member})"
  fmt = STRING_OR_FAILED_FORMAT
  checking_for checking_message("size of #{type}", headers), fmt do
    if size = try_constant(expr, prelude, opts, &b)
      $defs.push(format("-DSIZEOF_%s=%s", type.tr_cpp, size))
      size
    end
  end
end
conftest_source ()

Returns the language-dependent source file name for configuration checks.

# File lib/mkmf.rb, line 488
def conftest_source
  CONFTEST_C
end
convertible_int (type, headers = nil, opts = nil, &b)

Returns the convertible integer type of the given type. You may optionally specify additional headers to search in for the type. convertible means actually the same type, or typedef’d from the same type.

If the type is an integer type and the convertible type is found, the following macros are passed as preprocessor constants to the compiler using the type name, in uppercase.

  • TYPEOF_, followed by the type name, followed by =X where “X” is the found convertible type name.

  • TYP2NUM and NUM2TYP, where TYP is the type name in uppercase with replacing an _t suffix with “T”, followed by =X where “X” is the macro name to convert type to an Integer object, and vice versa.

For example, if foobar_t is defined as unsigned long, then convertible_int("foobar_t") would return “unsigned long”, and define these macros:

#define TYPEOF_FOOBAR_T unsigned long
#define FOOBART2NUM ULONG2NUM
#define NUM2FOOBART NUM2ULONG
# File lib/mkmf.rb, line 1544
def convertible_int(type, headers = nil, opts = nil, &b)
  type, macname = *type
  checking_for("convertible type of #{type}", STRING_OR_FAILED_FORMAT) do
    if UNIVERSAL_INTS.include?(type)
      type
    else
      typedef, member, prelude = typedef_expr(type, headers, &b)
      if member
        prelude << "static rbcv_typedef_ rbcv_var;"
        compat = UNIVERSAL_INTS.find {|t|
          try_static_assert("sizeof(rbcv_var.#{member}) == sizeof(#{t})", [prelude], opts, &b)
        }
      else
        next unless signed = try_signedness(typedef, member, [prelude])
        u = "unsigned " if signed > 0
        prelude << "extern rbcv_typedef_ foo();"
        compat = UNIVERSAL_INTS.find {|t|
          try_compile([prelude, "extern #{u}#{t} foo();"].join("\n"), opts, werror: true, &b)
        }
      end
      if compat
        macname ||= type.sub(/_(?=t\z)/, '').tr_cpp
        conv = (compat == "long long" ? "LL" : compat.upcase)
        compat = "#{u}#{compat}"
        typename = type.tr_cpp
        $defs.push(format("-DSIZEOF_%s=SIZEOF_%s", typename, compat.tr_cpp))
        $defs.push(format("-DTYPEOF_%s=%s", typename, compat.quote))
        $defs.push(format("-DPRI_%s_PREFIX=PRI_%s_PREFIX", macname, conv))
        conv = (u ? "U" : "") + conv
        $defs.push(format("-D%s2NUM=%s2NUM", macname, conv))
        $defs.push(format("-DNUM2%s=NUM2%s", macname, conv))
        compat
      end
    end
  end
end
create_header (header = "extconf.h")

Generates a header file consisting of the various macro definitions generated by other methods such as have_func and have_header. These are then wrapped in a custom #ifndef based on the header file name, which defaults to “extconf.h”.

For example:

# extconf.rb
require 'mkmf'
have_func('realpath')
have_header('sys/utime.h')
create_header
create_makefile('foo')

The above script would generate the following extconf.h file:

#ifndef EXTCONF_H
#define EXTCONF_H
#define HAVE_REALPATH 1
#define HAVE_SYS_UTIME_H 1
#endif

Given that the create_header method generates a file based on definitions set earlier in your extconf.rb file, you will probably want to make this one of the last methods you call in your script.

# File lib/mkmf.rb, line 1840
def create_header(header = "extconf.h")
  message "creating %s\n", header
  sym = header.tr_cpp
  hdr = ["#ifndef #{sym}\n#define #{sym}\n"]
  for line in $defs
    case line
    when /^-D([^=]+)(?:=(.*))?/
      hdr << "#define #$1 #{$2 ? Shellwords.shellwords($2)[0].gsub(/(?=\t+)/, "\\\n") : 1}\n"
    when /^-U(.*)/
      hdr << "#undef #$1\n"
    end
  end
  hdr << "#endif\n"
  hdr = hdr.join("")
  log_src(hdr, "#{header} is")
  unless (File.read(header) == hdr rescue false)
    File.open(header, "wb") do |hfile|
      hfile.write(hdr)
    end
  end
  $extconf_h = header
end
create_makefile (target, srcprefix = nil) { |conf| ... }

Generates the Makefile for your extension, passing along any options and preprocessor constants that you may have generated through other methods.

The target name should correspond the name of the global function name defined within your C extension, minus the Init_. For example, if your C extension is defined as Init_foo, then your target would simply be “foo”.

If any “/” characters are present in the target name, only the last name is interpreted as the target name, and the rest are considered toplevel directory names, and the generated Makefile will be altered accordingly to follow that directory structure.

For example, if you pass “test/foo” as a target name, your extension will be installed under the “test” directory. This means that in order to load the file within a Ruby program later, that directory structure will have to be followed, e.g. require 'test/foo'.

The srcprefix should be used when your source files are not in the same directory as your build script. This will not only eliminate the need for you to manually copy the source files into the same directory as your build script, but it also sets the proper target_prefix in the generated Makefile.

Setting the target_prefix will, in turn, install the generated binary in a directory under your RbConfig::CONFIG['sitearchdir'] that mimics your local filesystem when you run make install.

For example, given the following file tree:

ext/
  extconf.rb
  test/
    foo.c

And given the following code:

create_makefile('test/foo', 'test')

That will set the target_prefix in the generated Makefile to “test”. That, in turn, will create the following file tree when installed via the make install command:

/path/to/ruby/sitearchdir/test/foo.so

It is recommended that you use this approach to generate your makefiles, instead of copying files around manually, because some third party libraries may depend on the target_prefix being set properly.

The srcprefix argument can be used to override the default source directory, i.e. the current directory. It is included as part of the VPATH and added to the list of INCFLAGS.

# File lib/mkmf.rb, line 2370
  def create_makefile(target, srcprefix = nil)
    $target = target
    libpath = $DEFLIBPATH|$LIBPATH
    message "creating Makefile\n"
    MakeMakefile.rm_f "#{CONFTEST}*"
    if CONFIG["DLEXT"] == $OBJEXT
      for lib in libs = $libs.split(' ')
        lib.sub!(/-l(.*)/, %%"lib\\1.#{$LIBEXT}"%)
      end
      $defs.push(format("-DEXTLIB='%s'", libs.join(",")))
    end

    if target.include?('/')
      target_prefix, target = File.split(target)
      target_prefix[0,0] = '/'
    else
      target_prefix = ""
    end

    srcprefix ||= "$(srcdir)/#{srcprefix}".chomp('/')
    RbConfig.expand(srcdir = srcprefix.dup)

    ext = ".#{$OBJEXT}"
    orig_srcs = Dir[File.join(srcdir, "*.{#{SRC_EXT.join(%q{,})}}")]
    if not $objs
      srcs = $srcs || orig_srcs
      $objs = []
      objs = srcs.inject(Hash.new {[]}) {|h, f|
        h.key?(o = File.basename(f, ".*") << ext) or $objs << o
        h[o] <<= f
        h
      }
      unless objs.delete_if {|b, f| f.size == 1}.empty?
        dups = objs.map {|b, f|
          "#{b[/.*\./]}{#{f.collect {|n| n[/([^.]+)\z/]}.join(',')}}"
        }
        abort "source files duplication - #{dups.join(", ")}"
      end
    else
      $objs.collect! {|o| File.basename(o, ".*") << ext} unless $OBJEXT == "o"
      srcs = $srcs || $objs.collect {|o| o.chomp(ext) << ".c"}
    end
    $srcs = srcs

    hdrs = Dir[File.join(srcdir, "*.{#{HDR_EXT.join(%q{,})}}")]

    target = nil if $objs.empty?

    if target and EXPORT_PREFIX
      if File.exist?(File.join(srcdir, target + '.def'))
        deffile = "$(srcdir)/$(TARGET).def"
        unless EXPORT_PREFIX.empty?
          makedef = %{$(RUBY) -pe "$$_.sub!(/^(?=\\w)/,'#{EXPORT_PREFIX}') unless 1../^EXPORTS$/i" #{deffile}}
        end
      else
        makedef = %{(echo EXPORTS && echo $(TARGET_ENTRY))}
      end
      if makedef
        $cleanfiles << '$(DEFFILE)'
        origdef = deffile
        deffile = "$(TARGET)-$(arch).def"
      end
    end
    origdef ||= ''

    if $extout and $INSTALLFILES
      $cleanfiles.concat($INSTALLFILES.collect {|files, dir|File.join(dir, files.delete_prefix('./'))})
      $distcleandirs.concat($INSTALLFILES.collect {|files, dir| dir})
    end

    if $extmk and $static
      $defs << "-DRUBY_EXPORT=1"
    end

    if $extmk and not $extconf_h
      create_header
    end

    libpath = libpathflag(libpath)

    dllib = target ? "$(TARGET).#{CONFIG['DLEXT']}" : ""
    staticlib = target ? "$(TARGET).#$LIBEXT" : ""
    conf = configuration(srcprefix)
    conf << "\
libpath = #{($DEFLIBPATH|$LIBPATH).join(" ")}
LIBPATH = #{libpath}
DEFFILE = #{deffile}

CLEANFILES = #{$cleanfiles.join(' ')}
DISTCLEANFILES = #{$distcleanfiles.join(' ')}
DISTCLEANDIRS = #{$distcleandirs.join(' ')}

extout = #{$extout && $extout.quote}
extout_prefix = #{$extout_prefix}
target_prefix = #{target_prefix}
LOCAL_LIBS = #{$LOCAL_LIBS}
LIBS = #{$LIBRUBYARG} #{$libs} #{$LIBS}
ORIG_SRCS = #{orig_srcs.collect(&File.method(:basename)).join(' ')}
SRCS = $(ORIG_SRCS) #{(srcs - orig_srcs).collect(&File.method(:basename)).join(' ')}
OBJS = #{$objs.join(" ")}
HDRS = #{hdrs.map{|h| '$(srcdir)/' + File.basename(h)}.join(' ')}
LOCAL_HDRS = #{$headers.join(' ')}
TARGET = #{target}
TARGET_NAME = #{target && target[/\A\w+/]}
TARGET_ENTRY = #{EXPORT_PREFIX || ''}Init_$(TARGET_NAME)
DLLIB = #{dllib}
EXTSTATIC = #{$static || ""}
STATIC_LIB = #{staticlib unless $static.nil?}
#{!$extout && defined?($installed_list) ? %[INSTALLED_LIST = #{$installed_list}\n] : ""}
TIMESTAMP_DIR = #{$extout && $extmk ? '$(extout)/.timestamp' : '.'}
" #"
    # TODO: fixme
    install_dirs.each {|d| conf << ("%-14s= %s\n" % d) if /^[[:upper:]]/ =~ d[0]}
    sodir = $extout ? '$(TARGET_SO_DIR)' : '$(RUBYARCHDIR)'
    n = '$(TARGET_SO_DIR)$(TARGET)'
    cleanobjs = ["$(OBJS)"]
    if $extmk
      %w[bc i s].each {|ex| cleanobjs << "$(OBJS:.#{$OBJEXT}=.#{ex})"}
    end
    if target
      config_string('cleanobjs') {|t| cleanobjs << t.gsub(/\$\*/, "$(TARGET)#{deffile ? '-$(arch)': ''}")}
    end
    conf << "\
TARGET_SO_DIR =#{$extout ? " $(RUBYARCHDIR)/" : ''}
TARGET_SO     = $(TARGET_SO_DIR)$(DLLIB)
CLEANLIBS     = #{'$(TARGET_SO) ' if target}#{config_string('cleanlibs') {|t| t.gsub(/\$\*/) {n}}}
CLEANOBJS     = #{cleanobjs.join(' ')} *.bak
TARGET_SO_DIR_TIMESTAMP = #{timestamp_file(sodir, target_prefix)}
" #"

    conf = yield(conf) if block_given?
    mfile = File.open("Makefile", "wb")
    mfile.puts(conf)
    mfile.print "
all:    #{$extout ? "install" : target ? "$(DLLIB)" : "Makefile"}
static: #{$extmk && !$static ? "all" : %[$(STATIC_LIB)#{$extout ? " install-rb" : ""}]}
.PHONY: all install static install-so install-rb
.PHONY: clean clean-so clean-static clean-rb
" #"
    mfile.print CLEANINGS
    fsep = config_string('BUILD_FILE_SEPARATOR') {|s| s unless s == "/"}
    if fsep
      sep = ":/=#{fsep}"
      fseprepl = proc {|s|
        s = s.gsub("/", fsep)
        s = s.gsub(/(\$\(\w+)(\))/) {$1+sep+$2}
        s.gsub(/(\$\{\w+)(\})/) {$1+sep+$2}
      }
      rsep = ":#{fsep}=/"
    else
      fseprepl = proc {|s| s}
      sep = ""
      rsep = ""
    end
    dirs = []
    mfile.print "install: install-so install-rb\n\n"
    dir = sodir.dup
    mfile.print("install-so: ")
    if target
      f = "$(DLLIB)"
      dest = "$(TARGET_SO)"
      stamp = '$(TARGET_SO_DIR_TIMESTAMP)'
      if $extout
        mfile.puts dest
        mfile.print "clean-so::\n"
        mfile.print "\t-$(Q)$(RM) #{fseprepl[dest]} #{fseprepl[stamp]}\n"
        mfile.print "\t-$(Q)$(RM_RF) #{fseprepl['$(CLEANLIBS)']}\n"
        mfile.print "\t-$(Q)$(RMDIRS) #{fseprepl[dir]}#{$ignore_error}\n"
      else
        mfile.print "#{f} #{stamp}\n"
        mfile.print "\t$(INSTALL_PROG) #{fseprepl[f]} #{dir}\n"
        if defined?($installed_list)
          mfile.print "\t@echo #{dir}/#{File.basename(f)}>>$(INSTALLED_LIST)\n"
        end
      end
      mfile.print "clean-static::\n"
      mfile.print "\t-$(Q)$(RM) $(STATIC_LIB)\n"
    else
      mfile.puts "Makefile"
    end
    mfile.print("install-rb: pre-install-rb do-install-rb install-rb-default\n")
    mfile.print("install-rb-default: pre-install-rb-default do-install-rb-default\n")
    mfile.print("pre-install-rb: Makefile\n")
    mfile.print("pre-install-rb-default: Makefile\n")
    mfile.print("do-install-rb:\n")
    mfile.print("do-install-rb-default:\n")
    for sfx, i in [["-default", [["lib/**/*.rb", "$(RUBYLIBDIR)", "lib"]]], ["", $INSTALLFILES]]
      files = install_files(mfile, i, nil, srcprefix) or next
      for dir, *files in files
        unless dirs.include?(dir)
          dirs << dir
          mfile.print "pre-install-rb#{sfx}: #{timestamp_file(dir, target_prefix)}\n"
        end
        for f in files
          dest = "#{dir}/#{File.basename(f)}"
          mfile.print("do-install-rb#{sfx}: #{dest}\n")
          mfile.print("#{dest}: #{f} #{timestamp_file(dir, target_prefix)}\n")
          mfile.print("\t$(Q) $(#{$extout ? 'COPY' : 'INSTALL_DATA'}) #{f} $(@D)\n")
          if defined?($installed_list) and !$extout
            mfile.print("\t@echo #{dest}>>$(INSTALLED_LIST)\n")
          end
          if $extout
            mfile.print("clean-rb#{sfx}::\n")
            mfile.print("\t-$(Q)$(RM) #{fseprepl[dest]}\n")
          end
        end
      end
      mfile.print "pre-install-rb#{sfx}:\n"
      if files.empty?
        mfile.print("\t@$(NULLCMD)\n")
      else
        q = "$(MAKE) -q do-install-rb#{sfx}"
        if $nmake
          mfile.print "!if \"$(Q)\" == \"@\"\n\t@#{q} || \\\n!endif\n\t"
        else
          mfile.print "\t$(Q1:0=@#{q} || )"
        end
        mfile.print "$(ECHO1:0=echo) installing#{sfx.sub(/^-/, " ")} #{target} libraries\n"
      end
      if $extout
        dirs.uniq!
        unless dirs.empty?
          mfile.print("clean-rb#{sfx}::\n")
          for dir in dirs.sort_by {|d| -d.count('/')}
            stamp = timestamp_file(dir, target_prefix)
            mfile.print("\t-$(Q)$(RM) #{fseprepl[stamp]}\n")
            mfile.print("\t-$(Q)$(RMDIRS) #{fseprepl[dir]}#{$ignore_error}\n")
          end
        end
      end
    end
    if target and !dirs.include?(sodir)
      mfile.print "$(TARGET_SO_DIR_TIMESTAMP):\n\t$(Q) $(MAKEDIRS) $(@D) #{sodir}\n\t$(Q) $(TOUCH) $@\n"
    end
    dirs.each do |d|
      t = timestamp_file(d, target_prefix)
      mfile.print "#{t}:\n\t$(Q) $(MAKEDIRS) $(@D) #{d}\n\t$(Q) $(TOUCH) $@\n"
    end

    mfile.print <<-SITEINSTALL

site-install: site-install-so site-install-rb
site-install-so: install-so
site-install-rb: install-rb

    SITEINSTALL

    return unless target

    mfile.print ".SUFFIXES: .#{(SRC_EXT + [$OBJEXT, $ASMEXT]).compact.join(' .')}\n"
    mfile.print "\n"

    compile_command = "\n\t$(ECHO) compiling $(<#{rsep})\n\t$(Q) %s\n\n"
    command = compile_command % COMPILE_CXX
    asm_command = compile_command.sub(/compiling/, 'translating') % ASSEMBLE_CXX
    CXX_EXT.each do |e|
      each_compile_rules do |rule|
        mfile.printf(rule, e, $OBJEXT)
        mfile.print(command)
        mfile.printf(rule, e, $ASMEXT)
        mfile.print(asm_command)
      end
    end
    command = compile_command % COMPILE_C
    asm_command = compile_command.sub(/compiling/, 'translating') % ASSEMBLE_C
    C_EXT.each do |e|
      each_compile_rules do |rule|
        mfile.printf(rule, e, $OBJEXT)
        mfile.print(command)
        mfile.printf(rule, e, $ASMEXT)
        mfile.print(asm_command)
      end
    end

    mfile.print "$(TARGET_SO): "
    mfile.print "$(DEFFILE) " if makedef
    mfile.print "$(OBJS) Makefile"
    mfile.print " $(TARGET_SO_DIR_TIMESTAMP)" if $extout
    mfile.print "\n"
    mfile.print "\t$(ECHO) linking shared-object #{target_prefix.sub(/\A\/(.*)/, '\1/')}$(DLLIB)\n"
    mfile.print "\t-$(Q)$(RM) $(@#{sep})\n"
    link_so = LINK_SO.gsub(/^/, "\t$(Q) ")
    if srcs.any?(&%r"\.(?:#{CXX_EXT.join('|')})\z".method(:===))
      link_so = link_so.sub(/\bLDSHARED\b/, '\&XX')
    end
    mfile.print link_so, "\n\n"
    unless $static.nil?
      mfile.print "$(STATIC_LIB): $(OBJS)\n\t-$(Q)$(RM) $(@#{sep})\n\t"
      mfile.print "$(ECHO) linking static-library $(@#{rsep})\n\t$(Q) "
      mfile.print "$(AR) #{config_string('ARFLAGS') || 'cru '}$@ $(OBJS)"
      config_string('RANLIB') do |ranlib|
        mfile.print "\n\t-$(Q)#{ranlib} $(@)#{$ignore_error}"
      end
    end
    mfile.print "\n\n"
    if makedef
      mfile.print "$(DEFFILE): #{origdef}\n"
      mfile.print "\t$(ECHO) generating $(@#{rsep})\n"
      mfile.print "\t$(Q) #{makedef} > $@\n\n"
    end

    depend = File.join(srcdir, "depend")
    if File.exist?(depend)
      mfile.print("###\n", *depend_rules(File.read(depend)))
    else
      mfile.print "$(OBJS): $(HDRS) $(ruby_headers)\n"
    end

    $makefile_created = true
  ensure
    mfile.close if mfile
  end
create_tmpsrc (src) { |src| ... }

Creats temporary source file from COMMON_HEADERS and src. Yields the created source string and uses the returned string as the source code, if the block is given.

# File lib/mkmf.rb, line 495
def create_tmpsrc(src)
  src = "#{COMMON_HEADERS}\n#{src}"
  src = yield(src) if block_given?
  src.gsub!(/[ \t]+$/, '')
  src.gsub!(/\A\n+|^\n+$/, '')
  src.sub!(/[^\n]\z/, "\\&\n")
  count = 0
  begin
    File.open(conftest_source, "wb") do |cfile|
      cfile.print src
    end
  rescue Errno::EACCES
    if (count += 1) < 5
      sleep 0.2
      retry
    end
  end
  src
end
depend_rules (depend)

Processes the data contents of the “depend” file. Each line of this file is expected to be a file name.

Returns the output of findings, in Makefile format.

# File lib/mkmf.rb, line 2255
def depend_rules(depend)
  suffixes = []
  depout = []
  cont = implicit = nil
  impconv = proc do
    each_compile_rules {|rule| depout << (rule % implicit[0]) << implicit[1]}
    implicit = nil
  end
  ruleconv = proc do |line|
    if implicit
      if /\A\t/ =~ line
        implicit[1] << line
        next
      else
        impconv[]
      end
    end
    if m = /\A\.(\w+)\.(\w+)(?:\s*:)/.match(line)
      suffixes << m[1] << m[2]
      implicit = [[m[1], m[2]], [m.post_match]]
      next
    elsif RULE_SUBST and /\A(?!\s*\w+\s*=)[$\w][^#]*:/ =~ line
      line.sub!(/\s*\#.*$/, '')
      comment = $&
      line.gsub!(%r"(\s)(?!\.)([^$(){}+=:\s\\,]+)(?=\s|\z)") {$1 + RULE_SUBST % $2}
      line = line.chomp + comment + "\n" if comment
    end
    depout << line
  end
  depend.each_line do |line|
    line.gsub!(/\.o\b/, ".#{$OBJEXT}")
    line.gsub!(/\{\$\(VPATH\)\}/, "") unless $nmake
    line.gsub!(/\$\((?:hdr|top)dir\)\/config.h/, $config_h)
    if $nmake && /\A\s*\$\(RM|COPY\)/ =~ line
      line.gsub!(%r"[-\w\./]{2,}"){$&.tr("/", "\\")}
      line.gsub!(/(\$\((?!RM|COPY)[^:)]+)(?=\))/, '\1:/=\\')
    end
    if /(?:^|[^\\])(?:\\\\)*\\$/ =~ line
      (cont ||= []) << line
      next
    elsif cont
      line = (cont << line).join
      cont = nil
    end
    ruleconv.call(line)
  end
  if cont
    ruleconv.call(cont.join)
  elsif implicit
    impconv.call
  end
  unless suffixes.empty?
    depout.unshift(".SUFFIXES: ." + suffixes.uniq.join(" .") + "\n\n")
  end
  if $extconf_h
    depout.unshift("$(OBJS): $(RUBY_EXTCONF_H)\n\n")
    depout.unshift("$(OBJS): $(hdrdir)/ruby/win32.h\n\n") if $mswin or $mingw
  end
  depout.flatten!
  depout
end
dir_config(target)
dir_config(target, prefix)
dir_config(target, idefault, ldefault)

Sets a target name that the user can then use to configure various “with” options with on the command line by using that name. For example, if the target is set to “foo”, then the user could use the --with-foo-dir=prefix, --with-foo-include=dir and --with-foo-lib=dir command line options to tell where to search for header/library files.

You may pass along additional parameters to specify default values. If one is given it is taken as default prefix, and if two are given they are taken as “include” and “lib” defaults in that order.

In any case, the return value will be an array of determined “include” and “lib” directories, either of which can be nil if no corresponding command line option is given when no default value is specified.

Note that dir_config only adds to the list of places to search for libraries and include files. It does not link the libraries into your application.

# File lib/mkmf.rb, line 1890
def dir_config(target, idefault=nil, ldefault=nil)
  key = [target, idefault, ldefault].compact.join("\0")
  if conf = $config_dirs[key]
    return conf
  end

  if dir = with_config(target + "-dir", (idefault unless ldefault))
    defaults = Array === dir ? dir : dir.split(File::PATH_SEPARATOR)
    idefault = ldefault = nil
  end

  idir = with_config(target + "-include", idefault)
  if conf = $arg_config.assoc("--with-#{target}-include")
    conf[1] ||= "${#{target}-dir}/include"
  end
  ldir = with_config(target + "-lib", ldefault)
  if conf = $arg_config.assoc("--with-#{target}-lib")
    conf[1] ||= "${#{target}-dir}/#{_libdir_basename}"
  end

  idirs = idir ? Array === idir ? idir.dup : idir.split(File::PATH_SEPARATOR) : []
  if defaults
    idirs.concat(defaults.collect {|d| d + "/include"})
    idir = ([idir] + idirs).compact.join(File::PATH_SEPARATOR)
  end
  unless idirs.empty?
    idirs.collect! {|d| "-I" + d}
    idirs -= Shellwords.shellwords($CPPFLAGS)
    unless idirs.empty?
      $CPPFLAGS = (idirs.quote << $CPPFLAGS).join(" ")
    end
  end

  ldirs = ldir ? Array === ldir ? ldir.dup : ldir.split(File::PATH_SEPARATOR) : []
  if defaults
    ldirs.concat(defaults.collect {|d| "#{d}/#{_libdir_basename}"})
    ldir = ([ldir] + ldirs).compact.join(File::PATH_SEPARATOR)
  end
  $LIBPATH = ldirs | $LIBPATH

  $config_dirs[key] = [idir, ldir]
end
dummy_makefile (srcdir)

Creates a stub Makefile.

# File lib/mkmf.rb, line 2224
  def dummy_makefile(srcdir)
    configuration(srcdir) << <<RULES << CLEANINGS
CLEANFILES = #{$cleanfiles.join(' ')}
DISTCLEANFILES = #{$distcleanfiles.join(' ')}

all install static install-so install-rb: Makefile
        @$(NULLCMD)
.PHONY: all install static install-so install-rb
.PHONY: clean clean-so clean-static clean-rb

RULES
  end
egrep_cpp (pat, src, opt = "", &b)

Returns whether or not the src can be preprocessed with the C preprocessor and matches with pat.

If a block given, it is called with the source before compilation. You can modify the source in the block.

pat

a Regexp or a String

src

a String which contains a C source

opt

a String which contains preprocessor options

NOTE: When pat is a Regexp the matching will be checked in process, otherwise egrep(1) will be invoked to check it.

# File lib/mkmf.rb, line 923
def egrep_cpp(pat, src, opt = "", &b)
  src = create_tmpsrc(src, &b)
  xpopen(cpp_command('', opt)) do |f|
    if Regexp === pat
      puts("    ruby -ne 'print if #{pat.inspect}'")
      f.grep(pat) {|l|
        puts "#{f.lineno}: #{l}"
        return true
      }
      false
    else
      puts("    egrep '#{pat}'")
      begin
        stdin = $stdin.dup
        $stdin.reopen(f)
        system("egrep", pat)
      ensure
        $stdin.reopen(stdin)
      end
    end
  end
ensure
  MakeMakefile.rm_f "#{CONFTEST}*"
  log_src(src)
end
enable_config (config, default=nil) { |config, default| ... }

Tests for the presence of an --enable-config or --disable-config option. Returns true if the enable option is given, false if the disable option is given, and the default value otherwise.

This can be useful for adding custom definitions, such as debug information.

Example:

if enable_config("debug")
   $defs.push("-DOSSL_DEBUG") unless $defs.include? "-DOSSL_DEBUG"
end
# File lib/mkmf.rb, line 1802
def enable_config(config, default=nil)
  if arg_config("--enable-"+config)
    true
  elsif arg_config("--disable-"+config)
    false
  elsif block_given?
    yield(config, default)
  else
    return default
  end
end
find_header (header, *paths)

Instructs mkmf to search for the given header in any of the paths provided, and returns whether or not it was found in those paths.

If the header is found then the path it was found on is added to the list of included directories that are sent to the compiler (via the -I switch).

# File lib/mkmf.rb, line 1290
def find_header(header, *paths)
  message = checking_message(header, paths)
  header = cpp_include(header)
  checking_for message do
    if try_header(header)
      true
    else
      found = false
      paths.each do |dir|
        opt = "-I#{dir}".quote
        if try_header(header, opt)
          $INCFLAGS << " " << opt
          found = true
          break
        end
      end
      found
    end
  end
end
find_library (lib, func, *paths, &b)

Returns whether or not the entry point func can be found within the library lib in one of the paths specified, where paths is an array of strings. If func is nil , then the main() function is used as the entry point.

If lib is found, then the path it was found on is added to the list of library paths searched and linked against.

# File lib/mkmf.rb, line 1164
def find_library(lib, func, *paths, &b)
  dir_config(lib)
  lib = with_config(lib+'lib', lib)
  paths = paths.flat_map {|path| path.split(File::PATH_SEPARATOR)}
  checking_for checking_message(func && func.funcall_style, LIBARG%lib) do
    libpath = $LIBPATH
    libs = append_library($libs, lib)
    begin
      until r = try_func(func, libs, &b) or paths.empty?
        $LIBPATH = libpath | [paths.shift]
      end
      if r
        $libs = libs
        libpath = nil
      end
    ensure
      $LIBPATH = libpath if libpath
    end
    r
  end
end
have_framework (fw, &b)

Returns whether or not the given framework can be found on your system. If found, a macro is passed as a preprocessor constant to the compiler using the framework name, in uppercase, prepended with HAVE_FRAMEWORK_.

For example, if have_framework('Ruby') returned true, then the HAVE_FRAMEWORK_RUBY preprocessor macro would be passed to the compiler.

If fw is a pair of the framework name and its header file name that header file is checked, instead of the normally used header file which is named same as the framework.

# File lib/mkmf.rb, line 1261
def have_framework(fw, &b)
  if Array === fw
    fw, header = *fw
  else
    header = "#{fw}.h"
  end
  checking_for fw do
    src = cpp_include("#{fw}/#{header}") << "\n" "int main(void){return 0;}"
    opt = " -framework #{fw}"
    if try_link(src, opt, &b) or (objc = try_link(src, "-ObjC#{opt}", &b))
      $defs.push(format("-DHAVE_FRAMEWORK_%s", fw.tr_cpp))
      # TODO: non-worse way than this hack, to get rid of separating
      # option and its argument.
      $LDFLAGS << " -ObjC" if objc and /(\A|\s)-ObjC(\s|\z)/ !~ $LDFLAGS
      $LIBS << opt
      true
    else
      false
    end
  end
end
have_func (func, headers = nil, opt = "", &b)

Returns whether or not the function func can be found in the common header files, or within any headers that you provide. If found, a macro is passed as a preprocessor constant to the compiler using the function name, in uppercase, prepended with HAVE_.

To check functions in an additional library, you need to check that library first using have_library(). The func shall be either mere function name or function name with arguments.

For example, if have_func('foo') returned true, then the HAVE_FOO preprocessor macro would be passed to the compiler.

# File lib/mkmf.rb, line 1198
def have_func(func, headers = nil, opt = "", &b)
  checking_for checking_message(func.funcall_style, headers, opt) do
    if try_func(func, $libs, headers, opt, &b)
      $defs << "-DHAVE_#{func.sans_arguments.tr_cpp}"
      true
    else
      false
    end
  end
end
have_header (header, preheaders = nil, opt = "", &b)

Returns whether or not the given header file can be found on your system. If found, a macro is passed as a preprocessor constant to the compiler using the header file name, in uppercase, prepended with HAVE_.

For example, if have_header('foo.h') returned true, then the HAVE_FOO_H preprocessor macro would be passed to the compiler.

# File lib/mkmf.rb, line 1238
def have_header(header, preheaders = nil, opt = "", &b)
  dir_config(header[/.*?(?=\/)|.*?(?=\.)/])
  checking_for header do
    if try_header(cpp_include(preheaders)+cpp_include(header), opt, &b)
      $defs.push(format("-DHAVE_%s", header.tr_cpp))
      true
    else
      false
    end
  end
end
have_library (lib, func = nil, headers = nil, opt = "", &b)

Returns whether or not the given entry point func can be found within lib. If func is nil, the main() entry point is used by default. If found, it adds the library to list of libraries to be used when linking your extension.

If headers are provided, it will include those header files as the header files it looks in when searching for func.

The real name of the library to be linked can be altered by --with-FOOlib configuration option.

# File lib/mkmf.rb, line 1138
def have_library(lib, func = nil, headers = nil, opt = "", &b)
  dir_config(lib)
  lib = with_config(lib+'lib', lib)
  checking_for checking_message(func && func.funcall_style, LIBARG%lib, opt) do
    if COMMON_LIBS.include?(lib)
      true
    else
      libs = append_library($libs, lib)
      if try_func(func, libs, headers, opt, &b)
        $libs = libs
        true
      else
        false
      end
    end
  end
end
have_macro (macro, headers = nil, opt = "", &b)

Returns whether or not macro is defined either in the common header files or within any headers you provide.

Any options you pass to opt are passed along to the compiler.

# File lib/mkmf.rb, line 1121
def have_macro(macro, headers = nil, opt = "", &b)
  checking_for checking_message(macro, headers, opt) do
    macro_defined?(macro, cpp_include(headers), opt, &b)
  end
end
have_struct_member (type, member, headers = nil, opt = "", &b)

Returns whether or not the struct of type type contains member. If it does not, or the struct type can’t be found, then false is returned. You may optionally specify additional headers in which to look for the struct (in addition to the common header files).

If found, a macro is passed as a preprocessor constant to the compiler using the type name and the member name, in uppercase, prepended with HAVE_.

For example, if have_struct_member('struct foo', 'bar') returned true, then the HAVE_STRUCT_FOO_BAR preprocessor macro would be passed to the compiler.

HAVE_ST_BAR is also defined for backward compatibility.

# File lib/mkmf.rb, line 1326
  def have_struct_member(type, member, headers = nil, opt = "", &b)
    checking_for checking_message("#{type}.#{member}", headers) do
      if try_compile(<<"SRC", opt, &b)
#{cpp_include(headers)}
/*top*/
int s = (char *)&((#{type}*)0)->#{member} - (char *)0;
#{MAIN_DOES_NOTHING}
SRC
        $defs.push(format("-DHAVE_%s_%s", type.tr_cpp, member.tr_cpp))
        $defs.push(format("-DHAVE_ST_%s", member.tr_cpp)) # backward compatibility
        true
      else
        false
      end
    end
  end
have_var (var, headers = nil, opt = "", &b)

Returns whether or not the variable var can be found in the common header files, or within any headers that you provide. If found, a macro is passed as a preprocessor constant to the compiler using the variable name, in uppercase, prepended with HAVE_.

To check variables in an additional library, you need to check that library first using have_library().

For example, if have_var('foo') returned true, then the HAVE_FOO preprocessor macro would be passed to the compiler.

# File lib/mkmf.rb, line 1220
def have_var(var, headers = nil, opt = "", &b)
  checking_for checking_message(var, headers, opt) do
    if try_var(var, headers, opt, &b)
      $defs.push(format("-DHAVE_%s", var.tr_cpp))
      true
    else
      false
    end
  end
end
log_src (src, heading="checked program was")

Logs src

# File lib/mkmf.rb, line 472
  def log_src(src, heading="checked program was")
    src = src.split(/^/)
    fmt = "%#{src.size.to_s.size}d: %s"
    Logging::message <<"EOM"
#{heading}:
/* begin */
EOM
    src.each_with_index {|line, no| Logging::message fmt, no+1, line}
    Logging::message <<"EOM"
/* end */

EOM
  end
modified? (target, times)

Returns time stamp of the target file if it exists and is newer than or equal to all of times.

# File lib/mkmf.rb, line 285
def modified?(target, times)
  (t = File.mtime(target)) rescue return nil
  Array === times or times = [times]
  t if times.all? {|n| n <= t}
end
pkg_config (pkg, *options)

Returns compile/link information about an installed library in a tuple of [cflags, ldflags, libs], by using the command found first in the following commands:

  1. If --with-{pkg}-config={command} is given via command line option: {command} {options}

  2. {pkg}-config {options}

  3. pkg-config {options} {pkg}

Where options is the option name without dashes, for instance "cflags" for the --cflags flag.

The values obtained are appended to $INCFLAGS, $CFLAGS, $LDFLAGS and $libs.

If one or more options argument is given, the config command is invoked with the options and a stripped output string is returned without modifying any of the global values mentioned above.

# File lib/mkmf.rb, line 1952
def pkg_config(pkg, *options)
  fmt = "not found"
  def fmt.%(x)
    x ? x.inspect : self
  end

  checking_for "pkg-config for #{pkg}", fmt do
    _, ldir = dir_config(pkg)
    if ldir
      pkg_config_path = "#{ldir}/pkgconfig"
      if File.directory?(pkg_config_path)
        Logging.message("PKG_CONFIG_PATH = %s\n", pkg_config_path)
        envs = ["PKG_CONFIG_PATH"=>[pkg_config_path, ENV["PKG_CONFIG_PATH"]].compact.join(File::PATH_SEPARATOR)]
      end
    end
    if pkgconfig = with_config("#{pkg}-config") and find_executable0(pkgconfig)
    # if and only if package specific config command is given
    elsif ($PKGCONFIG ||=
           (pkgconfig = with_config("pkg-config") {config_string("PKG_CONFIG") || "pkg-config"}) &&
           find_executable0(pkgconfig) && pkgconfig) and
         xsystem([*envs, $PKGCONFIG, "--exists", pkg])
      # default to pkg-config command
      pkgconfig = $PKGCONFIG
      args = [pkg]
    elsif find_executable0(pkgconfig = "#{pkg}-config")
    # default to package specific config command, as a last resort.
    else
      pkgconfig = nil
    end
    if pkgconfig
      get = proc {|opts|
        opts = Array(opts).map { |o| "--#{o}" }
        opts = xpopen([*envs, pkgconfig, *opts, *args], err:[:child, :out], &:read)
        Logging.open {puts opts.each_line.map{|s|"=> #{s.inspect}"}}
        opts.strip if $?.success?
      }
    end
    orig_ldflags = $LDFLAGS
    if get and !options.empty?
      get[options]
    elsif get and try_ldflags(ldflags = get['libs'])
      if incflags = get['cflags-only-I']
        $INCFLAGS << " " << incflags
        cflags = get['cflags-only-other']
      else
        cflags = get['cflags']
      end
      libs = get['libs-only-l']
      if cflags
        $CFLAGS += " " << cflags
        $CXXFLAGS += " " << cflags
      end
      if libs
        ldflags = (Shellwords.shellwords(ldflags) - Shellwords.shellwords(libs)).quote.join(" ")
      else
        libs, ldflags = Shellwords.shellwords(ldflags).partition {|s| s =~ /-l([^ ]+)/ }.map {|l|l.quote.join(" ")}
      end
      $libs += " " << libs

      $LDFLAGS = [orig_ldflags, ldflags].join(' ')
      Logging::message "package configuration for %s\n", pkg
      Logging::message "incflags: %s\ncflags: %s\nldflags: %s\nlibs: %s\n\n",
                       incflags, cflags, ldflags, libs
      [[incflags, cflags].join(' '), ldflags, libs]
    else
      Logging::message "package configuration for %s is not found\n", pkg
      nil
    end
  end
end
what_type? (type, member = nil, headers = nil, &b)

Returns a string represents the type of type, or member of type if member is not nil.

# File lib/mkmf.rb, line 1624
def what_type?(type, member = nil, headers = nil, &b)
  m = "#{type}"
  var = val = "*rbcv_var_"
  func = "rbcv_func_(void)"
  if member
    m << "." << member
  else
    type, member = type.split('.', 2)
  end
  if member
    val = "(#{var}).#{member}"
  end
  prelude = [cpp_include(headers).split(/^/)]
  prelude << ["typedef #{type} rbcv_typedef_;\n",
              "extern rbcv_typedef_ *#{func};\n",
              "rbcv_typedef_ #{var};\n",
             ]
  type = "rbcv_typedef_"
  fmt = member && !(typeof = have_typeof?) ? "seems %s" : "%s"
  if typeof
    var = "*rbcv_member_"
    func = "rbcv_mem_func_(void)"
    member = nil
    type = "rbcv_mem_typedef_"
    prelude[-1] << "typedef #{typeof}(#{val}) #{type};\n"
    prelude[-1] << "extern #{type} *#{func};\n"
    prelude[-1] << "#{type} #{var};\n"
    val = var
  end
  def fmt.%(x)
    x ? super : "unknown"
  end
  checking_for checking_message(m, headers), fmt do
    if scalar_ptr_type?(type, member, prelude, &b)
      if try_static_assert("sizeof(*#{var}) == 1", prelude)
        return "string"
      end
      ptr = "*"
    elsif scalar_type?(type, member, prelude, &b)
      unless member and !typeof or try_static_assert("(#{type})-1 < 0", prelude)
        unsigned = "unsigned"
      end
      ptr = ""
    else
      next
    end
    type = UNIVERSAL_INTS.find do |t|
      pre = prelude
      unless member
        pre += [["#{unsigned} #{t} #{ptr}#{var};\n",
                 "extern #{unsigned} #{t} #{ptr}*#{func};\n"]]
      end
      try_static_assert("sizeof(#{ptr}#{val}) == sizeof(#{unsigned} #{t})", pre)
    end
    type or next
    [unsigned, type, ptr].join(" ").strip
  end
end
with_config (config, default=nil) { |config, default| ... }

Tests for the presence of a --with-config or --without-config option. Returns true if the with option is given, false if the without option is given, and the default value otherwise.

This can be useful for adding custom definitions, such as debug information.

Example:

if with_config("debug")
   $defs.push("-DOSSL_DEBUG") unless $defs.include? "-DOSSL_DEBUG"
end
# File lib/mkmf.rb, line 1767
def with_config(config, default=nil)
  config = config.sub(/^--with[-_]/, '')
  val = arg_config("--with-"+config) do
    if arg_config("--without-"+config)
      false
    elsif block_given?
      yield(config, default)
    else
      break default
    end
  end
  case val
  when "yes"
    true
  when "no"
    false
  else
    val
  end
end
with_cppflags (flags) { || ... }

Sets $CPPFLAGS to flags and yields. If the block returns a falsy value, $CPPFLAGS is reset to its previous value, remains set to flags otherwise.

flags

a C preprocessor flag as a String

# File lib/mkmf.rb, line 695
def with_cppflags(flags)
  cppflags = $CPPFLAGS
  $CPPFLAGS = flags.dup
  ret = yield
ensure
  $CPPFLAGS = cppflags unless ret
end
xpopen (command, *mode, &block)

Executes command similarly to xsystem, but yields opened pipe.

# File lib/mkmf.rb, line 457
def xpopen command, *mode, &block
  env, commands = expand_command(command)
  command = [env_quote(env), command].join(' ')
  Logging::open do
    case mode[0]
    when nil, Hash, /^r/
      puts "#{command} |"
    else
      puts "| #{command}"
    end
    IO.popen(env, commands, *mode, &block)
  end
end
xsystem(command, werror: false) → true or false

Executes command with expanding variables, and returns the exit status like as Kernel#system. If werror is true and the error output is not empty, returns false. The output will logged.

# File lib/mkmf.rb, line 438
def xsystem(command, werror: false)
  env, command = expand_command(command)
  Logging::open do
    puts [env_quote(env), command.quote].join(' ')
    if werror
      result = nil
      Logging.postpone do |log|
        output = IO.popen(env, command, &:read)
        result = ($?.success? and File.zero?(log.path))
        output
      end
      result
    else
      system(env, *command)
    end
  end
end