rbplusplus 0.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/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