easy-swig 1.0

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.
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