rbplusplus 0.8 → 0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. data/Rakefile +13 -13
  2. data/TODO +9 -41
  3. data/lib/rbplusplus/builders/allocation_strategy.rb +57 -0
  4. data/lib/rbplusplus/builders/base.rb +115 -212
  5. data/lib/rbplusplus/builders/class.rb +129 -115
  6. data/lib/rbplusplus/builders/const.rb +30 -0
  7. data/lib/rbplusplus/builders/const_converter.rb +52 -0
  8. data/lib/rbplusplus/builders/constructor.rb +19 -0
  9. data/lib/rbplusplus/builders/director.rb +149 -0
  10. data/lib/rbplusplus/builders/director_method.rb +20 -0
  11. data/lib/rbplusplus/builders/enumeration.rb +19 -26
  12. data/lib/rbplusplus/builders/extension.rb +42 -54
  13. data/lib/rbplusplus/builders/global_function.rb +18 -0
  14. data/lib/rbplusplus/builders/helpers/class.rb +74 -0
  15. data/lib/rbplusplus/builders/helpers/enumeration.rb +28 -0
  16. data/lib/rbplusplus/builders/helpers/module.rb +22 -0
  17. data/lib/rbplusplus/builders/include.rb +32 -0
  18. data/lib/rbplusplus/builders/instance_variable.rb +36 -0
  19. data/lib/rbplusplus/builders/method.rb +14 -0
  20. data/lib/rbplusplus/builders/method_base.rb +136 -0
  21. data/lib/rbplusplus/builders/module.rb +37 -51
  22. data/lib/rbplusplus/builders/module_function.rb +16 -0
  23. data/lib/rbplusplus/builders/static_method.rb +14 -0
  24. data/lib/rbplusplus/extension.rb +140 -28
  25. data/lib/rbplusplus/logger.rb +45 -0
  26. data/lib/rbplusplus/module.rb +55 -1
  27. data/lib/rbplusplus/transformers/class.rb +116 -35
  28. data/lib/rbplusplus/transformers/function.rb +14 -16
  29. data/lib/rbplusplus/transformers/method.rb +26 -1
  30. data/lib/rbplusplus/transformers/namespace.rb +12 -0
  31. data/lib/rbplusplus/transformers/node.rb +47 -54
  32. data/lib/rbplusplus/transformers/node_cache.rb +5 -9
  33. data/lib/rbplusplus/writers/multiple_files_writer.rb +290 -88
  34. data/lib/rbplusplus/writers/single_file_writer.rb +36 -14
  35. data/lib/rbplusplus.rb +44 -18
  36. data/test/allocation_strategies_test.rb +33 -0
  37. data/test/class_methods_encapsulate_test.rb +59 -0
  38. data/test/class_methods_test.rb +2 -35
  39. data/test/classes_test.rb +72 -2
  40. data/test/compiling_test.rb +13 -0
  41. data/test/constructors_test.rb +9 -18
  42. data/test/custom_code_test.rb +53 -0
  43. data/test/default_arguments_test.rb +69 -0
  44. data/test/director_test.rb +173 -0
  45. data/test/enumerations_test.rb +29 -0
  46. data/test/extension_test.rb +7 -2
  47. data/test/file_writers_test.rb +11 -4
  48. data/test/function_pointer_test.rb +56 -0
  49. data/test/function_pointers_classes_test.rb +27 -0
  50. data/test/generated/extconf.rb +2 -2
  51. data/test/headers/Adder.cpp +8 -0
  52. data/test/headers/Adder.h +31 -1
  53. data/test/headers/alloc_strats.h +26 -0
  54. data/test/headers/class_methods.h +30 -0
  55. data/test/headers/code/custom_to_from_ruby.cpp +11 -0
  56. data/test/headers/code/custom_to_from_ruby.hpp +13 -0
  57. data/test/headers/constructors.h +8 -20
  58. data/test/headers/default_arguments.h +49 -0
  59. data/test/headers/director.h +148 -0
  60. data/test/headers/enums.h +33 -0
  61. data/test/headers/function_pointers.h +32 -0
  62. data/test/headers/function_pointers_class.h +26 -0
  63. data/test/headers/needs_code.h +10 -0
  64. data/test/headers/overload.h +0 -3
  65. data/test/headers/subclass.h +10 -0
  66. data/test/headers/to_from_ruby.h +6 -4
  67. data/test/headers/ugly_interface.h +4 -7
  68. data/test/modules_test.rb +11 -6
  69. data/test/overloading_test.rb +6 -2
  70. data/test/subclass_test.rb +20 -10
  71. data/test/test_helper.rb +6 -1
  72. data/test/to_from_ruby_test.rb +0 -2
  73. data/test/wrap_as_test.rb +28 -37
  74. metadata +89 -57
  75. data/lib/rbplusplus/builders/types_manager.rb +0 -93
  76. data/lib/rbplusplus/transformers/constructor.rb +0 -4
  77. data/lib/rbplusplus/transformers/module.rb +0 -71
  78. data/lib/rbplusplus/transformers/node_reference.rb +0 -30
  79. data/test/headers/ugly_helper.h +0 -18
  80. data/test/object_persistence_test.rb +0 -44
