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
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env ruby
2
+ require 'optparse'
3
+
4
+ require_relative '../lib/easy-swig'
5
+
6
+ options = {}
7
+
8
+ opt_parser = OptionParser.new do |opt|
9
+ opt.banner = "Welcome to Easy Swig - SWIG made easy!!"
10
+ opt.banner = "Usage: easy-swig COMMAND [OPTIONS]"
11
+ opt.separator ""
12
+ opt.separator "Commands (Use given options to override defaults)"
13
+ opt.separator " doxygen: Parses header files (.h) and generates XMLs using Doxygen. Tune with --input, --doxy, --name, --html"
14
+ opt.separator " generate: Generates SWIG Interface Files from XMLs. Tune with --csv, --doxy, --gen, --name, --lang, --custom,"
15
+ opt.separator " swig: Runs SWIG against the specified interface files. Tune with --target, --input, --output, --include, --lang"
16
+ opt.separator ""
17
+ opt.separator "Options"
18
+
19
+ opt.on("-d", "--default DIRECTORY", "Uses the given directory for default values for all needed options") do |dir|
20
+ options[:default] = dir
21
+ end
22
+
23
+ opt.on("-n", "--name MODULE_NAME", "Name of the module. Used to name generated resources") do |name|
24
+ options[:mod_name] = name
25
+ end
26
+
27
+ opt.on("-c", "--csv FILE", "CSV configuration file input") do |file|
28
+ options[:csvfile] = file
29
+ end
30
+
31
+ opt.on("-i", "--input DIRECTORY", "Directory for your project's header files") do |dir|
32
+ options[:hdir] = dir
33
+ end
34
+
35
+ opt.on("-I", "--include DIRECTORY", "Directory for additional header files not part of your project. Example: /usr/include") do |dir|
36
+ options[:incdir] ||= []
37
+ options[:incdir] << dir
38
+ end
39
+
40
+ opt.on("-x", "--doxy DIRECTORY", "Directory generated by Doxygen (which include a /xml subdirectory for all XML files") do |dir|
41
+ options[:xdir] = dir
42
+ end
43
+
44
+ opt.on("-m", "--html", "Instructs Doxygen to also generate HTML documentation for the header files") do
45
+ options[:html] = true
46
+ end
47
+
48
+ opt.on("-g", "--gen DIRECTORY", "Output directory for Easy-Swig generated .i file(s) aka. 'target'") do |dir|
49
+ options[:gdir] = dir
50
+ end
51
+
52
+ opt.on("-o", "--output DIRECTORY", "Output directory for SWIG generated files") do |dir|
53
+ options[:odir] = dir
54
+ end
55
+
56
+ opt.on("-l", "--lang LANGUAGE", "Target language for SWIG") do |lang|
57
+ options[:lang] = lang
58
+ end
59
+
60
+ opt.on("-t", "--target PATH", "Target .i file as input for SWIG. For directories, SWIG will be invoked once for every .i file") do |path|
61
+ options[:target] = path
62
+ end
63
+
64
+ opt.on("-u", "--custom FILE", "Custom .i file to be appended at the beginning of the generated .i target. Used for advanced configuration") do |file|
65
+ options[:custom] = file
66
+ end
67
+
68
+ opt.on("-s", "--no_stl", "Disable support for the C++ Standard Template Library STL") do |file|
69
+ options[:no_stl] = true
70
+ end
71
+
72
+ opt.on("-h", "--help", "Help") do
73
+ puts opt_parser
74
+ end
75
+
76
+ opt.separator ""
77
+ opt.separator "Default option values:"
78
+ opt.separator ""
79
+ opt.separator "-d (--default DIRECTORY) Defaults to the current working directory"
80
+ opt.separator "-n (--name MODULE_NAME) Defaults to the name of the default directory"
81
+ opt.separator "-c (--csv FILE) Defaults to: path/to/default/dir/api.csv"
82
+ opt.separator "-i (--input DIRECTORY) Defaults to: path/to/default/dir/include"
83
+ opt.separator "-I (--include DIRECTORY) Defaults to linux include path: usr/include"
84
+ opt.separator "-x (--doxy DIRECTORY) Defaults to: path/to/default/dir/easy-swig/doxygen"
85
+ opt.separator "-m (--html) Defaults to false"
86
+ opt.separator "-s (--no_stl) Defaults to false"
87
+ opt.separator "-g (--gen DIRECTORY) Defaults to path/to/default/dir/easy-swig/generate"
88
+ opt.separator "-o (--output DIRECTORY) Defaults to : path/to/default/dir/easy-swig/swig"
89
+ opt.separator "-l (--lang LANGUAGE) Defaults to C# (csharp)"
90
+ opt.separator "-t (--target PATH) Defaults to path/to/default/dir/easy-swig/generate/<MODULE_NAME>.i"
91
+ opt.separator "-u (--custom FILE) Defaults to path/to/default/dir/custom_config.i"
92
+
93
+ end
94
+
95
+ opt_parser.parse!
96
+
97
+ config = ::EasySwig::Config.new
98
+
99
+ if !options[:default].nil? && Dir.exists?(options[:default])
100
+ puts 'Default directory: ' + options[:default]
101
+ config.default_dir = options[:default]
102
+ else
103
+ puts 'No valid default directory was provided: Use current working directory as default? (y/n)'
104
+ $stdout.flush
105
+ answer = $stdin.gets
106
+ exit! unless answer =~ /^[yY]/
107
+ end
108
+
109
+ config.module_name = options[:mod_name]
110
+ config.doxy_dir = options[:xdir]
111
+ config.headers_dir = options[:hdir]
112
+ config.csv_file = options[:csvfile]
113
+ config.output_dir = options[:odir]
114
+ config.generate_dir = options[:gdir]
115
+ config.html = options[:html]
116
+ config.lang = options[:lang]
117
+ config.stl_support = options[:no_stl].nil? ? 'YES' : nil
118
+
119
+ options[:custom] ||= "custom_config.i"
120
+
121
+ if File.exists?(options[:custom])
122
+ puts 'Custom configuration file: ' + options[:custom]
123
+ config.custom_file = options[:custom]
124
+ else
125
+ config.custom_file = nil
126
+ puts 'No custom configuration file found'
127
+ end
128
+
129
+ config.target_file = options[:target]
130
+ config.includes_dir = options[:incdir]
131
+
132
+ config.set_defaults
133
+
134
+ case ARGV[0]
135
+ when "doxygen"
136
+ EasySwig::doxygen(config)
137
+ when "generate"
138
+ EasySwig::generate(config)
139
+ when "swig"
140
+ EasySwig::swig(config)
141
+ else
142
+ puts opt_parser
143
+ end
data/lib/easy-swig.rb ADDED
@@ -0,0 +1,71 @@
1
+ require 'rubygems'
2
+ require '/home/david/workspace/ruby-doxygen-parser/lib/doxyparser'
3
+ require 'fileutils'
4
+ require 'logger'
5
+
6
+ require_relative 'config'
7
+ require_relative 'util/utilities'
8
+ require_relative 'util/print'
9
+ require_relative 'util/logger'
10
+ require_relative 'util/query'
11
+ require_relative 'generators/properties'
12
+ require_relative 'generators/generator_util'
13
+ require_relative 'generators/class_generator'
14
+ require_relative 'generators/namespace_generator'
15
+ require_relative 'generators/hfile_generator'
16
+ require_relative 'generators/generator'
17
+ require_relative 'features'
18
+ require_relative 'apinodes/api_node'
19
+ require_relative 'apinodes/api_namespace'
20
+ require_relative 'apinodes/api_function'
21
+ require_relative 'apinodes/api_class'
22
+ require_relative 'apinodes/api_method'
23
+ require_relative 'apinodes/api_group'
24
+ require_relative 'apinodes/inc_file'
25
+ require_relative 'apinodes/api_variable'
26
+ require_relative 'apinodes/api_attribute'
27
+ require_relative 'apinodes/api_enum'
28
+
29
+ require_relative 'csharp/generators/csharp_generator'
30
+ require_relative 'csharp/generators/csharp_namespace_generator'
31
+ require_relative 'csharp/generators/csharp_class_generator'
32
+ require_relative 'csharp/csharp_features'
33
+
34
+ require_relative 'tasks/swig_task'
35
+
36
+ require_relative 'tasks/doxygen_task'
37
+ require_relative 'readers/csv_parser'
38
+ require_relative 'tasks/hfiles_manager'
39
+ require_relative 'tasks/generate_task'
40
+
41
+ module EasySwig
42
+ class << self
43
+
44
+ # Parses header files (.h) and generates intermediate XML representation using Doxygen (Doxyparser)
45
+ # For more information consult Doxyparser documentation {Doxyparser::gen_xml_docs}
46
+ # @param [Config] config optional configuration for personalized settings. If nothing given, {Config} defaults used
47
+ def doxygen(config = EasySwig::Config.new)
48
+ task = DoxygenTask.new(config)
49
+ task.generate
50
+ task.dispose
51
+ end
52
+
53
+ # Generates SWIG Interface Files (.i) from Doxygen intermediate XML representation.
54
+ # This process is configured using a specially crafted CSV file (see documentation),
55
+ # A subdirectory for every found namespace is created and here are saved the generated .i swig files
56
+ # @param [Config] config optional configuration for personalized settings. If nothing given, {Config} defaults used
57
+ def generate(config = EasySwig::Config.new)
58
+ task = GenerateTask.new(config)
59
+ task.generate
60
+ task.dispose
61
+ end
62
+
63
+ # Runs SWIG against the specified target interface files (.i) And generated wrappers in the output directory (see {Config})
64
+ # @param [Config] config optional configuration for personalized settings. If nothing given, {Config} defaults used
65
+ def swig(config = EasySwig::Config.new)
66
+ task = SwigTask.new(config)
67
+ task.run_swig
68
+ task.dispose
69
+ end
70
+ end
71
+ end
data/lib/features.rb ADDED
@@ -0,0 +1,39 @@
1
+ module EasySwig
2
+
3
+ class Features
4
+
5
+ def Features.create_instance(lang)
6
+ ret = case lang
7
+ when 'java'
8
+ EasySwig::Java::JavaFeatures.new
9
+ when 'csharp'
10
+ EasySwig::Csharp::CsharpFeatures.new
11
+ else
12
+ EasySwig::Csharp::CsharpFeatures.new
13
+ end
14
+ ret
15
+ end
16
+
17
+ def empty?
18
+ false
19
+ end
20
+
21
+ def to_s
22
+ ""
23
+ end
24
+
25
+ def to_str
26
+ to_s
27
+ end
28
+
29
+ def infer_native_name(node)
30
+ node.basename ||= node.target_name.gsub(".", "::").gsub(/_([a-z])/i) { |match|
31
+ $1.upcase
32
+ }
33
+ end
34
+
35
+ def infer_target_name(node)
36
+ node.target_name ||= node.basename.gsub("::", ".")
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,209 @@
1
+ module EasySwig
2
+
3
+ class ClassGenerator
4
+ include GeneratorUtil
5
+
6
+ def initialize(api_cls, all_types, all_innerclasses, all_friends)
7
+ @api_class = api_cls
8
+ @all_types = all_types
9
+ @all_innerclasses = all_innerclasses
10
+ @all_friends = all_friends
11
+ @types = {}
12
+ end
13
+
14
+ def process_types
15
+ swig_file = ''
16
+ swig_file << process_types_for(@api_class)
17
+ @api_class.api_innerclasses.each { |c|
18
+ swig_file << process_types_for(c)
19
+ }
20
+ swig_file
21
+ end
22
+
23
+ def process_types_for(klass)
24
+ swig_file = ''
25
+ klass.parent_types(:all).each { |p_type|
26
+ begin
27
+ process_type(p_type, klass)
28
+ rescue ArgumentError; end
29
+ }
30
+ remove_methods = []
31
+ klass.api_methods.each { |m|
32
+ begin
33
+ process_type(m.type, klass) unless m.constructor? || m.destructor?
34
+ m.params.each { |p|
35
+ process_type(p.type, klass)
36
+ }
37
+ rescue ArgumentError
38
+ remove_methods << m
39
+ swig_file << %Q{%rename("$ignore") #{klass.name}::#{m.basename};} + "\n"
40
+ end
41
+ }
42
+ klass.api_methods.reject! { |m| remove_methods.include?(m) }
43
+
44
+ remove_attributes = []
45
+ klass.api_attributes.each { |a|
46
+ begin
47
+ process_type(a.type, klass)
48
+ rescue ArgumentError
49
+ remove_attributes << a
50
+ swig_file << %Q{%rename("$ignore") #{klass.name}::#{a.basename};} + "\n"
51
+ end
52
+ }
53
+ klass.api_attributes.reject! { |a| remove_attributes.include?(a) }
54
+ swig_file
55
+ end
56
+
57
+ def gen_methods
58
+ swig_file = ''
59
+ @api_class.ignored_methods.each { |m|
60
+ swig_file << %Q{%rename("$ignore") #{@api_class.name}::#{m.basename};} + "\n"
61
+ }
62
+ properties = {}
63
+ @api_class.api_methods.each { |m|
64
+ if m.basename=='getTextureAddressingMode'
65
+ puts "Aqui"
66
+ end
67
+ features = m.features
68
+ if features.properties
69
+ getter = m.getter_for
70
+ setter = m.setter_for
71
+ if setter
72
+ properties[m.target_name] ||= EasySwig::Properties.new
73
+ properties[m.target_name].setter = m.basename
74
+ properties[m.target_name].static = m.static
75
+ properties[m.target_name].type_setter ||= m.params[0].type
76
+ elsif getter
77
+ properties[m.target_name] ||= EasySwig::Properties.new
78
+ properties[m.target_name].getter = m.basename
79
+ properties[m.target_name].static = m.static
80
+ properties[m.target_name].type_getter = m.type
81
+ else
82
+ swig_file << %Q{%rename(#{m.target_name}) #{m.name};\n} unless is_operator?(m.basename) || @api_class.is_enum_or_innerclass?(m.target_name)
83
+ next
84
+ end
85
+ properties[m.target_name].class_name = @api_class.name
86
+ else
87
+ swig_file << %Q{%rename(#{m.target_name}) #{m.name};\n} unless is_operator?(m.basename) || @api_class.is_enum_or_innerclass?(m.target_name)
88
+ end
89
+ }
90
+ properties.each { |attribute, property|
91
+ next if property.getter.nil?
92
+ next if property.type_setter != property.type_getter
93
+ type = property.type_getter
94
+ case get_reference_type(type, @api_class)
95
+ when :ptr
96
+ macro = 'attribute'
97
+ escaped_typename = type.escaped_name.gsub('>::type', '>') # TODO only for Ogre
98
+ when :ref
99
+ macro = 'attribute2'
100
+ next # Ref Properties not supported
101
+ when :str
102
+ macro = 'attributestring'
103
+ escaped_typename = type.escaped_name
104
+ when :prim_ptr
105
+ macro = 'attribute'
106
+ escaped_typename = type.name
107
+ when :val
108
+ macro = 'attributeval'
109
+ next # Properties per Value not supported
110
+ end
111
+ unless macro.nil?
112
+ macro << '_static' if property.static
113
+ swig_file << "%#{macro}(#{property.class_name}, #{escaped_typename}, #{attribute}, #{property.getter}, #{property.setter});\n"
114
+ end
115
+ }
116
+ swig_file
117
+ end
118
+
119
+ def gen_attributes
120
+ swig_file = ''
121
+ @api_class.ignored_attributes.each { |m|
122
+ swig_file << %Q{%rename("$ignore") #{m.name};} + "\n"
123
+ }
124
+ @api_class.api_attributes.each { |a|
125
+ swig_file << %Q{%rename(#{a.target_name}) #{a.name};} + "\n"
126
+ }
127
+ swig_file
128
+ end
129
+
130
+ def gen_enums
131
+ swig_file = ''
132
+ @api_class.ignored_enums.each { |m|
133
+ swig_file << %Q{%rename("$ignore") #{m.name};} + "\n"
134
+ }
135
+ @api_class.api_enums.each { |enum|
136
+ if anonymous_enum?(enum) # @Anonymous Enums
137
+ swig_file << "\nnamespace #{@api_class.parent.name} {"
138
+ swig_file << enum_snippet(enum)
139
+ swig_file << "\t}\n"
140
+ swig_file << ignore_enum_values_snippet(enum)
141
+ else
142
+ swig_file << %Q{%rename(#{enum.target_name}) #{enum.name};} + "\n"
143
+ end
144
+ }
145
+ swig_file
146
+ end
147
+
148
+ def gen_innerclasses
149
+ swig_file = ''
150
+ @api_class.ignored_innerclasses.each { |c|
151
+ swig_file << %Q{%rename("$ignore") #{@api_class.name}::#{c.basename};} + "\n"
152
+ }
153
+ @api_class.api_innerclasses.each { |c|
154
+ if c.nested_support # Innerclasses
155
+ swig_file << nested_workaround(c)
156
+ else
157
+ swig_file << %Q{%rename(#{c.target_name}) #{@api_class.name}::#{c.basename};\n}
158
+ end
159
+ }
160
+ swig_file
161
+ end
162
+
163
+ def gen_parent_templates
164
+ swig_file = "\n"
165
+ @types.invert.each { |expanded, typename|
166
+ swig_file << template_snippet(typename, expanded)
167
+ }
168
+ swig_file
169
+ end
170
+
171
+ def gen_type_templates
172
+ swig_file = "\n"
173
+ @types.invert.each { |expanded, typename|
174
+ swig_file << template_snippet(typename, expanded)
175
+ }
176
+ swig_file
177
+ end
178
+
179
+ def gen_friends
180
+ swig_file = ''
181
+ if @api_class.friend_support
182
+ namespace = @api_class.parent
183
+ friend_functions = @api_class.friends.delete_if{ |f| f.is_class? || f.is_qualified? }
184
+ return '' if friend_functions.empty?
185
+
186
+ swig_file << "namespace #{namespace.name} {\n"
187
+ swig_file << "\tclass #{@api_class.basename};\n"
188
+ friend_functions.each { |f|
189
+ next if @all_friends[f.basename + f.args]
190
+ swig_file << "\t#{f.type.name} #{name_for_friend(f.basename, f.params.size)}#{f.args};\n"
191
+ }
192
+ swig_file << "}\n"
193
+ swig_file << "%{\nnamespace #{namespace.name} {\n"
194
+ swig_file << "\tclass #{@api_class.basename};\n"
195
+ friend_functions.each { |f|
196
+ signature = f.basename + f.args
197
+ next if @all_friends[signature]
198
+ @all_friends[signature] = 1
199
+ param_names = f.params.map { |p| p.declname }.join(', ') # TODO What happens when declname is empty
200
+ friend_name = name_for_friend(f.basename, f.params.size)
201
+ swig_file << "\t#{f.type.name} #{friend_name}#{f.args}{\n"
202
+ swig_file << "\t\treturn #{f.basename}(#{param_names});\n\t}\n"
203
+ }
204
+ swig_file << "}\n%}\n"
205
+ end
206
+ swig_file
207
+ end
208
+ end
209
+ end