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