rogerdpack-RubyInline 3.8.2.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.
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
+