module Mkmf::Lite

The Lite module scopes the Mkmf module to differentiate it from the Mkmf module in the standard library.

Constants

MKMF_LITE_VERSION

The version of the mkmf-lite library

Public Instance Methods

check_sizeof(type, headers = []) click to toggle source

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 161
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
check_valueof(constant, headers = []) click to toggle source

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 138
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
have_func(function, headers = []) click to toggle source

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 99
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
have_header(header, *directories) click to toggle source

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 77
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
have_struct_member(struct_type, struct_member, headers = []) click to toggle source

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 122
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

cpp_command() click to toggle source

rubocop:disable Layout/LineLength

# File lib/mkmf/lite.rb, line 34
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
cpp_defs() click to toggle source
# File lib/mkmf/lite.rb, line 23
def cpp_defs
  RbConfig::CONFIG['DEFS']
end
cpp_libraries() click to toggle source
# File lib/mkmf/lite.rb, line 57
def cpp_libraries
  return if File::ALT_SEPARATOR && RbConfig::CONFIG['CPP'] =~ /^cl/
  return if jruby?

  if cpp_command =~ /clang/i
    '-Lrt -Ldl -Lcrypt -Lm'
  else
    '-lrt -ldl -lcrypt -lm'
  end
end
cpp_out_file() click to toggle source
# File lib/mkmf/lite.rb, line 47
def cpp_out_file
  if File::ALT_SEPARATOR && RbConfig::CONFIG['CPP'] =~ /^cl/
    '/Feconftest.exe'
  else
    '-o conftest.exe'
  end
end
cpp_source_file() click to toggle source
# File lib/mkmf/lite.rb, line 43
def cpp_source_file
  'conftest.c'
end
get_header_string(headers) click to toggle source

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 182
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.map{ |h| "#include <#{h}>" }.join("\n")
end
get_template_file(file) click to toggle source

Retrieve the path to the template file name.

# File lib/mkmf/lite.rb, line 298
def get_template_file(file)
  File.join(File.dirname(__FILE__), 'templates', file)
end
jruby?() click to toggle source
# File lib/mkmf/lite.rb, line 27
def jruby?
  defined?(JRUBY_VERSION) ? true : false
end
read_template(file) click to toggle source

Slurp the contents of the template file for evaluation later.

# File lib/mkmf/lite.rb, line 292
def read_template(file)
  File.read(get_template_file(file))
end
try_to_compile(code, command_options = nil) click to toggle source

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 258
def try_to_compile(code, command_options = nil)
  begin
    boolean = false
    stderr_orig = $stderr.dup
    stdout_orig = $stdout.dup

    Dir.chdir(Dir.tmpdir) do
      File.write(cpp_source_file, code)

      if command_options
        command  = "#{cpp_command} #{command_options} #{cpp_libraries} #{cpp_defs} "
      else
        command  = "#{cpp_command} #{cpp_libraries} #{cpp_defs} "
      end

      command += "#{cpp_out_file} "
      command += cpp_source_file

      $stderr.reopen(IO::NULL)
      $stdout.reopen(IO::NULL)
      boolean = system(command)
    end
  ensure
    FileUtils.rm_f(cpp_source_file)
    FileUtils.rm_f(cpp_out_file)
    $stdout.reopen(stdout_orig)
    $stderr.reopen(stderr_orig)
  end

  boolean
end
try_to_execute(code) click to toggle source

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 209
def try_to_execute(code)
  begin
    result = 0

    stderr_orig = $stderr.dup
    stdout_orig = $stdout.dup

    Dir.chdir(Dir.tmpdir) do
      File.write(cpp_source_file, code)

      command  = "#{cpp_command} #{cpp_libraries} #{cpp_defs} "
      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
    end
  ensure
    FileUtils.rm_f(cpp_source_file)
    FileUtils.rm_f(cpp_out_file)
    $stderr.reopen(stderr_orig)
    $stdout.reopen(stdout_orig)
  end

  result
end