模块 MakeMakefile

mkmf.rb 被 Ruby C 扩展使用来生成一个 Makefile,该 Makefile 将正确编译 C 扩展并将其链接到 Ruby 和第三方库。

常量

ASSEMBLE_C

在生成的 Makefile 中将 C 文件转换为汇编源文件的命令

ASSEMBLE_CXX

在生成的 Makefile 中将 C++ 文件转换为汇编源文件的命令

CLEANINGS

将清理扩展构建目录的 Makefile 规则

COMMON_HEADERS

Ruby C 扩展的通用头文件

COMMON_LIBS

Ruby C 扩展的通用库

COMPILE_C

在生成的 Makefile 中编译 C 文件的命令

COMPILE_CXX

在生成的 Makefile 中编译 C++ 文件的命令

COMPILE_RULES

make 编译规则

CONFIG

使用 Ruby 构建时的默认值的 Makefile 配置。

CXX_EXT

使用 C++ 编译器编译的文件扩展名

C_EXT

使用 C 编译器编译的文件扩展名

EXPORT_PREFIX

自动添加到导出的符号的前缀

HDR_EXT

头文件的扩展名

LANGUAGE

此模块所针对的语言

LIBARG

将库添加到链接器的参数

LIBPATHFLAG

将库路径添加到链接器的参数

链接共享库的命令

MAIN_DOES_NOTHING

一个不执行任何工作的 C main 函数

ORIG_LIBPATH

LIB 环境变量的已保存的原始值

RPATHFLAG

将运行时库路径添加到链接器的参数

RULE_SUBST

NMake 规则中的替换

RbConfig

构建的扩展运行的目标平台的 RbConfig。

SRC_EXT

源文件的扩展名

编译程序以测试链接库的命令

UNIVERSAL_INTS

convertible_int 的类型名称

公共类方法

rm_f(*files) 单击以切换源代码

删除 files

# File 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) 单击以切换源代码

递归删除 files

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

私有类方法

[](name) 单击以切换源代码

检索 name 语言的模块。

# File mkmf.rb, line 2974
def self.[](name)
  @lang.fetch(name)
end
[]=(name, mod) 单击以切换源代码

定义 name 语言的模块。

# File mkmf.rb, line 2980
def self.[]=(name, mod)
  @lang[name] = mod
end

公共实例方法

append_cflags(flags, **opts) 单击以切换源代码

检查每个给定的 C 编译器标志是否可接受,如果可接受,则将其追加到 $CFLAGS

flags

作为 StringArray 的 C 编译器标志

# File 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) 单击以切换源代码

检查每个给定的链接器标志是否可接受,如果可接受,则将其追加到 $LDFLAGS

flags

作为 StringArray 的链接器标志

# File 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) 单击以切换源代码

返回给定 type 的有符号性。您可以选择指定其他 headers 来搜索 type

如果找到 type 并且是数值类型,则使用 type 名称,以大写形式,前面加上 SIGNEDNESS_OF_,后跟 type 名称,再后跟 =X,其中 “X” 是正整数(如果 type 是无符号的),负整数(如果 type 是有符号的),作为预处理器常量传递给编译器。

例如,如果 size_t 定义为无符号,那么 check_signedness('size_t') 将返回 +1,并且 SIGNEDNESS_OF_SIZE_T=+1 预处理器宏将传递给编译器。对于 check_signedness('int'),将设置 SIGNEDNESS_OF_INT=-1 宏。

# File 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) 单击以切换源代码

返回给定 type 的大小。您可以选择指定其他 headers 来搜索 type

如果找到,则使用类型名称(大写形式),前面加上 SIZEOF_,后跟类型名称,再后跟 =X,其中 “X” 是实际大小,作为预处理器常量传递给编译器。

例如,如果 check_sizeof('mystruct') 返回 12,则 SIZEOF_MYSTRUCT=12 预处理器宏将传递给编译器。

# File 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() 单击以切换源代码

返回用于配置检查的与语言相关的源文件名。

# File mkmf.rb, line 488
def conftest_source
  CONFTEST_C
end
convertible_int(type, headers = nil, opts = nil, &b) 单击以切换源代码

返回给定 type 的可转换整数类型。您可以选择指定其他 headers 来搜索 type可转换实际上表示相同的类型,或者从相同类型定义而来。

如果 type 是整数类型,并且找到了 可转换 类型,则将以下宏作为预处理器常量传递给编译器,使用 type 名称(大写形式)。

  • TYPEOF_,后跟 type 名称,再后跟 =X,其中 “X” 是找到的 可转换 类型名称。

  • TYP2NUMNUM2TYP,其中 TYPtype 名称,大写形式,并将 _t 后缀替换为 “T”,后跟 =X,其中 “X” 是将 type 转换为 Integer 对象的宏名称,反之亦然。

例如,如果 foobar_t 定义为 unsigned long,那么 convertible_int("foobar_t") 将返回 “unsigned long”,并定义以下宏

#define TYPEOF_FOOBAR_T unsigned long
#define FOOBART2NUM ULONG2NUM
#define NUM2FOOBART NUM2ULONG
# File 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") 单击以切换源代码