data/Rakefile CHANGED
@@ -3,7 +3,7 @@ require 'rake/rdoctask'
3
3
  require 'rake/contrib/sshpublisher'
4
4
 
5
5
  PROJECT_NAME = "rb++"
6
- RBPLUSPLUS_VERSION = "0.8"
6
+ RBPLUSPLUS_VERSION = "0.9"
7
7
 
8
8
  task :default => :test
9
9
 
@@ -16,10 +16,13 @@ task :default => :test
16
16
  # tests individually
17
17
  desc "Run the tests"
18
18
  task :test do
19
- files = FileList["test/*_test.rb"]
20
- puts files.inspect
21
- files.each do |file|
22
- sh "ruby #{file}"
19
+ require 'rbconfig'
20
+ FileList["test/*_test.rb"].each do |file|
21
+ # To allow multiple ruby installs (like a multiruby test suite), I need to get
22
+ # the exact ruby binary that's linked to the ruby running the Rakefile. Just saying
23
+ # "ruby" will find the system's installed ruby and be worthless
24
+ ruby = File.join(Config::CONFIG["bindir"], Config::CONFIG["RUBY_INSTALL_NAME"])
25
+ sh "#{ruby} #{file}"
23
26
  end
24
27
  end
25
28
 
@@ -37,14 +40,11 @@ PROJECT_WEB_PATH = "/var/www/gforge-projects/rbplusplus"
37
40
  namespace :web do
38
41
  desc "Build website"
39
42
  task :build => :rdoc do |t|
40
- cd "website" do
41
- sh "webgen"
42
- end
43
-
44
43
  unless File.directory?("publish")
45
44
  mkdir "publish"
46
45
  end
47
- sh "cp -r website/output/* publish/"
46
+
47
+ sh "jekyll --pygment website publish/"
48
48
  sh "cp -r html/* publish/rbplusplus/"
49
49
  end
50
50
 
@@ -63,7 +63,7 @@ spec = Gem::Specification.new do |s|
63
63
  s.name = "rbplusplus"
64
64
  s.version = RBPLUSPLUS_VERSION
65
65
  s.summary = 'Ruby library to generate Rice wrapper code'
66
- s.homepage = 'http://rbplusplus.rubyforge.org/rbplusplus'
66
+ s.homepage = 'http://rbplusplus.rubyforge.org'
67
67
  s.rubyforge_project = "rbplusplus"
68
68
  s.author = 'Jason Roelofs'
69
69
  s.email = 'jameskilton@gmail.com'
@@ -73,8 +73,8 @@ Rb++ combines the powerful query interface of rbgccxml and the Rice library to
73
73
  make Ruby wrapping extensions easier to write than ever.
74
74
  END
75
75
 
76
- s.add_dependency "rbgccxml", "0.8"
77
- s.add_dependency "rice", ">= 1.0.2"
76
+ s.add_dependency "rbgccxml", "~> 0.9"
77
+ s.add_dependency "rice", "~> 1.2.0"
78
78
 
