ffi2-generators 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE +25 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/ffi2-generators.gemspec +21 -0
- data/lib/ffi2/generators.rb +164 -0
- data/lib/ffi2/generators/constants.rb +178 -0
- data/lib/ffi2/generators/file_processor.rb +85 -0
- data/lib/ffi2/generators/structures.rb +198 -0
- data/lib/ffi2/generators/types.rb +142 -0
- data/lib/ffi2/generators/version.rb +5 -0
- metadata +84 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8f4b27c539662e9b2d469ed8e8345bb8ed5f0244
|
4
|
+
data.tar.gz: 3a6992fb5b224faf1ec1f85aacaa4b6bbbab7a4c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b31668fd36d8fdeb444659bc7a78d7682c648a8b98e99b37f86c23eb199939944c0d6a93ee57b447b92e370dff6241bd1daa860bd5e820c86f5d066c7378dd93
|
7
|
+
data.tar.gz: 611e20ede0e0de89462f2142923154c637f0d9574550ac348220a7d82ddffc3b5a618f360c18324affe164ceb911fba2723c635cfeecbc9c489b057485497465
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
Copyright (c) 2013, Brian Shirai
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
8
|
+
list of conditions and the following disclaimer.
|
9
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
this list of conditions and the following disclaimer in the documentation
|
11
|
+
and/or other materials provided with the distribution.
|
12
|
+
3. Neither the name of the library nor the names of its contributors may be
|
13
|
+
used to endorse or promote products derived from this software without
|
14
|
+
specific prior written permission.
|
15
|
+
|
16
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
17
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
18
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
19
|
+
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY DIRECT,
|
20
|
+
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
21
|
+
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
22
|
+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
23
|
+
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
24
|
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
25
|
+
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Ffi2::Generators
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'ffi2-generators'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install ffi2-generators
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require './lib/ffi2/generators/version'
|
3
|
+
|
4
|
+
Gem::Specification.new do |spec|
|
5
|
+
spec.name = "ffi2-generators"
|
6
|
+
spec.version = FFI::Generators::VERSION
|
7
|
+
spec.authors = ["Brian Shirai"]
|
8
|
+
spec.email = ["brixen@gmail.com"]
|
9
|
+
spec.description = %q{Utilities for generating constants, types, and structs for FFI.}
|
10
|
+
spec.summary = %q{Utilities for generating constants, types, and structs for FFI.}
|
11
|
+
spec.homepage = "https://github.com/rubinius/ffi2-generators"
|
12
|
+
spec.license = "BSD"
|
13
|
+
|
14
|
+
spec.files = `git ls-files`.split($/)
|
15
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
16
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
17
|
+
spec.require_paths = ["lib"]
|
18
|
+
|
19
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
20
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
21
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
require "ffi2/generators/version"
|
2
|
+
require "ffi2/generators/constants"
|
3
|
+
require "ffi2/generators/structures"
|
4
|
+
require "ffi2/generators/types"
|
5
|
+
require "ffi2/generators/file_processor"
|
6
|
+
|
7
|
+
require "rbconfig"
|
8
|
+
|
9
|
+
module FFI
|
10
|
+
module Generators
|
11
|
+
|
12
|
+
##
|
13
|
+
# BodyGuard does your file system dirty work and cleans up after any
|
14
|
+
# fallout.
|
15
|
+
|
16
|
+
class BodyGuardError < Exception; end
|
17
|
+
|
18
|
+
class BodyGuard
|
19
|
+
def initialize(subject, label, platform)
|
20
|
+
@subject = subject
|
21
|
+
@label = label.gsub(/\W/, '-')
|
22
|
+
@platform = platform
|
23
|
+
end
|
24
|
+
|
25
|
+
##
|
26
|
+
# Orchestrates a workflow by querying the subject at each stage and cleans
|
27
|
+
# up if problems arise before raising an exception.
|
28
|
+
#
|
29
|
+
# Expects the subject to respond to the following methods:
|
30
|
+
#
|
31
|
+
# #source io
|
32
|
+
# Where io is an IO instance used to create the source for later
|
33
|
+
# stages.
|
34
|
+
#
|
35
|
+
# #prepare name, target
|
36
|
+
# Where name is the source file name and target is the file that would
|
37
|
+
# be created by the prepare process. The method should return the
|
38
|
+
# command to run.
|
39
|
+
#
|
40
|
+
# #prepare_failed
|
41
|
+
# The method should return the error message for #raise to which will
|
42
|
+
# be appended the output of running the command returned by #prepare.
|
43
|
+
#
|
44
|
+
# #process target
|
45
|
+
# Where target is the same as passed to #prepare. The method should
|
46
|
+
# return the command to run. If no further options or changes are
|
47
|
+
# needed, #process should just return target.
|
48
|
+
#
|
49
|
+
# #process_failed
|
50
|
+
# The method should return the error message for #raise to which will
|
51
|
+
# be appended the output of running the command returned by #process.
|
52
|
+
#
|
53
|
+
# The #perform method returns the result of running the command returned
|
54
|
+
# by the #process method.
|
55
|
+
|
56
|
+
def perform
|
57
|
+
begin
|
58
|
+
name = "rbx-ffi-generators-#{@label}"
|
59
|
+
source = File.expand_path name + @platform.source_ext
|
60
|
+
target = File.expand_path name + @platform.executable_ext
|
61
|
+
|
62
|
+
File.open source, "wb" do |f|
|
63
|
+
@subject.source f
|
64
|
+
end
|
65
|
+
|
66
|
+
if preparer = @subject.prepare(source, target)
|
67
|
+
handle preparer, :prepare_failed
|
68
|
+
else
|
69
|
+
target = source
|
70
|
+
end
|
71
|
+
|
72
|
+
processor = @subject.process target
|
73
|
+
return handle(processor, :process_failed)
|
74
|
+
ensure
|
75
|
+
remove source, target
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def handle(command, failure)
|
80
|
+
result = `#{command}`
|
81
|
+
Process.waitpid $?.pid rescue nil
|
82
|
+
|
83
|
+
unless $?.success?
|
84
|
+
result = result.split("\n").map { |l| "\t#{l}" }.join "\n"
|
85
|
+
msg = "#{@subject.send failure}:\n#{result}"
|
86
|
+
raise BodyGuardError, msg
|
87
|
+
end
|
88
|
+
|
89
|
+
result
|
90
|
+
end
|
91
|
+
|
92
|
+
def remove(*names)
|
93
|
+
names.each do |name|
|
94
|
+
File.delete name if File.exists? name
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
class Platform
|
100
|
+
# TODO: Make these configurable to enable cross-compiling
|
101
|
+
|
102
|
+
def initialize(kind=:c)
|
103
|
+
@kind = kind
|
104
|
+
end
|
105
|
+
|
106
|
+
def source_ext
|
107
|
+
case @kind
|
108
|
+
when :c
|
109
|
+
".c"
|
110
|
+
when :cpp
|
111
|
+
".cpp"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def executable_ext
|
116
|
+
windows? ? ".exe" : ""
|
117
|
+
end
|
118
|
+
|
119
|
+
def defines
|
120
|
+
case @kind
|
121
|
+
when :c
|
122
|
+
RbConfig::CONFIG["CFLAGS"]
|
123
|
+
when :cpp, :cxx
|
124
|
+
RbConfig::CONFIG["CPPFLAGS"] || RbConfig["CXXFLAGS"]
|
125
|
+
else
|
126
|
+
RbConfig::CONFIG["CFLAGS"]
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def windows?
|
131
|
+
RUBY_PLATFORM =~ /mswin|mingw/
|
132
|
+
end
|
133
|
+
|
134
|
+
def compiler
|
135
|
+
case @kind
|
136
|
+
when :c
|
137
|
+
RbConfig::CONFIG["CC"]
|
138
|
+
when :cpp, :cxx
|
139
|
+
RbConfig::CONFIG["CXX"] || RbConfig::CONFIG["CC"]
|
140
|
+
else
|
141
|
+
RbConfig::CONFIG["CC"]
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def language
|
146
|
+
case @kind
|
147
|
+
when :c
|
148
|
+
"c"
|
149
|
+
when :cpp, :cxx
|
150
|
+
"c++"
|
151
|
+
else
|
152
|
+
"c"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def compile(include_dirs, source, target)
|
157
|
+
includes = include_dirs.map { |i| "-I#{i}" }.join(" ")
|
158
|
+
compile_options = "#{defines} -x #{language} #{includes} -Wall -Werror"
|
159
|
+
|
160
|
+
"#{compiler} #{compile_options} #{source} -o #{target} 2>&1"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
module FFI
|
2
|
+
module Generators
|
3
|
+
##
|
4
|
+
# Constants turns C constants into ruby values.
|
5
|
+
|
6
|
+
class Constants
|
7
|
+
|
8
|
+
class Constant
|
9
|
+
attr_reader :name, :format, :cast
|
10
|
+
attr_accessor :value
|
11
|
+
|
12
|
+
def initialize(name, format, cast, ruby_name=nil, converter=nil)
|
13
|
+
@name = name
|
14
|
+
@format = format
|
15
|
+
@cast = cast
|
16
|
+
@ruby_name = ruby_name
|
17
|
+
@converter = converter
|
18
|
+
@value = nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def value?
|
22
|
+
@value != nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def converted_value
|
26
|
+
@converter ? @converter.call(@value) : @value
|
27
|
+
end
|
28
|
+
|
29
|
+
def ruby_name
|
30
|
+
@ruby_name || @name
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_ruby
|
34
|
+
"#{ruby_name} = #{converted_value}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
attr_reader :constants
|
40
|
+
|
41
|
+
##
|
42
|
+
# Creates a new constant generator that uses +prefix+ as a name, and an
|
43
|
+
# options hash.
|
44
|
+
#
|
45
|
+
# The only option is :required, which if set to true raises an error if a
|
46
|
+
# constant you have requested was not found.
|
47
|
+
#
|
48
|
+
# When passed a block, #calculate is automatically called at the end of
|
49
|
+
# the block, otherwise you must call it yourself.
|
50
|
+
|
51
|
+
def initialize(prefix=nil, options={})
|
52
|
+
@includes = []
|
53
|
+
@include_dirs = []
|
54
|
+
@constants = {}
|
55
|
+
@prefix = prefix
|
56
|
+
@platform = Platform.new
|
57
|
+
|
58
|
+
@required = options[:required]
|
59
|
+
|
60
|
+
if block_given?
|
61
|
+
yield self
|
62
|
+
calculate
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def [](name)
|
67
|
+
@constants[name].value
|
68
|
+
end
|
69
|
+
|
70
|
+
##
|
71
|
+
# Request the value for C constant +name+. +format+ is a printf format
|
72
|
+
# string to print the value out, and +cast+ is a C cast for the value.
|
73
|
+
# +ruby_name+ allows you to give the constant an alternate ruby name for
|
74
|
+
# #to_ruby. +converter+ or +converter_proc+ allow you to convert the
|
75
|
+
# value from a string to the appropriate type for #to_ruby.
|
76
|
+
|
77
|
+
def const(name, format=nil, cast=nil, ruby_name=nil, converter=nil, &block)
|
78
|
+
format ||= '%ld'
|
79
|
+
cast ||= '(long)'
|
80
|
+
|
81
|
+
if block and converter
|
82
|
+
raise ArgumentError, "Supply only converter or converter block"
|
83
|
+
end
|
84
|
+
|
85
|
+
converter = block if converter.nil?
|
86
|
+
|
87
|
+
const = Constant.new name, format, cast, ruby_name, converter
|
88
|
+
@constants[name.to_s] = const
|
89
|
+
return const
|
90
|
+
end
|
91
|
+
|
92
|
+
def source(io)
|
93
|
+
io.puts "#include <stdio.h>"
|
94
|
+
|
95
|
+
@includes.each do |inc|
|
96
|
+
io.puts "#include <#{inc}>"
|
97
|
+
end
|
98
|
+
|
99
|
+
io.puts "#include <stddef.h>\n\n"
|
100
|
+
io.puts "int main(int argc, char **argv)\n{"
|
101
|
+
|
102
|
+
@constants.each_value do |const|
|
103
|
+
io.puts <<-EOF
|
104
|
+
#ifdef #{const.name}
|
105
|
+
printf("#{const.name} #{const.format}\\n", #{const.cast}#{const.name});
|
106
|
+
#endif
|
107
|
+
EOF
|
108
|
+
end
|
109
|
+
|
110
|
+
io.puts "\n\treturn 0;\n}"
|
111
|
+
end
|
112
|
+
|
113
|
+
def prepare(name, target)
|
114
|
+
@platform.compile(@include_dirs, name, target)
|
115
|
+
end
|
116
|
+
|
117
|
+
def prepare_failed
|
118
|
+
"Compilation error generating constants #{@prefix}"
|
119
|
+
end
|
120
|
+
|
121
|
+
def process(target)
|
122
|
+
target
|
123
|
+
end
|
124
|
+
|
125
|
+
def process_failed
|
126
|
+
"Error generating constants #{@prefix}"
|
127
|
+
end
|
128
|
+
|
129
|
+
def calculate
|
130
|
+
output = BodyGuard.new(self, @prefix, @platform).perform
|
131
|
+
|
132
|
+
output.each_line do |line|
|
133
|
+
line =~ /^(\S+)\s(.*)$/
|
134
|
+
const = @constants[$1]
|
135
|
+
const.value = $2
|
136
|
+
end
|
137
|
+
|
138
|
+
missing_constants = @constants.reject { |_, c| c.value? }.keys
|
139
|
+
|
140
|
+
if @required and not missing_constants.empty?
|
141
|
+
raise "Missing required constants for #{@prefix}: #{missing_constants.join ', '}"
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def write_constants(io)
|
146
|
+
@constants.each do |name, constant|
|
147
|
+
io.print @prefix, "."
|
148
|
+
io.puts constant.to_ruby
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
##
|
153
|
+
# Outputs values for discovered constants. If the constant's value was
|
154
|
+
# not discovered it is not omitted.
|
155
|
+
|
156
|
+
def to_ruby
|
157
|
+
@constants.sort_by { |name,| name }.map do |name, constant|
|
158
|
+
if constant.value?
|
159
|
+
constant.to_ruby
|
160
|
+
else
|
161
|
+
"# #{name} not defined"
|
162
|
+
end
|
163
|
+
end.join "\n"
|
164
|
+
end
|
165
|
+
|
166
|
+
def include(i)
|
167
|
+
@includes << i
|
168
|
+
end
|
169
|
+
|
170
|
+
def include_dir(i)
|
171
|
+
@include_dirs << i
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
176
|
+
|
177
|
+
ConstGenerator = Generators::Constants
|
178
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module FFI
|
2
|
+
|
3
|
+
# Processes a file containing Ruby code with blocks of FFI definitions
|
4
|
+
# delimited by @@@. The blocks are replaced with Ruby code produced by
|
5
|
+
# running the FFI generators contained in the blocks. For example:
|
6
|
+
#
|
7
|
+
# module Something
|
8
|
+
# @@@
|
9
|
+
# constants do |c|
|
10
|
+
# c.include 'somefile.h'
|
11
|
+
#
|
12
|
+
# c.const 'MAX'
|
13
|
+
# c.const 'MIN'
|
14
|
+
# end
|
15
|
+
# @@@
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# would be converted to:
|
19
|
+
#
|
20
|
+
# module Something
|
21
|
+
# MAX = 1
|
22
|
+
# MIN = 2
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# assuming that
|
26
|
+
#
|
27
|
+
# #define MAX 1
|
28
|
+
# #define MIN 2
|
29
|
+
#
|
30
|
+
# was contained in the file 'something.h'.
|
31
|
+
|
32
|
+
class FileProcessor
|
33
|
+
|
34
|
+
def initialize(ffi_name, rb_name)
|
35
|
+
@name = File.basename rb_name, '.rb'
|
36
|
+
|
37
|
+
definitions = File.read ffi_name
|
38
|
+
|
39
|
+
replacement = definitions.gsub(/^( *)@@@(.*?)@@@/m) do
|
40
|
+
@constants = []
|
41
|
+
@structs = []
|
42
|
+
|
43
|
+
indent = $1
|
44
|
+
line_count = $2.count("\n") + 1
|
45
|
+
|
46
|
+
instance_eval $2
|
47
|
+
|
48
|
+
lines = []
|
49
|
+
@constants.each { |c| lines << c.to_ruby }
|
50
|
+
@structs.each { |s| lines << s.generate_layout }
|
51
|
+
|
52
|
+
# expand multiline blocks
|
53
|
+
lines = lines.join("\n").split "\n"
|
54
|
+
lines = lines.map { |line| indent + line }
|
55
|
+
|
56
|
+
# preserve source line numbers in output
|
57
|
+
padding = line_count - lines.length
|
58
|
+
lines += [nil] * padding if padding >= 0
|
59
|
+
|
60
|
+
lines.join "\n"
|
61
|
+
end
|
62
|
+
|
63
|
+
File.open rb_name, 'wb' do |f|
|
64
|
+
f.puts "# This file is generated #{self.class} from #{ffi_name}."
|
65
|
+
f.puts
|
66
|
+
f.puts replacement
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def constants(options={}, &block)
|
71
|
+
@constants << FFI::ConstGenerator.new(@name, options, &block)
|
72
|
+
end
|
73
|
+
|
74
|
+
def struct(&block)
|
75
|
+
@structs << FFI::StructGenerator.new(@name, &block)
|
76
|
+
end
|
77
|
+
|
78
|
+
##
|
79
|
+
# Utility converter for constants
|
80
|
+
|
81
|
+
def to_s
|
82
|
+
proc { |obj| obj.to_s.inspect }
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,198 @@
|
|
1
|
+
module FFI
|
2
|
+
module Generators
|
3
|
+
##
|
4
|
+
# Generates an FFI Struct layout.
|
5
|
+
#
|
6
|
+
# Given the @@@ portion in:
|
7
|
+
#
|
8
|
+
# module Zlib::ZStream < FFI::Struct
|
9
|
+
# @@@
|
10
|
+
# name "struct z_stream_s"
|
11
|
+
# include "zlib.h"
|
12
|
+
#
|
13
|
+
# field :next_in, :pointer
|
14
|
+
# field :avail_in, :uint
|
15
|
+
# field :total_in, :ulong
|
16
|
+
#
|
17
|
+
# # ...
|
18
|
+
# @@@
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# Structures will create the layout:
|
22
|
+
#
|
23
|
+
# layout :next_in, :pointer, 0,
|
24
|
+
# :avail_in, :uint, 4,
|
25
|
+
# :total_in, :ulong, 8,
|
26
|
+
# # ...
|
27
|
+
#
|
28
|
+
# StructGenerator does its best to pad the layout it produces to preserve
|
29
|
+
# line numbers. Place the struct definition as close to the top of the file
|
30
|
+
# for best results.
|
31
|
+
|
32
|
+
class Structures
|
33
|
+
|
34
|
+
##
|
35
|
+
# A field in a Struct.
|
36
|
+
|
37
|
+
class Field
|
38
|
+
|
39
|
+
attr_reader :name
|
40
|
+
attr_reader :type
|
41
|
+
attr_reader :offset
|
42
|
+
attr_accessor :size
|
43
|
+
|
44
|
+
def initialize(name, type)
|
45
|
+
@name = name
|
46
|
+
@type = type
|
47
|
+
@offset = nil
|
48
|
+
@size = nil
|
49
|
+
end
|
50
|
+
|
51
|
+
def offset=(o)
|
52
|
+
@offset = o
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_config(name)
|
56
|
+
buf = []
|
57
|
+
buf << "rbx.platform.#{name}.#{@name}.offset = #{@offset}"
|
58
|
+
buf << "rbx.platform.#{name}.#{@name}.size = #{@size}"
|
59
|
+
buf << "rbx.platform.#{name}.#{@name}.type = #{@type}" if @type
|
60
|
+
buf
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
attr_accessor :size
|
66
|
+
attr_reader :fields
|
67
|
+
|
68
|
+
def initialize(name)
|
69
|
+
@name = name
|
70
|
+
@struct_name = nil
|
71
|
+
@includes = []
|
72
|
+
@include_dirs = []
|
73
|
+
@fields = []
|
74
|
+
@found = false
|
75
|
+
@size = nil
|
76
|
+
@platform = Platform.new
|
77
|
+
|
78
|
+
if block_given? then
|
79
|
+
yield self
|
80
|
+
calculate
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def source(io)
|
85
|
+
io.puts "#include <stdio.h>"
|
86
|
+
|
87
|
+
@includes.each do |inc|
|
88
|
+
io.puts "#include <#{inc}>"
|
89
|
+
end
|
90
|
+
|
91
|
+
io.puts "#include <stddef.h>\n\n"
|
92
|
+
io.puts "int main(int argc, char **argv)\n{"
|
93
|
+
io.puts " #{@struct_name} s;"
|
94
|
+
io.puts %[ printf("sizeof(#{@struct_name}) %u\\n", (unsigned int) sizeof(#{@struct_name}));]
|
95
|
+
|
96
|
+
@fields.each do |field|
|
97
|
+
io.puts <<-EOF
|
98
|
+
printf("#{field.name} %u %u\\n", (unsigned int) offsetof(#{@struct_name}, #{field.name}),
|
99
|
+
(unsigned int) sizeof(s.#{field.name}));
|
100
|
+
EOF
|
101
|
+
end
|
102
|
+
|
103
|
+
io.puts "\n return 0;\n}"
|
104
|
+
end
|
105
|
+
|
106
|
+
def prepare(name, target)
|
107
|
+
@platform.compile(@include_dirs, name, target)
|
108
|
+
end
|
109
|
+
|
110
|
+
def prepare_failed
|
111
|
+
"Compilation error generating struct #{@name} (#{@struct_name})"
|
112
|
+
end
|
113
|
+
|
114
|
+
def process(target)
|
115
|
+
target
|
116
|
+
end
|
117
|
+
|
118
|
+
def process_failed
|
119
|
+
"Error generating struct #{@name} (#{@struct_name})"
|
120
|
+
end
|
121
|
+
|
122
|
+
def calculate
|
123
|
+
raise "struct name not set" if @struct_name.nil?
|
124
|
+
|
125
|
+
output = BodyGuard.new(self, @struct_name, @platform).perform.split "\n"
|
126
|
+
|
127
|
+
sizeof = output.shift
|
128
|
+
unless @size
|
129
|
+
m = /\s*sizeof\([^)]+\) (\d+)/.match sizeof
|
130
|
+
@size = m[1]
|
131
|
+
end
|
132
|
+
|
133
|
+
line_no = 0
|
134
|
+
output.each do |line|
|
135
|
+
md = line.match(/.+ (\d+) (\d+)/)
|
136
|
+
@fields[line_no].offset = md[1].to_i
|
137
|
+
@fields[line_no].size = md[2].to_i
|
138
|
+
|
139
|
+
line_no += 1
|
140
|
+
end
|
141
|
+
|
142
|
+
@found = true
|
143
|
+
end
|
144
|
+
|
145
|
+
def field(name, type=nil)
|
146
|
+
field = Field.new(name, type)
|
147
|
+
@fields << field
|
148
|
+
return field
|
149
|
+
end
|
150
|
+
|
151
|
+
def found?
|
152
|
+
@found
|
153
|
+
end
|
154
|
+
|
155
|
+
def write_config(io)
|
156
|
+
io.puts "rbx.platform.#{@name}.sizeof = #{@size}"
|
157
|
+
|
158
|
+
@fields.each { |field| io.puts field.to_config(@name) }
|
159
|
+
end
|
160
|
+
|
161
|
+
def generate_layout
|
162
|
+
buf = ""
|
163
|
+
|
164
|
+
@fields.each_with_index do |field, i|
|
165
|
+
if buf.empty?
|
166
|
+
buf << "layout :#{field.name}, :#{field.type}, #{field.offset}"
|
167
|
+
else
|
168
|
+
buf << " :#{field.name}, :#{field.type}, #{field.offset}"
|
169
|
+
end
|
170
|
+
|
171
|
+
if i < @fields.length - 1
|
172
|
+
buf << ",\n"
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
buf
|
177
|
+
end
|
178
|
+
|
179
|
+
def get_field(name)
|
180
|
+
@fields.find { |f| name == f.name }
|
181
|
+
end
|
182
|
+
|
183
|
+
def include(i)
|
184
|
+
@includes << i
|
185
|
+
end
|
186
|
+
|
187
|
+
def include_dir(i)
|
188
|
+
@include_dirs << i
|
189
|
+
end
|
190
|
+
|
191
|
+
def name(n)
|
192
|
+
@struct_name = n
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
StructGenerator = Generators::Structures
|
198
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
module FFI
|
2
|
+
module Generators
|
3
|
+
class Types
|
4
|
+
|
5
|
+
##
|
6
|
+
# Maps different C types to the C type representations we use
|
7
|
+
|
8
|
+
TYPE_MAP = {
|
9
|
+
"char" => :char,
|
10
|
+
"signed char" => :char,
|
11
|
+
"__signed char" => :char,
|
12
|
+
"unsigned char" => :uchar,
|
13
|
+
|
14
|
+
"short" => :short,
|
15
|
+
"signed short" => :short,
|
16
|
+
"signed short int" => :short,
|
17
|
+
"unsigned short" => :ushort,
|
18
|
+
"unsigned short int" => :ushort,
|
19
|
+
|
20
|
+
"int" => :int,
|
21
|
+
"signed int" => :int,
|
22
|
+
"unsigned int" => :uint,
|
23
|
+
|
24
|
+
"long" => :long,
|
25
|
+
"long int" => :long,
|
26
|
+
"signed long" => :long,
|
27
|
+
"signed long int" => :long,
|
28
|
+
"unsigned long" => :ulong,
|
29
|
+
"unsigned long int" => :ulong,
|
30
|
+
"long unsigned int" => :ulong,
|
31
|
+
|
32
|
+
"long long" => :long_long,
|
33
|
+
"long long int" => :long_long,
|
34
|
+
"signed long long" => :long_long,
|
35
|
+
"signed long long int" => :long_long,
|
36
|
+
"unsigned long long" => :ulong_long,
|
37
|
+
"unsigned long long int" => :ulong_long,
|
38
|
+
|
39
|
+
"char *" => :string,
|
40
|
+
"void *" => :pointer,
|
41
|
+
}
|
42
|
+
|
43
|
+
def self.generate
|
44
|
+
new.generate
|
45
|
+
end
|
46
|
+
|
47
|
+
def initialize
|
48
|
+
@platform = Platform.new
|
49
|
+
end
|
50
|
+
|
51
|
+
def source(io)
|
52
|
+
io.puts "#include <stdint.h>"
|
53
|
+
io.puts "#include <sys/types.h>"
|
54
|
+
unless @platform.windows?
|
55
|
+
io.puts "#include <sys/socket.h>"
|
56
|
+
io.puts "#include <sys/resource.h>"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def prepare(name, target)
|
61
|
+
# we have nothing to do in this stage
|
62
|
+
end
|
63
|
+
|
64
|
+
def process(target)
|
65
|
+
"#{@platform.compiler} -E #{@platform.defines} #{target}"
|
66
|
+
end
|
67
|
+
|
68
|
+
def process_failed
|
69
|
+
"Error generating C types"
|
70
|
+
end
|
71
|
+
|
72
|
+
def generate
|
73
|
+
typedefs = BodyGuard.new(self, "ffi_types_generator", @platform).perform
|
74
|
+
|
75
|
+
code = ""
|
76
|
+
|
77
|
+
typedefs.split(/\n/).each do |type|
|
78
|
+
# We only care about single line typedef
|
79
|
+
next unless type =~ /typedef/
|
80
|
+
# Ignore unions or structs
|
81
|
+
next if type =~ /union|struct/
|
82
|
+
|
83
|
+
# strip off the starting typedef and ending ;
|
84
|
+
type.gsub!(/^(.*typedef\s*)/, "")
|
85
|
+
type.gsub!(/\s*;\s*$/, "")
|
86
|
+
|
87
|
+
parts = type.split(/\s+/)
|
88
|
+
def_type = parts.join(" ")
|
89
|
+
|
90
|
+
# GCC does mapping with __attribute__ stuf, also see
|
91
|
+
# http://hal.cs.berkeley.edu/cil/cil016.html section 16.2.7. Problem
|
92
|
+
# with this is that the __attribute__ stuff can either occur before or
|
93
|
+
# after the new type that is defined...
|
94
|
+
if type =~ /__attribute__/
|
95
|
+
if parts.last =~ /__QI__|__HI__|__SI__|__DI__|__word__/
|
96
|
+
|
97
|
+
# In this case, the new type is BEFORE __attribute__ we need to
|
98
|
+
# find the final_type as the type before the part that starts with
|
99
|
+
# __attribute__
|
100
|
+
final_type = ""
|
101
|
+
parts.each do |p|
|
102
|
+
break if p =~ /__attribute__/
|
103
|
+
final_type = p
|
104
|
+
end
|
105
|
+
else
|
106
|
+
final_type = parts.pop
|
107
|
+
end
|
108
|
+
|
109
|
+
def_type = case type
|
110
|
+
when /__QI__/ then "char"
|
111
|
+
when /__HI__/ then "short"
|
112
|
+
when /__SI__/ then "int"
|
113
|
+
when /__DI__/ then "long long"
|
114
|
+
when /__word__/ then "long"
|
115
|
+
else "int"
|
116
|
+
end
|
117
|
+
|
118
|
+
def_type = "unsigned #{def_type}" if type =~ /unsigned/
|
119
|
+
else
|
120
|
+
final_type = parts.pop
|
121
|
+
def_type = parts.join(" ")
|
122
|
+
end
|
123
|
+
|
124
|
+
if type = TYPE_MAP[def_type]
|
125
|
+
code << "rbx.platform.typedef.#{final_type} = #{type}\n"
|
126
|
+
TYPE_MAP[final_type] = TYPE_MAP[def_type]
|
127
|
+
else
|
128
|
+
# Fallback to an ordinary pointer if we don't know the type
|
129
|
+
if def_type =~ /\*/
|
130
|
+
code << "rbx.platform.typedef.#{final_type} = pointer\n"
|
131
|
+
TYPE_MAP[final_type] = :pointer
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
code
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
TypesGenerator = Generators::Types
|
142
|
+
end
|
metadata
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ffi2-generators
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Brian Shirai
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-08-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
description: Utilities for generating constants, types, and structs for FFI.
|
42
|
+
email:
|
43
|
+
- brixen@gmail.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- .gitignore
|
49
|
+
- Gemfile
|
50
|
+
- LICENSE
|
51
|
+
- README.md
|
52
|
+
- Rakefile
|
53
|
+
- ffi2-generators.gemspec
|
54
|
+
- lib/ffi2/generators.rb
|
55
|
+
- lib/ffi2/generators/constants.rb
|
56
|
+
- lib/ffi2/generators/file_processor.rb
|
57
|
+
- lib/ffi2/generators/structures.rb
|
58
|
+
- lib/ffi2/generators/types.rb
|
59
|
+
- lib/ffi2/generators/version.rb
|
60
|
+
homepage: https://github.com/rubinius/ffi2-generators
|
61
|
+
licenses:
|
62
|
+
- BSD
|
63
|
+
metadata: {}
|
64
|
+
post_install_message:
|
65
|
+
rdoc_options: []
|
66
|
+
require_paths:
|
67
|
+
- lib
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - '>='
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '0'
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
requirements: []
|
79
|
+
rubyforge_project:
|
80
|
+
rubygems_version: 2.0.7
|
81
|
+
signing_key:
|
82
|
+
specification_version: 4
|
83
|
+
summary: Utilities for generating constants, types, and structs for FFI.
|
84
|
+
test_files: []
|