生成一个头文件,其中包含其他方法(如 have_func 和 have_header)生成的各种宏定义。然后,这些定义将包装在基于 header 文件名的自定义 #ifndef 中,默认为“extconf.h”。

例如

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

上面的脚本将生成以下 extconf.h 文件

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

鉴于 create_header 方法会根据在 extconf.rb 文件中较早设置的定义生成文件,因此您可能希望将其作为脚本中调用的最后一个方法之一。

# File 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| ... } 单击以切换源代码

为您的扩展生成 Makefile,传递您可能通过其他方法生成的任何选项和预处理器常量。

target 名称应与 C 扩展中定义的全局函数名称相对应,减去 Init_。例如,如果您的 C 扩展定义为 Init_foo,则您的目标将仅仅是“foo”。

如果目标名称中存在任何“/”字符,则只有最后一个名称被解释为目标名称,其余名称被视为顶层目录名称,并且生成的 Makefile 将进行相应更改以遵循该目录结构。

例如,如果您传递 “test/foo” 作为目标名称,则您的扩展将安装在“test”目录下。这意味着为了稍后在 Ruby 程序中加载该文件,必须遵循该目录结构,例如 require 'test/foo'

当您的源文件与构建脚本不在同一目录中时,应使用 srcprefix。这不仅消除了您手动将源文件复制到与构建脚本相同目录的需要,而且还在生成的 Makefile 中设置了正确的 target_prefix

设置 target_prefix 反过来会在您运行 make install 时,将生成的二进制文件安装在 RbConfig::CONFIG['sitearchdir'] 下的目录中,该目录模仿您的本地文件系统。

例如,给定以下文件树

ext/
  extconf.rb
  test/
    foo.c

并给定以下代码

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

这将在生成的 Makefile 中将 target_prefix 设置为 “test”。反过来,当通过 make install 命令安装时,这将创建以下文件树

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

建议您使用此方法来生成 Makefile,而不是手动复制文件,因为某些第三方库可能依赖于正确设置的 target_prefix

srcprefix 参数可用于覆盖默认源目录,即当前目录。它包含在 VPATH 中,并添加到 INCFLAGS 列表中。

# File mkmf.rb, line 2383
  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| ... } 单击以切换源代码

COMMON_HEADERSsrc 创建临时源文件。如果给定了代码块,则返回创建的源字符串,并使用返回的字符串作为源代码。

# File 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) 点击切换源代码

处理 “depend” 文件的数据内容。此文件的每一行都应为一个文件名。

以 Makefile 格式返回结果。

# File mkmf.rb, line 2268
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)

设置一个 target 名称,用户可以使用该名称在命令行上配置各种 “with” 选项。例如,如果目标设置为 “foo”,则用户可以使用 --with-foo-dir=prefix--with-foo-include=dir--with-foo-lib=dir 命令行选项来指定搜索头文件/库文件的位置。

您可以传递其他参数来指定默认值。如果给出一个参数,则将其视为默认的 prefix,如果给出两个参数,则按顺序将其视为 “include” 和 “lib” 的默认值。

在任何情况下,返回值都将是一个确定的 “include” 和 “lib” 目录数组,如果在未指定默认值时未给出相应的命令行选项,则其中任何一个都可以为 nil。

请注意,dir_config 仅添加到搜索库和头文件的位置列表中。它不会将库链接到您的应用程序中。

# File 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) 点击切换源代码

创建存根 Makefile。

# File mkmf.rb, line 2237
  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) 点击切换源代码

返回是否可以使用 C 预处理器预处理 src 并与 pat 匹配。

如果给定了代码块,则在编译之前使用源代码调用该代码块。您可以在该代码块中修改源代码。

pat

一个正则表达式或一个字符串

src

一个包含 C 源代码的字符串

opt

一个包含预处理器选项的字符串

注意:当 pat 是一个正则表达式时,将在进程中检查匹配,否则将调用 egrep(1) 来检查匹配。

# File 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| ... } 点击切换源代码

测试是否存在 --enable-config--disable-config 选项。如果给出了 enable 选项,则返回 true;如果给出了 disable 选项,则返回 false;否则返回默认值。

这对于添加自定义定义(例如调试信息)非常有用。

示例

if enable_config("debug")
   $defs.push("-DOSSL_DEBUG") unless $defs.include? "-DOSSL_DEBUG"
end
# File 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) 点击切换源代码

指示 mkmf 在提供的任何 paths 中搜索给定的 header,并返回是否在这些路径中找到了该头文件。

如果找到头文件,则将其所在的路径添加到发送到编译器(通过 -I 开关)的包含目录列表中。

# File 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) 点击切换源代码

返回是否可以在指定的 paths 之一的库 lib 中找到入口点 func,其中 paths 是一个字符串数组。如果 funcnil,则使用 main() 函数作为入口点。

如果找到 lib,则将其所在的路径添加到搜索和链接的库路径列表中。

# File 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) 点击切换源代码

返回是否可以在您的系统上找到给定的 framework。如果找到,则使用框架名称(大写,前缀为 HAVE_FRAMEWORK_)作为预处理器常量传递给编译器。

