lib/mkmf/lite.rb in mkmf-lite-0.2.1 vs lib/mkmf/lite.rb in mkmf-lite-0.2.2

- old
+ new

@@ -1,14 +1,20 @@ require 'erb' require 'rbconfig' require 'tmpdir' require 'ptools' +if File::ALT_SEPARATOR && RUBY_VERSION.to_f < 1.9 + require 'win32/open3' +else + require 'open3' +end + module Mkmf module Lite # The version of the mkmf-lite library - MKMF_LITE_VERSION = '0.2.1' + MKMF_LITE_VERSION = '0.2.2' @@cpp_command = Config::CONFIG['CC'] || Config::CONFIG['CPP'] @@cpp_outfile = Config::CONFIG['CPPOUTFILE'] || "-o conftest.i" @@cpp_srcfile = 'conftest.c' @@ -60,11 +66,11 @@ # to compile with the function declaration. try_to_compile(ptr_code) || try_to_compile(std_code) 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, + # +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. # @@ -74,10 +80,31 @@ code = erb.result(binding) try_to_compile(code) end + # 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 + # + 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 + private # 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. @@ -90,17 +117,65 @@ def get_header_string(headers) headers = [headers] unless headers.is_a?(Array) common_headers = Config::CONFIG['COMMON_HEADERS'] - unless common_headers.nil? || common_headers.empty? - headers += Config::CONFIG['COMMON_HEADERS'].split + 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 + + # 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. + # + def try_to_execute(code) + begin + result = 0 + + stderr_orig = $stderr.dup + + Dir.chdir(Dir.tmpdir){ + File.open(@@cpp_srcfile, 'w'){ |fh| fh.write(code) } + + command = @@cpp_command + ' ' + command += @@cpp_outfile + ' ' + command += @@cpp_srcfile + + $stderr.reopen(File.null) + + if system(command) + Open3.popen3("./conftest.i") do |stdin, stdout, stderr| + stdin.close + stderr.close + result = stdout.gets.chomp.to_i + end + else + raise "Failed to compile source code:\n===\n" + code + "===" + end + } + ensure + File.delete(@@cpp_srcfile) if File.exists?(@@cpp_srcfile) + File.delete(@@cpp_outfile) if File.exists?(@@cpp_outfile) + $stderr.reopen(stderr_orig) + end + + result 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.