Generates an FFI Struct layout.
Given the @@@ portion in:
module 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.
# File lib/ffi/tools/struct_generator.rb, line 39 39: def initialize(name, options = {}) 40: @name = name 41: @struct_name = nil 42: @includes = [] 43: @fields = [] 44: @found = false 45: @size = nil 46: 47: if block_given? then 48: yield self 49: calculate self.class.options.merge(options) 50: end 51: end
# File lib/ffi/tools/struct_generator.rb, line 58 58: def calculate(options = {}) 59: binary = File.join Dir.tmpdir, "rb_struct_gen_bin_#{Process.pid}" 60: 61: raise "struct name not set" if @struct_name.nil? 62: 63: Tempfile.open("#{@name}.struct_generator") do |f| 64: f.puts "#include <stdio.h>" 65: 66: @includes.each do |inc| 67: f.puts "#include <#{inc}>" 68: end 69: 70: f.puts "#include <stddef.h>\n\n" 71: f.puts "int main(int argc, char **argv)\n{" 72: f.puts " #{@struct_name} s;" 73: f.puts %[ printf("sizeof(#{@struct_name}) %u\\n", (unsigned int) sizeof(#{@struct_name}));] 74: 75: @fields.each do |field| 76: f.puts printf("#{field.name} %u %u\\n", (unsigned int) offsetof(#{@struct_name}, #{field.name}), (unsigned int) sizeof(s.#{field.name})); 77: end 78: 79: f.puts "\n return 0;\n}" 80: f.flush 81: 82: output = `gcc #{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` 83: 84: unless $?.success? then 85: @found = false 86: output = output.split("\n").map { |l| "\t#{l}" }.join "\n" 87: raise "Compilation error generating struct #{@name} (#{@struct_name}):\n#{output}" 88: end 89: end 90: 91: output = `#{binary}`.split "\n" 92: File.unlink(binary + (FFI::Platform.windows? ? ".exe" : "")) 93: sizeof = output.shift 94: unless @size 95: m = /\s*sizeof\([^)]+\) (\d+)/.match sizeof 96: @size = m[1] 97: end 98: 99: line_no = 0 100: output.each do |line| 101: md = line.match(/.+ (\d+) (\d+)/) 102: @fields[line_no].offset = md[1].to_i 103: @fields[line_no].size = md[2].to_i 104: 105: line_no += 1 106: end 107: 108: @found = true 109: end
# File lib/ffi/tools/struct_generator.rb, line 124 124: def dump_config(io) 125: io.puts "rbx.platform.#{@name}.sizeof = #{@size}" 126: 127: @fields.each { |field| io.puts field.to_config(@name) } 128: end
# File lib/ffi/tools/struct_generator.rb, line 114 114: def field(name, type=nil) 115: field = Field.new(name, type) 116: @fields << field 117: return field 118: end
# File lib/ffi/tools/struct_generator.rb, line 120 120: def found? 121: @found 122: end
# File lib/ffi/tools/struct_generator.rb, line 130 130: def generate_layout 131: buf = "" 132: 133: @fields.each_with_index do |field, i| 134: if buf.empty? 135: buf << "layout :#{field.name}, :#{field.type}, #{field.offset}" 136: else 137: buf << " :#{field.name}, :#{field.type}, #{field.offset}" 138: end 139: 140: if i < @fields.length - 1 141: buf << ",\n" 142: end 143: end 144: 145: buf 146: end
# File lib/ffi/tools/struct_generator.rb, line 148 148: def get_field(name) 149: @fields.find { |f| name == f.name } 150: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.