模块 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 配置。
- CONFTEST_CXX
- CXX_EXT
使用 C++ 编译器编译的文件的扩展名
- C_EXT
使用 C 编译器编译的文件的扩展名
- EXPORT_PREFIX
- HDR_EXT
头文件的扩展名
- LIBARG
将库添加到链接器的参数
- LIBPATHFLAG
将库路径添加到链接器的参数
- LINK_SO
链接共享库的命令
- MAIN_DOES_NOTHING
不执行任何工作的 C 主函数
- 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
预处理器宏将传递给编译器。对于 check_signedness('int')
,将设置 SIGNEDNESS_OF_INT=-1
宏。
# 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
定义为无符号长整型,那么convertible_int("foobar_t")
将返回“无符号长整型”,并定义以下宏
#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)生成的各种宏定义。然后,这些定义将被包装在一个基于header
文件名(默认为“extconf.h”)的自定义#ifndef
中。
例如
# 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”。
在任何情况下,返回值都将是一个包含确定的“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
创建一个简单的 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
返回函数 func
是否可以在通用头文件中或您提供的任何 headers
中找到。如果找到,一个宏将作为预处理器常量传递给编译器,使用函数名称(大写),并在前面加上 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
返回给定的入口点 func
是否可以在 lib
中找到。如果 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