例如,如果 have_framework('Ruby') 返回 true,则 HAVE_FRAMEWORK_RUBY 预处理器宏将传递给编译器。

如果 fw 是框架名称及其头文件名称的组合,则会检查该头文件,而不是通常使用的与框架同名的头文件。

# File 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) 点击切换源代码

返回是否可以在公共头文件中或您提供的任何 headers 中找到函数 func。如果找到,则使用函数名称(大写,前缀为 HAVE_)作为预处理器常量传递给编译器。

要检查其他库中的函数,您需要首先使用 have_library() 检查该库。func 应为纯函数名称或带有参数的函数名称。

例如,如果 have_func('foo') 返回 true,则 HAVE_FOO 预处理器宏将传递给编译器。

# File 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) 点击切换源代码

返回是否可以在您的系统上找到给定的 header 文件。如果找到,则使用头文件名(大写,前缀为 HAVE_)作为预处理器常量传递给编译器。

例如,如果 have_header('foo.h') 返回 true,则 HAVE_FOO_H 预处理器宏将传递给编译器。

# File 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) 点击切换源代码

返回是否可以在 lib 中找到给定的入口点 func。如果 funcnil,则默认使用 main() 入口点。如果找到,则将其添加到链接扩展时要使用的库列表中。

如果提供了 headers,它将在搜索 func 时将这些头文件包含在它查找的头文件中。

可以通过 --with-FOOlib 配置选项更改要链接的库的实际名称。

# File 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) 点击切换源代码

返回是否在公共头文件中或您提供的任何 headers 中定义了 macro

您传递给 opt 的任何选项都将传递给编译器。

# File 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) 点击切换源代码

返回类型为 type 的结构是否包含 member。如果它不包含,或者找不到该结构类型,则返回 false。您可以选择指定其他 headers 来查找结构(除了公共头文件之外)。

如果找到,则使用类型名称和成员名称(大写,前缀为 HAVE_)作为预处理器常量传递给编译器。

例如,如果 have_struct_member('struct foo', 'bar') 返回 true,则 HAVE_STRUCT_FOO_BAR 预处理器宏将传递给编译器。

为了向后兼容,还定义了 HAVE_ST_BAR

# File 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) 点击切换源代码

返回是否可以在公共头文件中或您提供的任何 headers 中找到变量 var。如果找到,则使用变量名称(大写,前缀为 HAVE_)作为预处理器常量传递给编译器。

要检查其他库中的变量,您需要首先使用 have_library() 检查该库。

例如,如果 have_var('foo') 返回 true,则 HAVE_FOO 预处理器宏将传递给编译器。

# File 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") 点击切换源代码

记录 src

# File 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) 点击切换源代码

如果 target 文件存在并且比所有 times 新或等于,则返回 target 文件的时间戳。

# File 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) 点击切换源代码

通过使用以下命令中首先找到的命令,以 [cflags, ldflags, libs] 元组的形式返回有关已安装库的编译/链接信息

  1. 如果通过命令行选项给出了 --with-{pkg}-config={command}{command} {options}

  2. {pkg}-config {options}

  3. pkg-config {options} {pkg}

其中 options 是没有破折号的选项名称,例如 --cflags 标志的 "cflags"

获得的值将附加到 $INCFLAGS$CFLAGS$LDFLAGS$libs

如果给出了一个或多个 options 参数,则会使用这些选项调用 config 命令,并返回经过剥离的输出字符串,而不会修改上述任何全局值。

# File 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") || ENV["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}"}}
        if $?.success?
          opts = opts.strip
          libarg, libpath = LIBARG, LIBPATHFLAG.strip
          opts = opts.shellsplit.map { |s|
            if s.start_with?('-l')
              libarg % s[2..]
            elsif s.start_with?('-L')
              libpath % s[2..]
            else
              s
            end
          }.quote.join(" ")
          opts
        end
      }
    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) 点击切换源代码

返回一个字符串,表示 type 的类型,如果 member 不为 nil,则表示 typemember 的类型。

# File 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| ... } 点击切换源代码

测试是否存在 --with-配置--without-配置 选项。如果给出了 with 选项,则返回 true;如果给出了 without 选项,则返回 false;否则返回默认值。

这对于添加自定义定义(例如调试信息)非常有用。

示例

if with_config("debug")
   $defs.push("-DOSSL_DEBUG") unless $defs.include? "-DOSSL_DEBUG"
end
# File 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) { || ... } 点击以切换源代码

$CPPFLAGS 设置为 flags 并执行 yield。如果代码块返回一个 falsy 值,则 $CPPFLAGS 将重置为其先前的值,否则仍然设置为 flags

flags

一个作为 String 的 C 预处理器标志

# File 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) 点击以切换源代码

与 xsystem 类似地执行 command,但会 yield 打开的管道。

# File 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 或 false 点击以切换源代码

执行带有展开变量的 command,并像 Kernel#system 一样返回退出状态。如果 werror 为 true 且错误输出不为空,则返回 false。输出将被记录。

# File 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

私有实例方法

rm_f(*files) 单击以切换源代码

删除 files

# File 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) 单击以切换源代码

递归删除 files

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