ffi2-generators 0.1.1

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.
@@ -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
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ffi2-generators.gemspec
4
+ gemspec
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.
@@ -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
@@ -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
@@ -0,0 +1,5 @@
1
+ module FFI
2
+ module Generators
3
+ VERSION = "0.1.1"
4
+ end
5
+ 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: []