MakeMakefile 模組

Ruby C 擴充套件使用 mkmf.rb 來產生 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

Makefile 組態,使用 Ruby 建置時的預設值。

CONFTEST_CXX
CXX_EXT

使用 C++ 編譯器編譯的檔案的副檔名

C_EXT

使用 C 編譯器編譯的檔案的副檔名

EXPORT_PREFIX
HDR_EXT

標頭檔案的副檔名

LIBARG

引數,用於將程式庫新增至連結器

LIBPATHFLAG

引數,用於將程式庫路徑新增至連結器

指令,用於連結共用程式庫

MAIN_DOES_NOTHING

不執行任何工作的 C main 函式

ORIG_LIBPATH
RPATHFLAG
RULE_SUBST
SRC_EXT

來源檔案的副檔名

用於編譯程式以測試連結函式庫的指令

UNIVERSAL_INTS

私有類別方法

[](name) 按一下以切換來源
# File lib/mkmf.rb, line 2844
def self.[](name)
  @lang.fetch(name)
end
[]=(name, mod) 按一下以切換來源
# File lib/mkmf.rb, line 2848
def self.[]=(name, mod)
  @lang[name] = mod
end

公開執行個體方法

append_cflags(flags, *opts) 按一下以切換來源

檢查每個給定的 C 編譯器旗標是否可接受,如果可以,則將其附加到 $CFLAGS

flags

C 編譯器旗標,為 String 或其 Array

# File lib/mkmf.rb, line 1015
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
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 預處理器巨集會傳遞給編譯器。SIGNEDNESS_OF_INT=-1 巨集會設定為 check_signedness('int')

# File lib/mkmf.rb, line 1416
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 lib/mkmf.rb, line 1387
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
convertible_int(type, headers = nil, opts = nil, &b) 按一下以切換來源

傳回給定 type 的可轉換整數類型。您可以選擇指定其他 headers 在其中搜尋 type可轉換 實際上表示相同的類型,或從相同的類型 typedef 而來。

如果 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 lib/mkmf.rb, line 1451
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)產生的各種巨集定義。這些定義會包裝在自訂的 #ifndef 中,其根據 header 檔名而定,預設為「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 lib/mkmf.rb, line 1743
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 時,會將產生的二進位檔安裝到 RbConfig::CONFIG['sitearchdir'] 下的目錄中,當您執行 make install 時,該目錄會模擬您的本機檔案系統。

例如,給定以下檔案樹

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 lib/mkmf.rb, line 2271
  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)$(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
depend_rules(depend) 按一下以切換來源

處理「depend」檔案的資料內容。此檔案的每一行預期都是一個檔案名稱。

以 Makefile 格式傳回結果。

# File lib/mkmf.rb, line 2156
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 lib/mkmf.rb, line 1793
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) 按一下以切換來源

建立一個 stub Makefile。

# File lib/mkmf.rb, line 2125
  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
enable_config(config, default=nil) { |config, default| ... } 按一下以切換原始碼

測試是否存在 --enable-config--disable-config 選項。如果給定啟用選項,則傳回 true;如果給定停用選項,則傳回 false;否則傳回預設值。

這有助於新增自訂定義,例如偵錯資訊。

範例

if enable_config("debug")
   $defs.push("-DOSSL_DEBUG") unless $defs.include? "-DOSSL_DEBUG"
end
# File lib/mkmf.rb, line 1705
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_executable(bin, path = nil) 按一下以切換原始碼

path 上搜尋可執行檔 bin。預設路徑為 PATH 環境變數。如果未定義,則會改為搜尋 /usr/local/bin、/usr/ucb、/usr/bin 和 /bin。

如果找到,則會傳回找到位置的完整路徑,包括可執行檔名稱。

請注意,此方法實際上不會影響產生的 Makefile。

# File lib/mkmf.rb, line 1635
def find_executable(bin, path = nil)
  checking_for checking_message(bin, path) do
    find_executable0(bin, path)
  end
end
find_header(header, *paths) 按一下以切換原始碼

指示 mkmf 在提供的任何 paths 中搜尋給定的 header,並傳回是否在這些路徑中找到它。

如果找到標頭,則將其找到的路徑新增到傳送到編譯器(透過 -I 開關)的包含目錄清單中。

# File lib/mkmf.rb, line 1199
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 lib/mkmf.rb, line 1073
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
find_type(type, opt, *headers, &b) 按一下以切換原始碼

傳回靜態類型 type 的定義位置。

您也可以將其他旗標傳遞給 opt,然後將其傳遞給編譯器。

另請參閱 have_type

# File lib/mkmf.rb, line 1296
def find_type(type, opt, *headers, &b)
  opt ||= ""
  fmt = "not found"
  def fmt.%(x)
    x ? x.respond_to?(:join) ? x.join(",") : x : self
  end
  checking_for checking_message(type, nil, opt), fmt do
    headers.find do |h|
      try_type(type, h, opt, &b)
    end
  end
end
have_const(const, headers = nil, opt = "", &b) 按一下以切換原始碼

傳回常數 const 是否已定義。您可以選擇將 consttype 傳遞為 [const, type],例如

have_const(%w[PTHREAD_MUTEX_INITIALIZER pthread_mutex_t], "pthread.h")

您也可以傳遞其他 headers 以檢查常見標頭檔案,以及傳遞其他旗標給 opt,然後將其傳遞給編譯器。

如果找到,會使用類型名稱(以大寫開頭)加上 HAVE_CONST_ 作為預處理器常數傳遞巨集給編譯器。

