rbind 0.0.16 → 0.0.17
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/rbind.rb +1 -0
- data/lib/rbind/clang/clang.rb +3699 -0
- data/lib/rbind/clang/clang_types.rb +327 -0
- data/lib/rbind/clang_parser.rb +451 -0
- data/lib/rbind/core.rb +7 -4
- data/lib/rbind/core/rattribute.rb +17 -21
- data/lib/rbind/core/rbase.rb +98 -64
- data/lib/rbind/core/rcallback.rb +15 -0
- data/lib/rbind/core/rclass.rb +79 -16
- data/lib/rbind/core/rdata_type.rb +40 -39
- data/lib/rbind/core/renum.rb +18 -0
- data/lib/rbind/core/rnamespace.rb +189 -52
- data/lib/rbind/core/roperation.rb +52 -20
- data/lib/rbind/core/rparameter.rb +43 -8
- data/lib/rbind/core/rpointer.rb +70 -0
- data/lib/rbind/core/rreference.rb +54 -0
- data/lib/rbind/core/rtemplate_class.rb +49 -0
- data/lib/rbind/core/rtype_qualifier.rb +60 -0
- data/lib/rbind/default_parser.rb +48 -36
- data/lib/rbind/generator_c.rb +2 -2
- data/lib/rbind/generator_extern.rb +1 -3
- data/lib/rbind/generator_ruby.rb +201 -47
- data/lib/rbind/logger.rb +3 -0
- data/lib/rbind/rbind.rb +25 -9
- data/lib/rbind/templates/c/CMakeLists.txt +6 -6
- data/lib/rbind/templates/ruby/rbind.rb +1 -1
- data/lib/rbind/templates/ruby/rmethod.rb +4 -1
- data/lib/rbind/templates/ruby/rnamespace.rb +2 -1
- data/lib/rbind/templates/ruby/roverloaded_method.rb +3 -1
- data/lib/rbind/templates/ruby/roverloaded_method_call.rb +1 -0
- data/lib/rbind/templates/ruby/roverloaded_static_method.rb +3 -2
- data/lib/rbind/templates/ruby/rstatic_method.rb +4 -1
- data/lib/rbind/templates/ruby/rtype.rb +19 -16
- data/lib/rbind/templates/ruby/rtype_template.rb +7 -0
- data/lib/rbind/{core/rstring.rb → types/std_string.rb} +8 -8
- data/lib/rbind/types/std_vector.rb +100 -0
- data/rbind.gemspec +2 -2
- data/test/headers/cfunctions.h +7 -0
- data/test/headers/classes.hpp +29 -0
- data/test/headers/constants.hpp +14 -0
- data/test/headers/enums.hpp +22 -0
- data/test/headers/std_string.hpp +26 -0
- data/test/headers/std_vector.hpp +31 -0
- data/test/headers/structs.hpp +34 -0
- data/test/headers/templates.hpp +20 -0
- data/test/test_clang_parser.rb +146 -0
- data/test/test_generator_ruby.rb +0 -5
- data/test/test_roperation.rb +144 -0
- data/test/test_rparameter.rb +88 -0
- metadata +24 -7
- data/lib/rbind/core/.roperation.rb.swp +0 -0
- data/lib/rbind/core/rconst.rb +0 -35
- data/lib/rbind/core/rstruct.rb +0 -87
- data/lib/rbind/core/rvector.rb +0 -27
data/lib/rbind/rbind.rb
CHANGED
@@ -2,9 +2,9 @@ require 'open3'
|
|
2
2
|
|
3
3
|
module Rbind
|
4
4
|
class Rbind
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
attr_reader :parser
|
6
|
+
attr_reader :generator_c
|
7
|
+
attr_reader :generator_ruby
|
8
8
|
attr_accessor :includes
|
9
9
|
attr_accessor :name
|
10
10
|
attr_accessor :pkg_config
|
@@ -46,12 +46,12 @@ module Rbind
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
-
def initialize(name)
|
49
|
+
def initialize(name,parser = DefaultParser.new)
|
50
50
|
@name = name
|
51
51
|
@includes = []
|
52
52
|
@pkg_config = []
|
53
53
|
@gems = []
|
54
|
-
@parser =
|
54
|
+
@parser = parser
|
55
55
|
lib_name = "rbind_#{name.downcase}"
|
56
56
|
@generator_c = GeneratorC.new(@parser,lib_name)
|
57
57
|
@generator_ruby = GeneratorRuby.new(@parser,name,lib_name)
|
@@ -87,20 +87,23 @@ module Rbind
|
|
87
87
|
|
88
88
|
# parses other rbind packages
|
89
89
|
def parse_extern
|
90
|
+
# extern package are always paresed with the default parser
|
91
|
+
local_parser = DefaultParser.new(parser)
|
90
92
|
paths = Rbind.rbind_pkg_paths(@pkg_config)
|
91
93
|
paths.each do |pkg|
|
92
94
|
config = YAML.load(File.open(File.join(pkg,"config.rbind")).read)
|
93
95
|
path = File.join(pkg,"extern.rbind")
|
94
96
|
::Rbind.log.info "parsing extern rbind pkg file #{path}"
|
95
|
-
|
97
|
+
local_parser.parse(File.open(path).read,config.ruby_module_name)
|
96
98
|
end
|
97
99
|
@gems.each do |gem|
|
98
100
|
path = Rbind.gem_path(gem)
|
99
101
|
config = YAML.load(File.open(File.join(path,"config.rbind")).read)
|
100
102
|
path = File.join(path,"extern.rbind")
|
101
103
|
::Rbind.log.info "parsing extern gem file #{path}"
|
102
|
-
|
104
|
+
local_parser.parse(File.open(path).read,config.ruby_module_name)
|
103
105
|
end
|
106
|
+
self
|
104
107
|
end
|
105
108
|
|
106
109
|
def parse_headers_dry(*headers)
|
@@ -191,9 +194,22 @@ module Rbind
|
|
191
194
|
@generator_c.libs
|
192
195
|
end
|
193
196
|
|
194
|
-
def
|
197
|
+
def add_std_string
|
195
198
|
@generator_c.includes << "<string>"
|
196
|
-
@parser.add_type(
|
199
|
+
@parser.add_type(StdString.new("std::string",@parser))
|
200
|
+
@parser.type_alias["basic_string"] = @parser.std.string
|
201
|
+
self
|
202
|
+
end
|
203
|
+
|
204
|
+
def add_std_vector
|
205
|
+
@generator_c.includes << "<vector>"
|
206
|
+
@parser.add_type(StdVector.new("std::vector"))
|
207
|
+
self
|
208
|
+
end
|
209
|
+
|
210
|
+
def add_std_types
|
211
|
+
add_std_vector
|
212
|
+
add_std_string
|
197
213
|
end
|
198
214
|
|
199
215
|
def method_missing(m,*args)
|
@@ -18,13 +18,13 @@ TARGET_LINK_LIBRARIES(<%= library_name %> <%= libs %> ${GEM_LIBRARIES})
|
|
18
18
|
|
19
19
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/rbind.pc.in ${CMAKE_CURRENT_BINARY_DIR}/<%= library_name %>.pc @ONLY)
|
20
20
|
|
21
|
-
set(ROOT_FOLDER ${CMAKE_CURRENT_SOURCE_DIR}
|
21
|
+
set(ROOT_FOLDER ${CMAKE_CURRENT_SOURCE_DIR}/../..)
|
22
22
|
if(GEM_INSTALL)
|
23
23
|
# local install
|
24
|
-
install(TARGETS <%= library_name %> LIBRARY DESTINATION ${ROOT_FOLDER}/lib
|
25
|
-
install(FILES types.h operations.h conversions.hpp DESTINATION ${ROOT_FOLDER}/
|
26
|
-
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/extern.rbind DESTINATION ${ROOT_FOLDER}/
|
27
|
-
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/config.rbind DESTINATION ${ROOT_FOLDER}/
|
24
|
+
install(TARGETS <%= library_name %> LIBRARY DESTINATION ${ROOT_FOLDER}/lib)
|
25
|
+
install(FILES types.h operations.h conversions.hpp DESTINATION ${ROOT_FOLDER}/include)
|
26
|
+
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/extern.rbind DESTINATION ${ROOT_FOLDER}/include)
|
27
|
+
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/config.rbind DESTINATION ${ROOT_FOLDER}/include)
|
28
28
|
else()
|
29
29
|
# global install
|
30
30
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/<%= library_name %>.pc DESTINATION lib/pkgconfig)
|
@@ -36,7 +36,7 @@ else()
|
|
36
36
|
FIND_PACKAGE(Ruby)
|
37
37
|
IF(NOT RUBY_INCLUDE_PATH)
|
38
38
|
MESSAGE(STATUS "Ruby library not found. Cannot install ruby extensions")
|
39
|
-
ELSEIF(
|
39
|
+
ELSEIF(RUBY_EXTENSIONS_AVAILABLE)
|
40
40
|
STRING(REGEX REPLACE ".*lib(32|64)?/?" "lib/" RUBY_LIBRARY_INSTALL_DIR ${RUBY_RUBY_LIB_PATH})
|
41
41
|
INSTALL(DIRECTORY ${ROOT_FOLDER}/lib/ruby/ DESTINATION ${RUBY_LIBRARY_INSTALL_DIR}
|
42
42
|
FILES_MATCHING PATTERN "*.rb")
|
@@ -8,7 +8,7 @@ module <%= name %>
|
|
8
8
|
extend FFI::Library
|
9
9
|
|
10
10
|
#load library <%= library_name %>
|
11
|
-
path = File.join(File.dirname(__FILE__),".."
|
11
|
+
path = File.join(File.dirname(__FILE__),"..")
|
12
12
|
path = if Dir.exist?(path)
|
13
13
|
Dir.chdir(path) do
|
14
14
|
path = Dir.glob("lib<%= library_name %>.*").first
|
@@ -1,5 +1,7 @@
|
|
1
|
-
|
1
|
+
<%= add_doc -%>
|
2
|
+
# @note wrapper for <%= signature %>
|
2
3
|
def <%=name%>(<%= wrap_parameters_signature %>)
|
4
|
+
<%= add_specialize_ruby -%>
|
3
5
|
<%- if return_type.basic_type? || operator? -%>
|
4
6
|
Rbind::<%= cname %>( <%= wrap_parameters_call %>)
|
5
7
|
<%- else -%>
|
@@ -11,4 +13,5 @@
|
|
11
13
|
result
|
12
14
|
<%- end -%>
|
13
15
|
end
|
16
|
+
<%= add_alias -%>
|
14
17
|
|
@@ -1,6 +1,8 @@
|
|
1
|
-
|
1
|
+
<%= add_doc -%>
|
2
|
+
# @note wrapper for overloaded method <%= name %>
|
2
3
|
def <%=name%>(*args)
|
3
4
|
<%= add_methods %>
|
4
5
|
raise ArgumentError, "No overloaded signature fits to: #{args.map(&:class)}"
|
5
6
|
end
|
7
|
+
<%= add_alias -%>
|
6
8
|
|
@@ -1,6 +1,7 @@
|
|
1
|
-
|
1
|
+
<%= add_doc -%>
|
2
|
+
# @note wrapper for overloaded method <%= name %>
|
2
3
|
def self.<%=name%>(*args)
|
3
4
|
<%= add_methods %>
|
4
5
|
raise ArgumentError, "No overloaded signature fits to: #{args.map(&:class)}"
|
5
6
|
end
|
6
|
-
|
7
|
+
<%= add_alias -%>
|
@@ -1,5 +1,5 @@
|
|
1
|
-
# @
|
2
|
-
# object wrapping <%= full_name %>
|
1
|
+
# @private
|
2
|
+
# @note object wrapping <%= full_name %>
|
3
3
|
class <%= name %>Struct < FFI::Struct
|
4
4
|
layout :version,:uchar,
|
5
5
|
:size,:size_t,
|
@@ -14,11 +14,12 @@ class <%= name %>Struct < FFI::Struct
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
+
<%= add_doc -%>
|
17
18
|
class <%= name %>
|
18
19
|
extend FFI::DataConverter
|
19
20
|
native_type FFI::Type::POINTER
|
20
21
|
|
21
|
-
# @
|
22
|
+
# @private
|
22
23
|
#
|
23
24
|
# Returns the *Struct type that Rbind uses to store additional information
|
24
25
|
# about the memory used by this object
|
@@ -28,6 +29,7 @@ class <%= name %>
|
|
28
29
|
<%= name %>Struct
|
29
30
|
end
|
30
31
|
|
32
|
+
<%= add_constructor_doc -%>
|
31
33
|
def self.new(*args)
|
32
34
|
if args.first.is_a?(FFI::Pointer) || args.first.is_a?(<%= name %>Struct)
|
33
35
|
raise ArgumentError, "too many arguments for creating #{self.name} from Pointer" unless args.size == 1
|
@@ -37,7 +39,7 @@ class <%= name %>
|
|
37
39
|
raise ArgumentError, "no constructor for #{self}(#{args.inspect})"
|
38
40
|
end
|
39
41
|
|
40
|
-
# @
|
42
|
+
# @private
|
41
43
|
def self.rbind_to_native(obj,context)
|
42
44
|
if obj.is_a? <%= name %>
|
43
45
|
obj.__obj_ptr__
|
@@ -46,14 +48,14 @@ class <%= name %>
|
|
46
48
|
end
|
47
49
|
end
|
48
50
|
|
49
|
-
# @
|
51
|
+
# @private
|
50
52
|
def self.rbind_from_native(ptr,context)
|
51
53
|
<%= name %>.new(ptr)
|
52
54
|
end
|
53
55
|
|
54
|
-
# @
|
56
|
+
# @private
|
55
57
|
#
|
56
|
-
# Performs the
|
58
|
+
# Performs the conversion a Ruby representation into the FFI representation
|
57
59
|
#
|
58
60
|
# @param [Object] obj the Ruby representation
|
59
61
|
# @param context necessary but undocumented argument from FFI
|
@@ -62,9 +64,9 @@ class <%= name %>
|
|
62
64
|
rbind_to_native(obj,context)
|
63
65
|
end
|
64
66
|
|
65
|
-
# @
|
67
|
+
# @private
|
66
68
|
#
|
67
|
-
# Performs the
|
69
|
+
# Performs the conversion from FFI into the Ruby representation that
|
68
70
|
# corresponds to this type
|
69
71
|
#
|
70
72
|
# @param [FFI::Pointer,FFI::AutoPointer] ptr
|
@@ -74,10 +76,10 @@ class <%= name %>
|
|
74
76
|
rbind_from_native(ptr,context)
|
75
77
|
end
|
76
78
|
|
77
|
-
# @
|
79
|
+
# @private
|
78
80
|
attr_reader :__obj_ptr__
|
79
81
|
|
80
|
-
# @
|
82
|
+
# @private
|
81
83
|
def initialize(ptr)
|
82
84
|
@__obj_ptr__ = if ptr.is_a? <%= name %>Struct
|
83
85
|
ptr
|
@@ -86,7 +88,7 @@ class <%= name %>
|
|
86
88
|
end
|
87
89
|
end
|
88
90
|
|
89
|
-
# @
|
91
|
+
# @private
|
90
92
|
# returns true if the underlying pointer is owner of
|
91
93
|
# the real object
|
92
94
|
def __owner__?
|
@@ -98,10 +100,6 @@ class <%= name %>
|
|
98
100
|
<%= add_to_s %>
|
99
101
|
end
|
100
102
|
|
101
|
-
# @!group Sepcializing
|
102
|
-
<%= add_specializing %>
|
103
|
-
# @!endgroup
|
104
|
-
|
105
103
|
# @!group Constants
|
106
104
|
<%= add_consts %>
|
107
105
|
# @!endgroup
|
@@ -109,7 +107,12 @@ class <%= name %>
|
|
109
107
|
# methods
|
110
108
|
<%= add_methods %>
|
111
109
|
|
110
|
+
# @!group Specializing
|
111
|
+
<%= add_specializing %>
|
112
|
+
# @!endgroup
|
113
|
+
|
112
114
|
# types
|
113
115
|
<%= add_types %>
|
116
|
+
|
114
117
|
end
|
115
118
|
|
@@ -1,30 +1,29 @@
|
|
1
1
|
module Rbind
|
2
|
-
class
|
2
|
+
class StdString < RClass
|
3
3
|
def initialize(name,root)
|
4
4
|
super(name)
|
5
5
|
|
6
6
|
size_t = root.type("size_t")
|
7
7
|
char = root.type("char")
|
8
8
|
string = root.type("c_string")
|
9
|
-
const_string =
|
9
|
+
const_string = string.to_const
|
10
10
|
bool = root.type("bool")
|
11
11
|
void = root.type("void")
|
12
12
|
int = root.type("int")
|
13
13
|
|
14
14
|
add_operation ROperation.new(self.name,nil)
|
15
|
-
add_operation ROperation.new(self.name,nil,RParameter.new("other",self))
|
16
|
-
add_operation ROperation.new(self.name,nil,RParameter.new("str",string),RParameter.new("size",size_t))
|
15
|
+
add_operation ROperation.new(self.name,nil,RParameter.new("other",self).to_const)
|
16
|
+
add_operation ROperation.new(self.name,nil,RParameter.new("str",string).to_const,RParameter.new("size",size_t))
|
17
17
|
add_operation ROperation.new("size",size_t)
|
18
18
|
add_operation ROperation.new("length",size_t)
|
19
19
|
add_operation ROperation.new("operator[]",char,RParameter.new("idx",size_t))
|
20
20
|
add_operation ROperation.new("c_str",const_string)
|
21
21
|
add_operation ROperation.new("empty",bool)
|
22
22
|
add_operation ROperation.new("clear",void)
|
23
|
-
add_operation ROperation.new("compare",int,RParameter.new("other",self))
|
24
|
-
add_operation ROperation.new("swap",void,RParameter.new("other",self)
|
25
|
-
end
|
23
|
+
add_operation ROperation.new("compare",int,RParameter.new("other",self).to_const)
|
24
|
+
add_operation ROperation.new("swap",void,RParameter.new("other",self))
|
26
25
|
|
27
|
-
|
26
|
+
specialize_ruby do
|
28
27
|
%Q$ def self.to_native(obj,context)
|
29
28
|
if obj.is_a? ::String
|
30
29
|
str = obj.to_str
|
@@ -36,6 +35,7 @@ module Rbind
|
|
36
35
|
def to_s
|
37
36
|
c_str
|
38
37
|
end$
|
38
|
+
end
|
39
39
|
end
|
40
40
|
end
|
41
41
|
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
module Rbind
|
2
|
+
class StdVector < RTemplateClass
|
3
|
+
def specialize(klass,*parameters)
|
4
|
+
if parameters.size != 1
|
5
|
+
raise ArgumentError,"StdVector does only support one template parameter. Got: #{parameters}}"
|
6
|
+
end
|
7
|
+
vector_type = parameters.flatten.first
|
8
|
+
|
9
|
+
klass.add_operation ROperation.new(klass.name,nil)
|
10
|
+
klass.add_operation ROperation.new(klass.name,nil,RParameter.new("other",klass).to_const)
|
11
|
+
|
12
|
+
para = Array.new
|
13
|
+
para << RParameter.new("size",type("size_t"))
|
14
|
+
para << RParameter.new("val",vector_type).default_value(vector_type.full_name+"()").to_const
|
15
|
+
klass.add_operation ROperation.new("resize",type("void"),para)
|
16
|
+
klass.add_operation ROperation.new("size",type("size_t"))
|
17
|
+
klass.add_operation ROperation.new("clear",type("void"))
|
18
|
+
klass.add_operation ROperation.new("capacity",type("size_t"))
|
19
|
+
klass.add_operation ROperation.new("empty",type("bool"))
|
20
|
+
klass.add_operation ROperation.new("reserve",type("void"),RParameter.new("size",type("size_t")))
|
21
|
+
klass.add_operation ROperation.new("operator[]",vector_type,RParameter.new("size",type("size_t")))
|
22
|
+
klass.add_operation ROperation.new("at",vector_type,RParameter.new("size",type("size_t")))
|
23
|
+
klass.add_operation ROperation.new("front",vector_type)
|
24
|
+
klass.add_operation ROperation.new("back",vector_type)
|
25
|
+
klass.add_operation ROperation.new("data",type("void *"))
|
26
|
+
klass.add_operation ROperation.new("push_back",type("void"),RParameter.new("other",vector_type).to_const)
|
27
|
+
klass.add_operation ROperation.new("pop_back",type("void"))
|
28
|
+
klass.add_operation ROperation.new("swap",type("void"),RParameter.new("other",klass))
|
29
|
+
# add ruby code to the front of the method
|
30
|
+
klass.operation("operator[]").specialize_ruby do
|
31
|
+
"validate_index(size)"
|
32
|
+
end
|
33
|
+
klass.operation("at").specialize_ruby do
|
34
|
+
"validate_index(size)"
|
35
|
+
end
|
36
|
+
|
37
|
+
specialize_ruby do
|
38
|
+
%Q$ def self.new(type,*args)
|
39
|
+
klass,elements = if type.class == Class
|
40
|
+
[type.name,[]]
|
41
|
+
else
|
42
|
+
e = Array(type) + args.flatten
|
43
|
+
args = []
|
44
|
+
[type.class.name,e]
|
45
|
+
end
|
46
|
+
#remove module name
|
47
|
+
klass = klass.split("::")
|
48
|
+
klass.shift if klass.size > 1
|
49
|
+
klass = klass.join("_")
|
50
|
+
raise ArgumentError,"no std::vector defined for \#{type}" unless self.const_defined?(klass)
|
51
|
+
v = self.const_get(klass).new(*args)
|
52
|
+
elements.each do |e|
|
53
|
+
v << e
|
54
|
+
end
|
55
|
+
v
|
56
|
+
end$
|
57
|
+
end
|
58
|
+
|
59
|
+
klass
|
60
|
+
end
|
61
|
+
|
62
|
+
# called from RTemplate when ruby_specialize is called for the instance
|
63
|
+
def specialize_ruby_specialization(klass)
|
64
|
+
%Q$ include Enumerable
|
65
|
+
alias get_element []
|
66
|
+
def [](idx)
|
67
|
+
validate_index(idx)
|
68
|
+
get_element(idx)
|
69
|
+
end
|
70
|
+
|
71
|
+
def validate_index(idx)
|
72
|
+
if idx < 0 || idx >= size
|
73
|
+
raise RangeError,"\#{idx} is out of range [0..\#{size-1}]"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
def each(&block)
|
77
|
+
if block
|
78
|
+
s = size
|
79
|
+
0.upto(s-1) do |i|
|
80
|
+
yield self[i]
|
81
|
+
end
|
82
|
+
else
|
83
|
+
Enumerator.new(self)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
def <<(val)
|
87
|
+
push_back(val)
|
88
|
+
self
|
89
|
+
end
|
90
|
+
def delete_if(&block)
|
91
|
+
v = self.class.new
|
92
|
+
each do |i|
|
93
|
+
v << i if !yield(i)
|
94
|
+
end
|
95
|
+
v.swap(self)
|
96
|
+
self
|
97
|
+
end$
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
data/rbind.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'rbind'
|
3
|
-
s.version = '0.0.
|
3
|
+
s.version = '0.0.17'
|
4
4
|
s.date = '2013-08-13'
|
5
5
|
s.platform = Gem::Platform::RUBY
|
6
6
|
s.authors = ['Alexander Duda']
|
@@ -11,7 +11,7 @@ Gem::Specification.new do |s|
|
|
11
11
|
'but is not tight to this library. It allows to import already wrapped types '\
|
12
12
|
'from other gems/libraries using rbind to share the same types across '\
|
13
13
|
'multiple gems/libraries. For now rbind uses a copy of the OpenCV python hdr_parser '\
|
14
|
-
'to parse c/c++ header files
|
14
|
+
'to parse c/c++ header files.'\
|
15
15
|
'This gem is still under heavy development and the API might change in the future.'
|
16
16
|
s.files = `git ls-files`.split("\n")
|
17
17
|
s.require_path = 'lib'
|