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
引數,用於將程式庫路徑新增至連結器
- LINK_SO
指令,用於連結共用程式庫
- MAIN_DOES_NOTHING
不執行任何工作的 C main 函式
- ORIG_LIBPATH
- RPATHFLAG
- RULE_SUBST
- SRC_EXT
來源檔案的副檔名
- TRY_LINK
用於編譯程式以測試連結函式庫的指令
- TRY_LINK_CXX
- UNIVERSAL_INTS
私有類別方法
# File lib/mkmf.rb, line 2844 def self.[](name) @lang.fetch(name) end
# File lib/mkmf.rb, line 2848 def self.[]=(name, mod) @lang[name] = mod end
公開執行個體方法
檢查每個給定的 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
傳回給定 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
傳回給定 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
傳回給定 type
的可轉換整數類型。您可以選擇指定其他 headers
在其中搜尋 type
。可轉換 實際上表示相同的類型,或從相同的類型 typedef 而來。
如果 type
是整數類型,且已找到 可轉換 類型,則會使用 type
名稱(大寫)將下列巨集作為預處理器常數傳遞給編譯器。
-
TYPEOF_
,後接type
名稱,再接=X
,其中「X」是已找到的 可轉換 類型名稱。 -
TYP2NUM
和NUM2TYP
,其中TYP
是type
名稱(大寫),將_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
產生標頭檔,其中包含由其他方法(例如 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
為您的擴充功能產生 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」檔案的資料內容。此檔案的每一行預期都是一個檔案名稱。
以 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
設定 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
建立一個 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 或 --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
在 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
指示 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
傳回是否可以在指定的 paths
中的函式庫 lib
中找到進入點 func
,其中 paths
是字串陣列。如果 func
為 nil
,則 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
傳回靜態類型 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
傳回常數 const
是否已定義。您可以選擇將 const
的 type
傳遞為 [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
傳回在您的系統上是否可以找到指定的 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
傳回是否可以在常見標頭檔或您提供的任何 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
傳回在您的系統上是否可以找到指定的 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
傳回在 lib
中是否可以找到指定的入口點 func
。如果 func
為 nil
,預設會使用 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
傳回 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
傳回 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
傳回靜態類型 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
傳回變數 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
傳回已安裝函式庫的編譯/連結資訊,在 [cflags, ldflags, libs]
的元組中,透過使用在下列指令中首先找到的指令
-
如果透過指令列選項提供
--with-{pkg}-config={command}
:{command} {options}
-
{pkg}-config {options}
-
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
傳回常數 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
傳回靜態類型 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 或 --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
私人實例方法
# 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
# File lib/mkmf.rb, line 2871 def conftest_source CONFTEST_CXX end
# 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
# File lib/mkmf.rb, line 2881 def link_command(ldflags, *opts) conf = link_config(ldflags, *opts) RbConfig::expand(TRY_LINK_CXX.dup, conf) end