module Mkmf::Lite
Constants
- MKMF_LITE_VERSION
The version of the mkmf-lite library
Public Instance Methods
Returns the sizeof type using headers, or common headers if no headers are specified.
If this method fails an error is raised. This could happen if the type can’t be found and/or the header files do not include the indicated type.
Example:
class Foo include Mkmf::Lite utsname = check_sizeof('struct utsname', 'sys/utsname.h') end
# File lib/mkmf/lite.rb, line 125 def check_sizeof(type, headers = []) headers = get_header_string(headers) erb = ERB.new(read_template('check_sizeof.erb')) code = erb.result(binding) try_to_execute(code) end
Returns the value of the given constant (which could also be a macro) using headers, or common headers if no headers are specified.
If this method fails an error is raised. This could happen if the constant can’t be found and/or the header files do not include the indicated constant.
# File lib/mkmf/lite.rb, line 104 def check_valueof(constant, headers = []) headers = get_header_string(headers) erb = ERB.new(read_template('check_valueof.erb')) code = erb.result(binding) try_to_execute(code) end
Check for the presence of the given function in the common header files, or within any headers that you provide.
Returns true if found, or false if not found.
# File lib/mkmf/lite.rb, line 69 def have_func(function, headers = []) headers = get_header_string(headers) erb_ptr = ERB.new(read_template('have_func_pointer.erb')) erb_std = ERB.new(read_template('have_func.erb')) ptr_code = erb_ptr.result(binding) std_code = erb_std.result(binding) # Check for just the function pointer first. If that fails, then try # to compile with the function declaration. try_to_compile(ptr_code) || try_to_compile(std_code) end
Check for the presence of the given header file. You may optionally provide a list of directories to search.
Returns true if found, or false if not found.
# File lib/mkmf/lite.rb, line 49 def have_header(header, *directories) erb = ERB.new(read_template('have_header.erb')) code = erb.result(binding) if directories.empty? options = nil else options = "" directories.each{ |dir| options += "-I#{dir} " } options.rstrip! end try_to_compile(code, options) end
Checks whether or not the struct of type struct_type contains the struct_member. If it does not, or the struct type cannot be found, then false is returned.
An optional list of headers may be specified, in addition to the common header files that are already searched.
# File lib/mkmf/lite.rb, line 90 def have_struct_member(struct_type, struct_member, headers = []) headers = get_header_string(headers) erb = ERB.new(read_template('have_struct_member.erb')) code = erb.result(binding) try_to_compile(code) end
Private Instance Methods
# File lib/mkmf/lite.rb, line 14 def cpp_command command = RbConfig::CONFIG['CC'] || RbConfig::CONFIG['CPP'] || File.which('cc') || File.which('gcc') || File.which('cl') raise "Compiler not found" unless command command end
TODO: We should adjust this based on OS. For now we’re using arguments I think you’ll typically see set on Linux and BSD.
# File lib/mkmf/lite.rb, line 34 def cpp_libraries if RbConfig::CONFIG['LIBS'] RbConfig::CONFIG['LIBS'] + RbConfig::CONFIG['LIBRUBYARG'] else '-lrt -ldl -lcrypt -lm' end end
# File lib/mkmf/lite.rb, line 24 def cpp_out_file if File::ALT_SEPARATOR && RbConfig::CONFIG['CPP'] =~ /^cl/ '/Feconftest.exe' else '-o conftest.exe' end end
# File lib/mkmf/lite.rb, line 20 def cpp_source_file 'conftest.c' end
Take an array of header file names (or convert it to an array if it’s a single argument), add the COMMON_HEADERS, flatten it out and remove any duplicates.
Finally, convert the result into a single string of ‘#include’ directives, each separated by a newline.
This string is then to be used at the top of the ERB templates.
# File lib/mkmf/lite.rb, line 144 def get_header_string(headers) headers = [headers] unless headers.is_a?(Array) common_headers = RbConfig::CONFIG['COMMON_HEADERS'] if common_headers.nil? || common_headers.empty? if headers.empty? headers = ['stdio.h', 'stdlib.h'] headers += 'windows.h' if File::ALT_SEPARATOR end else headers += common_headers.split end headers = headers.flatten.uniq headers = headers.map{ |h| "#include <#{h}>" }.join("\n") headers end
Retrieve the path to the template file name.
# File lib/mkmf/lite.rb, line 262 def get_template_file(file) File.join(File.dirname(__FILE__), 'templates', file) end
Slurp the contents of the template file for evaluation later.
# File lib/mkmf/lite.rb, line 256 def read_template(file) IO.read(get_template_file(file)) end
Create a temporary bit of C source code in the temp directory, and try to compile it. If it succeeds, return true. Otherwise, return false.
Note that $stderr is temporarily redirected to the null device because we don’t actually care about the reason for failure.
# File lib/mkmf/lite.rb, line 222 def try_to_compile(code, command_options=nil) begin boolean = false stderr_orig = $stderr.dup stdout_orig = $stdout.dup Dir.chdir(Dir.tmpdir){ File.open(cpp_source_file, 'w'){ |fh| fh.write(code) } if command_options command = cpp_command + ' ' + command_options + ' ' else command = cpp_command + ' ' end command += cpp_out_file + ' ' command += cpp_source_file $stderr.reopen(IO::NULL) $stdout.reopen(IO::NULL) boolean = system(command) } ensure File.delete(cpp_source_file) if File.exists?(cpp_source_file) File.delete(cpp_out_file) if File.exists?(cpp_out_file) $stdout.reopen(stdout_orig) $stderr.reopen(stderr_orig) end boolean end
Create a temporary bit of C source code in the temp directory, and try to compile it. If it succeeds attempt to run the generated code. The code generated is expected to print a number to STDOUT, which is then grabbed and returned as an integer.
Note that $stderr is temporarily redirected to the null device because we don’t actually care about the reason for failure, though a Ruby error is raised if the compilation step fails.
# File lib/mkmf/lite.rb, line 173 def try_to_execute(code) begin result = 0 stderr_orig = $stderr.dup stdout_orig = $stdout.dup Dir.chdir(Dir.tmpdir){ File.open(cpp_source_file, 'w'){ |fh| fh.write(code) } command = cpp_command + ' ' command += cpp_out_file + ' ' command += cpp_source_file # Temporarily close these $stderr.reopen(IO::NULL) $stdout.reopen(IO::NULL) if system(command) $stdout.reopen(stdout_orig) # We need this back for open3 to work. conftest = File::ALT_SEPARATOR ? "conftest.exe" : "./conftest.exe" Open3.popen3(conftest) do |stdin, stdout, stderr| stdin.close stderr.close result = stdout.gets.chomp.to_i end else raise "Failed to compile source code with command '#{command}':\n===\n" + code + "===" end } ensure File.delete(cpp_source_file) if File.exists?(cpp_source_file) File.delete(cpp_out_file) if File.exists?(cpp_out_file) $stderr.reopen(stderr_orig) $stdout.reopen(stdout_orig) end result end