ffi-inliner 0.2.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -3,4 +3,5 @@
3
3
  ^#
4
4
  pkg
5
5
  announcement.txt
6
+ cache
6
7
 
data/History.txt CHANGED
@@ -1,3 +1,18 @@
1
+ == 0.2.3 / 2009-12-07
2
+
3
+ * Minor enhancements
4
+ * Add Builder#include method to include library and user header files
5
+ * Add Builder#library method to link with custom libraries
6
+ * Add experimental support for wrapping C++ through C. When using
7
+ GPlusPlus compiler, the inliner will automatically wraps C code
8
+ inside an extern "C" block.
9
+ * Bug fix
10
+ * Fix Issue#2: TCC generated shared libraries do not work in Windows
11
+ (thanks to Luis Lavena)
12
+ * Changes in the API
13
+ * Rename #compiler in #use_compiler
14
+ * Now #compiler returns the current compiler
15
+
1
16
  == 0.2.2 / 2009-08-05
2
17
 
3
18
  * Bug fix
data/README.rdoc CHANGED
@@ -10,11 +10,11 @@ With ffi-inliner you can run C code within your ruby script.
10
10
 
11
11
  * Mix C snippets in your Ruby code and gulp it on the fly!
12
12
  * It's based on Ruby-FFI so the C code you inject is portable across
13
- Ruby implementation!
13
+ Ruby implementations!
14
14
  * Yep, it means that you can run it on JRuby too!
