Class: FFI::ConstGenerator

Inherits:
Object
  • Object
show all
Defined in:
lib/ffi/tools/const_generator.rb

Overview

ConstGenerator turns C constants into ruby values.

Defined Under Namespace

Classes: Constant

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(prefix = nil, options = {}) ⇒ ConstGenerator

Creates a new constant generator that uses prefix as a name, and an options hash.

The only option is :required, which if set to true raises an error if a constant you have requested was not found.

When passed a block, #calculate is automatically called at the end of the block, otherwise you must call it yourself.



23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/ffi/tools/const_generator.rb', line 23

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

Instance Attribute Details

#constantsObject (readonly)

Returns the value of attribute constants.



11
12
13
# File 'lib/ffi/tools/const_generator.rb', line 11

def constants
  @constants
end

Class Method Details

.optionsObject



39
40
41
# File 'lib/ffi/tools/const_generator.rb', line 39

def self.options
  @options
end

.options=(options) ⇒ Object



36
37
38
# File 'lib/ffi/tools/const_generator.rb', line 36

def self.options=(options)
  @options = options
end

Instance Method Details

#[](name) ⇒ Object



42
43
44
# File 'lib/ffi/tools/const_generator.rb', line 42

def [](name)
  @constants[name].value
end

#calculate(options = {}) ⇒ Object



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/ffi/tools/const_generator.rb', line 69

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
  #ifdef #{const.name}
  printf("#{const.name} #{const.format}\\n", #{const.cast}#{const.name});
  #endif
      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

#const(name, format = nil, cast = '', ruby_name = nil, converter = nil, &converter_proc) ⇒ Object

Request the value for C constant name. format is a printf format string to print the value out, and cast is a C cast for the value. ruby_name allows you to give the constant an alternate ruby name for #to_ruby. converter or converter_proc allow you to convert the value from a string to the appropriate type for #to_ruby.



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/ffi/tools/const_generator.rb', line 53

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

#dump_constants(io) ⇒ Object



118
119
120
121
122
123
# File 'lib/ffi/tools/const_generator.rb', line 118

def dump_constants(io)
  @constants.each do |name, constant|
    name = [@prefix, name].join '.'
    io.puts "#{name} = #{constant.converted_value}"
  end
end

#include(i) ⇒ Object



139
140
141
# File 'lib/ffi/tools/const_generator.rb', line 139

def include(i)
  @includes << i
end

#to_rubyObject

Outputs values for discovered constants. If the constant’s value was not discovered it is not omitted.



129
130
131
132
133
134
135
136
137
# File 'lib/ffi/tools/const_generator.rb', line 129

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