class FFI::ConstGenerator
def self.options
def self.options @options end
def self.options=(options)
def self.options=(options) @options = options end
def [](name)
def [](name) @constants[name].value end
def calculate(options = {})
def calculate(options = {}) binary = File.join Dir.tmpdir, "rb_const_gen_bin_#{Process.pid}" Tempfile.open("#{@prefix}.const_generator") do |f| f.puts "#include <stdio.h>" @includes.each do |inc| f.puts "#include <#{inc}>" end f.puts "#include <stddef.h>\n\n" f.puts "int main(int argc, char **argv)\n{" @constants.each_value do |const| f.puts <<-EOF fdef #{const.name} intf("#{const.name} #{const.format}\\n", #{const.cast}#{const.name}); ndif EOF end f.puts "\n\treturn 0;\n}" f.flush output = `gcc #{options[:cppflags]} -D_DARWIN_USE_64_BIT_INODE -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -x c -Wall -Werror #{f.path} -o #{binary} 2>&1` unless $?.success? then output = output.split("\n").map { |l| "\t#{l}" }.join "\n" raise "Compilation error generating constants #{@prefix}:\n#{output}" end end output = `#{binary}` File.unlink(binary + (FFI::Platform.windows? ? ".exe" : "")) output.each_line do |line| line =~ /^(\S+)\s(.*)$/ const = @constants[$1] const.value = $2 end missing_constants = @constants.select do |name, constant| constant.value.nil? end.map { |name,| name } if @required and not missing_constants.empty? then raise "Missing required constants for #{@prefix}: #{missing_constants.join ', '}" end end
def const(name, format = nil, cast = '', ruby_name = nil, converter = nil,
def const(name, format = nil, cast = '', ruby_name = nil, converter = nil, &converter_proc) format ||= '%d' cast ||= '' if converter_proc and converter then raise ArgumentError, "Supply only converter or converter block" end converter = converter_proc if converter.nil? const = Constant.new name, format, cast, ruby_name, converter @constants[name.to_s] = const return const end
def dump_constants(io)
def dump_constants(io) @constants.each do |name, constant| name = [@prefix, name].join '.' io.puts "#{name} = #{constant.converted_value}" end end
def include(i)
def include(i) @includes << i end
def initialize(prefix = nil, options = {})
def initialize(prefix = nil, options = {}) @includes = [] @constants = {} @prefix = prefix @required = options[:required] @options = options if block_given? then yield self calculate self.class.options.merge(options) end end
def to_ruby
def to_ruby @constants.sort_by { |name,| name }.map do |name, constant| if constant.value.nil? then "# #{name} not available" else constant.to_ruby end end.join "\n" end