easy-swig 1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +15 -0
  2. data/MIT_LICENSE +19 -0
  3. data/README.md +86 -0
  4. data/lib/apinodes/api_attribute.rb +6 -0
  5. data/lib/apinodes/api_class.rb +207 -0
  6. data/lib/apinodes/api_enum.rb +10 -0
  7. data/lib/apinodes/api_function.rb +13 -0
  8. data/lib/apinodes/api_group.rb +13 -0
  9. data/lib/apinodes/api_method.rb +12 -0
  10. data/lib/apinodes/api_namespace.rb +104 -0
  11. data/lib/apinodes/api_node.rb +109 -0
  12. data/lib/apinodes/api_variable.rb +12 -0
  13. data/lib/apinodes/inc_file.rb +118 -0
  14. data/lib/config.rb +56 -0
  15. data/lib/csharp/csharp_features.rb +100 -0
  16. data/lib/csharp/generators/csharp_class_generator.rb +14 -0
  17. data/lib/csharp/generators/csharp_generator.rb +17 -0
  18. data/lib/csharp/generators/csharp_namespace_generator.rb +33 -0
  19. data/lib/easy-swig-cli.rb +143 -0
  20. data/lib/easy-swig.rb +71 -0
  21. data/lib/features.rb +39 -0
  22. data/lib/generators/class_generator.rb +209 -0
  23. data/lib/generators/generator.rb +124 -0
  24. data/lib/generators/generator_util.rb +135 -0
  25. data/lib/generators/hfile_generator.rb +56 -0
  26. data/lib/generators/namespace_generator.rb +58 -0
  27. data/lib/generators/properties.rb +13 -0
  28. data/lib/readers/csv_parser.rb +127 -0
  29. data/lib/tasks/doxygen_task.rb +28 -0
  30. data/lib/tasks/generate_task.rb +85 -0
  31. data/lib/tasks/hfiles_manager.rb +86 -0
  32. data/lib/tasks/swig_task.rb +69 -0
  33. data/lib/util/logger.rb +36 -0
  34. data/lib/util/print.rb +72 -0
  35. data/lib/util/query.rb +136 -0
  36. data/lib/util/utilities.rb +101 -0
  37. data/spec/accessors/accessors_spec.rb +45 -0
  38. data/spec/anonymousEnums/anonymous_enums_spec.rb +49 -0
  39. data/spec/friends/friends_spec.rb +45 -0
  40. data/spec/innerclasses/innerclasses_spec.rb +48 -0
  41. data/spec/namespacePrefixes/namespace_prefixes_spec.rb +39 -0
  42. data/spec/squish/squish_spec.rb +40 -0
  43. data/spec/subdirectories/subdirectories_spec.rb +41 -0
  44. data/spec/templates/templates_spec.rb +34 -0
  45. metadata +107 -0
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MDNkNmY2ZmU4YTlkM2Y1YTFmYTA0ZWE2NTc3MmE3NWQ0ZjRhMmE5Yg==
5
+ data.tar.gz: !binary |-
6
+ ODE0YmIxYTM1MGU5NTU1YzQ5ZjcwMzliMDBkNjk5ZDY3ZjA5ZmQ5OQ==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ M2E2NDljMzMxZThlZWQ1YzdkYTE5OGJjZGExOTE0ODMxOTc0MjU3Mjc3NTdl
10
+ MGRmZDE2N2UyY2E5OTM5OWMxNzYwYzU3OWE5YmFkZTliYTE0ZTE4YzIyNmRi
11
+ N2QyMTQ2NGEwZDU1OGIxOWZlMTExMmJlMDFmZjA5YmVkYmRlMDM=
12
+ data.tar.gz: !binary |-
13
+ OWQwZWQ2MTVmZTFhMWJhNDZmY2NiYzdlY2NhNDBhN2VkYzU0MzMzMWI1ZmJk
14
+ OGJiODFjMzIwOWI2NTE4YjZmMGY1NTliNWVlZTBkZDAyMTNlZDMzNWMzNjIy
15
+ MTQ1YjUzZDU2YWNjNzY0NDBmOGZhZTBmOTRhODA5MzUzOGE0MjE=
data/MIT_LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2013 David Fuenmayor
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,86 @@
1
+ EasySwig
2
+ =========
3
+
4
+ Automatically generates wrappers for C/C++ code using SWIG.
5
+
6
+ This is both a Ruby Gem and a CLI Tool. Feed it with a directory containing the library's header files (the ones you want to wrap) and a CSV File with basic configuration (see usage). EasySwig will generate the corresponding SWIG interface files (.i) in an output directory. EasySwig also offers a facade allowing you to directly call SWIG in order to generate wrappers in the target language.
7
+
8
+ EasySwig supports currently only C#. There is ongoing work on other languages support.
9
+
10
+
11
+ Installation and Use
12
+ ------------
13
+
14
+ EasySwig relies on the Doxyparser gem (https://github.com/davfuenmayor/ruby-doxygen-parser) which on his part depends on Nokogiri (http://nokogiri.org) and Doxygen (www.doxygen.org). Refer to Doxyparser for more information.
15
+
16
+ To actually generate wrappers you may also want to install SWIG (http://www.swig.org/). Currently only SWIG version 2.0.11 is supported. There is ongoing work on 3.0.0 version support.
17
+
18
+
19
+ For using EasySwig you need to create a folder with the following structure:
20
+ (it is recommended to use the subfolder 'spec/Example' as a template)
21
+
22
+ - A subfolder named 'include' with the header files you want to wrap.
23
+ - A CSV file named 'api.csv' with the name of the namespace you want to wrap and which classes to wrap/ignore.
24
+ - An optional file (optionally named custom_config.i) with %include statements for those header files found in subdirectories (otherwise SWIG won't find them)
25
+
26
+ There are three operations you can do with EasySwig: 'doxygen', 'generate' and 'swig'
27
+ 1. doxygen: Generate Doxygen XML intermediate representation. Here you need the Doxyparser gem installed on your system.
28
+ 2. generate: Generate SWIG Interface Files (.i) from the previously generated Doxygen XML intermediate representation
29
+ 3. swig: Generate wrappers from your header files and the previously generated .i files. Here you need SWIG installed on your system.
30
+
31
+
32
+ As Ruby Gem
33
+ -----------
34
+
35
+ ```shell
36
+ gem install doxyparser
37
+ ```
38
+ or add the following line to your Gemfile:
39
+
40
+ ```ruby
41
+ gem 'doxyparser'
42
+ ```
43
+ and run `bundle install` from your shell.
44
+
45
+ Call EasySwig from your Ruby code using default values:
46
+
47
+ ```ruby
48
+ # Will use as target directory your current working directory (be careful!)
49
+ EasySwig::doxygen
50
+ EasySwig::generate
51
+ EasySwig::swig
52
+ ```
53
+ Or with some configuration:
54
+
55
+ ```ruby
56
+ config = EasySwig::Config.new('path/to/your/directory')
57
+ config.stl_support = nil # Inactivate default support for the STL Library
58
+ config.html = true # Generates HTML documentation for your header files
59
+ EasySwig::doxygen(config)
60
+ EasySwig::generate(config)
61
+ config.includes_dir = '/usr/lib/gcc/i686-linux-gnu/4.6.3/include' # Add other system include files you want SWIG to import during wrapper generation
62
+ EasySwig::swig(config)
63
+ ```
64
+
65
+ You can consult the documentation (generated with YARD) in doc/ subfolder for more information about the EasySwig::Config class and its default values.
66
+
67
+
68
+ As CLI
69
+ ----------
70
+
71
+ Download (or clone) this repository. An illustrative example of how to use EasySwig as CLI-Tool is found in subfolder spec/Example.
72
+ Open the corresponding README file and follow the instructions. This Example project can also be used as template for your own wrappers.
73
+
74
+ ```shell
75
+ cd spec/Example
76
+ # Displays command help. Useful for parameter syntax, options and default values
77
+ sh ../../bin/easyswig.sh -h
78
+ # Generate Doxygen XML Intermediate representation. Found in generated subfolder: 'easy-swig/doxygen'
79
+ sh ../../bin/easyswig.sh doxygen
80
+ # Generate SWIG Interface Files (.i). Found in generated subfolder: 'easy-swig/generate'
81
+ sh ../../bin/easyswig.sh generate
82
+ # Generate Wrappers using your installed version of SWIG. Found in generated subfolder: 'easy-swig/swig'
83
+ sh ../../bin/easyswig.sh swig
84
+ ```
85
+
86
+
@@ -0,0 +1,6 @@
1
+ module EasySwig
2
+ class ApiAttribute < ApiVariable
3
+ attr_accessor :match_static
4
+
5
+ end
6
+ end
@@ -0,0 +1,207 @@
1
+ module EasySwig
2
+
3
+ class ApiClass < ApiNode
4
+
5
+ attr_accessor :api_methods
6
+ attr_accessor :api_attributes
7
+ attr_accessor :api_innerclasses
8
+ attr_accessor :api_enums
9
+ attr_accessor :ignored_methods
10
+ attr_accessor :ignored_attributes
11
+ attr_accessor :ignored_innerclasses
12
+ attr_accessor :ignored_enums
13
+ attr_accessor :match_subclasses
14
+ attr_accessor :match_superclasses
15
+ attr_accessor :group
16
+ attr_accessor :wrap_innerclasses
17
+ attr_accessor :wrap_methods
18
+ attr_accessor :wrap_enums
19
+ attr_accessor :wrap_attributes
20
+ attr_accessor :nested_support
21
+ attr_accessor :friend_support
22
+
23
+ def initialize(hash)
24
+ super(hash)
25
+ @lookup_cache = {}
26
+ @api_methods=[]
27
+ @api_attributes=[]
28
+ @api_innerclasses=[]
29
+ @api_enums=[]
30
+ @ignored_methods=[]
31
+ @ignored_attributes=[]
32
+ @ignored_innerclasses=[]
33
+ @ignored_enums=[]
34
+ @director=false
35
+ end
36
+
37
+ def api_nodes
38
+ [self]
39
+ end
40
+
41
+ def file
42
+ return wrapped_node.file if node_type != 'innerclass'
43
+ return parent.file
44
+ end
45
+
46
+ def assoc_with_node(clazz)
47
+ super(clazz)
48
+ assoc_methods
49
+ assoc_attributes
50
+ assoc_innerclasses
51
+ assoc_enums
52
+ end
53
+
54
+ def assoc_methods
55
+ all_methods = @wrapped_node.methods + @wrapped_node.methods('public', 'static', nil)
56
+ all_methods.reject!{ |f| f.basename == f.basename.upcase }
57
+ ign_methods = @ignored_methods.map{|i| i.basename}
58
+ if @wrap_methods
59
+ all_methods.each { |m|
60
+ if not ign_methods.include?(m.basename)
61
+ hash = {'features' => @features, 'node_type' => 'method', 'parent' => self, 'basename' => m.basename }
62
+ @api_methods << ApiMethod.new(hash).assoc_with_node(m)
63
+ end
64
+ }
65
+ else
66
+ assoc_functions(all_methods, @api_methods, @ignored_methods)
67
+ end
68
+ end
69
+
70
+ def assoc_attributes
71
+ all_attributes = @wrapped_node.attributes + @wrapped_node.attributes('public', 'static')
72
+ ign_attributes = @ignored_attributes.map{|i| i.basename}
73
+ if @wrap_attributes
74
+ all_attributes.each { |a|
75
+ if not ign_attributes.include?(a.basename)
76
+ hash = {'features' => @features, 'node_type' => 'attribute', 'parent' => self, 'basename' => a.basename }
77
+ @api_attributes << ApiAttribute.new(hash).assoc_with_node(a)
78
+ end
79
+ }
80
+ else
81
+ assoc_members(all_attributes, @api_attributes, @ignored_attributes)
82
+ end
83
+ end
84
+
85
+ def assoc_innerclasses
86
+ all_innerclasses = @wrapped_node.innerclasses
87
+ ign_innerclasses = @ignored_innerclasses.map{|i| i.basename}
88
+ if @wrap_innerclasses
89
+ all_innerclasses.each { |c|
90
+ if !ign_innerclasses.include?(c.basename)
91
+ hash = {'wrap_methods' => 'x',
92
+ 'wrap_attributes' => 'x',
93
+ 'wrap_enums' => 'x',
94
+ 'wrap_innerclasses' => 'x',
95
+ 'node_type' => 'innerclass',
96
+ 'nested_support' => @nested_support,
97
+ 'features' => @features,
98
+ 'parent' => self,
99
+ 'basename' => c.basename }
100
+ @api_innerclasses << ApiClass.new(hash).assoc_with_node(c)
101
+ end
102
+ }
103
+ else
104
+ assoc_members(all_innerclasses, @api_innerclasses, @ignored_innerclasses)
105
+ end
106
+ end
107
+
108
+ def assoc_enums
109
+ all_enums = @wrapped_node.enums
110
+ ign_enums = @ignored_enums.map{ |i| i.basename }
111
+ if @wrap_enums
112
+ all_enums.each { |e|
113
+ if !ign_enums.include?(e.basename)
114
+ hash = {'features' => @features, 'node_type' => 'enum', 'parent' => self, 'basename' => e.basename }
115
+ @api_enums << ApiEnum.new(hash).assoc_with_node(e)
116
+ end
117
+ }
118
+ else
119
+ assoc_members(all_enums, @api_enums, @ignored_enums)
120
+ end
121
+ self
122
+ end
123
+
124
+ def lookup_node(type)
125
+ cached = @lookup_cache[type.escaped_name]
126
+ return cached if cached
127
+ looked_up = _lookup_node(type)
128
+ @lookup_cache[type.escaped_name] = looked_up
129
+ looked_up
130
+ end
131
+
132
+ def _lookup_node(type) # TODO use lookup methods of Doxyparser and avoid selects
133
+ return Doxyparser::Type.new({ name: type.basename, dir: ""}) if is_primitive?(type.basename)
134
+ return type if is_std?(type.basename)
135
+ return @wrapped_node if type.basename == @basename
136
+ expanded_type = expand_typedefs(type)
137
+ if expanded_type != type
138
+ looked_up_type = lookup_node(expanded_type)
139
+ return expanded_type if looked_up_type.nil? # TODO why?
140
+ looked_up = expanded_type.name.gsub(expanded_type.escaped_name, looked_up_type.escaped_name)
141
+ return Doxyparser::Type.new({ name: looked_up, dir: ""})
142
+ end
143
+ innerclass = innerclasses.select { |ic| ic.basename == type.basename }
144
+ unless innerclass.empty?
145
+ if type.template?
146
+ new_name = type.escaped_name.gsub(type.basename, innerclass[0].name)
147
+ return Doxyparser::Type.new({ name: new_name, dir: innerclass[0].dir})
148
+ end
149
+ return innerclass[0]
150
+ end
151
+ enum = enums.select { |ie| ie.basename == type.basename }
152
+ return enum[0] unless enum.empty?
153
+ if node_type == 'innerclass'
154
+ sibling = parent.innerclasses.select { |c| c.basename == type.basename }
155
+ else
156
+ siblings = parent.classes + parent.structs
157
+ sibling = siblings.select { |c| c.basename == type.basename }
158
+ end
159
+ unless sibling.empty?
160
+ if type.template?
161
+ new_name = type.escaped_name.gsub(type.basename, sibling[0].name)
162
+ return Doxyparser::Type.new({ name: new_name, dir: sibling[0].dir})
163
+ end
164
+ return sibling[0]
165
+ end
166
+ parent_enum = parent.enums.select { |e| e.basename == type.basename }
167
+ return parent_enum[0] unless parent_enum.empty?
168
+ if node_type == 'innerclass'
169
+ return parent.lookup_node(type)
170
+ else
171
+ puts "Type '" + type.basename+ "' not found in class '" + @wrapped_node.name + "'\n-> Function or variable will be ignored."
172
+ return nil
173
+ end
174
+ end
175
+
176
+ def expand_typedefs(type)
177
+ typedef = visible_typedefs.select { |t| t.basename == type.basename } # TODO use Doxyparser, avoid selects
178
+ return type if typedef.empty?
179
+ new_type = typedef[0].type.dup
180
+ new_type.name = type.name.gsub(type.escaped_name, new_type.name)
181
+ expand_typedefs(new_type)
182
+ end
183
+
184
+ def visible_typedefs
185
+ if node_type == 'innerclass'
186
+ return typedefs(:all)
187
+ end
188
+ namespace = @parent
189
+ files_included = file.files_included
190
+ ns_typedefs = [] if namespace.nil?
191
+ ns_typedefs ||= namespace.typedefs.select { |typedef|
192
+ included_in_files(typedef, [file, *files_included])
193
+ }
194
+ typedefs(:all) + file.typedefs + ns_typedefs
195
+ end
196
+
197
+ def included_in_files(typedef, files) ## TODO Optimize this
198
+ files.any? { |file|
199
+ ::File.basename(typedef.location) =~ %r{#{file.basename}:\d+$}
200
+ }
201
+ end
202
+
203
+ def is_enum_or_innerclass?(target_name)
204
+ (innerclasses + enums).any?{ |c| c.basename == target_name}
205
+ end
206
+ end
207
+ end
@@ -0,0 +1,10 @@
1
+ module EasySwig
2
+
3
+ class ApiEnum < ApiNode
4
+
5
+ def api_nodes
6
+ self
7
+ end
8
+
9
+ end
10
+ end
@@ -0,0 +1,13 @@
1
+ module EasySwig
2
+
3
+ class ApiFunction < ApiNode
4
+ attr_accessor :match_signature
5
+ attr_accessor :match_type
6
+
7
+ def api_nodes
8
+ self
9
+ end
10
+
11
+ end
12
+
13
+ end
@@ -0,0 +1,13 @@
1
+ module EasySwig
2
+
3
+ class ApiGroup < ApiNode
4
+
5
+ attr_accessor :wrap_classes
6
+ attr_accessor :wrap_functions
7
+ attr_accessor :wrap_enums
8
+ attr_accessor :wrap_variables
9
+ attr_accessor :nested_support
10
+ attr_accessor :friend_support
11
+
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ module EasySwig
2
+
3
+ class ApiMethod < ApiFunction
4
+ attr_accessor :match_constructor
5
+ attr_accessor :match_static
6
+
7
+ def constructor?
8
+ @target_name==parent.target_name
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,104 @@
1
+ module EasySwig
2
+
3
+ class ApiNamespace < ApiNode
4
+
5
+ attr_accessor :api_classes
6
+ attr_accessor :api_variables
7
+ attr_accessor :api_functions
8
+ attr_accessor :api_enums
9
+ attr_accessor :ignored_functions
10
+ attr_accessor :ignored_variables
11
+ attr_accessor :ignored_classes
12
+ attr_accessor :ignored_enums
13
+ attr_accessor :wrap_classes
14
+ attr_accessor :wrap_functions
15
+ attr_accessor :wrap_enums
16
+ attr_accessor :wrap_variables
17
+ attr_accessor :nested_support
18
+ attr_accessor :friend_support
19
+
20
+ def initialize(hash)
21
+ super(hash)
22
+ @api_classes=[]
23
+ @api_variables=[]
24
+ @api_functions=[]
25
+ @api_enums=[]
26
+ @ignored_functions=[]
27
+ @ignored_variables=[]
28
+ @ignored_classes=[]
29
+ @ignored_enums=[]
30
+ end
31
+
32
+ def api_nodes
33
+ return [self] if @api_classes.empty? # Only if there are no classes in this namespace
34
+ @api_classes
35
+ end
36
+
37
+ def assoc_with_node(ns)
38
+ super(ns)
39
+ all_functions = ns.functions
40
+ all_functions.reject!{ |f| f.basename == f.basename.upcase }
41
+ all_classes = ns.classes + ns.structs
42
+ all_enums = ns.enums
43
+ all_variables = ns.variables
44
+ ign_functions = @ignored_functions.map{|i| i.basename}
45
+ ign_classes = @ignored_classes.map{|i| i.basename}
46
+ ign_enums = @ignored_enums.map{|i| i.basename}
47
+ ign_variables = @ignored_variables.map{|i| i.basename}
48
+
49
+ if @wrap_functions
50
+ all_functions.each { |f|
51
+ if not ign_functions.include?(f.basename)
52
+ hash = {'features' => @features, 'node_type' => 'function', 'parent' => self, 'basename' => f.basename }
53
+ @api_functions << ApiFunction.new(hash).assoc_with_node(f)
54
+ end
55
+ }
56
+ else
57
+ assoc_functions(all_functions, @api_functions, @ignored_functions)
58
+ end
59
+
60
+ if @wrap_classes
61
+ all_classes.each { |c|
62
+ if not ign_classes.include?(c.basename)
63
+ hash = {'wrap_methods' => 'x',
64
+ 'wrap_attributes' => 'x',
65
+ 'wrap_enums' => 'x',
66
+ 'wrap_innerclasses' => 'x',
67
+ 'node_type' => 'class',
68
+ 'nested_support' => @nested_support,
69
+ 'friend_support' => @friend_support,
70
+ 'features' => @features,
71
+ 'parent' => self,
72
+ 'basename' => c.basename }
73
+ @api_classes << ApiClass.new(hash).assoc_with_node(c)
74
+ end
75
+ }
76
+ else
77
+ assoc_members(all_classes, @api_classes, @ignored_classes)
78
+ end
79
+
80
+ if @wrap_variables
81
+ all_variables.each { |v|
82
+ if not ign_variables.include?(v.basename)
83
+ hash = {'features' => @features, 'node_type' => 'variable', 'parent' => self, 'basename' => v.basename}
84
+ @api_variables << ApiVariable.new(hash).assoc_with_node(v)
85
+ end
86
+ }
87
+ else
88
+ assoc_members(all_variables, @api_variables, @ignored_variables)
89
+ end
90
+ if @wrap_enums
91
+ all_enums.each { |e|
92
+ if not ign_enums.include?(e.basename)
93
+ hash = {'features' => @features, 'node_type' => 'enum', 'parent' => self, 'basename' => e.basename }
94
+ @api_enums << ApiEnum.new(hash).assoc_with_node(e)
95
+ end
96
+ }
97
+ else
98
+ assoc_members(all_enums, @api_enums, @ignored_enums)
99
+ end
100
+ self
101
+ end
102
+
103
+ end
104
+ end