rbplusplus 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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