Class: Ruby2CExtension::Compiler

Inherits:
Object
  • Object
show all
Defined in:
lib/ruby2cext/compiler.rb

Constant Summary collapse

NODE_TRANSFORM_OPTIONS =
{:include_node => true, :keep_newline_nodes => true}
COMPILE_COMMAND =
"#{conf["LDSHARED"]} #{cflags} -I . -I #{conf["archdir"]}"
DLEXT =

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, logger = nil) ⇒ Compiler

Returns a new instance of Compiler.



16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/ruby2cext/compiler.rb', line 16

def initialize(name, logger = nil)
	@name = name
	@logger = logger
	@funs = []
	@funs_reuseable = {}
	@toplevel_funs = []
	@sym_man = Tools::SymbolManager.new
	@global_man = Tools::GlobalManager.new
	@uniq_names = Tools::UniqueNames.new
	@helpers = {}
	@plugins = []
	@preprocessors = {}
end

Instance Attribute Details

#loggerObject (readonly)

Returns the value of attribute logger.



14
15
16
# File 'lib/ruby2cext/compiler.rb', line 14

def logger
  @logger
end

#nameObject (readonly)

Returns the value of attribute name.



14
15
16
# File 'lib/ruby2cext/compiler.rb', line 14

def name
  @name
end

#pluginsObject (readonly)

Returns the value of attribute plugins.



14
15
16
# File 'lib/ruby2cext/compiler.rb', line 14

def plugins
  @plugins
end

Class Method Details

.compile_c_file_to_dllib(c_file_name, logger = nil) ⇒ Object

compiles a C file using the compiler from rbconfig



224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/ruby2cext/compiler.rb', line 224

def self.compile_c_file_to_dllib(c_file_name, logger = nil)
	unless c_file_name =~ /\.c\z/
		raise Ruby2CExtError, "#{c_file_name} is no C file"
	end
	dl_name = c_file_name.sub(/c\z/, DLEXT)
	cmd = "#{COMPILE_COMMAND} -o #{dl_name} #{c_file_name}"
	if RUBY_PLATFORM =~ /mswin32/
		cmd << " -link /INCREMENTAL:no /EXPORT:Init_#{File.basename(c_file_name, ".c")}"
	end
	logger.info(cmd) if logger
	unless system(cmd) # run it
		raise Ruby2CExtError, "error while executing '#{cmd}'"
	end
	dl_name
end

Instance Method Details

#add_fun(code, base_name) ⇒ Object



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/ruby2cext/compiler.rb', line 124

def add_fun(code, base_name)
	unless (name = @funs_reuseable[code])
		name = un(base_name)
		lines = code.split("\n")
		unless lines.shift =~ /^\s*static / # first line needs static
			raise Ruby2CExtError::Bug, "trying to add a non static function"
		end
		if lines.grep(/^\s*static /).empty? # only reuseably without static variables
			@funs_reuseable[code] = name
		end
		unless code.sub!("FUNNAME", name)
			raise Ruby2CExtError::Bug, "trying to add a function without FUNNAME"
		end
		@funs << code
	end
	name
end

#add_helper(str) ⇒ Object



120
121
122
# File 'lib/ruby2cext/compiler.rb', line 120

def add_helper(str)
	@helpers[str] ||= true
end

#add_plugin(plugin_class, *args) ⇒ Object



142
143
144
# File 'lib/ruby2cext/compiler.rb', line 142

def add_plugin(plugin_class, *args)
	@plugins << plugin_class.new(self, *args)
end

#add_plugins(options) ⇒ Object



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/ruby2cext/compiler.rb', line 146

def add_plugins(options)
	if options[:warnings]
		require "ruby2cext/plugins/warnings"
		add_plugin(Plugins::Warnings)
	end
	if (opt = options[:optimizations])
		if opt == :all
			opt = {
				:const_cache=>true, :case_optimize=>true,
				:builtin_methods=>true, :inline_methods=>true
			}
		end
		if opt[:const_cache]
			require "ruby2cext/plugins/const_cache"
			add_plugin(Plugins::ConstCache)
		end
		if opt[:case_optimize]
			require "ruby2cext/plugins/case_optimize"
			add_plugin(Plugins::CaseOptimize)
		end
		if opt[:inline_methods]
			require "ruby2cext/plugins/inline_methods"
			add_plugin(Plugins::InlineMethods)
		end
		if (builtins = opt[:builtin_methods])
			require "ruby2cext/plugins/builtin_methods"
			if Array === builtins
				builtins = builtins.map { |b| b.to_s.to_sym } # allow symbols, strings and the actual classes to work
			else
				builtins = Plugins::BuiltinMethods::SUPPORTED_BUILTINS
			end
			add_plugin(Plugins::BuiltinMethods, builtins)
		end
	end
	if (ri_args = options[:require_include])
		require "ruby2cext/plugins/require_include"
		unless Array === ri_args.first
			ri_args = [ri_args] # to allow just an array of include paths to also work
		end
		add_plugin(Plugins::RequireInclude, *ri_args)
	end
end

#add_preprocessor(node_type, &pp_proc) ⇒ Object

