class FFI::StructGenerator
Generates an FFI
Struct
layout.
Given the @@@ portion in:
class Zlib::ZStream < FFI::Struct @@@ name "struct z_stream_s" include "zlib.h" field :next_in, :pointer field :avail_in, :uint field :total_in, :ulong # ... @@@ end
StructGenerator
will create the layout:
layout :next_in, :pointer, 0, :avail_in, :uint, 4, :total_in, :ulong, 8, # ...
StructGenerator
does its best to pad the layout it produces to preserve line numbers. Place the struct definition as close to the top of the file for best results.
Attributes
Public Class Methods
# File lib/ffi/tools/struct_generator.rb, line 39 def initialize(name, options = {}) @name = name @struct_name = nil @includes = [] @fields = [] @found = false @size = nil if block_given? then yield self calculate self.class.options.merge(options) end end
# File lib/ffi/tools/struct_generator.rb, line 55 def self.options @options end
# File lib/ffi/tools/struct_generator.rb, line 52 def self.options=(options) @options = options end
Public Instance Methods
# File lib/ffi/tools/struct_generator.rb, line 58 def calculate(options = {}) binary = File.join Dir.tmpdir, "rb_struct_gen_bin_#{Process.pid}" raise "struct name not set" if @struct_name.nil? Tempfile.open("#{@name}.struct_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{" f.puts " #{@struct_name} s;" f.puts %[ printf("sizeof(#{@struct_name}) %u\\n", (unsigned int) sizeof(#{@struct_name}));] @fields.each do |field| f.puts <<-EOF printf("#{field.name} %u %u\\n", (unsigned int) offsetof(#{@struct_name}, #{field.name}), (unsigned int) sizeof(s.#{field.name})); EOF end f.puts "\n return 0;\n}" f.flush cc = ENV['CC'] || 'gcc' output = `#{cc} #{options[:cppflags]} #{options[:cflags]} -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 @found = false output = output.split("\n").map { |l| "\t#{l}" }.join "\n" raise "Compilation error generating struct #{@name} (#{@struct_name}):\n#{output}" end end output = `#{binary}`.split "\n" File.unlink(binary + (FFI::Platform.windows? ? ".exe" : "")) sizeof = output.shift unless @size m = /\s*sizeof\([^)]+\) (\d+)/.match sizeof @size = m[1] end line_no = 0 output.each do |line| md = line.match(/.+ (\d+) (\d+)/) @fields[line_no].offset = md[1].to_i @fields[line_no].size = md[2].to_i line_no += 1 end @found = true end
# File lib/ffi/tools/struct_generator.rb, line 125 def dump_config(io) io.puts "rbx.platform.#{@name}.sizeof = #{@size}" @fields.each { |field| io.puts field.to_config(@name) } end
# File lib/ffi/tools/struct_generator.rb, line 115 def field(name, type=nil) field = Field.new(name, type) @fields << field return field end
# File lib/ffi/tools/struct_generator.rb, line 121 def found? @found end
# File lib/ffi/tools/struct_generator.rb, line 131 def generate_layout buf = "" @fields.each_with_index do |field, i| if buf.empty? buf << "layout :#{field.name}, :#{field.type}, #{field.offset}" else buf << " :#{field.name}, :#{field.type}, #{field.offset}" end if i < @fields.length - 1 buf << ",\n" end end buf end
# File lib/ffi/tools/struct_generator.rb, line 149 def get_field(name) @fields.find { |f| name == f.name } end
# File lib/ffi/tools/struct_generator.rb, line 153 def include(i) @includes << i end
# File lib/ffi/tools/struct_generator.rb, line 157 def name(n) @struct_name = n end