例如,如果 have_const('foo') 傳回 true,則會將 HAVE_CONST_FOO 預處理器巨集傳遞給編譯器。

# File lib/mkmf.rb, line 1344
def have_const(const, headers = nil, opt = "", &b)
  checking_for checking_message([*const].compact.join(' '), headers, opt) do
    try_const(const, headers, opt, &b)
  end
end
have_framework(fw, &b) 按一下以切換來源

傳回在您的系統上是否可以找到指定的 framework。如果找到,會使用框架名稱(以大寫開頭)加上 HAVE_FRAMEWORK_ 作為預處理器常數傳遞巨集給編譯器。

例如,如果 have_framework('Ruby') 傳回 true,則會將 HAVE_FRAMEWORK_RUBY 預處理器巨集傳遞給編譯器。

如果 fw 是框架名稱及其標頭檔名稱的一組,則會檢查標頭檔,而不是使用與框架名稱相同的通常使用的標頭檔。

# File lib/mkmf.rb, line 1170
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 lib/mkmf.rb, line 1107
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 lib/mkmf.rb, line 1147
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 lib/mkmf.rb, line 1047
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) 按一下以切換來源

傳回 macro 是否在共通標頭檔案或您提供的任何 headers 中定義。

您傳遞給 opt 的任何選項都會傳遞給編譯器。

# File lib/mkmf.rb, line 1030
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 lib/mkmf.rb, line 1235
  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_type(type, headers = nil, opt = "", &b) 按一下以切換來源

傳回靜態類型 type 是否已定義。您也可以傳遞其他 headers 來檢查,除了共通標頭檔案之外。

您也可以將其他旗標傳遞給 opt,然後將其傳遞給編譯器。

如果找到,會使用類型名稱 (大寫) 傳遞巨集作為預處理器常數給編譯器,並加上 HAVE_TYPE_ 前綴。

例如,如果 have_type('foo') 傳回 true,則會將 HAVE_TYPE_FOO 預處理器巨集傳遞給編譯器。

# File lib/mkmf.rb, line 1283
def have_type(type, headers = nil, opt = "", &b)
  checking_for checking_message(type, headers, opt) do
    try_type(type, headers, opt, &b)
  end
end
have_var(var, headers = nil, opt = "", &b) 按一下以切換來源

傳回變數 var 是否可以在常見的標頭檔中找到,或是在您提供的任何 headers 中找到。如果找到,會將巨集作為預處理器常數傳遞給編譯器,使用變數名稱,以大寫字母開頭,並加上 HAVE_

若要檢查其他函式庫中的變數,您需要先使用 have_library() 檢查該函式庫。

例如,如果 have_var('foo') 傳回 true,則 HAVE_FOO 預處理器巨集會傳遞給編譯器。

# File lib/mkmf.rb, line 1129
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
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 參數,則會使用選項呼叫設定指令,並傳回一個剝離的輸出字串,而不會修改上面提到的任何全域值。

# File lib/mkmf.rb, line 1855
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
try_const(const, headers = nil, opt = "", &b) 按一下以切換來源

傳回常數 const 是否已定義。

另請參閱 have_const

# File lib/mkmf.rb, line 1313
  def try_const(const, headers = nil, opt = "", &b)
    const, type = *const
    if try_compile(<<"SRC", opt, &b)
#{cpp_include(headers)}
/*top*/
typedef #{type || 'int'} conftest_type;
conftest_type conftestval = #{type ? '' : '(int)'}#{const};
SRC
      $defs.push(format("-DHAVE_CONST_%s", const.tr_cpp))
      true
    else
      false
    end
  end
try_type(type, headers = nil, opt = "", &b) 按一下以切換來源

傳回靜態類型 type 是否已定義。

另請參閱 have_type

# File lib/mkmf.rb, line 1256
  def try_type(type, headers = nil, opt = "", &b)
    if try_compile(<<"SRC", opt, &b)
#{cpp_include(headers)}
/*top*/
typedef #{type} conftest_type;
int conftestval[sizeof(conftest_type)?1:-1];
SRC
      $defs.push(format("-DHAVE_TYPE_%s", type.tr_cpp))
      true
    else
      false
    end
  end
with_config(config, default=nil) { |config, default| ... } 按一下以切換來源

測試是否存在 --with-config--without-config 選項。如果提供 with 選項,則傳回 true;如果提供 without 選項,則傳回 false;否則傳回預設值。

這有助於新增自訂定義,例如偵錯資訊。

範例

if with_config("debug")
   $defs.push("-DOSSL_DEBUG") unless $defs.include? "-DOSSL_DEBUG"
end
# File lib/mkmf.rb, line 1670
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

私人實例方法

cc_command(opt="") 按一下以切換來源
# File lib/mkmf.rb, line 2875
def cc_command(opt="")
  conf = cc_config(opt)
  RbConfig::expand("$(CXX) #$INCFLAGS #$CPPFLAGS #$CXXFLAGS #$ARCH_FLAG #{opt} -c #{CONFTEST_CXX}",
                   conf)
end
conftest_source() 按一下以切換來源
# File lib/mkmf.rb, line 2871
def conftest_source
  CONFTEST_CXX
end
have_devel?() 按一下以切換來源
# File lib/mkmf.rb, line 2863
def have_devel?
  unless defined? @have_devel
    @have_devel = true
    @have_devel = try_link(MAIN_DOES_NOTHING)
  end
  @have_devel
end