ruby2cext 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Changelog +27 -0
- data/README +44 -0
- data/bin/rb2cx +178 -0
- data/doc/eval2c.html +281 -0
- data/doc/index.html +218 -0
- data/doc/limitations.html +581 -0
- data/doc/optimizations.html +222 -0
- data/doc/rb2cx.html +151 -0
- data/doc/style.css +27 -0
- data/lib/ruby2cext/c_function.rb +621 -0
- data/lib/ruby2cext/common_node_comp.rb +1409 -0
- data/lib/ruby2cext/compiler.rb +242 -0
- data/lib/ruby2cext/error.rb +15 -0
- data/lib/ruby2cext/eval2c.rb +129 -0
- data/lib/ruby2cext/parser.rb +36 -0
- data/lib/ruby2cext/plugin.rb +24 -0
- data/lib/ruby2cext/plugins/builtin_methods.rb +820 -0
- data/lib/ruby2cext/plugins/case_optimize.rb +105 -0
- data/lib/ruby2cext/plugins/const_cache.rb +38 -0
- data/lib/ruby2cext/plugins/inline_methods.rb +69 -0
- data/lib/ruby2cext/plugins/require_include.rb +71 -0
- data/lib/ruby2cext/plugins/warnings.rb +123 -0
- data/lib/ruby2cext/scopes.rb +227 -0
- data/lib/ruby2cext/str_to_c_strlit.rb +12 -0
- data/lib/ruby2cext/tools.rb +84 -0
- data/lib/ruby2cext/version.rb +22 -0
- data/testfiles/bench.rb +116 -0
- data/testfiles/eval2c/test_eval2c.rb +37 -0
- data/testfiles/test.rb +615 -0
- data/testfiles/vmode_test.rb +73 -0
- data/testfiles/warn_test.rb +35 -0
- metadata +87 -0
data/Changelog
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
Changelog
|
2
|
+
|
3
|
+
-- 0.2.0
|
4
|
+
|
5
|
+
* New features:
|
6
|
+
* New plugin architecture for optimizations and other things
|
7
|
+
* Optimization plugins: const_cache, builtin_methods, inline_methods,
|
8
|
+
case_optimize
|
9
|
+
* Other plugins: require_include, warnings
|
10
|
+
* Eval2C for compilation at runtime
|
11
|
+
* Available as gem now
|
12
|
+
|
13
|
+
* Improvements:
|
14
|
+
* Implemented control flow "through" rescue/ensure clauses
|
15
|
+
* Better vmode handling
|
16
|
+
* Support for Ruby 1.8.5 and 1.8.6
|
17
|
+
* Improved performance for global variables
|
18
|
+
|
19
|
+
* Fixes:
|
20
|
+
* Workaround for an issue with ALLOCA_N in while loops
|
21
|
+
* Fixed a problem with case/when (missing newline nodes)
|
22
|
+
* Removed libraries from compile command
|
23
|
+
* Various other small fixes
|
24
|
+
|
25
|
+
-- 0.1.0
|
26
|
+
|
27
|
+
* Initial release
|
data/README
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
|
2
|
+
Ruby2CExtension
|
3
|
+
===============
|
4
|
+
|
5
|
+
Ruby2CExtension is a Ruby to C extension translator/compiler. It takes any Ruby
|
6
|
+
source file, parses it using Ruby's builtin parser and then translates the
|
7
|
+
abstract syntax tree into "equivalent" C extension code.
|
8
|
+
|
9
|
+
|
10
|
+
Requirements
|
11
|
+
------------
|
12
|
+
|
13
|
+
* Ruby 1.8.4, 1.8.5 or 1.8.6
|
14
|
+
* RubyNode (available at http://rubynode.rubyforge.org/)
|
15
|
+
|
16
|
+
|
17
|
+
Installation
|
18
|
+
------------
|
19
|
+
|
20
|
+
Just run (as root):
|
21
|
+
|
22
|
+
gem install ruby2cext
|
23
|
+
|
24
|
+
Or if you do not use the gem:
|
25
|
+
|
26
|
+
ruby setup.rb
|
27
|
+
|
28
|
+
This will install Ruby2CExtension to the default locations. Optionally you can
|
29
|
+
supply some options to setup.rb to customize the installation (see "ruby
|
30
|
+
setup.rb --help").
|
31
|
+
|
32
|
+
|
33
|
+
Documentation
|
34
|
+
-------------
|
35
|
+
|
36
|
+
Please see doc/index.html for more documentation.
|
37
|
+
|
38
|
+
|
39
|
+
License
|
40
|
+
-------
|
41
|
+
|
42
|
+
Copyright 2006-2007 Dominik Bathon <dbatml@gmx.de>
|
43
|
+
|
44
|
+
Ruby2CExtension is licensed under the same terms as Ruby.
|
data/bin/rb2cx
ADDED
@@ -0,0 +1,178 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "ruby2cext/compiler"
|
4
|
+
require "ruby2cext/version"
|
5
|
+
require "getoptlong"
|
6
|
+
require "logger"
|
7
|
+
|
8
|
+
include Ruby2CExtension
|
9
|
+
|
10
|
+
def usage(logger)
|
11
|
+
logger.warn(<<EOS.strip)
|
12
|
+
Usage: rb2cx [options] file.rb ...
|
13
|
+
|
14
|
+
Translates the given Ruby file into an equivalent C extension. The result is
|
15
|
+
stored in file.c. It will then be compiled into a shared object file, unless
|
16
|
+
the option --only-c is given.
|
17
|
+
|
18
|
+
If multiple files are given, each file will be handled separately.
|
19
|
+
|
20
|
+
=== General Options:
|
21
|
+
|
22
|
+
-h / --help print this help
|
23
|
+
-c / --only-c only translate to C
|
24
|
+
-v / --verbose print status messages
|
25
|
+
-w / --warnings print warnings for things that might not work as expected
|
26
|
+
-V / --version print the Ruby2CExtension version
|
27
|
+
|
28
|
+
=== Include Option:
|
29
|
+
|
30
|
+
-I / --include path
|
31
|
+
|
32
|
+
If a Ruby file "require"s another Ruby file and that file can be found in the
|
33
|
+
given path, then it will be included in the C extension. This option can be
|
34
|
+
used multiple times with different paths, the paths are then searched in the
|
35
|
+
given order. Use --verbose to see which files were included.
|
36
|
+
|
37
|
+
=== Optimization Options:
|
38
|
+
|
39
|
+
-O / --optimization <optimization>
|
40
|
+
|
41
|
+
Where <optimization> is one of the following:
|
42
|
+
|
43
|
+
const_cache
|
44
|
+
enables local constant lookup caching
|
45
|
+
|
46
|
+
builtin_methods
|
47
|
+
optimizes calls to many methods of builtin types
|
48
|
+
|
49
|
+
inline_methods
|
50
|
+
inlines the methods nil?, equal? and __send__
|
51
|
+
|
52
|
+
case_optimize
|
53
|
+
optimizes case statments with nil, true, false or Fixnums
|
54
|
+
|
55
|
+
all
|
56
|
+
enables all of the above optimizations
|
57
|
+
|
58
|
+
=== Examples:
|
59
|
+
|
60
|
+
rb2cx -wv file.rb
|
61
|
+
rb2cx -I . -O all file.rb
|
62
|
+
rb2cx -I . -I ../libs -O const_cache -O builtin_methods -w file.rb
|
63
|
+
EOS
|
64
|
+
end
|
65
|
+
|
66
|
+
def compile_file(file_name, plugins, include_paths, only_c, logger)
|
67
|
+
bn = File.basename(file_name)
|
68
|
+
unless bn =~ /\A(.*)\.rb\w?\z/
|
69
|
+
raise "#{file_name} is no ruby file"
|
70
|
+
end
|
71
|
+
name = $1;
|
72
|
+
unless name =~ /\A\w+\z/
|
73
|
+
raise "'#{name}' is not a valid extension name"
|
74
|
+
end
|
75
|
+
file_name = File.join(File.dirname(file_name), bn)
|
76
|
+
|
77
|
+
logger.info("reading #{file_name}")
|
78
|
+
source_str = IO.read(file_name)
|
79
|
+
|
80
|
+
logger.info("translating #{file_name} to C")
|
81
|
+
c = Compiler.new(name, logger)
|
82
|
+
unless include_paths.empty?
|
83
|
+
plugins = plugins.merge({:require_include => [include_paths, [file_name]]})
|
84
|
+
end
|
85
|
+
logger.debug("plugins = #{plugins.inspect}")
|
86
|
+
c.add_plugins(plugins)
|
87
|
+
logger.debug("plugins used: #{c.plugins.map { |pi| pi.class }.inspect}")
|
88
|
+
c.add_rb_file(source_str, file_name)
|
89
|
+
c_code = c.to_c_code
|
90
|
+
|
91
|
+
c_file_name = File.join(File.dirname(file_name), "#{name}.c")
|
92
|
+
logger.info("writing #{c_file_name}")
|
93
|
+
File.open(c_file_name, "w") { |f| f.puts(c_code) }
|
94
|
+
|
95
|
+
unless only_c
|
96
|
+
logger.info("compiling #{c_file_name}")
|
97
|
+
Compiler.compile_c_file_to_dllib(c_file_name, logger)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def main
|
102
|
+
opts = GetoptLong.new(
|
103
|
+
["--help", "-h", GetoptLong::NO_ARGUMENT],
|
104
|
+
["--only-c", "-c", GetoptLong::NO_ARGUMENT],
|
105
|
+
["--verbose", "-v", GetoptLong::NO_ARGUMENT],
|
106
|
+
["--version", "-V", GetoptLong::NO_ARGUMENT],
|
107
|
+
["--debug", GetoptLong::NO_ARGUMENT], # undocumented
|
108
|
+
["--warnings", "-w", GetoptLong::NO_ARGUMENT],
|
109
|
+
["--include", "-I", GetoptLong::REQUIRED_ARGUMENT],
|
110
|
+
["--optimization", "-O", GetoptLong::REQUIRED_ARGUMENT]
|
111
|
+
)
|
112
|
+
|
113
|
+
logger = Logger.new(STDERR)
|
114
|
+
logger.formatter = proc { |severity, time, progname, msg| "#{msg}\n" }
|
115
|
+
logger.level = Logger::WARN
|
116
|
+
|
117
|
+
only_c = false
|
118
|
+
include_paths = []
|
119
|
+
optimizations = {}
|
120
|
+
all_optimizations = false
|
121
|
+
plugins = {}
|
122
|
+
version_printed = false
|
123
|
+
begin
|
124
|
+
opts.each do |opt, arg|
|
125
|
+
case opt
|
126
|
+
when "--help"
|
127
|
+
usage(logger)
|
128
|
+
exit
|
129
|
+
when "--only-c"
|
130
|
+
only_c = true
|
131
|
+
when "--verbose"
|
132
|
+
logger.level = Logger::INFO
|
133
|
+
when "--version"
|
134
|
+
unless version_printed
|
135
|
+
logger.warn(Ruby2CExtension::FULL_VERSION_STRING)
|
136
|
+
version_printed = true
|
137
|
+
end
|
138
|
+
when "--debug"
|
139
|
+
logger.level = Logger::DEBUG
|
140
|
+
when "--warnings"
|
141
|
+
plugins[:warnings] = true
|
142
|
+
when "--include"
|
143
|
+
unless File.directory?(arg)
|
144
|
+
raise "'#{arg}' is no directory"
|
145
|
+
end
|
146
|
+
include_paths << arg
|
147
|
+
when "--optimization"
|
148
|
+
case (arg = arg.to_sym)
|
149
|
+
when :const_cache, :case_optimize, :builtin_methods, :inline_methods
|
150
|
+
optimizations[arg] = true
|
151
|
+
when :all
|
152
|
+
all_optimizations = true
|
153
|
+
else
|
154
|
+
raise "unknown optimization: #{arg}"
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
if ARGV.empty? && !version_printed
|
159
|
+
raise "No files given"
|
160
|
+
end
|
161
|
+
rescue => e
|
162
|
+
logger.error("#{e} ('rb2cx --help' for help)")
|
163
|
+
exit 1
|
164
|
+
end
|
165
|
+
|
166
|
+
plugins[:optimizations] = all_optimizations ? :all : optimizations
|
167
|
+
|
168
|
+
begin
|
169
|
+
ARGV.each { |fn|
|
170
|
+
compile_file(fn, plugins, include_paths, only_c, logger)
|
171
|
+
}
|
172
|
+
rescue RuntimeError, SyntaxError => e
|
173
|
+
logger.error(e)
|
174
|
+
exit 1
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
main
|
data/doc/eval2c.html
ADDED
@@ -0,0 +1,281 @@
|
|
1
|
+
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Eval2C</title>
|
5
|
+
<link href="style.css" media="all" rel="Stylesheet" type="text/css">
|
6
|
+
</head>
|
7
|
+
<body>
|
8
|
+
<h1>Eval2C</h1>
|
9
|
+
|
10
|
+
|
11
|
+
<p>Eval2C is a class that allows the compilation of Ruby code to a C extension at
|
12
|
+
runtime. The compiled C extension will be <code>require</code>d automatically
|
13
|
+
and the compiled code will be available as a Proc.</p>
|
14
|
+
|
15
|
+
|
16
|
+
<p>It is easy to integrate Eval2C into existing scripts or libraries to improve
|
17
|
+
performance. It is also pretty simple to provide fallback code for when
|
18
|
+
Ruby2CExtension is not available.</p>
|
19
|
+
|
20
|
+
|
21
|
+
<p>Sections: <a href="#section1">Basic Usage</a>, <a href="#section2">Options</a>, <a href="#section3">Implementation Details</a>, <a href="#section4">Using Eval2C Only When It Is Available</a>.</p>
|
22
|
+
|
23
|
+
|
24
|
+
<h2 id="section1">Basic Usage</h2>
|
25
|
+
|
26
|
+
|
27
|
+
<p>Here is an example:</p>
|
28
|
+
|
29
|
+
|
30
|
+
<pre><code>require "ruby2cext/eval2c"
|
31
|
+
|
32
|
+
$e2c = Ruby2CExtension::Eval2C.new
|
33
|
+
|
34
|
+
$e2c.toplevel_eval("puts 'hello'") # prints hello
|
35
|
+
</code></pre>
|
36
|
+
|
37
|
+
<p>First you need to create an Eval2C instance, this instance basically stores
|
38
|
+
the options/configuration. In the above example no options were given, so the
|
39
|
+
defaults are used. The available options are explained below.</p>
|
40
|
+
|
41
|
+
|
42
|
+
<p>The last line of the example does what the method name suggests: the string of
|
43
|
+
Ruby code is evaluated at the toplevel (i.e. not inside any class scope). To
|
44
|
+
be more precise, that last line is equivalent to this Ruby code:</p>
|
45
|
+
|
46
|
+
|
47
|
+
<pre><code>$some_global_var = proc { puts 'hello' }
|
48
|
+
$some_global_var.call
|
49
|
+
</code></pre>
|
50
|
+
|
51
|
+
The proc is compiled to a C extension, that C expension is then
|
52
|
+
<code>require</code>d and finally the proc is called.
|
53
|
+
|
54
|
+
<p>The implementation of <code>toplevel_eval</code> is actually pretty simple:</p>
|
55
|
+
|
56
|
+
|
57
|
+
<pre><code>def toplevel_eval(code_str)
|
58
|
+
compile_to_proc(code_str).call
|
59
|
+
end
|
60
|
+
</code></pre>
|
61
|
+
|
62
|
+
<p>So the main work is done by <code>compile_to_proc</code>, which can also be used
|
63
|
+
directly:</p>
|
64
|
+
|
65
|
+
|
66
|
+
<pre><code>$e2c.compile_to_proc("|a,b| a+b").call(2, 3) # => 5
|
67
|
+
</code></pre>
|
68
|
+
|
69
|
+
<p>There are some things to note: The proc won’t have access to local variables
|
70
|
+
and it will be defined at toplevel, which is important for constant lookup:</p>
|
71
|
+
|
72
|
+
|
73
|
+
<pre><code>SOME_CONST = :toplevel
|
74
|
+
|
75
|
+
class A
|
76
|
+
SOME_CONST = :A
|
77
|
+
$e2c.compile_to_proc("SOME_CONST").call # => :toplevel, not :A!
|
78
|
+
end
|
79
|
+
</code></pre>
|
80
|
+
|
81
|
+
<p>Eval2C also offers equivalents to Ruby’s <code>module_eval</code>/<code>class_eval</code> and
|
82
|
+
<code>instance_eval</code>:</p>
|
83
|
+
|
84
|
+
|
85
|
+
<pre><code>class A
|
86
|
+
$e2c.module_eval(self, %{
|
87
|
+
def initialize(x)
|
88
|
+
@x = x
|
89
|
+
end
|
90
|
+
def foo(y)
|
91
|
+
@x + y
|
92
|
+
end
|
93
|
+
})
|
94
|
+
end
|
95
|
+
|
96
|
+
A.new(5).foo(6) # => 11
|
97
|
+
|
98
|
+
$e2c.instance_eval("4321", "reverse") # => "1234"
|
99
|
+
</code></pre>
|
100
|
+
|
101
|
+
<p>Their implementations also use <code>compile_to_proc</code> and they are very similar to
|
102
|
+
the implementation of <code>toplevel_eval</code>:</p>
|
103
|
+
|
104
|
+
|
105
|
+
<pre><code>def module_eval(mod, code_str)
|
106
|
+
mod.module_eval(&compile_to_proc(code_str))
|
107
|
+
end
|
108
|
+
alias :class_eval :module_eval
|
109
|
+
|
110
|
+
def instance_eval(object, code_str)
|
111
|
+
object.instance_eval(&compile_to_proc(code_str))
|
112
|
+
end
|
113
|
+
</code></pre>
|
114
|
+
|
115
|
+
<p>With <code>module_eval</code>/<code>class_eval</code> it is possible to selectively compile some
|
116
|
+
performance critical methods to C and leave the rest in Ruby.</p>
|
117
|
+
|
118
|
+
|
119
|
+
<p>But there is one more thing: <code>compile_methods</code>.</p>
|
120
|
+
|
121
|
+
|
122
|
+
<p>Defining the methods using <code>module_eval</code> as in the last example, has at least
|
123
|
+
one downside: it won’t play nice with syntax-highlighting text editors,
|
124
|
+
because the code of the methods is actually in a string. It would be much
|
125
|
+
nicer to <code>def</code> the methods the normal way and then just compile them
|
126
|
+
afterwards.</p>
|
127
|
+
|
128
|
+
|
129
|
+
<p>Thanks to <code>compile_methods</code> this is possible:</p>
|
130
|
+
|
131
|
+
|
132
|
+
<pre><code>class A
|
133
|
+
def initialize(x)
|
134
|
+
@x = x
|
135
|
+
end
|
136
|
+
def foo(y)
|
137
|
+
@x + y
|
138
|
+
end
|
139
|
+
|
140
|
+
$e2c.compile_methods(self, :initialize, :foo)
|
141
|
+
end
|
142
|
+
|
143
|
+
A.new(5).foo(6) # => 11
|
144
|
+
</code></pre>
|
145
|
+
|
146
|
+
<p><code>compile_methods</code> will grab the methods’ node trees using RubyNode, compile
|
147
|
+
them to C and then replace the old methods with the C versions. The visibility
|
148
|
+
of the old methods will be preserved.</p>
|
149
|
+
|
150
|
+
|
151
|
+
<p>There are some limitations: <code>compile_methods</code> only works with methods defined
|
152
|
+
using <code>def</code> and the methods must not need a cref (for more informations on
|
153
|
+
what crefs are please see the respective section in
|
154
|
+
<a href="limitations.html">limitations</a>). This mainly means that constants must be
|
155
|
+
prefixed with a double colon: <code>Array</code> needs a cref, while <code>::Array</code> does not.</p>
|
156
|
+
|
157
|
+
|
158
|
+
<h2 id="section2">Options</h2>
|
159
|
+
|
160
|
+
|
161
|
+
<p>Eval2C is configured with an option hash, the available options are:</p>
|
162
|
+
|
163
|
+
|
164
|
+
<ul>
|
165
|
+
<li><code>:path</code>: this is the path where the compiled extensions are stored, the
|
166
|
+
default is the directory <code>.ruby2cext</code> in the current users home dir.</li>
|
167
|
+
<li><code>:prefix</code>: this is the prefix for the names of the generated extensions, the
|
168
|
+
names of the extensions are generated from this prefix and a <span class="caps">SHA1</span> digest of
|
169
|
+
the code and options. The default is <code>"eval2c"</code> and it is generally not
|
170
|
+
necessary to change it.</li>
|
171
|
+
<li><code>:plugins</code>: this is used to configure the options for the compilation
|
172
|
+
(compare <a href="rb2cx.html">rb2cx</a>) and it is another option hash. The default is
|
173
|
+
<code>{:optimizations => :all}</code>, but all of the following keys are
|
174
|
+
possible:
|
175
|
+
<ul>
|
176
|
+
<li><code>:warnings</code>: if set to <code>true</code>, then warnings about possible problems will
|
177
|
+
be sent to the logger (see below).</li>
|
178
|
+
<li><code>:require_include</code>: can be set to a single search path or an array of
|
179
|
+
search paths (equivalent to the <code>-I</code> option of <code>rb2cx</code>).</li>
|
180
|
+
<li><code>:optimizations</code>: yet another option hash to configure the
|
181
|
+
<a href="optimizations.html">optimizations</a>. The valid keys are <code>:const_cache</code>,
|
182
|
+
<code>:builtin_methods</code>, <code>:inline_methods</code> and <code>:case_optimize</code>, each with
|
183
|
+
<code>true</code> or <code>false</code> as value. To just use all optimizations it is also
|
184
|
+
possible to just use the symbol <code>:all</code> instead of the hash.</li>
|
185
|
+
</ul>
|
186
|
+
</li>
|
187
|
+
<li><code>:logger</code>: used for log messages, can be either <code>nil</code> or an instance of
|
188
|
+
<code>Logger</code> (<code>require "logger"</code>). The default is <code>nil</code>, which means
|
189
|
+
that no output is generated.</li>
|
190
|
+
<li><code>:force_recompile</code>: if this is set to <code>true</code>, then each generated extension
|
191
|
+
is (re)compiled, even if it is already available from a previous run. The
|
192
|
+
default is <code>false</code>.</li>
|
193
|
+
</ul>
|
194
|
+
|
195
|
+
|
196
|
+
<p>Some examples:</p>
|
197
|
+
|
198
|
+
|
199
|
+
<pre><code>Eval2C.new(:path => ".", :plugins => {})
|
200
|
+
Eval2C.new(:plugins => {:require_include=>[".", "../lib"], :optimizations=>:all})
|
201
|
+
Eval2C.new(:plugins => {:warnings=>true, :optimizations=>{:builtin_methods=>true}})
|
202
|
+
Eval2C.new(:prefix => "my_lib_name", :logger => Logger.new(STDOUT))
|
203
|
+
</code></pre>
|
204
|
+
|
205
|
+
<h2 id="section3">Implementation Details</h2>
|
206
|
+
|
207
|
+
|
208
|
+
<p>The <code>compile_to_proc</code> method works as follows:</p>
|
209
|
+
|
210
|
+
|
211
|
+
<ol>
|
212
|
+
<li>The name of the extension is determined from the given prefix combined with
|
213
|
+
the <span class="caps">SHA1</span> digest of the Ruby code and some extra data (Ruby version,
|
214
|
+
Ruby2CExtension version and plugin options). So the code determines the
|
215
|
+
extension name and if the same code is compiled with the same version and
|
216
|
+
settings, the name will be the same. This allows very simple and efficient
|
217
|
+
caching of the compiled extensions.</li>
|
218
|
+
<li>From the extension name a global variable name is constructed. The compiled
|
219
|
+
proc will be stored in that global variable by the generated C extension.</li>
|
220
|
+
<li>If the extension with the generated name already exists, then we are
|
221
|
+
basically done and can skip this step. Otherwise the Ruby proc code is
|
222
|
+
translated to C and compiled to a C extension in the specified path.</li>
|
223
|
+
<li>The generated or already existing C extension is <code>require</code>d.</li>
|
224
|
+
<li>The global variable that now contains the compiled proc is read and the
|
225
|
+
result is returned.</li>
|
226
|
+
</ol>
|
227
|
+
|
228
|
+
|
229
|
+
<p>The <code>compile_methods</code> method works similar.</p>
|
230
|
+
|
231
|
+
|
232
|
+
<p>As mentioned above, this scheme allows very efficient caching of the C
|
233
|
+
extensions. Basically each extension is only compiled once when the
|
234
|
+
program/library is executed for the first time. In later runs the extension
|
235
|
+
will already exist and just be <code>require</code>d.</p>
|
236
|
+
|
237
|
+
|
238
|
+
<p>Adding the Ruby version and the Ruby2CExtension version to the digest avoids
|
239
|
+
problems if multiple versions of Ruby or Ruby2CExtension are installed and
|
240
|
+
used in parallel and it also guarantees that the extensions are automatically
|
241
|
+
recompiled if Ruby or Ruby2CExtension are updated.</p>
|
242
|
+
|
243
|
+
|
244
|
+
<h2 id="section4">Using Eval2C Only When It Is Available</h2>
|
245
|
+
|
246
|
+
|
247
|
+
<p>Having Ruby2CExtension as a hard dependency just for some speedup might not
|
248
|
+
be acceptable for many projects, in particular since Ruby2CExtension requires
|
249
|
+
that a C compiler is available at runtime.</p>
|
250
|
+
|
251
|
+
|
252
|
+
<p>An alternative that avoids the hard dependency is to use Ruby2CExtension only
|
253
|
+
if it is installed anyway and otherwise just execute the Ruby code as usual.
|
254
|
+
This works works very well if Eval2C and <code>compile_methods</code> is used, for
|
255
|
+
example:</p>
|
256
|
+
|
257
|
+
|
258
|
+
<pre><code>begin
|
259
|
+
require "ruby2cext/eval2c"
|
260
|
+
$e2c = Ruby2CExtension::Eval2C.new
|
261
|
+
rescue LoadError
|
262
|
+
$e2c = nil
|
263
|
+
end
|
264
|
+
|
265
|
+
class A
|
266
|
+
def initialize(x)
|
267
|
+
@x = x
|
268
|
+
end
|
269
|
+
def foo(y)
|
270
|
+
@x + y
|
271
|
+
end
|
272
|
+
|
273
|
+
$e2c && $e2c.compile_methods(self, :initialize, :foo)
|
274
|
+
end
|
275
|
+
</code></pre>
|
276
|
+
|
277
|
+
<p>So if Ruby2CExtension is available, then the methods will be compiled and
|
278
|
+
fast, otherwise the Ruby code will just work as usual (but it might be a bit
|
279
|
+
slower of course).</p>
|
280
|
+
</body>
|
281
|
+
</html>
|