rbplusplus 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/rbplusplus.rb ADDED
@@ -0,0 +1,31 @@
1
+ $:.unshift File.expand_path(File.dirname(__FILE__))
2
+ $:.unshift File.expand_path(File.dirname(__FILE__) + "/rbplusplus")
3
+
4
+ gem 'rbgccxml'
5
+ require 'rbgccxml'
6
+
7
+ require 'inflector'
8
+ require 'rbplusplus/rbplusplus'
9
+
10
+ module RbPlusPlus
11
+
12
+ RBPP_COMMENT = "// This file generated by rb++"
13
+
14
+ autoload :Extension, "rbplusplus/extension"
15
+ autoload :RbModule, "rbplusplus/module"
16
+
17
+ module Builders
18
+ autoload :Base, "rbplusplus/builders/base"
19
+ autoload :ClassBuilder, "rbplusplus/builders/class"
20
+ autoload :ExtensionBuilder, "rbplusplus/builders/extension"
21
+ autoload :ModuleBuilder, "rbplusplus/builders/module"
22
+ end
23
+
24
+ module Writers
25
+ autoload :Base, "rbplusplus/writers/base"
26
+ autoload :ExtensionWriter, "rbplusplus/writers/extension"
27
+ autoload :MultipleFilesWriter, "rbplusplus/writers/multiple_files_writer"
28
+ autoload :SingleFileWriter, "rbplusplus/writers/single_file_writer"
29
+ end
30
+ end
31
+
@@ -0,0 +1,116 @@
1
+ module RbPlusPlus
2
+ module Builders
3
+
4
+ # Top class for all source generation classes. A builder has three seperate
5
+ # code "parts" to fill up for the source writer:
6
+ #
7
+ # includes
8
+ # declarations
9
+ # body
10
+ #
11
+ # includes:
12
+ # The list of #include's needed for this builder's code to compile
13
+ #
14
+ # declarations:
15
+ # Any extra required functions or class declarations that will be defined
16
+ # outside of the main body of the code
17
+ #
18
+ # body:
19
+ # The body is the code that will go in the main control function
20
+ # of the file. For extensions, it's Init_extension_name() { [body] }.
21
+ # For classes it's usually a register_ClassName() { [body] }, and so on.
22
+ #
23
+ # All builders can access their parent and add pieces of code to any of these
24
+ # three parts
25
+ #
26
+ class Base
27
+
28
+ attr_reader :name, :node
29
+
30
+ # Any given builder has a list of sub-builders of any type
31
+ attr_accessor :builders
32
+
33
+ # Builders need to constcuct the following code parts
34
+ #
35
+ # The list of includes this builder needs
36
+ attr_accessor :includes
37
+ # The list of declarations to add
38
+ attr_accessor :declarations
39
+ # The body code
40
+ attr_accessor :body
41
+
42
+ # Link to the parent builder who created said builder
43
+ attr_accessor :parent
44
+
45
+ # The name of the C++ variable related to this builder.
46
+ attr_accessor :rice_variable
47
+ attr_accessor :rice_variable_type
48
+
49
+ # Create a new builder.
50
+ def initialize(name, parser)
51
+ @name = name
52
+ @node = parser
53
+ @builders = []
54
+ @includes = []
55
+ @declarations = []
56
+ @body = []
57
+ end
58
+
59
+ # All builders must implement this method
60
+ def build
61
+ raise "Builder needs to implement #build"
62
+ end
63
+
64
+ # Builders should use to_s to make finishing touches on the generated
65
+ # code before it gets written out to a file.
66
+ def to_s
67
+ [self.includes.flatten.uniq, "", self.declarations, "", self.body].flatten.join("\n")
68
+ end
69
+
70
+ # Get the full qualified name of the related gccxml node
71
+ def qualified_name
72
+ @node.qualified_name
73
+ end
74
+
75
+ # Register all classes
76
+ def build_classes
77
+ @node.classes.each do |klass|
78
+ builder = ClassBuilder.new(self, klass)
79
+ builder.build
80
+ builders << builder
81
+ end
82
+ end
83
+
84
+ # Compatibility with Rice 1.0.1's explicit self requirement, build a quick
85
+ # wrapper that includes a self and discards it, forwarding the call as needed.
86
+ #
87
+ # Returns: the name of the wrapper function
88
+ def build_function_wrapper(function)
89
+ wrapper_func = "wrap_#{function.qualified_name.gsub(/::/, "_")}"
90
+
91
+ proto_string = ["Rice::Object self"]
92
+ call_string = []
93
+
94
+ function.arguments.map{|arg| [arg.cpp_type.name, arg.name]}.each do |parts|
95
+ type = parts[0]
96
+ name = parts[1]
97
+ proto_string << "#{type} #{name}"
98
+ call_string << "#{name}"
99
+ end
100
+
101
+ proto_string = proto_string.join(",")
102
+ call_string = call_string.join(",")
103
+ return_type = function.return_type.name
104
+ returns = "" if return_type == "void"
105
+ returns ||= "return"
106
+
107
+ declarations << "#{return_type} #{wrapper_func}(#{proto_string}) {"
108
+ declarations << "\t#{returns} #{function.qualified_name}(#{call_string});"
109
+ declarations << "}"
110
+
111
+ wrapper_func
112
+ end
113
+
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,62 @@
1
+ module RbPlusPlus
2
+ module Builders
3
+
4
+ # This class handles generating source for Class nodes
5
+ class ClassBuilder < Base
6
+
7
+ # Different initializer to keep things clean
8
+ def initialize(parent, node)
9
+ super(node.name, node)
10
+ self.parent = parent
11
+ end
12
+
13
+ def build
14
+ class_name = node.name
15
+ full_name = node.qualified_name
16
+ self.rice_variable = "rb_c#{class_name}"
17
+ self.rice_variable_type = "Rice::Data_Type<#{full_name}>"
18
+
19
+ includes << "#include <rice/Class.hpp>"
20
+ includes << "#include <rice/Data_Type.hpp>"
21
+ includes << "#include <rice/Constructor.hpp>"
22
+ includes << "#include \"#{node.file_name(false)}\""
23
+
24
+ class_defn = "\t#{rice_variable_type} #{rice_variable} = "
25
+ if !parent.is_a?(ExtensionBuilder)
26
+ class_defn += "Rice::define_class_under<#{full_name}>(#{parent.rice_variable}, \"#{class_name}\");"
27
+ else
28
+ class_defn += "Rice::define_class<#{full_name}>(\"#{class_name}\");"
29
+ end
30
+
31
+ body << class_defn
32
+
33
+ # Constructors
34
+ node.constructors.each do |init|
35
+ args = [full_name, init.arguments.map {|a| a.cpp_type }].flatten
36
+ body << "\t#{rice_variable}.define_constructor(Rice::Constructor<#{args.join(",")}>());"
37
+ end
38
+
39
+ # Methods
40
+ node.methods.each do |method|
41
+ m = "define_method"
42
+ name = method.qualified_name
43
+
44
+ if method.static?
45
+ m = "define_singleton_method"
46
+ name = build_function_wrapper(method)
47
+ end
48
+
49
+ body << "\t#{rice_variable}.#{m}(\"#{Inflector.underscore(method.name)}\", &#{name});"
50
+ end
51
+
52
+ # Nested Classes
53
+ node.classes.each do |klass|
54
+ b = ClassBuilder.new(self, klass)
55
+ b.build
56
+ builders << b
57
+ end
58
+ end
59
+
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,48 @@
1
+ module RbPlusPlus
2
+ module Builders
3
+
4
+ # This class takes in all classes to be wrapped and builds
5
+ # the top-level extension Init code
6
+ class ExtensionBuilder < Base
7
+
8
+ # Need to be given the list of modules as they are a special case
9
+ attr_accessor :modules
10
+
11
+ def build
12
+ includes << "#include <rice/global_function.hpp>"
13
+
14
+ body << "extern \"C\""
15
+ body << "void Init_#{@name}() {"
16
+
17
+ # Explicitly ignore anything from the :: namespace
18
+ if @node.name != "::"
19
+ @node.functions.each do |func|
20
+ includes << "#include \"#{func.file_name(false)}\""
21
+ wrapper_name = build_function_wrapper(func)
22
+ body << "\tdefine_global_function(\"#{Inflector.underscore(func.name)}\", &#{wrapper_name});"
23
+ end
24
+
25
+ build_classes
26
+ end
27
+
28
+ build_modules
29
+ end
30
+
31
+ def build_modules
32
+ @modules.each do |mod|
33
+ builder = ModuleBuilder.new(self, mod)
34
+ builder.build
35
+ builders << builder
36
+ end
37
+ end
38
+
39
+ # Finish up the required code before doing final output
40
+ def to_s
41
+ includes << "using namespace Rice;"
42
+ body << "}"
43
+
44
+ super
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,67 @@
1
+ module RbPlusPlus
2
+ module Builders
3
+
4
+ # This class handles generating source for a requested Module
5
+ class ModuleBuilder < Base
6
+
7
+ # Initializer takes the parent object and the RbModule construction
8
+ def initialize(parent, mod)
9
+ super(mod.name, mod.node)
10
+ @module = mod
11
+ self.parent = parent
12
+ end
13
+
14
+ def build
15
+ # Using qualified name with underscores here to allow for nested modules
16
+ # of the same name
17
+ self.rice_variable = "rb_m#{self.qualified_name.gsub(/::/, "_")}"
18
+ self.rice_variable_type = "Rice::Module"
19
+
20
+ includes << "#include <rice/Module.hpp>"
21
+
22
+ mod_defn = "\t#{rice_variable_type} #{rice_variable} = "
23
+ if !parent.is_a?(ExtensionBuilder)
24
+ mod_defn += "Rice::define_module_under(#{parent.rice_variable}, \"#{name}\");"
25
+ else
26
+ mod_defn += "Rice::define_module(\"#{name}\");"
27
+ end
28
+
29
+ body << mod_defn
30
+
31
+ # If a namespace has been given to this module, find and wrap the appropriate code
32
+ if @node
33
+ build_functions
34
+ build_classes
35
+ end
36
+
37
+ # Build each inner module
38
+ @module.modules.each do |mod|
39
+ builder = ModuleBuilder.new(self, mod)
40
+ builder.build
41
+ builders << builder
42
+ end
43
+ end
44
+
45
+ # Process functions to be added to this module
46
+ def build_functions
47
+ @node.functions.each do |func|
48
+ includes << "#include \"#{func.file_name(false)}\""
49
+
50
+ func_name = Inflector.underscore(func.name)
51
+ wrapped_name = build_function_wrapper(func)
52
+ body << "\t#{self.rice_variable}.define_module_function(\"#{func_name}\", &#{wrapped_name});"
53
+ end
54
+ end
55
+
56
+ # Special name handling. Qualified name is simply the name of this module
57
+ def qualified_name
58
+ if parent.is_a?(ModuleBuilder)
59
+ "#{parent.qualified_name}::#{self.name}"
60
+ else
61
+ self.name
62
+ end
63
+ end
64
+
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,183 @@
1
+ module RbPlusPlus
2
+
3
+ # This is the starting class for Rb++ wrapping. All Rb++ projects start with this
4
+ # class:
5
+ #
6
+ # Extension.new "extension_name" do |e|
7
+ # ...
8
+ # end
9
+ #
10
+ # "extension_name" is what the resulting Ruby library will be named, aka in your code
11
+ # you will have
12
+ #
13
+ # require "extension_name"
14
+ #
15
+ # It is recommended that you use the block format of this class's initializer.
16
+ # If you want more fine-grained control of the whole process, don't use
17
+ # the block format. Instead you should do the following:
18
+ #
19
+ # e = Extension.new "extension_name"
20
+ # ...
21
+ #
22
+ # The following calls are required in both formats:
23
+ #
24
+ # #sources - The directory / array / name of C++ header files to parse.
25
+ #
26
+ # In the non-block format, the following calls are required:
27
+ #
28
+ # #working_dir - Specify the directory where the code will be generated. This needs
29
+ # to be a full path.
30
+ #
31
+ # In the non-block format, you need to manually fire the different steps of the
32
+ # code generation process, and in this order:
33
+ #
34
+ # #build - Fires the code generation process
35
+ #
36
+ # #write - Writes out the generated code into files
37
+ #
38
+ # #compile - Compiles the generated code into a Ruby extension.
39
+ #
40
+ class Extension
41
+
42
+ # Where will the generated code be put
43
+ attr_accessor :working_dir
44
+
45
+ # The list of modules to create
46
+ attr_accessor :modules
47
+
48
+ # List of extra include directives
49
+ attr_accessor :includes
50
+
51
+ # List of directories for library searching
52
+ attr_accessor :lib_paths
53
+
54
+ # List of libraries to link
55
+ attr_accessor :libraries
56
+
57
+ # Create a new Ruby extension with a given name. This name will be
58
+ # the module built into the extension.
59
+ # This constructor can be standalone or take a block.
60
+ def initialize(name, &block)
61
+ @name = name
62
+ @modules = []
63
+ @writer_mode = :multiple
64
+ @includes = []
65
+ @lib_paths = []
66
+ @libraries = []
67
+
68
+ if block
69
+ build_working_dir(&block)
70
+ block.call(self)
71
+ build
72
+ write
73
+ compile
74
+ end
75
+ end
76
+
77
+ # Define where we can find the header files to parse
78
+ # Can give an array of directories, a glob, or just a string.
79
+ # All file names should be full paths, not partial.
80
+ #
81
+ # Options can be the following:
82
+ #
83
+ # * <tt>:include_paths</tt> - An array or string of full paths to be added as -I flags
84
+ # * <tt>:library_paths</tt> - An array or string of full paths to be added as -L flags
85
+ # * <tt>:libraries</tt> - An array or string of full paths to be added as -l flags
86
+ def sources(dirs, options = {})
87
+ parser_options = {}
88
+
89
+ if (paths = options.delete(:include_paths))
90
+ @includes << paths
91
+ parser_options[:includes] = @includes
92
+ end
93
+
94
+ if (lib_paths = options.delete(:library_paths))
95
+ @lib_paths << lib_paths
96
+ end
97
+
98
+ if (libs = options.delete(:libraries))
99
+ @libraries << libs
100
+ end
101
+
102
+ @parser = RbGCCXML.parse(dirs, parser_options)
103
+ end
104
+
105
+ # Set a namespace to be the main namespace used for this extension.
106
+ # Specifing a namespace on the Extension itself will mark functions,
107
+ # class, enums, etc to be globally available to Ruby (aka not in it's own
108
+ # module)
109
+ def namespace(name)
110
+ @node = @parser.namespaces(name)
111
+ end
112
+
113
+ # Mark that this extension needs to create a Ruby module of
114
+ # a give name. Like Extension, this can be used with or without
115
+ # a block.
116
+ def module(name, &block)
117
+ m = RbModule.new(name, @parser, &block)
118
+ @modules << m
119
+ m
120
+ end
121
+
122
+ # How should we write out the source code? This can be one of two modes:
123
+ # * <tt>:multiple</tt> (default) - Each class and module gets it's own set of hpp/cpp files
124
+ # * <tt>:single</tt> - Everything gets written to a single file
125
+ def writer_mode(mode)
126
+ raise "Unknown writer mode #{mode}" unless [:multiple, :single].include?(mode)
127
+ @writer_mode = mode
128
+ end
129
+
130
+ # Start the code generation process.
131
+ def build
132
+ raise ConfigurationError.new("Must specify working directory") unless @working_dir
133
+ raise ConfigurationError.new("Must specify which sources to wrap") unless @parser
134
+
135
+ @builder = Builders::ExtensionBuilder.new(@name, @node || @parser)
136
+ @builder.modules = @modules
137
+ @builder.build
138
+ end
139
+
140
+ # Write out the generated code into files.
141
+ # #build must be called before this step or nothing will be written out
142
+ def write
143
+ prepare_working_dir
144
+
145
+ # Create the code
146
+ writer_class = @writer_mode == :multiple ? Writers::MultipleFilesWriter : Writers::SingleFileWriter
147
+ writer_class.new(@builder, @working_dir).write
148
+
149
+ # Create the extconf.rb
150
+ extconf = Writers::ExtensionWriter.new(@builder, @working_dir)
151
+ extconf.includes = @includes
152
+ extconf.library_paths = @lib_paths
153
+ extconf.libraries = @libraries
154
+ extconf.write
155
+ end
156
+
157
+ # Compile the extension.
158
+ # This will create an rbpp_compile.log file in @working_dir. View this
159
+ # file to see the full compilation process including any compiler
160
+ # errors / warnings.
161
+ def compile
162
+ FileUtils.cd @working_dir do
163
+ system("ruby extconf.rb > rbpp_compile.log 2>&1")
164
+ system("make >> rbpp_compile.log 2>&1")
165
+ end
166
+ end
167
+
168
+ protected
169
+
170
+ # If the working dir doesn't exist, make it
171
+ # and if it does exist, clean it out
172
+ def prepare_working_dir
173
+ FileUtils.mkdir_p @working_dir unless File.directory?(@working_dir)
174
+ FileUtils.rm_rf "#{@working_dir}/*"
175
+ end
176
+
177
+ # Cool little eval / binding hack, from need.rb
178
+ def build_working_dir(&block)
179
+ @working_dir = File.expand_path(
180
+ File.join(File.dirname(eval("__FILE__", block.binding)), "generated"))
181
+ end
182
+ end
183
+ end