mkmf.rb is used by Ruby C extensions to generate a Makefile which will correctly compile and link the C extension to Ruby and a third-party library.
The makefile configuration using the defaults from when Ruby was built.
Extensions for files compiled with a C compiler
Extensions for files complied with a C++ compiler
Extensions for source files
Extensions for header files
Common headers for Ruby C extensions
Common libraries for Ruby C extensions
make compile rules
Command which will compile C files in the generated Makefile
Command which will compile C++ files in the generated Makefile
Command which will translate C files to assembler sources in the generated Makefile
Command which will translate C++ files to assembler sources in the generated Makefile
Command which will compile a program in order to test linking a library
Command which will link a shared library
Argument which will add a library path to the linker
Argument which will add a library to the linker
A C main function which does no work
Makefile rules that will clean the extension build directory
# File tmp/rubies/ruby-2.5.9/lib/mkmf.rb, line 1361
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
Returns the signedness of the given type
. You may optionally specify additional headers
to search in for the type
.
If the type
is found and is a numeric type, a macro is passed as a preprocessor constant to the compiler using the type
name, in uppercase, prepended with SIGNEDNESS_OF_
, followed by the type
name, followed by =X
where “X” is positive integer if the type
is unsigned and a negative integer if the type
is signed.
For example, if size_t
is defined as unsigned, then check_signedness('size_t')
would return +1 and the SIGNEDNESS_OF_SIZE_T=+1
preprocessor macro would be passed to the compiler. The SIGNEDNESS_OF_INT=-1
macro would be set for check_signedness('int')
# File tmp/rubies/ruby-2.5.9/lib/mkmf.rb, line 1332
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
Returns the size of the given type
. You may optionally specify additional headers
to search in for the type
.
If found, a macro is passed as a preprocessor constant to the compiler using the type name, in uppercase, prepended with SIZEOF_
, followed by the type name, followed by =X
where “X” is the actual size.
For example, if check_sizeof('mystruct')
returned 12, then the SIZEOF_MYSTRUCT=12
preprocessor macro would be passed to the compiler.
# File tmp/rubies/ruby-2.5.9/lib/mkmf.rb, line 1396
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
Returns the convertible integer type of the given type
. You may optionally specify additional headers
to search in for the type
. convertible means actually the same type, or typedef’d from the same type.
If the type
is an integer type and the convertible type is found, the following macros are passed as preprocessor constants to the compiler using the type
name, in uppercase.
-
TYPEOF_
, followed by thetype
name, followed by=X
where “X” is the found convertible type name. -
TYP2NUM
andNUM2TYP
, whereTYP
is thetype
name in uppercase with replacing an_t
suffix with “T”, followed by=X
where “X” is the macro name to converttype
to anInteger
object, and vice versa.
For example, if foobar_t
is defined as unsigned long, then convertible_int("foobar_t")
would return “unsigned long”, and define these macros:
#define TYPEOF_FOOBAR_T unsigned long #define FOOBART2NUM ULONG2NUM #define NUM2FOOBART NUM2ULONG
# File tmp/rubies/ruby-2.5.9/lib/mkmf.rb, line 1688
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 (IO.read(header) == hdr rescue false)
open(header, "wb") do |hfile|
hfile.write(hdr)
end
end
$extconf_h = header
end
Generates a header file consisting of the various macro definitions generated by other methods such as have_func
and have_header. These are then wrapped in a custom #ifndef
based on the header
file name, which defaults to “extconf.h”.
For example:
# extconf.rb require 'mkmf' have_func('realpath') have_header('sys/utime.h') create_header create_makefile('foo')
The above script would generate the following extconf.h file:
#ifndef EXTCONF_H #define EXTCONF_H #define HAVE_REALPATH 1 #define HAVE_SYS_UTIME_H 1 #endif
Given that the create_header
method generates a file based on definitions set earlier in your extconf.rb file, you will probably want to make this one of the last methods you call in your script.
# File tmp/rubies/ruby-2.5.9/lib/mkmf.rb, line 2187
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{,})}}")].sort
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.sort.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.sub(/\A\.\//, ''))})
$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)'
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 = *.#{$OBJEXT} #{config_string('cleanobjs') {|t| t.gsub(/\$\*/, "$(TARGET)#{deffile ? '-$(arch)': ''}")} if target} *.bak
" #"
conf = yield(conf) if block_given?
mfile = 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 = timestamp_file(dir, target_prefix)
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
dirs.unshift(sodir) if target and !dirs.include?(sodir)
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 " #{timestamp_file(sodir, target_prefix)}" 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} $(@) 2> /dev/null || true"
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
Generates the Makefile for your extension, passing along any options and preprocessor constants that you may have generated through other methods.
The target
name should correspond the name of the global function name defined within your C extension, minus the Init_
. For example, if your C extension is defined as Init_foo
, then your target would simply be “foo”.
If any “/” characters are present in the target name, only the last name is interpreted as the target name, and the rest are considered toplevel directory names, and the generated Makefile will be altered accordingly to follow that directory structure.
For example, if you pass “test/foo” as a target name, your extension will be installed under the “test” directory. This means that in order to load the file within a Ruby program later, that directory structure will have to be followed, e.g. require 'test/foo'
.
The srcprefix
should be used when your source files are not in the same directory as your build script. This will not only eliminate the need for you to manually copy the source files into the same directory as your build script, but it also sets the proper target_prefix
in the generated Makefile.
Setting the target_prefix
will, in turn, install the generated binary in a directory under your RbConfig::CONFIG['sitearchdir']
that mimics your local filesystem when you run make install
.
For example, given the following file tree:
ext/ extconf.rb test/ foo.c
And given the following code:
create_makefile('test/foo', 'test')
That will set the target_prefix
in the generated Makefile to “test”. That, in turn, will create the following file tree when installed via the make install
command:
/path/to/ruby/sitearchdir/test/foo.so
It is recommended that you use this approach to generate your makefiles, instead of copying files around manually, because some third party libraries may depend on the target_prefix
being set properly.
The srcprefix
argument can be used to override the default source directory, i.e. the current directory. It is included as part of the VPATH
and added to the list of INCFLAGS
.
# File tmp/rubies/ruby-2.5.9/lib/mkmf.rb, line 2071
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)
line.gsub!(%r"\$\(hdrdir\)/(?!ruby(?![^:;/\s]))(?=[-\w]+\.h)", '\&ruby/')
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
Processes the data contents of the “depend” file. Each line of this file is expected to be a file name.
Returns the output of findings, in Makefile format.
# File tmp/rubies/ruby-2.5.9/lib/mkmf.rb, line 1738
def dir_config(target, idefault=nil, ldefault=nil)
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)
$arg_config.last[1] ||= "${#{target}-dir}/include"
ldir = with_config(target + "-lib", ldefault)
$arg_config.last[1] ||= "${#{target}-dir}/#{_libdir_basename}"
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
[idir, ldir]
end
Sets a target
name that the user can then use to configure various “with” options with on the command line by using that name. For example, if the target is set to “foo”, then the user could use the --with-foo-dir=prefix
, --with-foo-include=dir
and --with-foo-lib=dir
command line options to tell where to search for header/library files.
You may pass along additional parameters to specify default values. If one is given it is taken as default prefix
, and if two are given they are taken as “include” and “lib” defaults in that order.
In any case, the return value will be an array of determined “include” and “lib” directories, either of which can be nil if no corresponding command line option is given when no default value is specified.
Note that dir_config
only adds to the list of places to search for libraries and include files. It does not link the libraries into your application.
# File tmp/rubies/ruby-2.5.9/lib/mkmf.rb, line 2040
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
creates a stub Makefile.
# File tmp/rubies/ruby-2.5.9/lib/mkmf.rb, line 1650
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
Tests for the presence of an --enable-
config or --disable-
config option. Returns true
if the enable option is given, false
if the disable option is given, and the default value otherwise.
This can be useful for adding custom definitions, such as debug information.
Example:
if enable_config("debug") $defs.push("-DOSSL_DEBUG") unless $defs.include? "-DOSSL_DEBUG" end
# File tmp/rubies/ruby-2.5.9/lib/mkmf.rb, line 1580
def find_executable(bin, path = nil)
checking_for checking_message(bin, path) do
find_executable0(bin, path)
end
end
Searches for the executable bin
on path
. The default path is your PATH
environment variable. If that isn’t defined, it will resort to searching /usr/local/bin, /usr/ucb, /usr/bin and /bin.
If found, it will return the full path, including the executable name, of where it was found.
Note that this method does not actually affect the generated Makefile.
# File tmp/rubies/ruby-2.5.9/lib/mkmf.rb, line 1146
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
Instructs mkmf to search for the given header
in any of the paths
provided, and returns whether or not it was found in those paths.
If the header is found then the path it was found on is added to the list of included directories that are sent to the compiler (via the -I
switch).
# File tmp/rubies/ruby-2.5.9/lib/mkmf.rb, line 1022
def find_library(lib, func, *paths, &b)
lib = with_config(lib+'lib', lib)
paths = paths.collect {|path| path.split(File::PATH_SEPARATOR)}.flatten
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
Returns whether or not the entry point func
can be found within the library lib
in one of the paths
specified, where paths
is an array of strings. If func
is nil
, then the main()
function is used as the entry point.
If lib
is found, then the path it was found on is added to the list of library paths searched and linked against.
# File tmp/rubies/ruby-2.5.9/lib/mkmf.rb, line 1243
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
Returns where the static type type
is defined.
You may also pass additional flags to opt
which are then passed along to the compiler.
See also have_type
.
# File tmp/rubies/ruby-2.5.9/lib/mkmf.rb, line 1291
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
Returns whether or not the constant const
is defined. You may optionally pass the type
of const
as [const, type]
, such as:
have_const(%w[PTHREAD_MUTEX_INITIALIZER pthread_mutex_t], "pthread.h")
You may also pass additional headers
to check against in addition to the common header files, and additional flags to opt
which are then passed along to the compiler.
If found, a macro is passed as a preprocessor constant to the compiler using the type name, in uppercase, prepended with HAVE_CONST_
.
For example, if have_const('foo')
returned true, then the HAVE_CONST_FOO
preprocessor macro would be passed to the compiler.
# File tmp/rubies/ruby-2.5.9/lib/mkmf.rb, line 1117
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
Returns whether or not the given framework
can be found on your system. If found, a macro is passed as a preprocessor constant to the compiler using the framework name, in uppercase, prepended with HAVE_FRAMEWORK_
.
For example, if have_framework('Ruby')
returned true, then the HAVE_FRAMEWORK_RUBY
preprocessor macro would be passed to the compiler.
If fw
is a pair of the framework name and its header file name that header file is checked, instead of the normally used header file which is named same as the framework.
# File tmp/rubies/ruby-2.5.9/lib/mkmf.rb, line 1055
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
Returns whether or not the function func
can be found in the common header files, or within any headers
that you provide. If found, a macro is passed as a preprocessor constant to the compiler using the function name, in uppercase, prepended with HAVE_
.
To check functions in an additional library, you need to check that library first using have_library()
. The func
shall be either mere function name or function name with arguments.
For example, if have_func('foo')
returned true
, then the HAVE_FOO
preprocessor macro would be passed to the compiler.
# File tmp/rubies/ruby-2.5.9/lib/mkmf.rb, line 1095
def have_header(header, preheaders = nil, opt = "", &b)
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
Returns whether or not the given header
file can be found on your system. If found, a macro is passed as a preprocessor constant to the compiler using the header file name, in uppercase, prepended with HAVE_
.
For example, if have_header('foo.h')
returned true, then the HAVE_FOO_H
preprocessor macro would be passed to the compiler.
# File tmp/rubies/ruby-2.5.9/lib/mkmf.rb, line 997
def have_library(lib, func = nil, headers = nil, opt = "", &b)
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
Returns whether or not the given entry point func
can be found within lib
. If func
is nil
, the main()
entry point is used by default. If found, it adds the library to list of libraries to be used when linking your extension.
If headers
are provided, it will include those header files as the header files it looks in when searching for func
.
The real name of the library to be linked can be altered by --with-FOOlib
configuration option.
# File tmp/rubies/ruby-2.5.9/lib/mkmf.rb, line 980
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
Returns whether or not macro
is defined either in the common header files or within any headers
you provide.
Any options you pass to opt
are passed along to the compiler.
# File tmp/rubies/ruby-2.5.9/lib/mkmf.rb, line 1182
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
Returns whether or not the struct of type type
contains member
. If it does not, or the struct type can’t be found, then false is returned. You may optionally specify additional headers
in which to look for the struct (in addition to the common header files).
If found, a macro is passed as a preprocessor constant to the compiler using the type name and the member name, in uppercase, prepended with HAVE_
.
For example, if have_struct_member('struct foo', 'bar')
returned true, then the HAVE_STRUCT_FOO_BAR
preprocessor macro would be passed to the compiler.
HAVE_ST_BAR
is also defined for backward compatibility.
# File tmp/rubies/ruby-2.5.9/lib/mkmf.rb, line 1230
def have_type(type, headers = nil, opt = "", &b)
checking_for checking_message(type, headers, opt) do
try_type(type, headers, opt, &b)
end
end
Returns whether or not the static type type
is defined. You may optionally pass additional headers
to check against in addition to the common header files.
You may also pass additional flags to opt
which are then passed along to the compiler.
If found, a macro is passed as a preprocessor constant to the compiler using the type name, in uppercase, prepended with HAVE_TYPE_
.
For example, if have_type('foo')
returned true, then the HAVE_TYPE_FOO
preprocessor macro would be passed to the compiler.
# File tmp/rubies/ruby-2.5.9/lib/mkmf.rb, line 1077
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
Returns whether or not the variable var
can be found in the common header files, or within any headers
that you provide. If found, a macro is passed as a preprocessor constant to the compiler using the variable name, in uppercase, prepended with HAVE_
.
To check variables in an additional library, you need to check that library first using have_library()
.
For example, if have_var('foo')
returned true, then the HAVE_FOO
preprocessor macro would be passed to the compiler.
# File tmp/rubies/ruby-2.5.9/lib/mkmf.rb, line 1791
def pkg_config(pkg, option=nil)
if pkgconfig = with_config("#{pkg}-config") and find_executable0(pkgconfig)
# iff package specific config command is given
elsif ($PKGCONFIG ||=
(pkgconfig = with_config("pkg-config", ("pkg-config" unless CROSS_COMPILING))) &&
find_executable0(pkgconfig) && pkgconfig) and
xsystem("#{$PKGCONFIG} --exists #{pkg}")
# default to pkg-config command
pkgconfig = $PKGCONFIG
get = proc {|opt|
opt = xpopen("#{$PKGCONFIG} --#{opt} #{pkg}", err:[:child, :out], &:read)
Logging.open {puts opt.each_line.map{|s|"=> #{s.inspect}"}}
opt.strip if $?.success?
}
elsif find_executable0(pkgconfig = "#{pkg}-config")
# default to package specific config command, as a last resort.
else
pkgconfig = nil
end
if pkgconfig
get ||= proc {|opt|
opt = xpopen("#{pkgconfig} --#{opt}", err:[:child, :out], &:read)
Logging.open {puts opt.each_line.map{|s|"=> #{s.inspect}"}}
opt.strip if $?.success?
}
end
orig_ldflags = $LDFLAGS
if get and option
get[option]
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 "cflags: %s\nldflags: %s\nlibs: %s\n\n",
cflags, ldflags, libs
[cflags, ldflags, libs]
else
Logging::message "package configuration for %s is not found\n", pkg
nil
end
end
Returns compile/link information about an installed library in a tuple of [cflags, ldflags, libs]
, by using the command found first in the following commands:
-
If
--with-{pkg}-config={command}
is given via command line option:{command} {option}
-
{pkg}-config {option}
-
pkg-config {option} {pkg}
Where {option} is, for instance, --cflags
.
The values obtained are appended to +$CFLAGS+, +$LDFLAGS+ and +$libs+.
If an option
argument is given, the config command is invoked with the option and a stripped output string is returned without modifying any of the global values mentioned above.
# File tmp/rubies/ruby-2.5.9/lib/mkmf.rb, line 1260
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
Returns whether or not the constant const
is defined.
See also have_const
# File tmp/rubies/ruby-2.5.9/lib/mkmf.rb, line 1203
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
Returns whether or not the static type type
is defined.
See also have_type
# File tmp/rubies/ruby-2.5.9/lib/mkmf.rb, line 1615
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
Tests for the presence of a --with-
config or --without-
config option. Returns true
if the with option is given, false
if the without option is given, and the default value otherwise.
This can be useful for adding custom definitions, such as debug information.
Example:
if with_config("debug") $defs.push("-DOSSL_DEBUG") unless $defs.include? "-DOSSL_DEBUG" end