rogerdpack-RubyInline 3.8.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/README.txt +139 -0
  2. data/lib/inline.rb +875 -0
  3. metadata +56 -0
data/README.txt ADDED
@@ -0,0 +1,139 @@
1
+ = Ruby Inline
2
+
3
+ * http://rubyforge.org/projects/rubyinline/
4
+ * http://rubyinline.rubyforge.org/RubyInline/
5
+ * http://www.zenspider.com/ZSS/Products/RubyInline/
6
+ * mailto:ryand-ruby@zenspider.com
7
+
8
+ == DESCRIPTION:
9
+
10
+ Inline allows you to write foreign code within your ruby code. It
11
+ automatically determines if the code in question has changed and
12
+ builds it only when necessary. The extensions are then automatically
13
+ loaded into the class/module that defines it.
14
+
15
+ You can even write extra builders that will allow you to write inlined
16
+ code in any language. Use Inline::C as a template and look at
17
+ Module#inline for the required API.
18
+
19
+ == PACKAGING:
20
+
21
+ To package your binaries into a gem, use hoe's INLINE and
22
+ FORCE_PLATFORM env vars.
23
+
24
+ Example:
25
+
26
+ rake package INLINE=1
27
+
28
+ or:
29
+
30
+ rake package INLINE=1 FORCE_PLATFORM=mswin32
31
+
32
+ See hoe for more details.
33
+
34
+ == FEATURES/PROBLEMS:
35
+
36
+ + Quick and easy inlining of your C or C++ code embedded in your ruby script.
37
+ + Extendable to work with other languages.
38
+ + Automatic conversion between ruby and C basic types
39
+ + char, unsigned, unsigned int, char *, int, long, unsigned long
40
+ + inline_c_raw exists for when the automatic conversion isn't sufficient.
41
+ + Only recompiles if the inlined code has changed.
42
+ + Pretends to be secure.
43
+ + Only requires standard ruby libraries, nothing extra to download.
44
+
45
+ == SYNOPSYS:
46
+
47
+ require "inline"
48
+ class MyTest
49
+ inline do |builder|
50
+ builder.c "
51
+ long factorial(int max) {
52
+ int i=max, result=1;
53
+ while (i >= 2) { result *= i--; }
54
+ return result;
55
+ }"
56
+ end
57
+ end
58
+ t = MyTest.new()
59
+ factorial_5 = t.factorial(5)
60
+
61
+ == SYNOPSYS (C++):
62
+
63
+ require 'inline'
64
+ class MyTest
65
+ inline(:C) do |builder|
66
+ builder.include '<iostream>'
67
+ builder.add_compile_flags '-x c++', '-lstdc++'
68
+ builder.c '
69
+ void hello(int i) {
70
+ while (i-- > 0) {
71
+ std::cout << "hello" << std::endl;
72
+ }
73
+ }'
74
+ end
75
+ end
76
+ t = MyTest.new()
77
+ t.hello(3)
78
+
79
+ == (PSEUDO)BENCHMARKS:
80
+
81
+ > make bench
82
+
83
+ Running native
84
+ Type = Native , Iter = 1000000, T = 28.70058100 sec, 0.00002870 sec / iter
85
+ Running primer - preloads the compiler and stuff
86
+ With full builds
87
+ Type = Inline C , Iter = 1000000, T = 7.55118600 sec, 0.00000755 sec / iter
88
+ Type = InlineRaw, Iter = 1000000, T = 7.54488300 sec, 0.00000754 sec / iter
89
+ Type = Alias , Iter = 1000000, T = 7.53243100 sec, 0.00000753 sec / iter
90
+ Without builds
91
+ Type = Inline C , Iter = 1000000, T = 7.59543300 sec, 0.00000760 sec / iter
92
+ Type = InlineRaw, Iter = 1000000, T = 7.54097200 sec, 0.00000754 sec / iter
93
+ Type = Alias , Iter = 1000000, T = 7.53654000 sec, 0.00000754 sec / iter
94
+
95
+ == PROFILING STRATEGY:
96
+
97
+ 0) Always keep a log of your progress and changes.
98
+ 1) Run code with 'time' and large dataset.
99
+ 2) Run code with '-rprofile' and smaller dataset, large enough to get good #s.
100
+ 3) Examine profile output and translate 1 bottleneck to C.
101
+ 4) Run new code with 'time' and large dataset. Repeat 2-3 if unsatisfied.
102
+ 5) Run final code with 'time' and compare to the first run.
103
+
104
+ == REQUIREMENTS:
105
+
106
+ + Ruby - 1.8.2 has been used on FreeBSD 4.6+ and MacOSX.
107
+ + POSIX compliant system (ie pretty much any UNIX, or Cygwin on MS platforms).
108
+ + A C/C++ compiler (the same one that compiled your ruby interpreter).
109
+ + test::unit for running tests ( http://testunit.talbott.ws/ ).
110
+
111
+ == INSTALL:
112
+
113
+ + make test (optional)
114
+ + make install
115
+
116
+ == LICENSE:
117
+
118
+ (The MIT License)
119
+
120
+ Copyright (c) 2001-2007 Ryan Davis, Zen Spider Software
121
+
122
+ Permission is hereby granted, free of charge, to any person obtaining
123
+ a copy of this software and associated documentation files (the
124
+ "Software"), to deal in the Software without restriction, including
125
+ without limitation the rights to use, copy, modify, merge, publish,
126
+ distribute, sublicense, and/or sell copies of the Software, and to
127
+ permit persons to whom the Software is furnished to do so, subject to
128
+ the following conditions:
129
+
130
+ The above copyright notice and this permission notice shall be
131
+ included in all copies or substantial portions of the Software.
132
+
133
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
134
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
135
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
136
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
137
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
138
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
139
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/lib/inline.rb ADDED
@@ -0,0 +1,875 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ ##
4
+ # Ruby Inline is a framework for writing ruby extensions in foreign
5
+ # languages.
6
+ #
7
+ # == SYNOPSIS
8
+ #
9
+ # require 'inline'
10
+ # class MyClass
11
+ # inline do |builder|
12
+ # builder.include "<math.h>"
13
+ # builder.c %q{
14
+ # long factorial(int max) {
15
+ # int i=max, result=1;
16
+ # while (i >= 2) { result *= i--; }
17
+ # return result;
18
+ # }
19
+ # }
20
+ # end
21
+ # end
22
+ #
23
+ # == DESCRIPTION
24
+ #
25
+ # Inline allows you to write foreign code within your ruby code. It
26
+ # automatically determines if the code in question has changed and
27
+ # builds it only when necessary. The extensions are then automatically
28
+ # loaded into the class/module that defines it.
29
+ #
30
+ # You can even write extra builders that will allow you to write
31
+ # inlined code in any language. Use Inline::C as a template and look
32
+ # at Module#inline for the required API.
33
+ #
34
+ # == PACKAGING
35
+ #
36
+ # To package your binaries into a gem, use hoe's INLINE and
37
+ # FORCE_PLATFORM env vars.
38
+ #
39
+ # Example:
40
+ #
41
+ # rake package INLINE=1
42
+ #
43
+ # or:
44
+ #
45
+ # rake package INLINE=1 FORCE_PLATFORM=mswin32
46
+ #
47
+ # See hoe for more details.
48
+ #
49
+
50
+ require "rbconfig"
51
+ require "digest/md5"
52
+ require 'fileutils'
53
+ require 'rubygems'
54
+
55
+ require 'zentest_mapping'
56
+
57
+ $TESTING = false unless defined? $TESTING
58
+
59
+ class CompilationError < RuntimeError; end
60
+
61
+ ##
62
+ # The Inline module is the top-level module used. It is responsible
63
+ # for instantiating the builder for the right language used,
64
+ # compilation/linking when needed, and loading the inlined code into
65
+ # the current namespace.
66
+
67
+ module Inline
68
+ VERSION = '3.8.2'
69
+
70
+ WINDOZE = /mswin|mingw/ =~ RUBY_PLATFORM
71
+ RUBINIUS = defined? RUBY_ENGINE
72
+ DEV_NULL = (WINDOZE ? 'nul' : '/dev/null')
73
+ GEM = (WINDOZE ? 'gem.bat' : 'gem')
74
+ RAKE = if WINDOZE then
75
+ 'rake.bat'
76
+ elsif RUBINIUS then
77
+ File.join(Gem.bindir, 'rake')
78
+ else
79
+ "#{Gem.ruby} -S rake"
80
+ end
81
+
82
+ warn "RubyInline v #{VERSION}" if $DEBUG
83
+
84
+ def self.register cls
85
+ registered_inline_classes << cls
86
+ registered_inline_classes.uniq!
87
+ end
88
+
89
+ def self.registered_inline_classes
90
+ @@registered_inline_classes ||= []
91
+ end
92
+
93
+ # rootdir can be forced using INLINEDIR variable
94
+ # if not defined, it should store in user HOME folder
95
+ #
96
+ # Under Windows user data can be stored in several locations:
97
+ #
98
+ # HOME
99
+ # HOMEDRIVE + HOMEPATH
100
+ # APPDATA
101
+ # USERPROFILE
102
+ #
103
+ # Perform a check in that other to see if the environment is defined
104
+ # and if so, use it. only try this on Windows.
105
+
106
+ def self.rootdir
107
+ env = ENV['INLINEDIR'] || ENV['HOME']
108
+
109
+ if env.nil? and WINDOZE then
110
+ # try HOMEDRIVE + HOMEPATH combination
111
+ if ENV['HOMEDRIVE'] && ENV['HOMEPATH'] then
112
+ env = ENV['HOMEDRIVE'] + ENV['HOMEPATH']
113
+ end
114
+
115
+ # no HOMEDRIVE? use APPDATA
116
+ env = ENV['APPDATA'] if env.nil? and ENV['APPDATA']
117
+
118
+ # bummer, still no env? then fall to USERPROFILE
119
+ env = ENV['USERPROFILE'] if env.nil? and ENV['USERPROFILE']
120
+ end
121
+
122
+ if env.nil? then
123
+ abort "Define INLINEDIR or HOME in your environment and try again"
124
+ end
125
+
126
+ unless defined? @@rootdir and env == @@rootdir and test ?d, @@rootdir then
127
+ rootdir = env
128
+ Dir.mkdir rootdir, 0700 unless test ?d, rootdir
129
+ Dir.assert_secure rootdir
130
+ @@rootdir = rootdir
131
+ end
132
+
133
+ @@rootdir
134
+ end
135
+
136
+ def self.directory
137
+ directory = File.join(rootdir, ".ruby_inline")
138
+ unless defined? @@directory and directory == @@directory
139
+ @@directory = File.join(self.rootdir, ".ruby_inline")
140
+ end
141
+ Dir.assert_secure directory
142
+ @@directory
143
+ end
144
+
145
+ ##
146
+ # Inline::C is the default builder used and the only one provided by
147
+ # Inline. It can be used as a template to write builders for other
148
+ # languages. It understands type-conversions for the basic types and
149
+ # can be extended as needed using #add_type_converter, #alias_type_converter
150
+ # and #remove_type_converter.
151
+
152
+ class C
153
+
154
+ include ZenTestMapping
155
+
156
+ MAGIC_ARITY_THRESHOLD = 15
157
+ MAGIC_ARITY = -1
158
+
159
+ ##
160
+ # Default C to ruby and ruby to C type map
161
+
162
+ TYPE_MAP = {
163
+ 'char' => [ 'NUM2CHR', 'CHR2FIX' ],
164
+
165
+ 'char *' => [ 'StringValuePtr', 'rb_str_new2' ],
166
+
167
+ 'double' => [ 'NUM2DBL', 'rb_float_new' ],
168
+
169
+ 'int' => [ "FI\X2INT", 'INT2FIX' ],
170
+ 'unsigned int' => [ 'NUM2UINT', 'UINT2NUM' ],
171
+ 'unsigned' => [ 'NUM2UINT', 'UINT2NUM' ],
172
+
173
+ 'long' => [ 'NUM2LONG', 'LONG2NUM' ],
174
+ 'unsigned long' => [ 'NUM2ULONG', 'ULONG2NUM' ],
175
+
176
+ 'long long' => [ 'NUM2LL', 'LL2NUM' ],
177
+ 'unsigned long long' => [ 'NUM2ULL', 'ULL2NUM' ],
178
+
179
+ 'off_t' => [ 'NUM2OFFT', 'OFFT2NUM' ],
180
+
181
+ 'VALUE' => [ '', '' ],
182
+ # Can't do these converters because they conflict with the above:
183
+ # ID2SYM(x), SYM2ID(x), F\IX2UINT(x)
184
+ }
185
+
186
+ def strip_comments(src)
187
+ # strip c-comments
188
+ src = src.gsub(%r%\s*/\*.*?\*/%m, '')
189
+ # strip cpp-comments
190
+ src = src.gsub(%r%^\s*//.*?\n%, '')
191
+ src = src.gsub(%r%[ \t]*//[^\n]*%, '')
192
+ src
193
+ end
194
+
195
+ def parse_signature(src, raw=false)
196
+
197
+ sig = self.strip_comments(src)
198
+ # strip preprocessor directives
199
+ sig.gsub!(/^\s*\#.*(\\\n.*)*/, '')
200
+ # strip {}s
201
+ sig.gsub!(/\{[^\}]*\}/, '{ }')
202
+ # clean and collapse whitespace
203
+ sig.gsub!(/\s+/, ' ')
204
+
205
+ unless defined? @types then
206
+ @types = 'void|' + @type_map.keys.map{|x| Regexp.escape(x)}.join('|')
207
+ end
208
+
209
+ if /(#{@types})\s*(\w+)\s*\(([^)]*)\)/ =~ sig then
210
+ return_type, function_name, arg_string = $1, $2, $3
211
+ args = []
212
+ arg_string.split(',').each do |arg|
213
+
214
+ # helps normalize into 'char * varname' form
215
+ arg = arg.gsub(/\s*\*\s*/, ' * ').strip
216
+
217
+ if /(((#{@types})\s*\*?)+)\s+(\w+)\s*$/ =~ arg then
218
+ args.push([$4, $1])
219
+ elsif arg != "void" then
220
+ warn "WAR\NING: '#{arg}' not understood"
221
+ end
222
+ end
223
+
224
+ arity = args.size
225
+ arity = MAGIC_ARITY if raw
226
+
227
+ return {
228
+ 'return' => return_type,
229
+ 'name' => function_name,
230
+ 'args' => args,
231
+ 'arity' => arity
232
+ }
233
+ end
234
+
235
+ raise SyntaxError, "Can't parse signature: #{sig}"
236
+ end # def parse_signature
237
+
238
+ def generate(src, options={})
239
+ options = {:expand_types=>options} unless Hash === options
240
+
241
+ expand_types = options[:expand_types]
242
+ singleton = options[:singleton]
243
+ result = self.strip_comments(src)
244
+
245
+ signature = parse_signature(src, !expand_types)
246
+ function_name = signature['name']
247
+ method_name = options[:method_name]
248
+ method_name ||= test_to_normal function_name
249
+ return_type = signature['return']
250
+ arity = signature['arity']
251
+
252
+ raise ArgumentError, "too many arguments" if arity > MAGIC_ARITY_THRESHOLD
253
+
254
+ if expand_types then
255
+ prefix = "static VALUE #{function_name}("
256
+ if arity == MAGIC_ARITY then
257
+ prefix += "int argc, VALUE *argv, VALUE self"
258
+ else
259
+ prefix += "VALUE self"
260
+ prefix += signature['args'].map { |arg, type| ", VALUE _#{arg}"}.join
261
+ end
262
+ prefix += ") {\n"
263
+ prefix += signature['args'].map { |arg, type|
264
+ " #{type} #{arg} = #{ruby2c(type)}(_#{arg});\n"
265
+ }.join
266
+
267
+ # replace the function signature (hopefully) with new sig (prefix)
268
+ result.sub!(/[^;\/\"\>]+#{function_name}\s*\([^\{]+\{/, "\n" + prefix)
269
+ result.sub!(/\A\n/, '') # strip off the \n in front in case we added it
270
+ unless return_type == "void" then
271
+ raise SyntaxError, "Couldn't find return statement for #{function_name}" unless
272
+ result =~ /return/
273
+ result.gsub!(/return\s+([^\;\}]+)/) do
274
+ "return #{c2ruby(return_type)}(#{$1})"
275
+ end
276
+ else
277
+ result.sub!(/\s*\}\s*\Z/, "\nreturn Qnil;\n}")
278
+ end
279
+ else
280
+ prefix = "static #{return_type} #{function_name}("
281
+ result.sub!(/[^;\/\"\>]+#{function_name}\s*\(/, prefix)
282
+ result.sub!(/\A\n/, '') # strip off the \n in front in case we added it
283
+ end
284
+
285
+ delta = if result =~ /\A(static.*?\{)/m then
286
+ $1.split(/\n/).size
287
+ else
288
+ warn "WAR\NING: Can't find signature in #{result.inspect}\n" unless $TESTING
289
+ 0
290
+ end
291
+
292
+ file, line = caller[1].split(/:/)
293
+ result = "# line #{line.to_i + delta} \"#{file}\"\n" + result unless $DEBUG and not $TESTING
294
+
295
+ @src << result
296
+ @sig[function_name] = [arity,singleton,method_name]
297
+
298
+ return result if $TESTING
299
+ end # def generate
300
+
301
+ ##
302
+ # Builds a complete C extension suitable for writing to a file and
303
+ # compiling.
304
+
305
+ def generate_ext
306
+ ext = []
307
+
308
+ if @include_ruby_first
309
+ @inc.unshift "#include \"ruby.h\""
310
+ else
311
+ @inc.push "#include \"ruby.h\""
312
+ end
313
+
314
+ ext << @inc
315
+ ext << nil
316
+ ext << @src.join("\n\n")
317
+ ext << nil
318
+ ext << nil
319
+ ext << "#ifdef __cplusplus"
320
+ ext << "extern \"C\" {"
321
+ ext << "#endif"
322
+ ext << " __declspec(dllexport)" if WINDOZE
323
+ ext << " void Init_#{module_name}() {"
324
+ ext << " VALUE c = rb_cObject;"
325
+
326
+ # TODO: use rb_class2path
327
+ # ext << " VALUE c = rb_path2class(#{@mod.name.inspect});"
328
+ ext << @mod.name.split("::").map { |n|
329
+ " c = rb_const_get(c, rb_intern(\"#{n}\"));"
330
+ }.join("\n")
331
+
332
+ ext << nil
333
+
334
+ @sig.keys.sort.each do |name|
335
+ method = ''
336
+ arity, singleton, method_name = @sig[name]
337
+ if singleton then
338
+ if method_name == 'allocate' then
339
+ raise "#{@mod}::allocate must have an arity of zero" if arity > 0
340
+ ext << " rb_define_alloc_func(c, (VALUE(*)(VALUE))#{name});"
341
+ next
342
+ end
343
+ method << " rb_define_singleton_method(c, \"#{method_name}\", "
344
+ else
345
+ method << " rb_define_method(c, \"#{method_name}\", "
346
+ end
347
+ method << "(VALUE(*)(ANYARGS))#{name}, #{arity});"
348
+ ext << method
349
+ end
350
+
351
+ ext << @init_extra.join("\n") unless @init_extra.empty?
352
+
353
+ ext << nil
354
+ ext << " }"
355
+ ext << "#ifdef __cplusplus"
356
+ ext << "}"
357
+ ext << "#endif"
358
+ ext << nil
359
+
360
+ ext.join "\n"
361
+ end
362
+
363
+ def module_name
364
+ unless defined? @module_name then
365
+ module_name = @mod.name.gsub('::','__')
366
+ md5 = Digest::MD5.new
367
+ @sig.keys.sort_by { |x| x.to_s }.each { |m| md5 << m.to_s }
368
+ @module_name = "Inline_#{module_name}_#{md5.to_s[0,4]}"
369
+ end
370
+ @module_name
371
+ end
372
+
373
+ def so_name
374
+ unless defined? @so_name then
375
+ @so_name = "#{Inline.directory}/#{module_name}.#{Config::CONFIG["DLEXT"]}"
376
+ end
377
+ @so_name
378
+ end
379
+
380
+ attr_reader :rb_file, :mod
381
+ attr_writer :mod
382
+ attr_accessor :src, :sig, :flags, :libs, :init_extra
383
+
384
+ ##
385
+ # Sets the name of the C struct for generating accessors. Used with
386
+ # #accessor, #reader, #writer.
387
+
388
+ attr_accessor :struct_name
389
+
390
+ def initialize(mod)
391
+ raise ArgumentError, "Class/Module arg is required" unless Module === mod
392
+ # new (but not on some 1.8s) -> inline -> real_caller|eval
393
+ stack = caller
394
+ meth = stack.shift until meth =~ /in .(inline|test_|setup)/ or stack.empty?
395
+ raise "Couldn't discover caller" if stack.empty?
396
+ real_caller = stack.first
397
+ real_caller = stack[3] if real_caller =~ /\(eval\)/
398
+ real_caller =~ /(.*):(\d+)/
399
+ real_caller = $1
400
+ @rb_file = File.expand_path real_caller
401
+
402
+ @mod = mod
403
+ @src = []
404
+ @inc = []
405
+ @sig = {}
406
+ @flags = []
407
+ @libs = []
408
+ @init_extra = []
409
+ @include_ruby_first = true
410
+ @inherited_methods = {}
411
+ @struct_name = nil
412
+
413
+ @type_map = TYPE_MAP.dup
414
+ end
415
+
416
+ ##
417
+ # Adds a #reader and #writer for a C struct member wrapped via
418
+ # Data_Wrap_Struct. +method+ is the ruby name to give the accessor,
419
+ # +type+ is the C type. Unless the C member name is overridden with
420
+ # +member+, the method name is used as the struct member.
421
+ #
422
+ # builder.struct_name = 'MyStruct'
423
+ # builder.accessor :title, 'char *'
424
+ # builder.accessor :stream_index, 'int', :index
425
+ #
426
+ # The latter accesses MyStruct->index via the stream_index method.
427
+
428
+ def accessor(method, type, member = method)
429
+ reader method, type, member
430
+ writer method, type, member
431
+ end
432
+
433
+ ##
434
+ # Adds a reader for a C struct member wrapped via Data_Wrap_Struct.
435
+ # +method+ is the ruby name to give the reader, +type+ is the C type.
436
+ # Unless the C member name is overridden with +member+, the method
437
+ # name is used as the struct member. See #accessor for an example.
438
+
439
+ def reader(method, type, member = method)
440
+ raise "struct name not set for reader #{method} #{type}" unless
441
+ @struct_name
442
+
443
+ c <<-C
444
+ VALUE #{method}() {
445
+ #{@struct_name} *pointer;
446
+
447
+ Data_Get_Struct(self, #{@struct_name}, pointer);
448
+
449
+ return #{c2ruby type}(pointer->#{member});
450
+ }
451
+ C
452
+ end
453
+
454
+ ##
455
+ # Adds a writer for a C struct member wrapped via Data_Get_Struct.
456
+ # +method+ is the ruby name to give the writer, +type+ is the C type.
457
+ # Unless the C member name is overridden with +member+, the method
458
+ # name is used as the struct member. See #accessor for an example.
459
+
460
+ def writer(method, type, member = method)
461
+ raise "struct name not set for writer #{method} #{type}" unless
462
+ @struct_name
463
+
464
+ c <<-C
465
+ VALUE #{method}_equals(VALUE value) {
466
+ #{@struct_name} *pointer;
467
+
468
+ Data_Get_Struct(self, #{@struct_name}, pointer);
469
+
470
+ pointer->#{member} = #{ruby2c type}(value);
471
+
472
+ return value;
473
+ }
474
+ C
475
+ end
476
+
477
+ ##
478
+ # Converts ruby type +type+ to a C type
479
+
480
+ def ruby2c(type)
481
+ raise ArgumentError, "Unknown type #{type.inspect}" unless @type_map.has_key? type
482
+ @type_map[type].first
483
+ end
484
+
485
+ ##
486
+ # Converts C type +type+ to a ruby type
487
+
488
+ def c2ruby(type)
489
+ raise ArgumentError, "Unknown type #{type.inspect}" unless @type_map.has_key? type
490
+ @type_map[type].last
491
+ end
492
+
493
+ ##
494
+ # Attempts to load pre-generated code returning true if it succeeds.
495
+
496
+ def load_cache
497
+ begin
498
+ file = File.join("inline", File.basename(so_name))
499
+ if require file then
500
+ dir = Inline.directory
501
+ warn "WAR\NING: #{dir} exists but is not being used" if test ?d, dir and $VERBOSE
502
+ return true
503
+ end
504
+ rescue LoadError
505
+ end
506
+ return false
507
+ end
508
+
509
+ ##
510
+ # Loads the generated code back into ruby
511
+
512
+ def load
513
+ require "#{so_name}" or raise LoadError, "require on #{so_name} failed"
514
+ end
515
+
516
+ ##
517
+ # Builds the source file, if needed, and attempts to compile it.
518
+
519
+ def build
520
+ so_name = self.so_name
521
+ so_exists = File.file? so_name
522
+ unless so_exists and File.mtime(rb_file) < File.mtime(so_name) then
523
+
524
+ unless File.directory? Inline.directory then
525
+ warn "NOTE: creating #{Inline.directory} for RubyInline" if $DEBUG
526
+ Dir.mkdir Inline.directory, 0700
527
+ end
528
+
529
+ src_name = "#{Inline.directory}/#{module_name}.c"
530
+ old_src_name = "#{src_name}.old"
531
+ should_compare = File.write_with_backup(src_name) do |io|
532
+ io.puts generate_ext
533
+ end
534
+
535
+ # recompile only if the files are different
536
+ recompile = true
537
+ if so_exists and should_compare and
538
+ FileUtils.compare_file(old_src_name, src_name) then
539
+ recompile = false
540
+
541
+ # Updates the timestamps on all the generated/compiled files.
542
+ # Prevents us from entering this conditional unless the source
543
+ # file changes again.
544
+ t = Time.now
545
+ File.utime(t, t, src_name, old_src_name, so_name)
546
+ end
547
+
548
+ if recompile then
549
+
550
+ hdrdir = %w(srcdir archdir rubyhdrdir).map { |name|
551
+ Config::CONFIG[name]
552
+ }.find { |dir|
553
+ dir and File.exist? File.join(dir, "/ruby.h")
554
+ } or abort "ERROR: Can't find header dir for ruby. Exiting..."
555
+
556
+ flags = @flags.join(' ')
557
+ libs = @libs.join(' ')
558
+
559
+ config_hdrdir = if RUBY_VERSION > '1.9' then
560
+ "-I #{File.join hdrdir, RbConfig::CONFIG['arch']}"
561
+ else
562
+ nil
563
+ end
564
+ filename = File.expand_path(src_name).inspect
565
+ so_filename = so_name.inspect
566
+ if RUBY_PLATFORM =~ /mingw/
567
+ filename.gsub!('"', '\'')
568
+ so_filename.gsub!('"', '\'')
569
+ end
570
+
571
+ cmd = [ Config::CONFIG['LDSHARED'],
572
+ flags,
573
+ Config::CONFIG['CCDLFLAGS'],
574
+ Config::CONFIG['CFLAGS'],
575
+ '-I', hdrdir,
576
+ config_hdrdir,
577
+ '-I', Config::CONFIG['includedir'],
578
+ "-L#{Config::CONFIG['libdir']}",
579
+ '-o', so_filename,
580
+ filename,
581
+ libs,
582
+ crap_for_windoze ].join(' ')
583
+
584
+ # TODO: remove after osx 10.5.2
585
+ cmd += ' -flat_namespace -undefined suppress' if
586
+ RUBY_PLATFORM =~ /darwin9\.[01]/
587
+ cmd += " 2> #{DEV_NULL}" if $TESTING and not $DEBUG
588
+
589
+ warn "Building #{so_name} with '#{cmd}'" if $DEBUG
590
+ result = `#{cmd}`
591
+ warn "Output:\n#{result}" if $DEBUG
592
+ if $? != 0 then
593
+ bad_src_name = src_name + ".bad"
594
+ File.rename src_name, bad_src_name
595
+ raise CompilationError, "error executing #{cmd.inspect}: #{$?}\nRenamed #{src_name} to #{bad_src_name}"
596
+ end
597
+
598
+ # NOTE: manifest embedding is only required when using VC8 ruby
599
+ # build or compiler.
600
+ # Errors from this point should be ignored if Config::CONFIG['arch']
601
+ # (RUBY_PLATFORM) matches 'i386-mswin32_80'
602
+ if WINDOZE and RUBY_PLATFORM =~ /_80$/ then
603
+ Dir.chdir Inline.directory do
604
+ cmd = "mt /manifest lib.so.manifest /outputresource:so.dll;#2"
605
+ warn "Embedding manifest with '#{cmd}'" if $DEBUG
606
+ result = `#{cmd}`
607
+ warn "Output:\n#{result}" if $DEBUG
608
+ if $? != 0 then
609
+ raise CompilationError, "error executing #{cmd}: #{$?}"
610
+ end
611
+ end
612
+ end
613
+
614
+ warn "Built successfully" if $DEBUG
615
+ end
616
+
617
+ else
618
+ warn "#{so_name} is up to date" if $DEBUG
619
+ end # unless (file is out of date)
620
+ end # def build
621
+
622
+ ##
623
+ # Returns extra compilation flags for windoze platforms. Ugh.
624
+
625
+ def crap_for_windoze
626
+ # gawd windoze land sucks
627
+ case RUBY_PLATFORM
628
+ when /mswin32/ then
629
+ " -link /LIBPATH:\"#{Config::CONFIG['libdir']}\" /DEFAULTLIB:\"#{Config::CONFIG['LIBRUBY']}\" /INCREMENTAL:no /EXPORT:Init_#{module_name}"
630
+ when /mingw32/ then
631
+ " -Wl,--enable-auto-import -L#{Config::CONFIG['libdir']} #{Config::CONFIG["LIBRUBYARG_SHARED"]}"
632
+ when /i386-cygwin/ then
633
+ ' -L/usr/local/lib -lruby.dll'
634
+ else
635
+ ''
636
+ end
637
+ end
638
+
639
+ ##
640
+ # Adds compiler options to the compiler command line. No
641
+ # preprocessing is done, so you must have all your dashes and
642
+ # everything.
643
+
644
+ def add_compile_flags(*flags)
645
+ @flags.push(*flags)
646
+ end
647
+
648
+ ##
649
+ # Adds linker flags to the link command line. No preprocessing is
650
+ # done, so you must have all your dashes and everything.
651
+
652
+ def add_link_flags(*flags)
653
+ @libs.push(*flags)
654
+ end
655
+
656
+ ##
657
+ # Adds custom content to the end of the init function.
658
+
659
+ def add_to_init(*src)
660
+ @init_extra.push(*src)
661
+ end
662
+
663
+ ##
664
+ # Registers C type-casts +r2c+ and +c2r+ for +type+.
665
+
666
+ def add_type_converter(type, r2c, c2r)
667
+ warn "WAR\NING: overridding #{type} on #{caller[0]}" if @type_map.has_key? type
668
+ @type_map[type] = [r2c, c2r]
669
+ end
670
+
671
+ ##
672
+ # Registers C type +alias_type+ as an alias of +existing_type+
673
+
674
+ def alias_type_converter(existing_type, alias_type)
675
+ warn "WAR\NING: overridding #{type} on #{caller[0]}" if
676
+ @type_map.has_key? alias_type
677
+
678
+ @type_map[alias_type] = @type_map[existing_type]
679
+ end
680
+
681
+ ##
682
+ # Unregisters C type-casts for +type+.
683
+
684
+ def remove_type_converter(type)
685
+ @type_map.delete type
686
+ end
687
+
688
+ ##
689
+ # Maps a ruby constant to C (with the same name)
690
+
691
+ def map_ruby_const(*names)
692
+ names.each do |name|
693
+ self.prefix "static VALUE #{name};"
694
+ self.add_to_init " #{name} = rb_const_get(c, rb_intern(#{name.to_s.inspect}));"
695
+ end
696
+ end
697
+
698
+ ##
699
+ # Maps a C constant to ruby. +names_and_types+ is a hash that maps the
700
+ # name of the constant to its C type.
701
+ #
702
+ # builder.map_c_const :C_NAME => :int
703
+ #
704
+ # If you wish to give the constant a different ruby name:
705
+ #
706
+ # builder.map_c_const :C_NAME => [:int, :RUBY_NAME]
707
+
708
+ def map_c_const(names_and_types)
709
+ names_and_types.each do |name, typ|
710
+ typ, ruby_name = Array === typ ? typ : [typ, name]
711
+ self.add_to_init " rb_define_const(c, #{ruby_name.to_s.inspect}, #{c2ruby(typ.to_s)}(#{name}));"
712
+ end
713
+ end
714
+
715
+ ##
716
+ # Adds an include to the top of the file. Don't forget to use
717
+ # quotes or angle brackets.
718
+
719
+ def include(header)
720
+ @inc << "#include #{header}"
721
+ end
722
+
723
+ ##
724
+ # Specifies that the the ruby.h header should be included *after* custom
725
+ # header(s) instead of before them.
726
+
727
+ def include_ruby_last
728
+ @include_ruby_first = false
729
+ end
730
+
731
+ ##
732
+ # Adds any amount of text/code to the source
733
+
734
+ def prefix(code)
735
+ @src << code
736
+ end
737
+
738
+ ##
739
+ # Adds a C function to the source, including performing automatic
740
+ # type conversion to arguments and the return value. The Ruby
741
+ # method name can be overridden by providing method_name. Unknown
742
+ # type conversions can be extended by using +add_type_converter+.
743
+
744
+ def c src, options = {}
745
+ options = {
746
+ :expand_types => true,
747
+ }.merge options
748
+ self.generate src, options
749
+ end
750
+
751
+ ##
752
+ # Same as +c+, but adds a class function.
753
+
754
+ def c_singleton src, options = {}
755
+ options = {
756
+ :expand_types => true,
757
+ :singleton => true,
758
+ }.merge options
759
+ self.generate src, options
760
+ end
761
+
762
+ ##
763
+ # Adds a raw C function to the source. This version does not
764
+ # perform any type conversion and must conform to the ruby/C
765
+ # coding conventions. The Ruby method name can be overridden
766
+ # by providing method_name.
767
+
768
+ def c_raw src, options = {}
769
+ self.generate src, options
770
+ end
771
+
772
+ ##
773
+ # Same as +c_raw+, but adds a class function.
774
+
775
+ def c_raw_singleton src, options = {}
776
+ options = {
777
+ :singleton => true,
778
+ }.merge options
779
+ self.generate src, options
780
+ end
781
+
782
+ end # class Inline::C
783
+ end # module Inline
784
+
785
+ class Module
786
+
787
+ ##
788
+ # options is a hash that allows you to pass extra data to your
789
+ # builder. The only key that is guaranteed to exist is :testing.
790
+
791
+ attr_reader :options
792
+
793
+ ##
794
+ # Extends the Module class to have an inline method. The default
795
+ # language/builder used is C, but can be specified with the +lang+
796
+ # parameter.
797
+
798
+ def inline(lang = :C, options={})
799
+ Inline.register self
800
+
801
+ case options
802
+ when TrueClass, FalseClass then
803
+ warn "WAR\NING: 2nd argument to inline is now a hash, changing to {:testing=>#{options}}" unless options
804
+ options = { :testing => options }
805
+ when Hash
806
+ options[:testing] ||= false
807
+ else
808
+ raise ArgumentError, "BLAH"
809
+ end
810
+
811
+ builder_class = begin
812
+ Inline.const_get(lang)
813
+ rescue NameError
814
+ require "inline/#{lang}"
815
+ Inline.const_get(lang)
816
+ end
817
+
818
+ @options = options
819
+ builder = builder_class.new self
820
+
821
+ yield builder
822
+
823
+ unless options[:testing] then
824
+ unless builder.load_cache then
825
+ builder.build
826
+ builder.load
827
+ end
828
+ end
829
+ end
830
+ end
831
+
832
+ class File
833
+
834
+ ##
835
+ # Equivalent to +File::open+ with an associated block, but moves
836
+ # any existing file with the same name to the side first.
837
+
838
+ def self.write_with_backup(path) # returns true if file already existed
839
+
840
+ # move previous version to the side if it exists
841
+ renamed = false
842
+ if test ?f, path then
843
+ renamed = true
844
+ File.rename path, path + ".old"
845
+ end
846
+
847
+ File.open(path, "w") do |io|
848
+ yield(io)
849
+ end
850
+
851
+ return renamed
852
+ end
853
+ end # class File
854
+
855
+ class Dir
856
+
857
+ ##
858
+ # +assert_secure+ checks that if a +path+ exists it has minimally
859
+ # writable permissions. If not, it prints an error and exits. It
860
+ # only works on +POSIX+ systems. Patches for other systems are
861
+ # welcome.
862
+
863
+ def self.assert_secure(path)
864
+ mode = File.stat(path).mode
865
+ unless ((mode % 01000) & 0022) == 0 then
866
+ if $TESTING then
867
+ raise SecurityError, "Directory #{path} is insecure"
868
+ else
869
+ abort "#{path} is insecure (#{'%o' % mode}). It may not be group or world writable. Exiting."
870
+ end
871
+ end
872
+ rescue Errno::ENOENT
873
+ # If it ain't there, it's certainly secure
874
+ end
875
+ end
metadata ADDED
@@ -0,0 +1,56 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rogerdpack-RubyInline
3
+ version: !ruby/object:Gem::Version
4
+ version: 3.8.2.1
5
+ platform: ruby
6
+ authors:
7
+ - Ryan Davis
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-05-16 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: fork of RubyInline that is mingw friendlier
17
+ email:
18
+ - rogerdpack@gmail.comm
19
+ executables: []
20
+
21
+ extensions: []
22
+
23
+ extra_rdoc_files:
24
+ - README.txt
25
+ files:
26
+ - lib/inline.rb
27
+ - README.txt
28
+ has_rdoc: false
29
+ homepage:
30
+ post_install_message:
31
+ rdoc_options:
32
+ - --main
33
+ - README.txt
34
+ require_paths:
35
+ - lib
36
+ required_ruby_version: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: "0"
41
+ version:
42
+ required_rubygems_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: "0"
47
+ version:
48
+ requirements: []
49
+
50
+ rubyforge_project:
51
+ rubygems_version: 1.2.0
52
+ signing_key:
53
+ specification_version: 3
54
+ summary: fork of RubyInline that is mingw friendlier
55
+ test_files: []
56
+