7 # ConstGenerator turns C constants into ruby values.
11 attr_reader :constants
14 # Creates a new constant generator that uses +prefix+ as a name, and an
17 # The only option is :required, which if set to true raises an error if a
18 # constant you have requested was not found.
20 # When passed a block, #calculate is automatically called at the end of
21 # the block, otherwise you must call it yourself.
23 def initialize(prefix = nil, options = {})
28 @required = options[:required]
37 @constants[name].value
41 # Request the value for C constant +name+. +format+ is a printf format
42 # string to print the value out, and +cast+ is a C cast for the value.
43 # +ruby_name+ allows you to give the constant an alternate ruby name for
44 # #to_ruby. +converter+ or +converter_proc+ allow you to convert the
45 # value from a string to the appropriate type for #to_ruby.
47 def const(name, format = nil, cast = '', ruby_name = nil, converter = nil,
52 if converter_proc and converter then
53 raise ArgumentError, "Supply only converter or converter block"
56 converter = converter_proc if converter.nil?
58 const = Constant.new name, format, cast, ruby_name, converter
59 @constants[name.to_s] = const
64 binary = File.join Dir.tmpdir, "rb_const_gen_bin_#{Process.pid}"
66 Tempfile.open("#{@prefix}.const_generator") do |f|
67 f.puts "#include <stdio.h>"
69 @includes.each do |inc|
70 f.puts "#include <#{inc}>"
73 f.puts "#include <stddef.h>\n\n"
74 f.puts "int main(int argc, char **argv)\n{"
76 @constants.each_value do |const|
79 printf("#{const.name} #{const.format}\\n", #{const.cast}#{const.name});
84 f.puts "\n\treturn 0;\n}"
87 output = `gcc -D_DARWIN_USE_64_BIT_INODE -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -x c -Wall -Werror #{f.path} -o #{binary} 2>&1`
89 unless $?.success? then
90 output = output.split("\n").map { |l| "\t#{l}" }.join "\n"
91 raise "Compilation error generating constants #{@prefix}:\n#{output}"
98 output.each_line do |line|
99 line =~ /^(\S+)\s(.*)$/
100 const = @constants[$1]
104 missing_constants = @constants.select do |name, constant|
106 end.map { |name,| name }
108 if @required and not missing_constants.empty? then
109 raise "Missing required constants for #{@prefix}: #{missing_constants.join ', '}"
113 def dump_constants(io)
114 @constants.each do |name, constant|
115 name = [@prefix, name].join '.'
116 io.puts "#{name} = #{constant.converted_value}"
121 # Outputs values for discovered constants. If the constant's value was
122 # not discovered it is not omitted.
125 @constants.sort_by { |name,| name }.map do |name, constant|
126 if constant.value.nil? then
127 "# #{name} not available"
140 class ConstGenerator::Constant
142 attr_reader :name, :format, :cast
145 def initialize(name, format, cast, ruby_name = nil, converter=nil)
149 @ruby_name = ruby_name
150 @converter = converter
156 @converter.call(@value)
167 "#{ruby_name} = #{converted_value}"