ffi 1.15.5-x64-mingw-ucrt
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +338 -0
- data/COPYING +49 -0
- data/Gemfile +14 -0
- data/LICENSE +24 -0
- data/LICENSE.SPECS +22 -0
- data/README.md +136 -0
- data/Rakefile +191 -0
- data/ffi.gemspec +42 -0
- data/lib/3.1/ffi_c.so +0 -0
- data/lib/ffi/abstract_memory.rb +44 -0
- data/lib/ffi/autopointer.rb +203 -0
- data/lib/ffi/buffer.rb +4 -0
- data/lib/ffi/callback.rb +4 -0
- data/lib/ffi/data_converter.rb +67 -0
- data/lib/ffi/enum.rb +296 -0
- data/lib/ffi/errno.rb +43 -0
- data/lib/ffi/ffi.rb +47 -0
- data/lib/ffi/io.rb +62 -0
- data/lib/ffi/library.rb +592 -0
- data/lib/ffi/managedstruct.rb +84 -0
- data/lib/ffi/memorypointer.rb +1 -0
- data/lib/ffi/platform/aarch64-darwin/types.conf +130 -0
- data/lib/ffi/platform/aarch64-freebsd/types.conf +128 -0
- data/lib/ffi/platform/aarch64-freebsd12/types.conf +181 -0
- data/lib/ffi/platform/aarch64-linux/types.conf +104 -0
- data/lib/ffi/platform/aarch64-openbsd/types.conf +134 -0
- data/lib/ffi/platform/arm-freebsd/types.conf +152 -0
- data/lib/ffi/platform/arm-freebsd12/types.conf +152 -0
- data/lib/ffi/platform/arm-linux/types.conf +132 -0
- data/lib/ffi/platform/i386-cygwin/types.conf +3 -0
- data/lib/ffi/platform/i386-darwin/types.conf +100 -0
- data/lib/ffi/platform/i386-freebsd/types.conf +152 -0
- data/lib/ffi/platform/i386-freebsd12/types.conf +152 -0
- data/lib/ffi/platform/i386-gnu/types.conf +107 -0
- data/lib/ffi/platform/i386-linux/types.conf +103 -0
- data/lib/ffi/platform/i386-netbsd/types.conf +126 -0
- data/lib/ffi/platform/i386-openbsd/types.conf +128 -0
- data/lib/ffi/platform/i386-solaris/types.conf +122 -0
- data/lib/ffi/platform/i386-windows/types.conf +52 -0
- data/lib/ffi/platform/ia64-linux/types.conf +104 -0
- data/lib/ffi/platform/mips-linux/types.conf +102 -0
- data/lib/ffi/platform/mips64-linux/types.conf +104 -0
- data/lib/ffi/platform/mips64el-linux/types.conf +104 -0
- data/lib/ffi/platform/mipsel-linux/types.conf +102 -0
- data/lib/ffi/platform/mipsisa32r6-linux/types.conf +102 -0
- data/lib/ffi/platform/mipsisa32r6el-linux/types.conf +102 -0
- data/lib/ffi/platform/mipsisa64r6-linux/types.conf +104 -0
- data/lib/ffi/platform/mipsisa64r6el-linux/types.conf +104 -0
- data/lib/ffi/platform/powerpc-aix/types.conf +180 -0
- data/lib/ffi/platform/powerpc-darwin/types.conf +100 -0
- data/lib/ffi/platform/powerpc-linux/types.conf +130 -0
- data/lib/ffi/platform/powerpc-openbsd/types.conf +156 -0
- data/lib/ffi/platform/powerpc64-linux/types.conf +104 -0
- data/lib/ffi/platform/powerpc64le-linux/types.conf +100 -0
- data/lib/ffi/platform/riscv64-linux/types.conf +104 -0
- data/lib/ffi/platform/s390-linux/types.conf +102 -0
- data/lib/ffi/platform/s390x-linux/types.conf +102 -0
- data/lib/ffi/platform/sparc-linux/types.conf +102 -0
- data/lib/ffi/platform/sparc-solaris/types.conf +128 -0
- data/lib/ffi/platform/sparc64-linux/types.conf +102 -0
- data/lib/ffi/platform/sparcv9-openbsd/types.conf +156 -0
- data/lib/ffi/platform/sparcv9-solaris/types.conf +128 -0
- data/lib/ffi/platform/x86_64-cygwin/types.conf +3 -0
- data/lib/ffi/platform/x86_64-darwin/types.conf +130 -0
- data/lib/ffi/platform/x86_64-dragonflybsd/types.conf +130 -0
- data/lib/ffi/platform/x86_64-freebsd/types.conf +128 -0
- data/lib/ffi/platform/x86_64-freebsd12/types.conf +158 -0
- data/lib/ffi/platform/x86_64-haiku/types.conf +117 -0
- data/lib/ffi/platform/x86_64-linux/types.conf +132 -0
- data/lib/ffi/platform/x86_64-msys/types.conf +119 -0
- data/lib/ffi/platform/x86_64-netbsd/types.conf +128 -0
- data/lib/ffi/platform/x86_64-openbsd/types.conf +134 -0
- data/lib/ffi/platform/x86_64-solaris/types.conf +122 -0
- data/lib/ffi/platform/x86_64-windows/types.conf +52 -0
- data/lib/ffi/platform.rb +185 -0
- data/lib/ffi/pointer.rb +167 -0
- data/lib/ffi/struct.rb +316 -0
- data/lib/ffi/struct_by_reference.rb +72 -0
- data/lib/ffi/struct_layout.rb +96 -0
- data/lib/ffi/struct_layout_builder.rb +227 -0
- data/lib/ffi/tools/const_generator.rb +232 -0
- data/lib/ffi/tools/generator.rb +105 -0
- data/lib/ffi/tools/generator_task.rb +32 -0
- data/lib/ffi/tools/struct_generator.rb +195 -0
- data/lib/ffi/tools/types_generator.rb +137 -0
- data/lib/ffi/types.rb +194 -0
- data/lib/ffi/union.rb +43 -0
- data/lib/ffi/variadic.rb +69 -0
- data/lib/ffi/version.rb +3 -0
- data/lib/ffi.rb +27 -0
- data/rakelib/ffi_gem_helper.rb +65 -0
- data/samples/getlogin.rb +8 -0
- data/samples/getpid.rb +8 -0
- data/samples/gettimeofday.rb +18 -0
- data/samples/hello.rb +8 -0
- data/samples/inotify.rb +60 -0
- data/samples/pty.rb +75 -0
- data/samples/qsort.rb +20 -0
- metadata +207 -0
@@ -0,0 +1,232 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
require 'open3'
|
3
|
+
|
4
|
+
module FFI
|
5
|
+
|
6
|
+
# ConstGenerator turns C constants into ruby values.
|
7
|
+
#
|
8
|
+
# @example a simple example for stdio
|
9
|
+
# require 'ffi/tools/const_generator'
|
10
|
+
# cg = FFI::ConstGenerator.new('stdio') do |gen|
|
11
|
+
# gen.const(:SEEK_SET)
|
12
|
+
# gen.const('SEEK_CUR')
|
13
|
+
# gen.const('seek_end') # this constant does not exist
|
14
|
+
# end # #calculate called automatically at the end of the block
|
15
|
+
#
|
16
|
+
# cg['SEEK_SET'] # => 0
|
17
|
+
# cg['SEEK_CUR'] # => 1
|
18
|
+
# cg['seek_end'] # => nil
|
19
|
+
# cg.to_ruby # => "SEEK_SET = 0\nSEEK_CUR = 1\n# seek_end not available"
|
20
|
+
class ConstGenerator
|
21
|
+
@options = {}
|
22
|
+
attr_reader :constants
|
23
|
+
|
24
|
+
# Creates a new constant generator that uses +prefix+ as a name, and an
|
25
|
+
# options hash.
|
26
|
+
#
|
27
|
+
# The only option is +:required+, which if set to +true+ raises an error if a
|
28
|
+
# constant you have requested was not found.
|
29
|
+
#
|
30
|
+
# @param [#to_s] prefix
|
31
|
+
# @param [Hash] options
|
32
|
+
# @return
|
33
|
+
# @option options [Boolean] :required
|
34
|
+
# @overload initialize(prefix, options)
|
35
|
+
# @overload initialize(prefix, options) { |gen| ... }
|
36
|
+
# @yieldparam [ConstGenerator] gen new generator is passed to the block
|
37
|
+
# When passed a block, {#calculate} is automatically called at the end of
|
38
|
+
# the block, otherwise you must call it yourself.
|
39
|
+
def initialize(prefix = nil, options = {})
|
40
|
+
@includes = ['stdio.h', 'stddef.h']
|
41
|
+
@constants = {}
|
42
|
+
@prefix = prefix
|
43
|
+
|
44
|
+
@required = options[:required]
|
45
|
+
@options = options
|
46
|
+
|
47
|
+
if block_given? then
|
48
|
+
yield self
|
49
|
+
calculate self.class.options.merge(options)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
# Set class options
|
53
|
+
# These options are merged with {#initialize} options when it is called with a block.
|
54
|
+
# @param [Hash] options
|
55
|
+
# @return [Hash] class options
|
56
|
+
def self.options=(options)
|
57
|
+
@options = options
|
58
|
+
end
|
59
|
+
# Get class options.
|
60
|
+
# @return [Hash] class options
|
61
|
+
def self.options
|
62
|
+
@options
|
63
|
+
end
|
64
|
+
# @param [String] name
|
65
|
+
# @return constant value (converted if a +converter+ was defined).
|
66
|
+
# Access a constant by name.
|
67
|
+
def [](name)
|
68
|
+
@constants[name].converted_value
|
69
|
+
end
|
70
|
+
|
71
|
+
# Request the value for C constant +name+.
|
72
|
+
#
|
73
|
+
# @param [#to_s] name C constant name
|
74
|
+
# @param [String] format a printf format string to print the value out
|
75
|
+
# @param [String] cast a C cast for the value
|
76
|
+
# @param ruby_name alternate ruby name for {#to_ruby}
|
77
|
+
#
|
78
|
+
# @overload const(name, format=nil, cast='', ruby_name=nil, converter=nil)
|
79
|
+
# +converter+ is a Method or a Proc.
|
80
|
+
# @param [#call] converter convert the value from a string to the appropriate
|
81
|
+
# type for {#to_ruby}.
|
82
|
+
# @overload const(name, format=nil, cast='', ruby_name=nil) { |value| ... }
|
83
|
+
# Use a converter block. This block convert the value from a string to the
|
84
|
+
# appropriate type for {#to_ruby}.
|
85
|
+
# @yieldparam value constant value
|
86
|
+
def const(name, format = nil, cast = '', ruby_name = nil, converter = nil,
|
87
|
+
&converter_proc)
|
88
|
+
format ||= '%d'
|
89
|
+
cast ||= ''
|
90
|
+
|
91
|
+
if converter_proc and converter then
|
92
|
+
raise ArgumentError, "Supply only converter or converter block"
|
93
|
+
end
|
94
|
+
|
95
|
+
converter = converter_proc if converter.nil?
|
96
|
+
|
97
|
+
const = Constant.new name, format, cast, ruby_name, converter
|
98
|
+
@constants[name.to_s] = const
|
99
|
+
return const
|
100
|
+
end
|
101
|
+
|
102
|
+
# Calculate constants values.
|
103
|
+
# @param [Hash] options
|
104
|
+
# @option options [String] :cppflags flags for C compiler
|
105
|
+
# @return [nil]
|
106
|
+
# @raise if a constant is missing and +:required+ was set to +true+ (see {#initialize})
|
107
|
+
def calculate(options = {})
|
108
|
+
binary_path = nil
|
109
|
+
|
110
|
+
Tempfile.open("#{@prefix}.const_generator") do |f|
|
111
|
+
binary_path = f.path + ".bin"
|
112
|
+
@includes.each do |inc|
|
113
|
+
f.puts "#include <#{inc}>"
|
114
|
+
end
|
115
|
+
f.puts "\nint main(int argc, char **argv)\n{"
|
116
|
+
|
117
|
+
@constants.each_value do |const|
|
118
|
+
f.puts <<-EOF
|
119
|
+
#ifdef #{const.name}
|
120
|
+
printf("#{const.name} #{const.format}\\n", #{const.cast}#{const.name});
|
121
|
+
#endif
|
122
|
+
EOF
|
123
|
+
end
|
124
|
+
|
125
|
+
f.puts "\n\treturn 0;\n}"
|
126
|
+
f.flush
|
127
|
+
|
128
|
+
cc = ENV['CC'] || 'gcc'
|
129
|
+
output = `#{cc} #{options[:cppflags]} -D_DARWIN_USE_64_BIT_INODE -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -x c -Wall -Werror #{f.path} -o #{binary_path} 2>&1`
|
130
|
+
|
131
|
+
unless $?.success? then
|
132
|
+
output = output.split("\n").map { |l| "\t#{l}" }.join "\n"
|
133
|
+
raise "Compilation error generating constants #{@prefix}:\n#{output}"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
output = `#{binary_path}`
|
138
|
+
File.unlink(binary_path + (FFI::Platform.windows? ? ".exe" : ""))
|
139
|
+
output.each_line do |line|
|
140
|
+
line =~ /^(\S+)\s(.*)$/
|
141
|
+
const = @constants[$1]
|
142
|
+
const.value = $2
|
143
|
+
end
|
144
|
+
|
145
|
+
missing_constants = @constants.select do |name, constant|
|
146
|
+
constant.value.nil?
|
147
|
+
end.map { |name,| name }
|
148
|
+
|
149
|
+
if @required and not missing_constants.empty? then
|
150
|
+
raise "Missing required constants for #{@prefix}: #{missing_constants.join ', '}"
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# Dump constants to +io+.
|
155
|
+
# @param [#puts] io
|
156
|
+
# @return [nil]
|
157
|
+
def dump_constants(io)
|
158
|
+
@constants.each do |name, constant|
|
159
|
+
name = [@prefix, name].join '.' if @prefix
|
160
|
+
io.puts "#{name} = #{constant.converted_value}"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# Outputs values for discovered constants. If the constant's value was
|
165
|
+
# not discovered it is not omitted.
|
166
|
+
# @return [String]
|
167
|
+
def to_ruby
|
168
|
+
@constants.sort_by { |name,| name }.map do |name, constant|
|
169
|
+
if constant.value.nil? then
|
170
|
+
"# #{name} not available"
|
171
|
+
else
|
172
|
+
constant.to_ruby
|
173
|
+
end
|
174
|
+
end.join "\n"
|
175
|
+
end
|
176
|
+
|
177
|
+
# Add additional C include file(s) to calculate constants from.
|
178
|
+
# @note +stdio.h+ and +stddef.h+ automatically included
|
179
|
+
# @param [List<String>, Array<String>] i include file(s)
|
180
|
+
# @return [Array<String>] array of include files
|
181
|
+
def include(*i)
|
182
|
+
@includes |= i.flatten
|
183
|
+
end
|
184
|
+
|
185
|
+
end
|
186
|
+
|
187
|
+
# This class hold constants for {ConstGenerator}
|
188
|
+
class ConstGenerator::Constant
|
189
|
+
|
190
|
+
attr_reader :name, :format, :cast
|
191
|
+
attr_accessor :value
|
192
|
+
|
193
|
+
# @param [#to_s] name
|
194
|
+
# @param [String] format a printf format string to print the value out
|
195
|
+
# @param [String] cast a C cast for the value
|
196
|
+
# @param ruby_name alternate ruby name for {#to_ruby}
|
197
|
+
# @param [#call] converter convert the value from a string to the appropriate
|
198
|
+
# type for {#to_ruby}.
|
199
|
+
def initialize(name, format, cast, ruby_name = nil, converter=nil)
|
200
|
+
@name = name
|
201
|
+
@format = format
|
202
|
+
@cast = cast
|
203
|
+
@ruby_name = ruby_name
|
204
|
+
@converter = converter
|
205
|
+
@value = nil
|
206
|
+
end
|
207
|
+
|
208
|
+
# Return constant value (converted if a +converter+ was defined).
|
209
|
+
# @return constant value.
|
210
|
+
def converted_value
|
211
|
+
if @converter
|
212
|
+
@converter.call(@value)
|
213
|
+
else
|
214
|
+
@value
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
# get constant ruby name
|
219
|
+
# @return [String]
|
220
|
+
def ruby_name
|
221
|
+
@ruby_name || @name
|
222
|
+
end
|
223
|
+
|
224
|
+
# Get an evaluable string from constant.
|
225
|
+
# @return [String]
|
226
|
+
def to_ruby
|
227
|
+
"#{ruby_name} = #{converted_value}"
|
228
|
+
end
|
229
|
+
|
230
|
+
end
|
231
|
+
|
232
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'ffi/tools/struct_generator'
|
2
|
+
require 'ffi/tools/const_generator'
|
3
|
+
|
4
|
+
module FFI
|
5
|
+
|
6
|
+
##
|
7
|
+
# Generate files with C structs for FFI::Struct and C constants.
|
8
|
+
#
|
9
|
+
# == A simple example
|
10
|
+
#
|
11
|
+
# In file +zlib.rb.ffi+:
|
12
|
+
# module Zlib
|
13
|
+
# @@@
|
14
|
+
# constants do |c|
|
15
|
+
# c.include "zlib.h"
|
16
|
+
# c.const :ZLIB_VERNUM
|
17
|
+
# end
|
18
|
+
# @@@
|
19
|
+
#
|
20
|
+
# class ZStream < FFI::Struct
|
21
|
+
#
|
22
|
+
# struct do |s|
|
23
|
+
# s.name "struct z_stream_s"
|
24
|
+
# s.include "zlib.h"
|
25
|
+
#
|
26
|
+
# s.field :next_in, :pointer
|
27
|
+
# s.field :avail_in, :uint
|
28
|
+
# s.field :total_in, :ulong
|
29
|
+
# end
|
30
|
+
# @@@
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# Translate the file:
|
35
|
+
# require "ffi/tools/generator"
|
36
|
+
# FFI::Generator.new "zlib.rb.ffi", "zlib.rb"
|
37
|
+
#
|
38
|
+
# Generates the file +zlib.rb+ with constant values and offsets:
|
39
|
+
# module Zlib
|
40
|
+
# ZLIB_VERNUM = 4784
|
41
|
+
#
|
42
|
+
# class ZStream < FFI::Struct
|
43
|
+
# layout :next_in, :pointer, 0,
|
44
|
+
# :avail_in, :uint, 8,
|
45
|
+
# :total_in, :ulong, 16
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# @see FFI::Generator::Task for easy integration in a Rakefile
|
49
|
+
class Generator
|
50
|
+
|
51
|
+
def initialize(ffi_name, rb_name, options = {})
|
52
|
+
@ffi_name = ffi_name
|
53
|
+
@rb_name = rb_name
|
54
|
+
@options = options
|
55
|
+
@name = File.basename rb_name, '.rb'
|
56
|
+
|
57
|
+
file = File.read @ffi_name
|
58
|
+
|
59
|
+
new_file = file.gsub(/^( *)@@@(.*?)@@@/m) do
|
60
|
+
@constants = []
|
61
|
+
@structs = []
|
62
|
+
|
63
|
+
indent = $1
|
64
|
+
original_lines = $2.count "\n"
|
65
|
+
|
66
|
+
instance_eval $2, @ffi_name, $`.count("\n")
|
67
|
+
|
68
|
+
new_lines = []
|
69
|
+
@constants.each { |c| new_lines << c.to_ruby }
|
70
|
+
@structs.each { |s| new_lines << s.generate_layout }
|
71
|
+
|
72
|
+
new_lines = new_lines.join("\n").split "\n" # expand multiline blocks
|
73
|
+
new_lines = new_lines.map { |line| indent + line }
|
74
|
+
|
75
|
+
padding = original_lines - new_lines.length
|
76
|
+
new_lines += [nil] * padding if padding >= 0
|
77
|
+
|
78
|
+
new_lines.join "\n"
|
79
|
+
end
|
80
|
+
|
81
|
+
open @rb_name, 'w' do |f|
|
82
|
+
f.puts "# This file is generated from `#{@ffi_name}'. Do not edit."
|
83
|
+
f.puts
|
84
|
+
f.puts new_file
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def constants(options = {}, &block)
|
89
|
+
@constants << FFI::ConstGenerator.new(@name, @options.merge(options), &block)
|
90
|
+
end
|
91
|
+
|
92
|
+
def struct(options = {}, &block)
|
93
|
+
@structs << FFI::StructGenerator.new(@name, @options.merge(options), &block)
|
94
|
+
end
|
95
|
+
|
96
|
+
##
|
97
|
+
# Utility converter for constants
|
98
|
+
|
99
|
+
def to_s
|
100
|
+
proc { |obj| obj.to_s.inspect }
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'ffi/tools/generator'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/tasklib'
|
4
|
+
|
5
|
+
##
|
6
|
+
# Add Rake tasks that generate files with C structs for FFI::Struct and C constants.
|
7
|
+
#
|
8
|
+
# @example a simple example for your Rakefile
|
9
|
+
# require "ffi/tools/generator_task"
|
10
|
+
# # Add a task to generate my_object.rb out of my_object.rb.ffi
|
11
|
+
# FFI::Generator::Task.new ["my_object.rb"], cflags: "-I/usr/local/mylibrary"
|
12
|
+
#
|
13
|
+
# The generated files are also added to the 'clear' task.
|
14
|
+
#
|
15
|
+
# @see FFI::Generator for a description of the file content
|
16
|
+
class FFI::Generator::Task < Rake::TaskLib
|
17
|
+
|
18
|
+
def initialize(rb_names, options={})
|
19
|
+
task :clean do rm_f rb_names end
|
20
|
+
|
21
|
+
rb_names.each do |rb_name|
|
22
|
+
ffi_name = "#{rb_name}.ffi"
|
23
|
+
|
24
|
+
file rb_name => ffi_name do |t|
|
25
|
+
puts "Generating #{rb_name}..." if Rake.application.options.trace
|
26
|
+
|
27
|
+
FFI::Generator.new ffi_name, rb_name, options
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,195 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
|
3
|
+
module FFI
|
4
|
+
|
5
|
+
##
|
6
|
+
# Generates an FFI Struct layout.
|
7
|
+
#
|
8
|
+
# Given the @@@ portion in:
|
9
|
+
#
|
10
|
+
# class Zlib::ZStream < FFI::Struct
|
11
|
+
# @@@
|
12
|
+
# name "struct z_stream_s"
|
13
|
+
# include "zlib.h"
|
14
|
+
#
|
15
|
+
# field :next_in, :pointer
|
16
|
+
# field :avail_in, :uint
|
17
|
+
# field :total_in, :ulong
|
18
|
+
#
|
19
|
+
# # ...
|
20
|
+
# @@@
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# StructGenerator will create the layout:
|
24
|
+
#
|
25
|
+
# layout :next_in, :pointer, 0,
|
26
|
+
# :avail_in, :uint, 4,
|
27
|
+
# :total_in, :ulong, 8,
|
28
|
+
# # ...
|
29
|
+
#
|
30
|
+
# StructGenerator does its best to pad the layout it produces to preserve
|
31
|
+
# line numbers. Place the struct definition as close to the top of the file
|
32
|
+
# for best results.
|
33
|
+
|
34
|
+
class StructGenerator
|
35
|
+
@options = {}
|
36
|
+
attr_accessor :size
|
37
|
+
attr_reader :fields
|
38
|
+
|
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
|
52
|
+
def self.options=(options)
|
53
|
+
@options = options
|
54
|
+
end
|
55
|
+
def self.options
|
56
|
+
@options
|
57
|
+
end
|
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 <<-EOF
|
77
|
+
printf("#{field.name} %u %u\\n", (unsigned int) offsetof(#{@struct_name}, #{field.name}),
|
78
|
+
(unsigned int) sizeof(s.#{field.name}));
|
79
|
+
EOF
|
80
|
+
end
|
81
|
+
|
82
|
+
f.puts "\n return 0;\n}"
|
83
|
+
f.flush
|
84
|
+
|
85
|
+
cc = ENV['CC'] || 'gcc'
|
86
|
+
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`
|
87
|
+
|
88
|
+
unless $?.success? then
|
89
|
+
@found = false
|
90
|
+
output = output.split("\n").map { |l| "\t#{l}" }.join "\n"
|
91
|
+
raise "Compilation error generating struct #{@name} (#{@struct_name}):\n#{output}"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
output = `#{binary}`.split "\n"
|
96
|
+
File.unlink(binary + (FFI::Platform.windows? ? ".exe" : ""))
|
97
|
+
sizeof = output.shift
|
98
|
+
unless @size
|
99
|
+
m = /\s*sizeof\([^)]+\) (\d+)/.match sizeof
|
100
|
+
@size = m[1]
|
101
|
+
end
|
102
|
+
|
103
|
+
line_no = 0
|
104
|
+
output.each do |line|
|
105
|
+
md = line.match(/.+ (\d+) (\d+)/)
|
106
|
+
@fields[line_no].offset = md[1].to_i
|
107
|
+
@fields[line_no].size = md[2].to_i
|
108
|
+
|
109
|
+
line_no += 1
|
110
|
+
end
|
111
|
+
|
112
|
+
@found = true
|
113
|
+
end
|
114
|
+
|
115
|
+
def field(name, type=nil)
|
116
|
+
field = Field.new(name, type)
|
117
|
+
@fields << field
|
118
|
+
return field
|
119
|
+
end
|
120
|
+
|
121
|
+
def found?
|
122
|
+
@found
|
123
|
+
end
|
124
|
+
|
125
|
+
def dump_config(io)
|
126
|
+
io.puts "rbx.platform.#{@name}.sizeof = #{@size}"
|
127
|
+
|
128
|
+
@fields.each { |field| io.puts field.to_config(@name) }
|
129
|
+
end
|
130
|
+
|
131
|
+
def generate_layout
|
132
|
+
buf = ""
|
133
|
+
|
134
|
+
@fields.each_with_index do |field, i|
|
135
|
+
if buf.empty?
|
136
|
+
buf << "layout :#{field.name}, :#{field.type}, #{field.offset}"
|
137
|
+
else
|
138
|
+
buf << " :#{field.name}, :#{field.type}, #{field.offset}"
|
139
|
+
end
|
140
|
+
|
141
|
+
if i < @fields.length - 1
|
142
|
+
buf << ",\n"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
buf
|
147
|
+
end
|
148
|
+
|
149
|
+
def get_field(name)
|
150
|
+
@fields.find { |f| name == f.name }
|
151
|
+
end
|
152
|
+
|
153
|
+
def include(i)
|
154
|
+
@includes << i
|
155
|
+
end
|
156
|
+
|
157
|
+
def name(n)
|
158
|
+
@struct_name = n
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
|
163
|
+
##
|
164
|
+
# A field in a Struct.
|
165
|
+
|
166
|
+
class StructGenerator::Field
|
167
|
+
|
168
|
+
attr_reader :name
|
169
|
+
attr_reader :type
|
170
|
+
attr_reader :offset
|
171
|
+
attr_accessor :size
|
172
|
+
|
173
|
+
def initialize(name, type)
|
174
|
+
@name = name
|
175
|
+
@type = type
|
176
|
+
@offset = nil
|
177
|
+
@size = nil
|
178
|
+
end
|
179
|
+
|
180
|
+
def offset=(o)
|
181
|
+
@offset = o
|
182
|
+
end
|
183
|
+
|
184
|
+
def to_config(name)
|
185
|
+
buf = []
|
186
|
+
buf << "rbx.platform.#{name}.#{@name}.offset = #{@offset}"
|
187
|
+
buf << "rbx.platform.#{name}.#{@name}.size = #{@size}"
|
188
|
+
buf << "rbx.platform.#{name}.#{@name}.type = #{@type}" if @type
|
189
|
+
buf
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|
193
|
+
|
194
|
+
end
|
195
|
+
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
|
3
|
+
module FFI
|
4
|
+
|
5
|
+
# @private
|
6
|
+
class TypesGenerator
|
7
|
+
|
8
|
+
##
|
9
|
+
# Maps different C types to the C type representations we use
|
10
|
+
|
11
|
+
TYPE_MAP = {
|
12
|
+
"char" => :char,
|
13
|
+
"signed char" => :char,
|
14
|
+
"__signed char" => :char,
|
15
|
+
"unsigned char" => :uchar,
|
16
|
+
|
17
|
+
"short" => :short,
|
18
|
+
"signed short" => :short,
|
19
|
+
"signed short int" => :short,
|
20
|
+
"unsigned short" => :ushort,
|
21
|
+
"unsigned short int" => :ushort,
|
22
|
+
|
23
|
+
"int" => :int,
|
24
|
+
"signed int" => :int,
|
25
|
+
"unsigned int" => :uint,
|
26
|
+
|
27
|
+
"long" => :long,
|
28
|
+
"long int" => :long,
|
29
|
+
"signed long" => :long,
|
30
|
+
"signed long int" => :long,
|
31
|
+
"unsigned long" => :ulong,
|
32
|
+
"unsigned long int" => :ulong,
|
33
|
+
"long unsigned int" => :ulong,
|
34
|
+
|
35
|
+
"long long" => :long_long,
|
36
|
+
"long long int" => :long_long,
|
37
|
+
"signed long long" => :long_long,
|
38
|
+
"signed long long int" => :long_long,
|
39
|
+
"unsigned long long" => :ulong_long,
|
40
|
+
"unsigned long long int" => :ulong_long,
|
41
|
+
|
42
|
+
"char *" => :string,
|
43
|
+
"void *" => :pointer,
|
44
|
+
}
|
45
|
+
|
46
|
+
def self.generate(options = {})
|
47
|
+
typedefs = nil
|
48
|
+
Tempfile.open 'ffi_types_generator' do |io|
|
49
|
+
io.puts <<-C
|
50
|
+
#include <stdint.h>
|
51
|
+
#include <stddef.h>
|
52
|
+
#include <sys/types.h>
|
53
|
+
#if !(defined(WIN32))
|
54
|
+
#include <sys/socket.h>
|
55
|
+
#include <netinet/in.h>
|
56
|
+
#include <sys/resource.h>
|
57
|
+
#endif
|
58
|
+
C
|
59
|
+
|
60
|
+
io.close
|
61
|
+
cc = ENV['CC'] || 'gcc'
|
62
|
+
cmd = "#{cc} -E -x c #{options[:cppflags]} -D_DARWIN_USE_64_BIT_INODE -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -c"
|
63
|
+
if options[:input]
|
64
|
+
typedefs = File.read(options[:input])
|
65
|
+
elsif options[:remote]
|
66
|
+
typedefs = `ssh #{options[:remote]} #{cmd} - < #{io.path}`
|
67
|
+
else
|
68
|
+
typedefs = `#{cmd} #{io.path}`
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
code = []
|
73
|
+
|
74
|
+
typedefs.each_line do |type|
|
75
|
+
# We only care about single line typedef
|
76
|
+
next unless type =~ /typedef/
|
77
|
+
# Ignore unions or structs
|
78
|
+
next if type =~ /union|struct/
|
79
|
+
|
80
|
+
# strip off the starting typedef and ending ;
|
81
|
+
type.gsub!(/^(.*typedef\s*)/, "")
|
82
|
+
type.gsub!(/\s*;\s*$/, "")
|
83
|
+
|
84
|
+
parts = type.split(/\s+/)
|
85
|
+
def_type = parts.join(" ")
|
86
|
+
|
87
|
+
# GCC does mapping with __attribute__ stuf, also see
|
88
|
+
# http://hal.cs.berkeley.edu/cil/cil016.html section 16.2.7. Problem
|
89
|
+
# with this is that the __attribute__ stuff can either occur before or
|
90
|
+
# after the new type that is defined...
|
91
|
+
if type =~ /__attribute__/
|
92
|
+
if parts.last =~ /__QI__|__HI__|__SI__|__DI__|__word__/
|
93
|
+
|
94
|
+
# In this case, the new type is BEFORE __attribute__ we need to
|
95
|
+
# find the final_type as the type before the part that starts with
|
96
|
+
# __attribute__
|
97
|
+
final_type = ""
|
98
|
+
parts.each do |p|
|
99
|
+
break if p =~ /__attribute__/
|
100
|
+
final_type = p
|
101
|
+
end
|
102
|
+
else
|
103
|
+
final_type = parts.pop
|
104
|
+
end
|
105
|
+
|
106
|
+
def_type = case type
|
107
|
+
when /__QI__/ then "char"
|
108
|
+
when /__HI__/ then "short"
|
109
|
+
when /__SI__/ then "int"
|
110
|
+
when /__DI__/ then "long long"
|
111
|
+
when /__word__/ then "long"
|
112
|
+
else "int"
|
113
|
+
end
|
114
|
+
|
115
|
+
def_type = "unsigned #{def_type}" if type =~ /unsigned/
|
116
|
+
else
|
117
|
+
final_type = parts.pop
|
118
|
+
def_type = parts.join(" ")
|
119
|
+
end
|
120
|
+
|
121
|
+
if type = TYPE_MAP[def_type]
|
122
|
+
code << "rbx.platform.typedef.#{final_type} = #{type}"
|
123
|
+
TYPE_MAP[final_type] = TYPE_MAP[def_type]
|
124
|
+
else
|
125
|
+
# Fallback to an ordinary pointer if we don't know the type
|
126
|
+
if def_type =~ /\*/
|
127
|
+
code << "rbx.platform.typedef.#{final_type} = pointer"
|
128
|
+
TYPE_MAP[final_type] = :pointer
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
code.sort.join("\n")
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|