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.
@@ -0,0 +1,56 @@
1
+ module RbPlusPlus
2
+ # Class representation of a ruby Module to be exposed in the extension.
3
+ # A Module acts much in the same way as Extension in that it can contain
4
+ # classes, functions, enumerations, etc.
5
+ class RbModule
6
+
7
+ # Modules can be nested
8
+ attr_accessor :modules
9
+
10
+ # Modules have a name
11
+ attr_accessor :name
12
+
13
+ # Access to the underlying RbGCCXML parser
14
+ attr_reader :node
15
+
16
+ # Registers a new module definition for this extension.
17
+ # Use Extension#module or RbModule#module instead
18
+ # of creating an instance of this class directly
19
+ #
20
+ # The block parameter is optional, you can also
21
+ # grab the reference of the Module and work with
22
+ # it as you want:
23
+ #
24
+ # module "Name" do |m|
25
+ # ...
26
+ # end
27
+ #
28
+ # or
29
+ #
30
+ # m = module "Name"
31
+ # ...
32
+ #
33
+ # Unlike Extension#new, no special processing is done
34
+ # in the block version, it's just there for convenience.
35
+ def initialize(name, parser, &block)
36
+ @name = name
37
+ @parser = parser
38
+ @modules = []
39
+
40
+ block.call(self) if block
41
+ end
42
+
43
+ # Specify a C++ namespace from which the contained code will
44
+ # be wrapped and exposed to Ruby under this Module.
45
+ def namespace(name)
46
+ @node = @parser.namespaces(name)
47
+ end
48
+
49
+ # Register another module to be defined inside of
50
+ # this module. Acts the same as Extension#module.
51
+ def module(name, &block)
52
+ @modules << RbModule.new(name, @parser, &block)
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,3 @@
1
+ module RbPlusPlus
2
+ class ConfigurationError < RuntimeError; end
3
+ end
@@ -0,0 +1,25 @@
1
+ module RbPlusPlus
2
+ module Writers
3
+
4
+ # Base class for all source code writers.
5
+ class Base
6
+
7
+ attr_reader :builder, :working_dir
8
+
9
+ # Writers all take a builder from which to write out
10
+ # the source code
11
+ def initialize(builder, working_dir)
12
+ @builder = builder
13
+ @working_dir = working_dir
14
+ end
15
+
16
+ # Write out the code. Must be implemented in a
17
+ # subclass
18
+ def write
19
+ raise "Writers must implement #write"
20
+ end
21
+
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,37 @@
1
+ module RbPlusPlus
2
+ module Writers
3
+
4
+ # Writes out the code for building the extension.
5
+ # This writer takes care of building the extconf.rb
6
+ # file with the appropriate options.
7
+ class ExtensionWriter < Base
8
+
9
+ # List of -I directives
10
+ attr_accessor :includes
11
+
12
+ # List of -L directives
13
+ attr_accessor :library_paths
14
+
15
+ # List of -l directives
16
+ attr_accessor :libraries
17
+
18
+ def write
19
+ extconf = File.join(working_dir, "extconf.rb")
20
+
21
+ @includes ||= []
22
+
23
+ inc_str = @includes.flatten.uniq.map {|i| "-I#{i}"}.join(" ")
24
+ lib_path_str = @library_paths.flatten.uniq.map {|i| "-L#{i}"}.join(" ")
25
+ lib_str = @libraries.flatten.uniq.map {|i| "-l#{i}"}.join(" ")
26
+
27
+ File.open(extconf, "w+") do |file|
28
+ file.puts "require \"rubygems\""
29
+ file.puts "require \"mkmf-rice\""
30
+ file.puts %Q($CPPFLAGS = $CPPFLAGS + " -I#{working_dir} #{inc_str} #{lib_path_str} #{lib_str}")
31
+ file.puts "create_makefile(\"#{builder.name}\")"
32
+ end
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,84 @@
1
+ module RbPlusPlus
2
+ module Writers
3
+ # Writer that takes a builder and writes out the code in
4
+ # multiple files.
5
+ class MultipleFilesWriter < Base
6
+
7
+ def write
8
+ _write_node(builder)
9
+ end
10
+
11
+ # How this works:
12
+ #
13
+ # We'll recurse through the builder heirarchy, starting at the bottom.
14
+ # This lets us to properly link up each file so that all classes / modules /
15
+ # functions get properly exposed.
16
+ def _write_node(node)
17
+ node.builders.each do |b|
18
+ _write_node(b)
19
+ end
20
+
21
+ filename = if node.parent
22
+ node.qualified_name.gsub(/::/, "_")
23
+ else
24
+ node.name
25
+ end
26
+
27
+ cpp_file = File.join(working_dir, "#{filename}.rb.cpp")
28
+
29
+ if node.parent
30
+ hpp_file = File.join(working_dir, "#{filename}.rb.hpp")
31
+ hpp_include = "#include \"#{hpp_file}\""
32
+ register_func = "register_#{filename}"
33
+
34
+ include_guard = "__RICE_GENERATED_#{filename}_HPP__"
35
+
36
+ register_func_arg = ""
37
+ register_func_prototype = ""
38
+
39
+ if !node.parent.is_a?(Builders::ExtensionBuilder)
40
+ register_func_arg = node.parent.rice_variable
41
+ register_func_prototype = "#{node.parent.rice_variable_type} #{register_func_arg}"
42
+ end
43
+
44
+ # Changes we need to make to the parent for everything to work across multiple
45
+ # files
46
+ #
47
+ # * Add an include to the hpp file
48
+ # * Add a call to the register method
49
+ node.parent.includes << hpp_include
50
+
51
+ node.parent.body << "#{register_func}(#{register_func_arg});"
52
+
53
+ # Modifications to this current node's code:
54
+ #
55
+ # * Add a register prototype to the header file
56
+ # * Set include in node to the header file
57
+ # * Wrap the body in a register method
58
+
59
+ File.open(hpp_file, "w+") do |hpp|
60
+ hpp.puts "#ifndef #{include_guard}"
61
+ hpp.puts "#define #{include_guard}"
62
+ hpp.puts ""
63
+ hpp.puts "void #{register_func}(#{register_func_prototype});"
64
+ hpp.puts "#endif"
65
+ end
66
+
67
+ node.includes << hpp_include
68
+
69
+ node.body = [
70
+ "void #{register_func}(#{register_func_prototype}) {",
71
+ node.body,
72
+ "}"
73
+ ]
74
+ end
75
+
76
+ File.open(cpp_file, "w+") do |cpp|
77
+ cpp.puts node.to_s
78
+ end
79
+ end
80
+
81
+
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,37 @@
1
+ module RbPlusPlus
2
+ module Writers
3
+ # Writer that takes a builder and writes out the code in
4
+ # one single file
5
+ class SingleFileWriter < Base
6
+
7
+ def write
8
+ process_code(builder)
9
+
10
+ filename = builder.name
11
+ cpp_file = File.join(working_dir, "#{filename}.rb.cpp")
12
+
13
+ File.open(cpp_file, "w+") do |cpp|
14
+ cpp.puts builder.to_s
15
+ end
16
+ end
17
+
18
+ protected
19
+
20
+ # What we do here is to go through the builder heirarchy
21
+ # and push all the code from children up to the parent,
22
+ # ending up with all the code in the top-level builder
23
+ def process_code(builder)
24
+ builder.builders.each do |b|
25
+ process_code(b)
26
+ end
27
+
28
+ return unless builder.parent
29
+
30
+ builder.parent.includes << builder.includes
31
+ builder.parent.body << builder.body
32
+ builder.parent.declarations << builder.declarations
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,67 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ context "Extension with wrapped classes" do
4
+
5
+ def setup
6
+ if !defined?(@@adder_built)
7
+ super
8
+ @@adder_built = true
9
+ Extension.new "adder" do |e|
10
+ e.sources full_dir("headers/Adder.h")
11
+ e.namespace "classes"
12
+ end
13
+
14
+ require 'adder'
15
+ end
16
+ end
17
+
18
+ specify "should make classes available as Ruby runtime constants" do
19
+ assert defined?(Adder), "Adder isn't defined"
20
+ end
21
+
22
+ specify "should make wrapped classes constructable" do
23
+ a = Adder.new
24
+ a.should.not.be.nil
25
+ end
26
+
27
+ specify "should make functions of the class available" do
28
+ # Wrapped method names default to underscore'd
29
+ adder = Adder.new
30
+ adder.add_integers(1,2).should == 3
31
+ adder.add_floats(1.0, 2.0).should.be.close(3.0, 0.001)
32
+ adder.add_strings("Hello", "World").should == "HelloWorld"
33
+ adder.get_class_name.should == "Adder"
34
+ end
35
+
36
+ # Explicit self
37
+ specify "should properly wrap static methods as class methods" do
38
+ Adder.do_adding(1, 2, 3, 4, 5).should == 15
39
+ end
40
+
41
+ end
42
+
43
+ context "Wrapping Classes within classes" do
44
+
45
+ def setup
46
+ if !defined?(@@nested_built)
47
+ super
48
+ @@nested_built = true
49
+ Extension.new "nested" do |e|
50
+ e.sources full_dir("headers/nested_classes.h")
51
+ e.namespace "classes"
52
+ end
53
+
54
+ require 'nested'
55
+ end
56
+ end
57
+
58
+ specify "should properly make nested classes available" do
59
+ assert defined?(TestClass)
60
+ assert defined?(TestClass::InnerClass)
61
+ assert defined?(TestClass::InnerClass::Inner2)
62
+
63
+ TestClass.new.should.not.be.nil
64
+ TestClass::InnerClass.new.should.not.be.nil
65
+ TestClass::InnerClass::Inner2.new.should.not.be.nil
66
+ end
67
+ end
@@ -0,0 +1,85 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ context "Compiler settings" do
4
+
5
+ specify "should be able to specify include paths" do
6
+ Extension.new "compiler" do |e|
7
+ e.sources full_dir("headers/with_includes.h"),
8
+ :include_paths => full_dir("headers/include")
9
+ e.namespace "code"
10
+ end
11
+
12
+ require 'compiler'
13
+
14
+ assert defined?(func)
15
+ end
16
+
17
+ specify "should be able to specify library paths" do
18
+ # Single path
19
+ e = Extension.new "libs_test"
20
+ e.working_dir = full_dir("generated")
21
+ e.sources full_dir("headers/empty.h"),
22
+ :library_paths => "/usr/lib/testing/123"
23
+ e.build
24
+ e.write
25
+
26
+ ext_file = full_dir("generated/extconf.rb")
27
+
28
+ contents = File.read(ext_file)
29
+
30
+ contents.should.match(%r(-L/usr/lib/testing/123))
31
+
32
+ # Clean up
33
+ `rm -rf #{full_dir('generated')}/*`
34
+
35
+ # Array of paths
36
+ e = Extension.new "libs_test"
37
+ e.working_dir = full_dir("generated")
38
+ e.sources full_dir("headers/empty.h"),
39
+ :library_paths => ["/usr/lib/testing/456", "/var/lib/stuff"]
40
+ e.build
41
+ e.write
42
+
43
+ ext_file = full_dir("generated/extconf.rb")
44
+
45
+ contents = File.read(ext_file)
46
+
47
+ contents.should.match(%r(-L/usr/lib/testing/456))
48
+ contents.should.match(%r(-L/var/lib/stuff))
49
+ end
50
+
51
+ specify "should be able to link to external libraries" do
52
+ # Single library
53
+ e = Extension.new "libs_test"
54
+ e.working_dir = full_dir("generated")
55
+ e.sources full_dir("headers/empty.h"),
56
+ :libraries => "lib123"
57
+ e.build
58
+ e.write
59
+
60
+ ext_file = full_dir("generated/extconf.rb")
61
+
62
+ contents = File.read(ext_file)
63
+
64
+ contents.should.match(%r(-llib123))
65
+
66
+ # Clean up
67
+ `rm -rf #{full_dir('generated')}/*`
68
+
69
+ # Array of libraries
70
+ e = Extension.new "libs_test"
71
+ e.working_dir = full_dir("generated")
72
+ e.sources full_dir("headers/empty.h"),
73
+ :libraries => ["ponzor", "wonko", "prankit"]
74
+ e.build
75
+ e.write
76
+
77
+ ext_file = full_dir("generated/extconf.rb")
78
+
79
+ contents = File.read(ext_file)
80
+
81
+ contents.should.match(%r(-lponzor))
82
+ contents.should.match(%r(-lwonko))
83
+ contents.should.match(%r(-lprankit))
84
+ end
85
+ end
@@ -0,0 +1,38 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ context "Ruby Extension creation" do
4
+
5
+ specify "should create a valid Ruby extension" do
6
+ Extension.new "ext_test" do |e|
7
+ e.sources full_dir("headers/empty.h")
8
+ end
9
+
10
+ should.not.raise LoadError do
11
+ require("ext_test")
12
+ end
13
+ end
14
+
15
+ specify "should create a valid Ruby extension without a block" do
16
+ e = Extension.new "extension"
17
+ e.sources full_dir("headers/empty.h")
18
+ e.working_dir = File.join(File.expand_path(File.dirname(__FILE__)), "generated")
19
+ e.build
20
+ e.write
21
+ e.compile
22
+
23
+ should.not.raise LoadError do
24
+ require("ext_test")
25
+ end
26
+ end
27
+
28
+ specify "should properly build working dir as deep as needed" do
29
+ should.not.raise Errno::ENOENT do
30
+ Extension.new "extension" do |e|
31
+ e.sources full_dir("headers/empty.h")
32
+ e.working_dir = File.join(File.expand_path(File.dirname(__FILE__)),
33
+ "generated", "path1", "path2")
34
+ end
35
+ end
36
+ end
37
+ end
38
+
@@ -0,0 +1,68 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ context "Multiple file writer (default)" do
4
+
5
+ setup do
6
+ @working_dir = File.expand_path(File.dirname(__FILE__) + "/generated")
7
+
8
+ e = Extension.new "adder"
9
+ e.working_dir = @working_dir
10
+ e.sources full_dir("headers/Adder.h")
11
+
12
+ e.module "Mod" do |m|
13
+ m.namespace "classes"
14
+ end
15
+
16
+ e.build
17
+ e.write
18
+ end
19
+
20
+ specify "should properly split up code into multiple files" do
21
+ files = Dir["#{@working_dir}/*"]
22
+ files.size.should == 6
23
+
24
+ %w(
25
+ extconf.rb
26
+ Mod.rb.cpp
27
+ Mod.rb.hpp
28
+ classes_Adder.rb.cpp
29
+ classes_Adder.rb.hpp
30
+ adder.rb.cpp
31
+ ).each do |wants|
32
+ assert_not_nil files.find {|got| File.basename(got) == wants }, "Didn't find #{wants}"
33
+ end
34
+ end
35
+
36
+ end
37
+
38
+ context "Single file writer" do
39
+
40
+ setup do
41
+ @working_dir = File.expand_path(File.dirname(__FILE__) + "/generated")
42
+
43
+ e = Extension.new "adder"
44
+ e.working_dir = @working_dir
45
+ e.sources full_dir("headers/Adder.h")
46
+ e.writer_mode :single
47
+
48
+ e.module "Mod" do |m|
49
+ m.namespace "classes"
50
+ end
51
+
52
+ e.build
53
+ e.write
54
+ end
55
+
56
+ specify "should properly write out all code in a single file" do
57
+ files = Dir["#{@working_dir}/*"]
58
+ files.size.should == 2
59
+
60
+ %w(
61
+ extconf.rb
62
+ adder.rb.cpp
63
+ ).each do |wants|
64
+ assert_not_nil files.find {|got| File.basename(got) == wants }, "Didn't find #{wants}"
65
+ end
66
+ end
67
+
68
+ end