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