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