preprocessors can be added by plugins. preprocessors are procs that take two arguments: the current cfun and the node (tree) to preprocess (which will have type node_type)

The proc can either return a (modified) node (tree) or string. If a node (tree) is returned then that will be translated as usual, if a string is returned, that string will be the result

Example, a preprocessor that replaces 23 with 42: add_preprocessor(:lit) { |cfun, node|

node.last[:lit] == 23 ? [:lit, {:lit=>42}] : node

}

Another way to do the same: add_preprocessor(:lit) { |cfun, node|

node.last[:lit] == 23 ? cfun.comp_lit(:lit=>42) : node

}

If multiple preprocessors are added for the same node type then they will be called after each other with the result of the previous one unless it is a string, then the following preprocessors are ignored



210
211
212
# File 'lib/ruby2cext/compiler.rb', line 210

def add_preprocessor(node_type, &pp_proc)
	(@preprocessors[node_type] ||= []) << pp_proc
end

#add_rb_file(source_str, file_name) ⇒ Object



90
91
92
93
94
# File 'lib/ruby2cext/compiler.rb', line 90

def add_rb_file(source_str, file_name)
	rb_file_to_toplevel_functions(source_str, file_name).each { |fn|
		add_toplevel(fn)
	}
end

#add_toplevel(function_name) ⇒ Object



60
61
62
# File 'lib/ruby2cext/compiler.rb', line 60

def add_toplevel(function_name)
	@toplevel_funs << function_name
end

#compile_toplevel_function(node_tree, private_vmode = true) ⇒ Object

non destructive: node_tree will not be changed



65
66
67
# File 'lib/ruby2cext/compiler.rb', line 65

def compile_toplevel_function(node_tree, private_vmode = true)
	CFunction::ToplevelScope.compile(self, node_tree, private_vmode)
end

#global_const(str, register_gc = true) ⇒ Object



103
104
105
# File 'lib/ruby2cext/compiler.rb', line 103

def global_const(str, register_gc = true)
	@global_man.get(str, true, register_gc)
end

#global_var(str) ⇒ Object



106
107
108
# File 'lib/ruby2cext/compiler.rb', line 106

def global_var(str)
	@global_man.get(str, false, true)
end

#log(str, warning = false) ⇒ Object



110
111
112
113
114
115
116
117
118
# File 'lib/ruby2cext/compiler.rb', line 110

def log(str, warning = false)
	if logger
		if warning
			logger.warn(str)
		else
			logger.info(str)
		end
	end
end

#preprocessors_for(node_type) ⇒ Object



214
215
216
# File 'lib/ruby2cext/compiler.rb', line 214

def preprocessors_for(node_type)
	@preprocessors[node_type]
end

#rb_file_to_toplevel_functions(source_str, file_name) ⇒ Object



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/ruby2cext/compiler.rb', line 71

def rb_file_to_toplevel_functions(source_str, file_name)
	res = []
	hash = Parser.parse_string(source_str, file_name)
	# add all BEGIN blocks, if available
	if (beg_tree = hash[:begin])
		beg_tree = beg_tree.transform(NODE_TRANSFORM_OPTIONS)
		if beg_tree.first == :block
			beg_tree.last.each { |s| res << compile_toplevel_function(s, false) }
		else
			res << compile_toplevel_function(beg_tree, false)
		end
	end
	# add toplevel scope
	if (tree = hash[:tree])
		res << compile_toplevel_function(tree.transform(NODE_TRANSFORM_OPTIONS))
	end
	res
end

#sym(sym) ⇒ Object



100
101
102
# File 'lib/ruby2cext/compiler.rb', line 100

def sym(sym)
	@sym_man.get(sym)
end

#to_c_code(time_stamp = Time.now) ⇒ Object



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/ruby2cext/compiler.rb', line 30

def to_c_code(time_stamp = Time.now)
	plugins_global = @plugins.map { |plugin| plugin.global_c_code }
	plugins_init = @plugins.map { |plugin| plugin.init_c_code }
	res = [
		"/* generated by #{FULL_VERSION_STRING} on #{time_stamp} */",
		"#include <ruby.h>",
		"#include <node.h>",
		"#include <env.h>",
		"#include <st.h>",
		"extern VALUE ruby_top_self;",
		"static VALUE org_ruby_top_self;",
		@sym_man.to_c_code,
		@global_man.to_c_code,
	]
	res.concat(@helpers.keys.sort)
	res.concat(plugins_global)
	res.concat(@funs)
	res << "void Init_#{@name}() {"
	res << "org_ruby_top_self = ruby_top_self;"
	# just to be sure
	res << "rb_global_variable(&org_ruby_top_self);"
	res << "init_syms();"
	res << "init_globals();"
	res << "NODE *cref = rb_node_newnode(NODE_CREF, rb_cObject, 0, 0);"
	res.concat(plugins_init)
	@toplevel_funs.each { |f| res << "#{f}(ruby_top_self, cref);" }
	res << "}"
	res.join("\n").split("\n").map { |l| l.strip }.reject { |l| l.empty? }.join("\n")
end

#un(str) ⇒ Object

uniq name



97
98
99
# File 'lib/ruby2cext/compiler.rb', line 97

def un(str)
	@uniq_names.get(str)
end