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.
- data/README.txt +139 -0
- data/lib/inline.rb +875 -0
- 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
|
+
|