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,124 @@
1
+ module EasySwig
2
+
3
+ class Generator
4
+ include Util
5
+
6
+ attr_accessor :namespace_generator
7
+ attr_accessor :class_generator
8
+ attr_accessor :hfile_generator
9
+
10
+ def self.create_instance(hfiles, api_namespace, config, log)
11
+ ret = case config.lang
12
+ when 'csharp'
13
+ EasySwig::Csharp::CsharpGenerator.new(hfiles, api_namespace, config, log)
14
+ when 'java'
15
+ EasySwig::Java::JavaGenerator.new(hfiles, api_namespace, config, log)
16
+ else
17
+ EasySwig::Generator.new(hfiles, api_namespace, config, log)
18
+ end
19
+ ret
20
+ end
21
+
22
+ def initialize(hfiles, api_namespace, config, log)
23
+ @config = config
24
+ @gen_dir = @config.generate_dir + '/' + @config.lang
25
+ @log = log
26
+ @hfiles = hfiles
27
+ @api_namespace = api_namespace
28
+ @target_name = api_namespace.target_name
29
+ @native_name = api_namespace.basename
30
+ @native_gen_dir = "#{@gen_dir}/#{@native_name}"
31
+ @all_types = {}
32
+ @all_innerclasses = {}
33
+ @all_friends = {}
34
+
35
+ if Dir.exists?(@native_gen_dir)
36
+ FileUtils.rm_r @native_gen_dir
37
+ end
38
+ FileUtils.mkdir_p @native_gen_dir
39
+ @log.info { "Created Generator directory for #{@native_name} in #{@native_gen_dir}" }
40
+
41
+ init_generators
42
+ end
43
+
44
+ def generate
45
+ @log.info { 'Generating interface files...' }
46
+
47
+ namespace_file = <<-IFILE.escape_heredoc
48
+ %module #{@target_name}
49
+
50
+ %include "standard_header.i"
51
+ %include "#{@config.custom_file}"
52
+
53
+ IFILE
54
+ namespace_file << @namespace_generator.new(@api_namespace).generate()
55
+
56
+ @hfiles.each { |hfile|
57
+ generate_ifile(hfile)
58
+ ifile = to_ifile(hfile.name)
59
+ namespace_file << %Q{%include "#{@native_name}/#{ifile}"\n}
60
+ }
61
+ write_file("#{@gen_dir}/#{namespace_filename(@native_name)}.i", namespace_file)
62
+ @log.info { "Interface file #{@gen_dir}/#{namespace_filename(@native_name)}.i created" }
63
+ namespace_filename(@native_name)
64
+ end
65
+
66
+ protected
67
+
68
+ def init_generators
69
+ @class_generator = EasySwig::ClassGenerator
70
+ @namespace_generator = EasySwig::NamespaceGenerator
71
+ @hfile_generator = EasySwig::HFileGenerator
72
+ end
73
+
74
+ def to_ifile(filename)
75
+ extension = File.extname(filename)
76
+ filename.gsub(extension, '.i')
77
+ end
78
+
79
+ def generate_ifile(hfile)
80
+ swig_file = <<-IFILE.escape_heredoc
81
+ %module #{@target_name}
82
+
83
+ %include "standard_header.i"
84
+ %include "custom_config.i"
85
+
86
+ // Include headers:
87
+ %{
88
+ #include "#{hfile.name}"
89
+ %}
90
+
91
+ // Rename and ignore members:
92
+
93
+ IFILE
94
+
95
+ @log.debug { "Generating renames for #{hfile.name} ..." }
96
+
97
+ swig_file << @hfile_generator.new(hfile).generate
98
+
99
+ class_generators = []
100
+
101
+ hfile.api_classes.each { |api_cls|
102
+ class_gen_instance = @class_generator.new(api_cls, @all_types, @all_innerclasses, @all_friends)
103
+ class_generators << class_gen_instance
104
+ swig_file << class_gen_instance.process_types
105
+ swig_file << class_gen_instance.gen_friends
106
+ swig_file << class_gen_instance.gen_methods
107
+ swig_file << class_gen_instance.gen_attributes
108
+ swig_file << class_gen_instance.gen_enums
109
+ swig_file << class_gen_instance.gen_innerclasses
110
+ swig_file << class_gen_instance.gen_type_templates
111
+ }
112
+ swig_file << %Q{%include "#{hfile.name}"}
113
+
114
+ ifile = to_ifile(hfile.name)
115
+ write_file(%Q{#{@native_gen_dir}/#{ifile}}, swig_file)
116
+ @log.info { "Interface file #{@native_name}/#{ifile} created" }
117
+ end
118
+
119
+ def namespace_filename native_name
120
+ "ns_#{native_name}"
121
+ end
122
+
123
+ end
124
+ end
@@ -0,0 +1,135 @@
1
+ module EasySwig
2
+ module GeneratorUtil
3
+ include Util
4
+ include Query
5
+
6
+ def nested_workaround(innerclass) # Innerclasses
7
+ return '' if innerclass.abstract?
8
+ return '' if no_public_constructors?(innerclass) || no_public_destructors?(innerclass)
9
+ return '' if @all_innerclasses[innerclass.target_name]
10
+
11
+ namespace = @api_class.parent
12
+ siblings = namespace.classes + namespace.structs
13
+ return '' if siblings.any?{ |c| c.basename == innerclass.basename }
14
+ swig_file = ''
15
+ own_typedefs = {}
16
+
17
+ swig_file << "namespace #{namespace.name} {\n"
18
+ swig_file << "\tclass #{innerclass.target_name} {\n"
19
+ swig_file << "\t\tpublic:\n"
20
+ innerclass.api_attributes.each { |attr|
21
+ typename = attr.type.name
22
+ if is_template?(typename)
23
+ typename = typename.gsub('>::type', '>') # TODO only for Ogre
24
+ end
25
+ swig_file << "\t\t#{attr.static.to_s} #{typename} #{attr.basename};\n"
26
+ }
27
+ innerclass.api_methods.each { |meth|
28
+ broken = false
29
+ type_typename = meth.type.name
30
+ if is_template?(type_typename)
31
+ type_typename = type_typename.gsub('>::type', '>') # TODO only for Ogre
32
+ end
33
+ param_typenames = ""
34
+ n = 1
35
+ meth.params.map{ |p|
36
+ typename = p.type.name
37
+ if is_template?(typename)
38
+ typename = typename.gsub('>::type', '>') # TODO only for Ogre
39
+ end
40
+ param_typenames << " #{typename} arg#{n},"
41
+ n += 1
42
+ }
43
+ next if broken
44
+ swig_file << "\t\t#{meth.static.to_s} #{type_typename} #{meth.basename}(#{param_typenames.chop});\n"
45
+ }
46
+ innerclass.api_enums.each { |enum|
47
+ swig_file << enum_snippet(enum)
48
+ }
49
+ @all_innerclasses[innerclass.target_name] = 1
50
+ swig_file << "\t};\n}\n"
51
+ swig_file << "%rename(#{innerclass.target_name}) #{innerclass.basename};\n"
52
+ swig_file << "%nestedworkaround #{@api_class.name}::#{innerclass.basename};\n"
53
+ swig_file << "%{\nnamespace #{namespace.name} {\n"
54
+ swig_file << "\ttypedef #{@api_class.name}::#{innerclass.basename} #{innerclass.target_name};\n"
55
+ swig_file << "}\n%}\n"
56
+ swig_file
57
+ end
58
+
59
+ def process_type(type, klass)
60
+ new_type = type.dup
61
+ new_type.name = type.escaped_name
62
+ new_typename = _process_type(new_type, klass)
63
+ type.name = type.name.gsub(type.escaped_name, new_typename)
64
+ end
65
+
66
+ def _process_type(type, klass)
67
+ raise ArgumentError if type.nil?
68
+ cached_type = @types[type.escaped_name]
69
+ return cached_type unless cached_type.nil?
70
+ # Don't instantiate templates for functions with type params
71
+ return nil if is_template_param?(type.escaped_name, klass)
72
+
73
+ typename = type.escaped_name.dup
74
+ if is_template?(typename)
75
+ nested_typenames_for(typename).each{ |t|
76
+ nested_type = Doxyparser::Type.new({ name: t, dir: ""})
77
+ typename.gsub!(t, _process_type(nested_type, klass))
78
+ }
79
+ expanded_typename = typename
80
+ else
81
+ lookedup = lookup_typename(type, klass)
82
+ raise ArgumentError if lookedup.nil?
83
+ if is_template?(lookedup) # Repeat recursively
84
+ lookedup_escaped = escape_const_ref_ptr(lookedup)
85
+ lookedup_type = Doxyparser::Type.new({ name: lookedup_escaped, dir: ""})
86
+ expanded_typename = lookedup.gsub!(lookedup_escaped, _process_type(lookedup_type, klass))
87
+ else
88
+ expanded_typename = lookedup
89
+ end
90
+ end
91
+ escaped_typename = escape_const_ref_ptr(expanded_typename)
92
+ unless @all_types.has_key?(escaped_typename)
93
+ @all_types[escaped_typename] = 1
94
+ @types[type.name] = escaped_typename
95
+ end
96
+ return expanded_typename
97
+ end
98
+
99
+ def template_snippet(typename, expanded)
100
+ swig_file = ''
101
+ expanded = expanded.gsub('>::type', '>') # TODO only for Ogre
102
+ if is_template?(expanded)
103
+ typename = name_for_template(expanded) if is_template?(typename) # TODO reconsider
104
+ swig_file << "%template(#{typename}) #{expanded};\n"
105
+ end
106
+ swig_file
107
+ end
108
+
109
+ def enum_snippet(enum)
110
+ snippet = "\n\t\tenum #{enum.basename} {"
111
+ aux = ''
112
+ enum.values.each { |v|
113
+ aux << "\n\t\t\t#{v.basename}"
114
+ if v.initializer
115
+ aux << " = #{v.initializer},"
116
+ else
117
+ aux << ","
118
+ end
119
+ }
120
+ aux.chomp! ','
121
+ snippet << aux
122
+ snippet << "\n\t\t};\n"
123
+ snippet
124
+ end
125
+
126
+ def ignore_enum_values_snippet(enum)
127
+ snippet = ""
128
+ enum.values.each { |v|
129
+ snippet << "%rename($ignore) #{enum.parent.name}::#{v.basename};\n"
130
+ }
131
+ snippet
132
+ end
133
+
134
+ end
135
+ end
@@ -0,0 +1,56 @@
1
+ module EasySwig
2
+ class HFileGenerator
3
+ include GeneratorUtil
4
+
5
+ def initialize(hfile)
6
+ @hfile = hfile
7
+ end
8
+
9
+ def generate
10
+ swig_file = generate_functions
11
+ swig_file << generate_variables
12
+ swig_file << generate_enums
13
+ swig_file << generate_classes
14
+ end
15
+
16
+ def generate_functions
17
+ swig_file = ''
18
+ @hfile.ignored_functions.each { |func|
19
+ swig_file << %Q{%rename("$ignore") #{func.basename};} + "\n"
20
+ }
21
+ swig_file
22
+ end
23
+
24
+ def generate_variables
25
+ swig_file = ''
26
+ @hfile.ignored_variables.each { |var|
27
+ swig_file << %Q{%rename("$ignore") #{var.basename};} + "\n"
28
+ }
29
+ swig_file
30
+ end
31
+
32
+ def generate_enums
33
+ swig_file = ''
34
+ @hfile.api_enums.each { |enum|
35
+ if anonymous_enum?(enum) # @Anonymous Enums
36
+ swig_file << enum_snippet(enum)
37
+ enum.values.each { |v|
38
+ swig_file << "%rename($ignore) ::#{v.basename};\n"
39
+ }
40
+ end
41
+ }
42
+ @hfile.ignored_enums.each { |enum|
43
+ swig_file << %Q{%rename("$ignore") #{enum.basename};} + "\n"
44
+ }
45
+ swig_file
46
+ end
47
+
48
+ def generate_classes
49
+ swig_file = ''
50
+ @hfile.ignored_classes.each { |cls|
51
+ swig_file << %Q{%rename("$ignore") #{cls.name};} + "\n"
52
+ }
53
+ swig_file
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,58 @@
1
+ module EasySwig
2
+
3
+ class NamespaceGenerator
4
+ include GeneratorUtil
5
+
6
+ def initialize(api_ns)
7
+ @api_ns = api_ns
8
+ end
9
+
10
+ def generate
11
+ swig_file = generate_functions
12
+ swig_file << generate_variables
13
+ swig_file << generate_enums
14
+ end
15
+
16
+ protected
17
+
18
+ def generate_functions
19
+ swig_file = ''
20
+ @api_ns.ignored_functions.each { |m|
21
+ swig_file << %Q{%rename("$ignore") #{m.name};} + "\n"
22
+ }
23
+ @api_ns.api_functions.each { |m|
24
+ swig_file << %Q{%rename(#{m.target_name}) #{m.name};} + "\n"
25
+ }
26
+ swig_file
27
+ end
28
+
29
+ def generate_variables
30
+ swig_file = ''
31
+ @api_ns.ignored_variables.each { |m|
32
+ swig_file << %Q{%rename("$ignore") #{m.name};} + "\n"
33
+ }
34
+ @api_ns.api_variables.each { |m|
35
+ swig_file << %Q{%rename(#{m.target_name}) #{m.name};} + "\n"
36
+ }
37
+ swig_file
38
+ end
39
+
40
+ def generate_enums
41
+ swig_file = ''
42
+ @api_ns.ignored_enums.each { |m|
43
+ swig_file << %Q{%rename("$ignore") #{m.name};} + "\n"
44
+ }
45
+ @api_ns.api_enums.each { |enum|
46
+ if anonymous_enum?(enum) # @Anonymous Enums
47
+ swig_file << "\nnamespace #{@api_ns.name} {\n"
48
+ swig_file << enum_snippet(enum)
49
+ swig_file << "\n}\n"
50
+ swig_file << ignore_enum_values_snippet(enum)
51
+ else
52
+ swig_file << %Q{%rename(#{enum.target_name}) #{enum.name};} + "\n"
53
+ end
54
+ }
55
+ swig_file
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,13 @@
1
+ module EasySwig
2
+
3
+ class Properties
4
+ attr_accessor :class_name
5
+ attr_accessor :getter
6
+ attr_accessor :setter
7
+ attr_accessor :attribute
8
+ attr_accessor :type_setter
9
+ attr_accessor :type_getter
10
+ attr_accessor :ref_type
11
+ attr_accessor :static
12
+ end
13
+ end
@@ -0,0 +1,127 @@
1
+ module EasySwig
2
+ module Readers
3
+ class CsvParser
4
+
5
+ attr_accessor :col_headers
6
+ attr_accessor :table
7
+
8
+ def initialize csv_file, lang
9
+ @lang = lang
10
+ @csv_file=csv_file
11
+ @col_headers = {}
12
+ @table = []
13
+ parse
14
+ end
15
+
16
+ def parse
17
+ File.open(@csv_file) { |f|
18
+ aux = f.readline
19
+ var = aux.chomp.split(%r{[,\t]})
20
+ until var[0]==nil || var[0].empty?
21
+ @col_headers[var[0]] = var[1..-1]
22
+ var = f.readline.chomp.split(%r{[,\t]})
23
+ end
24
+ until f.eof?
25
+ row = f.readline.chomp.split(%r{[,\t]})
26
+ row.each { |r| r.strip! }
27
+ rowtype=row[0]
28
+ if rowtype.nil? || rowtype.empty?
29
+ next
30
+ end
31
+ row = @col_headers[rowtype].zip(row[1..-1]).flatten
32
+ lang_features = EasySwig::Features.create_instance @lang
33
+ line = Hash['features', lang_features, 'node_type', rowtype, *row].delete_if { |k, v| k=="" || v.nil? || v.empty? }
34
+ @table << line
35
+ end
36
+ }
37
+ end
38
+
39
+ # Generate namespace object representations from an input CSV file
40
+ def namespaces_from_csv
41
+ obj=nil
42
+ namespaces=[]
43
+ last_ns=nil
44
+ last_class=nil
45
+
46
+ @table.each { |type|
47
+ ignore = type['ignore']
48
+ obj=nil
49
+ case type['node_type']
50
+ when 'namespace'
51
+ obj=ApiNamespace.new(type)
52
+ last_ns=obj
53
+ namespaces << obj
54
+ when 'class'
55
+ type['parent'] = last_ns
56
+ obj=ApiClass.new(type)
57
+ last_class=obj
58
+ if ignore
59
+ last_ns.ignored_classes << obj
60
+ else
61
+ last_ns.api_classes << obj
62
+ end
63
+ when 'enum'
64
+ type['parent'] = last_ns
65
+ obj=ApiEnum.new(type)
66
+ if ignore
67
+ last_ns.ignored_enums << obj
68
+ else
69
+ last_ns.api_enums << obj
70
+ end
71
+ when 'function'
72
+ type['parent'] = last_ns
73
+ obj=ApiFunction.new(type)
74
+ if ignore
75
+ last_ns.ignored_functions << obj
76
+ else
77
+ last_ns.api_functions << obj
78
+ end
79
+ when 'variable'
80
+ type['parent'] = last_ns
81
+ obj=ApiVariable.new(type)
82
+ if ignore
83
+ last_ns.ignored_variables << obj
84
+ else
85
+ last_ns.api_variables << obj
86
+ end
87
+ when 'method'
88
+ type['parent'] = last_class
89
+ obj=ApiMethod.new(type)
90
+ if ignore
91
+ last_class.ignored_methods << obj
92
+ else
93
+ last_class.api_methods << obj
94
+ end
95
+ when 'innerclass'
96
+ type['parent'] = last_class
97
+ obj=ApiClass.new(type)
98
+ if ignore
99
+ last_class.ignored_innerclasses << obj
100
+ else
101
+ last_class.api_innerclasses << obj
102
+ end
103
+ when 'innerenum'
104
+ type['parent'] = last_class
105
+ obj=ApiEnum.new(type)
106
+ if ignore
107
+ last_class.ignored_enums << obj
108
+ else
109
+ last_class.api_enums << obj
110
+ end
111
+ when 'attribute'
112
+ type['parent'] = last_class
113
+ obj=ApiAttribute.new(type)
114
+ if ignore
115
+ last_class.ignored_attributes << obj
116
+ else
117
+ last_class.api_attributes << obj
118
+ end
119
+ else
120
+ raise "Malformed CSV Input: Node type " + type["node_type"] + " does not exist"
121
+ end
122
+ }
123
+ namespaces
124
+ end
125
+ end
126
+ end
127
+ end