79
79
  patterns = [
80
80
  'TODO',
data/TODO CHANGED
@@ -1,45 +1,13 @@
1
- TODO
2
- * Figure out how to fix the error when running rake test
3
- * Allowing use of "have_library"
4
- * Other mkmf functions?
1
+ Parts to move into rbgccxml:
5
2
 
6
- * rbgccxml: Continue working with QueryResult#find, making it as powerful
7
- and robust as possible.
3
+ - Class purely_virtual? (check the abstract attribute)
4
+ - Namespace#methods ?
8
5
 
9
- * Rest of the GCCXML types (as needed):
10
- - ArrayType
11
- - Converter
12
- - Destructor
13
- - Field
14
- - FunctionType
15
- - OperatorFunction
16
- - OperatorMethod
17
- - OffsetType
18
- - Union
19
- - Variable
6
+ Move documentation to YARD?
20
7
 
21
- STARTED
22
- * Adding constructor
23
- - Test constructor arguments
24
-
25
- DONE
26
- * Adding -I entries
27
- * Adding -L and -l entries
28
- * Adding classes to global (define_class)
29
- * Adding modules
30
- * Adding methods to modules (define_module_function)
31
- * Adding classes to modules / other classes (define_class_under)
32
- * Adding methods to classes (.define_method)
33
- * Adding classes to classes (ad infinitum)
34
- * Look into file naming when dealing with Modules
35
- * Nested Modules
36
- * Recognizing and building to_ruby / from_ruby methods for const type references
37
- * Moving the to_ruby / from_ruby definitions to a place that all will know about them
38
- * Enumerations
39
- * Proper TypesManager dealings with single_file writer mode
40
- * Working with the defined C++ heirarchy chain
41
-
42
- No longer waiting on rice - just building a wrapper myself now.
43
- * Adding global methods (Kernel-level methods)
44
- * Adding class-level methods (define_singleton_method)
8
+ Use sdoc?
45
9
 
10
+ Logging:
11
+ - Method overloads, wrap what a method is getting named as
12
+ - More detail into the writing, multiple and single file writers
13
+ - More debug messages in the building process
@@ -0,0 +1,57 @@
1
+ module RbPlusPlus
2
+ module Builders
3
+
4
+ # Handles code generation for telling Rice how to allocate / deallocate
5
+ # classes. See ClassNode#check_allocation_strategies.
6
+ class AllocationStrategyNode < Base
7
+
8
+ def initialize(parent, code, has_public_constructor, has_public_destructor)
9
+ super(code, parent)
10
+ @public_constructor = has_public_constructor
11
+ @public_destructor = has_public_destructor
12
+ end
13
+
14
+ # Used by MultipleFileWriter to only wrap a given type once.
15
+ def qualified_name
16
+ "#{self.code.qualified_name}_AllocStrat"
17
+ end
18
+
19
+ def build
20
+ end
21
+
22
+ def write
23
+ includes << "#include <rice/Allocation_Strategies.hpp>"
24
+
25
+ node_name = self.code.qualified_name
26
+ code = <<-END
27
+ namespace Rice {
28
+ template<>
29
+ struct Default_Allocation_Strategy< #{node_name} > {
30
+ static #{node_name} * allocate();
31
+ static void free(#{node_name} * obj);
32
+ };
33
+ }
34
+ END
35
+
36
+ declarations << code
37
+
38
+ pre = "Rice::Default_Allocation_Strategy< #{node_name} >::"
39
+
40
+ tmp = "#{node_name} * #{pre}allocate() { return "
41
+ tmp += @public_constructor ? "new #{node_name};" : "NULL;"
42
+ tmp += " }"
43
+
44
+ registrations << tmp
45
+
46
+ tmp = "void #{pre}free(#{node_name} * obj) { "
47
+ tmp += @public_destructor ? "delete obj;" : ""
48
+ tmp += " }"
49
+
50
+ registrations << tmp
51
+ end
52
+
53
+ end
54
+
55
+ end
56
+ end
57
+
@@ -1,256 +1,159 @@
1
1
  module RbPlusPlus
2
2
  module Builders
3
3
 
4
- # Top class for all source generation classes. A builder has three seperate
5
- # code "parts" to fill up for the source writer:
4
+ # Base class for all code generation nodes
6
5
  #
7
- # includes
8
- # declarations
9
- # body
6
+ # A Node is simply a handler for one complete statement or block of C++ code.
10
7
  #
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
8
+ # The code generation system for Rb++ is a two step process.
9
+ # We first, starting with an ExtensionNode, build up an internal representation
10
+ # of the resulting code, setting up all the code nodes required for proper
11
+ # wrapping of the library.
25
12
  #
13
+ # Once that's in place, then we run through the tree, actually generating
14
+ # the C++ wrapper code.
26
15
  class Base
27
16
 
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
17
+ # List of includes for this node
36
18
  attr_accessor :includes
37
- # The list of declarations to add
19
+
20
+ # List of declaration nodes for this node
38
21
  attr_accessor :declarations
39
- # The body code
40
- attr_accessor :body
41
22
 
42
- # Link to the parent builder who created said builder
23
+ # List of registeration nodes for this node
24
+ attr_accessor :registrations
25
+
26
+ # Link to the parent node of this node
43
27
  attr_accessor :parent
44
28
 
45
- # The name of the C++ variable related to this builder.
29
+ # Link to the underlying rbgccxml node this node is writing code for
30
+ attr_accessor :code
31
+
32
+ # List of children nodes
33
+ attr_accessor :nodes
34
+
35
+ # List of children nodes that generate code that the entire extension
36
+ # needs to be able to read. Code that fits here includes any auto-generated
37
+ # to_/from_ruby and any Allocation Strategies
38
+ attr_accessor :global_nodes
39
+
40
+ # The Rice variable name for this node
46
41
  attr_accessor :rice_variable
42
+
43
+ # The type of the rice_variable
47
44
  attr_accessor :rice_variable_type
48
45
 
49
- # Create a new builder.
50
- def initialize(name, parser)
51
- @name = name
52
- @node = parser
53
- @builders = []
46
+ def initialize(code, parent = nil)
47
+ @code = code
48
+ @parent = parent
54
49
  @includes = []
55
50
  @declarations = []
56
- @body = []
57
- @registered_nodes = []
51
+ @registrations = []
52
+ @nodes = []
53
+ @global_nodes = []
58
54
  end
59
-
60
- # adds a register function to the Init or register of this node
61
- def register_node(node, register_func)
62
- @registered_nodes << [node, register_func]
63
- end
64
-
65
- private
66
-
67
- def nested_level(node, level=0)
68
- return level if node.is_a?(RbGCCXML::Namespace) || node.is_a?(RbGCCXML::Enumeration)
69
- return level if node.super_classes.length == 0
70
- node.super_classes.each do |sup|
71
- level = nested_level(sup, level+1)
72
- end
73
- return level
55
+
56
+ # Does this builder node have child nodes?
57
+ def has_children?
58
+ @nodes && !@nodes.empty?
74
59
  end
75
-
76
- public
77
-
78
- # Sorts the registered nodes by hierachy, registering the base classes
79
- # first.
80
- #
81
- # this is necessary for Rice to know about inheritance
82
- def registered_nodes
83
- #sort by hierachy
84
- nodes = @registered_nodes.sort_by do |build, func|
85
- if build.node.nil?
86
- 0
87
- else
88
- nested_level(build.node)
60
+
61
+ # Trigger the construction of the internal representation of a given node.
62
+ # All nodes must implement this.
63
+ def build
64
+ raise "Nodes must implement #build"
65
+ end
66
+
67
+ # After #build has run, this then triggers the actual generation of the C++
68
+ # code and returns the final string.
69
+ # All nodes must implement this.
70
+ def write
71
+ raise "Nodes must implement #write"
72
+ end
73
+
74
+ # Once building is done, the resulting node tree needs to be sorted according
75
+ # to subclass / superclass definitions. Like anything with C++, Rice needs to
76
+ # know about base classes before it can build sub classes. We go through
77
+ # each node's children, sorting them according to this.
78
+ def sort
79
+ @nodes.each { |n| n.sort }
80
+
81
+ # sort_by lets us build an array of numbers that Ruby then uses
82
+ # to sort the list. Our method here is to simply specify the
83
+ # depth a given class is in a heirarchy, as bigger numbers end
84
+ # up sorted farther down the list
85
+ @nodes =
86
+ @nodes.sort_by do |a|
87
+ a.is_a?(ClassNode) ? superclass_count(a.code) : 0
89
88
  end
90
- end
91
-
92
- #collect the sorted members
93
- nodes.collect do |node, func|
94
- func
95
- end
96
- end
97
-
98
- # The name of the header file to include
99
- # This is the file default, so long as it matches one of the export files
100
- # If not this returns all exported files.
101
- #
102
- # This was added to workaround badly declared namespaces
103
- def header_files(node)
104
- file = node.file_name(false)
105
- return [file] if self.class.sources.include?(file)
106
- self.class.sources
107
- end
108
-
109
- # Adds the necessary includes in order to compile the specified node
110
- def add_includes_for(node)
111
- header_files(node).each do |header|
112
- includes << "#include \"#{header}\""
113
- end
114
- end
115
-
116
- # Include any user specified include files
117
- def add_additional_includes
118
- self.class.additional_includes.each do |inc|
119
- includes << "#include \"#{inc}\""
120
- end
121
- end
122
-
123
- # Set a list of user specified include files
124
- def self.additional_includes=(addl)
125
- @@additional_includes = addl
126
- end
127
-
128
- # Get an array of user specified include files
129
- def self.additional_includes
130
- @@additional_includes || []
131
- end
132
-
133
- # A list of all the source files. This is used in order to prevent files
134
- # that are not in the list from being included and mucking things up
135
- def self.sources=(sources)
136
- @@sources = sources
137
- end
138
-
139
- # Retrieves a list of user specified source files
140
- def self.sources
141
- @@sources || []
142
89
  end
143
90
 
144
- # All builders must implement this method
145
- def build
146
- raise "Builder needs to implement #build"
91
+ # Proxy method for writers
92
+ def qualified_name
93
+ self.code.qualified_name
147
94
  end
148
95
 
149
- # Builders should use to_s to make finishing touches on the generated
150
- # code before it gets written out to a file.
151
- def to_s
152
- extras = []
153
- #Weird trailing } needs to be destroyed!!!
154
- if self.body.flatten[-1].strip == "}"
155
- extras << self.body.delete_at(-1)
96
+ protected
97
+
98
+ # Count the heirarchy depth of a given class node
99
+ def superclass_count(node)
100
+ count = 0
101
+ n = node
102
+ while n = n.superclass
103
+ count += 1
156
104
  end
157
-
158
- return [
159
- self.includes.flatten.uniq,
160
- "",
161
- self.declarations,
162
- "",
163
- self.body,
164
- "",
165
- self.registered_nodes,
166
- extras
167
- ].flatten.join("\n")
105
+ count
168
106
  end
169
107
 
170
- # Get the full qualified name of the related gccxml node
171
- def qualified_name
172
- @node.qualified_name
108
+ # Should this node be wrapped as it is or has the user
109
+ # specified something else for this node?
110
+ def do_not_wrap?(node)
111
+ node.ignored? || (node.moved_to && node.moved_to != self.code) || !node.public?
173
112
  end
174
113
 
175
- # Register all classes
176
- def build_classes(classes = nil)
177
- classes ||= [@node.classes, @node.structs].flatten
178
- classes.each do |klass|
179
- next if klass.ignored? || klass.moved?
180
- next unless klass.public?
181
- builder = ClassBuilder.new(self, klass)
182
- builder.build
183
- builders << builder
184
- end
114
+ # Given a new node, build it and add it to our nodes list
115
+ def add_child(node)
116
+ node.build
117
+ nodes << node
185
118
  end
186
119
 
187
- # Find and wrap up all enumerations
188
- def build_enumerations
189
- @node.enumerations.each do |enum|
190
- builder = EnumerationBuilder.new(self, enum)
191
- builder.build
192
- builders << builder
193
- end
120
+ # Add a node to the "globals" list. See the declaration of global_nodes
121
+ def add_global_child(node)
122
+ node.build
123
+ global_nodes << node
194
124
  end
195
125
 
196
- # Compatibility with Rice 1.0.1's explicit self requirement, build a quick
197
- # wrapper that includes a self and discards it, forwarding the call as needed.
126
+ # Any node can also have a typedef. There are cases where it's
127
+ # much better to use a typedef instead of the original fully qualified
128
+ # name, for example deep template definitions (say, any STL structures).
129
+ # This method will look for the best Typedef to use for this node.
130
+ # This lookup can be disabled on a per-node basis by calling node.disable_typedef_lookup,
131
+ # as there are cases where the typedef search works against the parsed code.
198
132
  #
199
- # Returns: the name of the wrapper function
200
- def build_function_wrapper(function, append="")
201
- return if function.ignored? || function.moved?
202
- wrapper_func = "wrap_#{function.qualified_name.gsub(/::/, "_")}#{append}"
133
+ # @returns the typedef node found or the node passed in (self.code by default)
134
+ def find_typedef(node = self.code)
135
+ return node if node.is_a?(RbGCCXML::FundamentalType)
203
136
 
204
- proto_string = ["Rice::Object self"]
205
- call_string = []
206
- function.arguments.map{|arg| [arg.cpp_type.to_s(true), arg.name]}.each do |parts|
207
- type = parts[0]
208
- name = parts[1]
209
- proto_string << "#{type} #{name}"
210
- call_string << "#{name}"
211
- end
137
+ found = last_found = node
212
138
 
213
- proto_string = proto_string.join(",")
214
- call_string = call_string.join(",")
215
- return_type = function.return_type.to_s(true)
216
- returns = "" if return_type == "void"
217
- returns ||= "return"
139
+ if !node._disable_typedef_lookup?
140
+ while found
141
+ last_found = found
142
+ typedef = RbGCCXML::XMLParsing.find(:node_type => "Typedef", :type => found.attributes["id"])
218
143
 
219
- declarations << "#{return_type} #{wrapper_func}(#{proto_string}) {"
220
- declarations << "\t#{returns} #{function.qualified_name}(#{call_string});"
221
- declarations << "}"
144
+ # Some typedefs have the access attribute, some don't. We want those without the attribute
145
+ # and those with the access="public". For this reason, we can't put :access => "public" in the
146
+ # query above.
147
+ found = (typedef && typedef.public?) ? typedef : nil
148
+ end
149
+ end
222
150
 
223
- wrapper_func
151
+ last_found
224
152
  end
225
153
 
226
- # Compatibility with Rice 1.0.1's method overloading issues. Build a quick
227
- # wrapper that includes a self, forwarding the call as needed.
228
- #
229
- # Returns: the name of the wrapper function
230
- def build_method_wrapper(cls, method, i)
231
- return if method.ignored? || method.moved?
232
- wrapper_func = "wrap_#{method.qualified_name.functionize}_#{i}"
233
- proto_string = ["#{cls.qualified_name} *self"]
234
- call_string = []
235
- method.arguments.map{|arg| [arg.cpp_type.to_s(true), arg.name]}.each do |parts|
236
- type = parts[0]
237
- name = parts[1]
238
- proto_string << "#{type} #{name}"
239
- call_string << "#{name}"
240
- end
241
-
242
- proto_string = proto_string.join(",")
243
- call_string = call_string.join(",")
244
- return_type = method.return_type.to_s(true)
245
- returns = "" if return_type == "void"
246
- returns ||= "return"
247
-
248
- declarations << "#{return_type} #{wrapper_func}(#{proto_string}) {"
249
- declarations << "\t#{returns} self->#{method.qualified_name}(#{call_string});"
250
- declarations << "}"
251
-
252
- wrapper_func
253
- end
154
+ alias find_typedef_for find_typedef
155
+
254
156
  end
157
+
255
158
  end
256
159
  end