15
15
  * Fast compilation through tcc[http://bellard.org/tcc/]
16
16
  * But it can use the system's compiler (e.g. gcc) on those platforms
17
- that don't support tcc (e.g. OSX)
17
+ that don't support tcc (e.g. OSX) or that don't have it installed
18
18
 
19
19
  == SYNOPSIS:
20
20
 
@@ -34,7 +34,9 @@ With ffi-inliner you can run C code within your ruby script.
34
34
  Foo.new.say_hello('foos')
35
35
 
36
36
  For other hints see the examples/ folder or visit the
37
- wiki[http://wiki.github.com/remogatto/ffi-inliner/tutorial]
37
+ wiki[http://wiki.github.com/remogatto/ffi-inliner/tutorial]. For a
38
+ "real" world example you may be interested to
39
+ ffi-life[http://github.com/remogatto/ffi-life].
38
40
 
39
41
  == REQUIREMENTS:
40
42
 
data/Rakefile CHANGED
@@ -26,11 +26,11 @@ PROJ.version = Inliner::VERSION
26
26
 
27
27
  PROJ.readme_file = 'README.rdoc'
28
28
 
29
- PROJ.rubyforge.name = 'ffi'
29
+ PROJ.rubyforge.name = 'ffi-inliner'
30
30
 
31
31
  PROJ.ann.paragraphs << 'FEATURES' << 'SYNOPSIS' << 'REQUIREMENTS' << 'DOWNLOAD/INSTALL' << 'CREDITS'
32
32
  PROJ.ann.email[:from] = 'andrea.fazzi@alcacoop.it'
33
- PROJ.ann.email[:to] << 'dev@ruby-ffi.kenai.com' << 'users@ruby-ffi.kenai.com'
33
+ PROJ.ann.email[:to] << 'ruby-ffi@googlegroups.com'
34
34
  PROJ.ann.email[:server] = 'smtp.gmail.com'
35
35
 
36
36
  PROJ.spec.opts << '--color' << '-fs'
@@ -59,21 +59,26 @@ module Inliner
59
59
  module Compilers
60
60
  class Compiler
61
61
  attr_reader :progname
62
- def self.check_and_create(fm = nil)
63
- compiler = new(fm)
62
+ def self.check_and_create(fm = nil, libraries = nil)
63
+ compiler = new(fm, libraries)
64
64
  unless compiler.exists?
65
65
  raise "Can't find compiler #{compiler.class}"
66
66
  else
67
67
  compiler
68
68
  end
69
69
  end
70
- def initialize(fm = nil)
70
+ def initialize(fm = nil, libraries = nil)
71
71
  @fm = fm
72
+ @libraries = libraries
72
73
  @progname = cmd.split.first
73
74
  end
74
75
  def compile
75
76
  raise "Compile error! See #{@fm.log_fn}" unless system(cmd)
76
77
  end
78
+ private
79
+ def libs
80
+ @libraries.inject("") { |str, lib| str << "-l#{lib} " } if @libraries
81
+ end
77
82
  end
78
83
 
79
84
  class GCC < Compiler
@@ -88,7 +93,17 @@ module Inliner
88
93
  end
89
94
  end
90
95
  def cmd
91
- "#{ldshared} -o \"#{@fm.so_fn}\" \"#{@fm.c_fn}\" 2>\"#{@fm.log_fn}\""
96
+ "#{ldshared} #{libs} -o \"#{@fm.so_fn}\" \"#{@fm.c_fn}\" 2>\"#{@fm.log_fn}\""
97
+ end
98
+ end
99
+
100
+ class GPlusPlus < GCC
101
+ def ldshared
102
+ if Config::CONFIG['target_os'] =~ /darwin/
103
+ 'g++ -dynamic -bundle -fPIC'
104
+ else
105
+ 'g++ -shared -fPIC'
106
+ end
92
107
  end
93
108
  end
94
109
 
@@ -97,29 +112,41 @@ module Inliner
97
112
  IO.popen("#{@progname}") { |f| f.gets } ? true : false
98
113
  end
99
114
  def cmd
100
- "tcc -shared -o \"#{@fm.so_fn}\" \"#{@fm.c_fn}\" 2>\"#{@fm.log_fn}\""
115
+ if Config::CONFIG['target_os'] =~ /mswin|mingw/
116
+ "tcc -rdynamic -shared #{libs} -o \"#{@fm.so_fn}\" \"#{@fm.c_fn}\" 2>\"#{@fm.log_fn}\""
117
+ else
118
+ "tcc -shared #{libs} -o \"#{@fm.so_fn}\" \"#{@fm.c_fn}\" 2>\"#{@fm.log_fn}\""
119
+ end
101
120
  end
102
121
  end
103
122
  end
104
123
 
105
124
  class Builder
106
- attr_reader :code
125
+ attr_reader :code, :compiler
107
126
  def initialize(mod, code = "", options = {})
108
127
  make_pointer_types
109
128
  @mod = mod
110
129
  @code = code
111
130
  @sig = [parse_signature(@code)] unless @code.empty?
112
- options = { :compiler => Compilers::GCC }.merge(options)
113
- @compiler = options[:compiler]
131
+ options = { :use_compiler => Compilers::GCC }.merge(options)
132
+ @compiler = options[:use_compiler]
114
133
  end
115
134
 
116
135
  def map(type_map)
117
136
  @types.merge!(type_map)
118
137
  end
119
138
 
139
+ def include(fn, options = {})
140
+ options[:quoted] ? @code << "#include \"#{fn}\"\n" : @code << "#include <#{fn}>\n"
141
+ end
142
+
143
+ def library(*libraries)
144
+ (@libraries ||= []).concat(libraries)
145
+ end
146
+
120
147
  def c(code)
121
148
  (@sig ||= []) << parse_signature(code)
122
- @code << code
149
+ @code << (@compiler == Compilers::GPlusPlus ? "extern \"C\" {\n#{code}\n}" : code )
123
150
  end
124
151
 
125
152
  def c_raw(code)
@@ -130,9 +157,17 @@ module Inliner
130
157
  @compiler = compiler
131
158
  end
132
159
 
160
+ def struct(ffi_struct)
161
+ @code << "typedef struct {"
162
+ ffi_struct.layout.fields.each do |field|
163
+ @code << "#{field} #{field.name};\n"
164
+ end
165
+ @code << "} #{ffi_struct.class.name}"
166
+ end
167
+
133
168
  def build
134
169
  @fm = FilenameManager.new(@mod, @code)
135
- @compiler = @compiler.check_and_create(@fm)
170
+ @compiler = @compiler.check_and_create(@fm, @libraries)
136
171
  unless @fm.cached?
137
172
  write_files(@code, @sig)
138
173
  @compiler.compile
@@ -173,14 +208,16 @@ module Inliner
173
208
  # Based on RubyInline code by Ryan Davis
174
209
  # Copyright (c) 2001-2007 Ryan Davis, Zen Spider Software
175
210
  def parse_signature(code)
211
+
176
212
  sig = strip_comments(code)
213
+
177
214
  # strip preprocessor directives
178
215
  sig.gsub!(/^\s*\#.*(\\\n.*)*/, '')
179
216
  # strip {}s
180
217
  sig.gsub!(/\{[^\}]*\}/, '{ }')
181
218
  # clean and collapse whitespace
182
219
  sig.gsub!(/\s+/, ' ')
183
-
220
+
184
221
  # types = 'void|int|char|char\s\*|void\s\*'
185
222
  types = @types.keys.map{|x| Regexp.escape(x)}.join('|')
186
223
  sig = sig.gsub(/\s*\*\s*/, ' * ').strip
@@ -215,15 +252,20 @@ module Inliner
215
252
  end
216
253
 
217
254
  def generate_ffi(sig)
255
+
218
256
  ffi_code = <<PREAMBLE
219
257
  extend FFI::Library
220
258
  ffi_lib '#{@fm.so_fn}'
221
259
 
222
260
  PREAMBLE
223
- sig.each do |s|
224
- args = s['args'].map { |arg| ":#{to_ffi_type(arg)}" }.join(',')
225
- ffi_code << "attach_function '#{s['name']}', [#{args}], :#{to_ffi_type(s['return'])}\n"
261
+
262
+ unless sig.nil?
263
+ sig.each do |s|
264
+ args = s['args'].map { |arg| ":#{to_ffi_type(arg)}" }.join(',')
265
+ ffi_code << "attach_function '#{s['name']}', [#{args}], :#{to_ffi_type(s['return'])}\n"
266
+ end
226
267
  end
268
+
227
269
  ffi_code
228
270
  end
229
271
  def write_c(code)
@@ -1,3 +1,3 @@
1
1
  module Inliner
2
- VERSION = '0.2.2'
2
+ VERSION = '0.2.3'
3
3
  end
@@ -1,6 +1,6 @@
1
1
  require File.expand_path(File.join(File.dirname(__FILE__), "../spec_helper"))
2
2
 
3
- describe Inliner do
3
+ describe 'Inliner' do
4
4
 
5
5
  before do
6
6
  module Foo
@@ -75,7 +75,7 @@ describe Inliner do
75
75
 
76
76
  end
77
77
 
78
- it 'should be configured using the block form' do
78
+ it 'should be configured using the block form' do
79
79
  module Foo
80
80
  inline do |builder|
81
81
  builder.c %q{
@@ -114,6 +114,27 @@ describe Inliner do
114
114
  my_struct = MyStruct.new
115
115
  Foo.use_my_struct(my_struct).should == my_struct.to_ptr
116
116
  end
117
+
118
+ it 'should allow users to include header files' do
119
+ module Foo
120
+ inline do |builder|
121
+ builder.include "stdio.h"
122
+ builder.include "local_header.h", :quoted => true
123
+ builder.code.should == "#include <stdio.h>\n#include \"local_header.h\"\n"
124
+ builder.stub!(:build)
125
+ end
126
+ end
127
+ end
128
+
129
+ it 'should allow users to add libraries' do
130
+ module Foo
131
+ inline do |builder|
132
+ builder.library 'foolib1', 'foolib2'
133
+ builder.stub!(:build)
134
+ end
135
+ end
136
+ end
137
+
117
138
  it 'should generate C struct from FFI::Struct' do
118
139
  pending do
119
140
  class MyStruct < FFI::Struct
@@ -138,24 +159,32 @@ EOC
138
159
  end
139
160
  end
140
161
  end
141
- # it 'should use different compiler as specified in the configuration block' do
142
- # tcc = mock('tcc', :exists? => true, :compile => nil)
143
- # Inliner::Compilers::TCC.should_receive(:new).and_return(tcc)
144
- # module Foo
145
- # inline do |builder|
146
- # builder.code = "int func_1() { return 0; }"
147
- # builder.compiler = Inliner::Compilers::TCC
148
- # end
149
- # end
150
- # end
151
-
152
- # it 'should be configured using the hash form' do
153
- # tcc = mock('tcc', :exists? => true, :compile => nil)
154
- # Inliner::Compilers::TCC.should_receive(:new).and_return(tcc)
155
- # module Foo
156
- # inline "int func_1() { return 1; }", :compiler => Inliner::Compilers::TCC
157
- # end
158
- # end
162
+
163
+ it 'should use different compiler as specified in the configuration block' do
164
+ module Foo
165
+ inline do |builder|
166
+ builder.use_compiler Inliner::Compilers::TCC
167
+ builder.c "int func_1() { return 1 + 1; }"
168
+ end
169
+ end
170
+ Foo.func_1.should == 2
171
+ end
172
+
173
+ it 'should return the current compiler' do
174
+ module Foo
175
+ inline do |builder|
176
+ builder.compiler.should == Inliner::Compilers::GCC
177
+ end
178
+ end
179
+ end
180
+
181
+ # it 'should be configured using the hash form' do
182
+ # tcc = mock('tcc', :exists? => true, :compile => nil)
183
+ # Inliner::Compilers::TCC.should_receive(:new).and_return(tcc)
184
+ # module Foo
185
+ # inline "int func_1() { return 1; }", :compiler => Inliner::Compilers::TCC
186
+ # end
187
+ # end
159
188
 
160
189
  it 'should raise errors' do
161
190
  lambda {
@@ -169,18 +198,55 @@ EOC
169
198
  end
170
199
  }.should raise_error(/Compile error/)
171
200
  end
172
-
173
- end
174
201
 
175
- describe Inliner::Compilers::Compiler do
176
- before do
177
- class DummyCC < Inliner::Compilers::Compiler
178
- def cmd
179
- "dummycc -shared"
202
+ describe 'Compiler' do
203
+ before do
204
+ class DummyCC < Inliner::Compilers::Compiler
205
+ def cmd
206
+ "dummycc -shared"
207
+ end
180
208
  end
181
209
  end
210
+ it 'should return the progname' do
211
+ DummyCC.new.progname.should == 'dummycc'
212
+ end
182
213
  end
183
- it 'should return the progname' do
184
- DummyCC.new.progname.should == 'dummycc'
214
+
215
+ describe 'GPlusPlus compiler' do
216
+
217
+ it 'should compile and link a shim C library that encapsulates C++ code' do
218
+
219
+ module Foo
220
+ inline do |builder|
221
+ builder.use_compiler Inliner::Compilers::GPlusPlus
222
+ builder.c_raw <<-code
223
+ #include <iostream>
224
+ #include <string>
225
+ using namespace std;
226
+ class Greeter {
227
+ public:
228
+ Greeter();
229
+ string say_hello();
230
+ };
231
+ Greeter::Greeter() { };
232
+ string Greeter::say_hello() {
233
+ return "Hello foos!";
234
+ };
235
+ code
236
+ builder.map 'char *' => 'string'
237
+ builder.c <<-code
238
+ const char* say_hello()
239
+ {
240
+ Greeter greeter;
241
+ return greeter.say_hello().c_str();
242
+ }
243
+ code
244
+ end
245
+ end
246
+ Foo.say_hello.should == 'Hello foos!'
247
+ end
248
+
185
249
  end
250
+
186
251
  end
252
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ffi-inliner
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrea Fazzi
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-08-05 00:00:00 +02:00
12
+ date: 2009-12-07 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -89,8 +89,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
89
89
  version:
90
90
  requirements: []
91
91
 
92
- rubyforge_project: ffi
93
- rubygems_version: 1.3.4
92
+ rubyforge_project: ffi-inliner
93
+ rubygems_version: 1.3.5
94
94
  signing_key:
95
95
  specification_version: 3
96
96
  summary: With ffi-inliner you can run C code within your ruby script