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/Rakefile +78 -0
- data/TODO +25 -0
- data/lib/inflections.rb +53 -0
- data/lib/inflector.rb +285 -0
- data/lib/jamis.rb +589 -0
- data/lib/rbplusplus.rb +31 -0
- data/lib/rbplusplus/builders/base.rb +116 -0
- data/lib/rbplusplus/builders/class.rb +62 -0
- data/lib/rbplusplus/builders/extension.rb +48 -0
- data/lib/rbplusplus/builders/module.rb +67 -0
- data/lib/rbplusplus/extension.rb +183 -0
- data/lib/rbplusplus/module.rb +56 -0
- data/lib/rbplusplus/rbplusplus.rb +3 -0
- data/lib/rbplusplus/writers/base.rb +25 -0
- data/lib/rbplusplus/writers/extension.rb +37 -0
- data/lib/rbplusplus/writers/multiple_files_writer.rb +84 -0
- data/lib/rbplusplus/writers/single_file_writer.rb +37 -0
- data/test/classes_test.rb +67 -0
- data/test/compiling_test.rb +85 -0
- data/test/extension_test.rb +38 -0
- data/test/file_writers_test.rb +68 -0
- data/test/functions_test.rb +26 -0
- data/test/headers/Adder.h +26 -0
- data/test/headers/Subtracter.hpp +20 -0
- data/test/headers/empty.h +3 -0
- data/test/headers/functions.h +19 -0
- data/test/headers/include/helper.h +8 -0
- data/test/headers/nested_classes.h +20 -0
- data/test/headers/with_includes.h +14 -0
- data/test/modules_test.rb +67 -0
- data/test/test_helper.rb +19 -0
- metadata +91 -0
@@ -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,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
|