ruby2cext 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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) # =&gt; 5
67
+ </code></pre>
68
+
69
+ <p>There are some things to note: The proc won&#8217;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 # =&gt; :toplevel, not :A!
78
+ end
79
+ </code></pre>
80
+
81
+ <p>Eval2C also offers equivalents to Ruby&#8217;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) # =&gt; 11
97
+
98
+ $e2c.instance_eval("4321", "reverse") # =&gt; "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(&#38;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(&#38;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&#8217;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) # =&gt; 11
144
+ </code></pre>
145
+
146
+ <p><code>compile_methods</code> will grab the methods&#8217; 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 =&gt; :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 =&gt; ".", :plugins =&gt; {})
200
+ Eval2C.new(:plugins =&gt; {:require_include=&gt;[".", "../lib"], :optimizations=&gt;:all})
201
+ Eval2C.new(:plugins =&gt; {:warnings=&gt;true, :optimizations=&gt;{:builtin_methods=&gt;true}})
202
+ Eval2C.new(:prefix =&gt; "my_lib_name", :logger =&gt; 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 &#38;&#38; $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>