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,109 @@
1
+ module EasySwig
2
+
3
+ class ApiNode
4
+ include Util
5
+
6
+ attr_accessor :basename
7
+ attr_accessor :target_name
8
+ attr_accessor :directory
9
+ attr_accessor :parent
10
+ attr_accessor :header_file
11
+ attr_reader :wrapped_node
12
+ attr_accessor :node_type
13
+ attr_accessor :match
14
+ attr_accessor :features
15
+ attr_accessor :ignore
16
+
17
+ # Redirects calls to lang_settings before completely initialized and to
18
+ # wrapper_node afterwards
19
+ def method_missing sym, *args
20
+ if @wrapped_node.nil?
21
+ @features.send sym, *args
22
+ else
23
+ @wrapped_node.send sym, *args
24
+ end
25
+ end
26
+
27
+ def to_str
28
+ if @fullname==nil
29
+ @fullname=fullname
30
+ end
31
+ @node_type+"::"+@fullname
32
+ end
33
+
34
+ def fullname
35
+ if @parent == nil
36
+ return @basename
37
+ else
38
+ return @parent.fullname+"::"+@basename
39
+ end
40
+ end
41
+
42
+ def initialize(hash)
43
+ hash.each_key { |k|
44
+ send(k.to_s+"=", hash[k]) # TODO features must always come first in the
45
+ # hash
46
+ }
47
+ @basename || @features.infer_native_name(self) unless @target_name.nil?
48
+ self
49
+ end
50
+
51
+ def assoc_with_node(node)
52
+ @wrapped_node = node
53
+ EasySwig::Logger.log %Q{Associating target #{@node_type}: #{@target_name} with native node: #{name}}
54
+ @basename = node.basename
55
+ @target_name || @features.infer_target_name(self)
56
+ self
57
+ end
58
+
59
+ def assoc_functions(all_functions, api_functions, ignored_functions)
60
+ api_mets=[]
61
+ new_mets=[]
62
+ del_mets=[]
63
+ api_functions.each { |f|
64
+ all_found=all_functions.select { |func| func.basename == f.basename }
65
+ if all_found.empty?
66
+ EasySwig::Logger.log("WARNING: Function not found: #{f.to_str}")
67
+ del_mets << f
68
+ next
69
+ end
70
+ if all_found.size > 1
71
+ EasySwig::Logger.log("WARNING: Found several matching functions for #{f.to_str}: "+all_found.map { |func| func.basename+func.args }.join(" -- ")+"All of them will be matched")
72
+ all_found[1..-1].each { |found|
73
+ new_met=f.clone
74
+ new_met.assoc_with_node found
75
+ new_mets << new_met
76
+ }
77
+ end
78
+ f.assoc_with_node all_found[0]
79
+ api_mets.push(*all_found)
80
+ }
81
+ api_functions.reject! { |f| del_mets.include?(f)}
82
+ api_functions.push(*new_mets)
83
+ ignored_functions.push(*(all_functions - api_mets));
84
+ end
85
+
86
+ def assoc_members(all_members, api_members, ignored_members)
87
+ api_mbrs=[]
88
+ del_mbrs=[]
89
+
90
+ api_members.each { |s|
91
+ found=nil
92
+ all_found=all_members.select { |str| str.basename == s.basename }
93
+ if all_found.empty?
94
+ EasySwig::Logger.log("WARNING: Member not found: #{s.to_str}")
95
+ del_mbrs << s
96
+ next
97
+ end
98
+ if all_found.size > 1
99
+ EasySwig::Logger.log("WARNING: Found several matching members for #{s.to_str}: "+all_found.join(" -- ")+" Only the first one will be matched")
100
+ end
101
+ found=all_found[0]
102
+ s.assoc_with_node found
103
+ api_mbrs.push found
104
+ }
105
+ api_members.reject! { |m| del_mbrs.include?(m) }
106
+ ignored_members.push(*(all_members - api_mbrs));
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,12 @@
1
+ module EasySwig
2
+
3
+ class ApiVariable < ApiNode
4
+
5
+ attr_accessor :match_type
6
+
7
+ def api_nodes
8
+ self
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,118 @@
1
+ module EasySwig
2
+
3
+ class IncFile < ApiNode
4
+
5
+ attr_accessor :api_namespaces
6
+ attr_accessor :ignored_namespaces
7
+ attr_accessor :api_classes
8
+ attr_accessor :ignored_classes
9
+ attr_accessor :api_enums
10
+ attr_accessor :ignored_enums
11
+ attr_accessor :api_functions
12
+ attr_accessor :ignored_functions
13
+ attr_accessor :api_variables
14
+ attr_accessor :ignored_variables
15
+ attr_accessor :num_included
16
+ attr_accessor :num_including
17
+ attr_accessor :wrap_functions
18
+ attr_accessor :wrap_variables
19
+ attr_accessor :wrap_enums
20
+
21
+ def initialize(hash)
22
+ super(hash)
23
+ @api_namespaces=[]
24
+ @ignored_namespaces=[]
25
+ @api_classes=[]
26
+ @ignored_classes=[]
27
+ @api_enums=[]
28
+ @ignored_enums=[]
29
+ @api_functions=[]
30
+ @ignored_functions=[]
31
+ @api_variables=[]
32
+ @ignored_variables=[]
33
+ @wrap_functions = true
34
+ @wrap_variables = true
35
+ @wrap_enums = true
36
+ @added_nodes = {}
37
+ end
38
+
39
+ def <=> other
40
+ if @num_including == 0 && other.num_including == 0
41
+ return(@num_included - @num_including) <=> (other.num_included- other.num_including)
42
+ end
43
+ return 1 if @num_including == 0
44
+ return -1 if other.num_including == 0
45
+ return(@num_included - @num_including) <=> (other.num_included- other.num_including)
46
+ end
47
+
48
+ def assoc_inner_node(api_node)
49
+ return if @added_nodes[api_node.wrapped_node]
50
+ case api_node.node_type
51
+ when "namespace"
52
+ @api_namespaces << api_node
53
+ when "function"
54
+ @api_functions << api_node
55
+ when "variable"
56
+ @api_variables << api_node
57
+ when "enum"
58
+ @api_enums << api_node
59
+ when "class"
60
+ @api_classes << api_node
61
+ end
62
+ @added_nodes[api_node.wrapped_node] = 1
63
+ end
64
+
65
+ def assoc_with_node(node)
66
+ super(node)
67
+ @num_included = files_included.size
68
+ @num_including = files_including.size
69
+ all_functions = node.functions
70
+ all_enums = node.enums
71
+ all_variables = node.variables
72
+
73
+ if @wrap_functions
74
+ all_functions.each { |f|
75
+ hash = {'features' => @features, 'node_type' => 'function', 'parent' => self, 'basename' => f.basename }
76
+ @api_functions << ApiFunction.new(hash).assoc_with_node(f)
77
+ }
78
+ end
79
+ if @wrap_variables
80
+ all_variables.each { |v|
81
+ hash = {'features' => @features, 'node_type' => 'variable', 'parent' => self, 'basename' => v.basename}
82
+ @api_variables << ApiVariable.new(hash).assoc_with_node(v)
83
+ }
84
+ end
85
+ if @wrap_enums
86
+ all_enums.each { |e|
87
+ hash = {'features' => @features, 'node_type' => 'enum', 'parent' => self, 'basename' => e.basename }
88
+ @api_enums << ApiEnum.new(hash).assoc_with_node(e)
89
+ }
90
+ end
91
+ self
92
+ end
93
+
94
+ def ignore_inner_nodes
95
+ aux = @api_namespaces.map { |n| n.wrapped_node }
96
+ @ignored_namespaces = namespaces.select { |ns| aux.include?(ns) == false }
97
+ aux = @api_functions.map { |n| n.wrapped_node }
98
+ @ignored_functions = functions.select { |func| aux.include?(func) == false }
99
+ aux = @api_variables.map { |n| n.wrapped_node }
100
+ @ignored_variables = variables.select { |var| aux.include?(var) == false }
101
+ aux = @api_enums.map { |n| n.wrapped_node }
102
+ @ignored_enums = enums.select { |enum| aux.include?(enum) == false }
103
+ aux = @api_classes.map { |n| n.wrapped_node }
104
+ file_classes = classes + structs
105
+ del = []
106
+ file_classes.each { |file_cls|
107
+ @api_classes.each { |api_cls|
108
+ if api_cls.api_innerclasses.map { |n| n.wrapped_node }.include?(file_cls)
109
+ del << file_cls
110
+ end
111
+ }
112
+ }
113
+ @ignored_classes = file_classes.select { |cls| !aux.include?(cls) && !del.include?(cls) }
114
+ @ignored_classes
115
+ end
116
+
117
+ end
118
+ end
data/lib/config.rb ADDED
@@ -0,0 +1,56 @@
1
+ module EasySwig
2
+
3
+ # Generation Settings
4
+ class Config
5
+ # [String] Working directory. Defaults to current working directory
6
+ attr_accessor :default_dir
7
+ # [String] Name of the module. Used to name generated resources. Defaults to the name of the default (working) directory
8
+ attr_accessor :module_name
9
+ # [String] Directory generated by Doxygen (which include a /xml subdirectory for all XML files. Defaults to: path/to/working/dir/easy-swig/doxygen
10
+ attr_accessor :doxy_dir
11
+ # [String] Directory for your project's header files. Defaults to: path/to/working/dir/include
12
+ attr_accessor :headers_dir
13
+ # [String] CSV configuration file input. Defaults to: path/to/working/dir/api.csv
14
+ attr_accessor :csv_file
15
+ # [String] Output directory for SWIG generated files. Defaults to : path/to/working/dir/easy-swig/swig
16
+ attr_accessor :output_dir
17
+ # [String] Output directory for Easy-Swig generated .i file(s) aka. 'target'. Defaults to path/to/working/dir/easy-swig/generate
18
+ attr_accessor :generate_dir
19
+ # [String] Instructs Doxygen to also generate HTML documentation for the header files. Defaults to nil (false)
20
+ attr_accessor :html
21
+ # [String] Target language for SWIG. Defaults to 'csharp' (C#)
22
+ attr_accessor :lang
23
+ # [String] Custom .i file to be appended at the beginning of the generated .i target. Used for advanced configuration
24
+ # Defaults to path/to/working/dir/custom_config.i
25
+ attr_accessor :custom_file
26
+ # [String] Target .i file as input for SWIG. If a directory SWIG will be invoked once for every found .i file
27
+ # Defaults to path/to/working/dir/easy-swig/generate/<MODULE_NAME>.i
28
+ attr_accessor :target_file
29
+ # [Array<String>] System include directories. Defaults to Linux include directory: ["/usr/include"]
30
+ attr_accessor :includes_dir
31
+ # [String] Special support for the Standard Template Library. Values 'YES'/'NO' (defaults to 'YES')
32
+ attr_accessor :stl_support
33
+
34
+ # Creates a new Configutation using the default values for a given directory
35
+ # @param [String] default_dir Working directory
36
+ def initialize(default_dir = nil)
37
+ @default_dir = default_dir
38
+ set_defaults
39
+ end
40
+
41
+ def set_defaults
42
+ @default_dir ||= Dir.getwd
43
+ @module_name ||= File.basename(@default_dir)
44
+ @output_dir ||= "#{@default_dir}/easy-swig/swig"
45
+ @doxy_dir ||= "#{@default_dir}/easy-swig/doxygen"
46
+ @generate_dir ||= "#{@default_dir}/easy-swig/generate"
47
+ @headers_dir ||= "#{@default_dir}/include"
48
+ @csv_file ||= "#{@default_dir}/api.csv"
49
+ @custom_file ||= "#{@default_dir}/custom_config.i"
50
+ @lang ||= "csharp"
51
+ @stl_support ||= 'YES'
52
+ @target_file ||= "#{@default_dir}/easy-swig/generate/#{@lang}/#{@module_name}.i"
53
+ @includes_dir ||= ["/usr/include"]
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,100 @@
1
+ module EasySwig
2
+ module Csharp
3
+
4
+ class CsharpFeatures < EasySwig::Features
5
+
6
+ attr_accessor :properties
7
+ attr_accessor :struct
8
+ attr_accessor :director
9
+ attr_accessor :immutable
10
+ attr_accessor :partial
11
+
12
+ def to_s
13
+ s = @properties ? 'properties': ''
14
+ s << @struct ? 'struct' : ''
15
+ s << @director ? 'director' : ''
16
+ s << @immutable ? 'immutable' : ''
17
+ end
18
+
19
+ def to_str
20
+ to_s
21
+ end
22
+
23
+ def infer_basename(node)
24
+ aux = node.target_name.gsub(/_([a-z])/i) { |match|
25
+ $1.upcase
26
+ }
27
+ case node.node_type
28
+ when 'function', 'attribute', 'variable'
29
+ node.basename = aux.gsub(/^([A-Z])/) { |match|
30
+ $1.downcase
31
+ }
32
+ when 'method'
33
+ if node.constructor?
34
+ node.basename = aux
35
+ else
36
+ node.basename = aux.gsub(/^([A-Z])/) { |match|
37
+ $1.downcase
38
+ }
39
+ end
40
+ when 'class', 'enum'
41
+ node.basename = aux.gsub(/^([a-z])/) { |match|
42
+ $1.upcase
43
+ }
44
+ when 'namespace'
45
+ node.basename = node.target_name.gsub(".", "::")
46
+ else
47
+ super node
48
+ end
49
+
50
+ end
51
+
52
+ def ignore_operator(name)
53
+ ['operator+','operator+=', 'operator-',
54
+ 'operator-=', 'operator*', 'operator*=',
55
+ 'operator/', 'operator/=', 'operator<',
56
+ 'operator<=', 'operator>', 'operator>=',
57
+ 'operator=', 'operator[]', 'operator==',
58
+ 'operator!=', 'operator^', 'operator~',
59
+ 'operator%', 'operator&', 'operator|',
60
+ 'operator&&', 'operator||', 'operator<<',
61
+ 'operator>>'].include?(name)
62
+ end
63
+
64
+ def infer_target_name(node)
65
+ case node.node_type
66
+ when 'method'
67
+ if node.basename.start_with? '~'
68
+ node.target_name = 'Dispose'
69
+ return
70
+ end
71
+ if @properties
72
+ node.target_name ||= node.setter_for
73
+ node.target_name ||= node.getter_for
74
+ if node.target_name
75
+ list = []
76
+ wnode = node.parent.wrapped_node
77
+ list.push(*wnode.methods)
78
+ list.push(*wnode.attributes)
79
+ if list.any? { |m| m.basename.capitalize == node.target_name.capitalize }
80
+ node.target_name.prepend('_')
81
+ end
82
+ end
83
+ end
84
+ node.target_name ||= node.basename
85
+ node.target_name = node.target_name.gsub(/^(_?)([a-z])/) { |match|
86
+ $2.upcase.prepend $1
87
+ }
88
+ when 'function', 'variable', 'attribute'
89
+ node.target_name ||= node.basename.gsub(/^([a-z])/) { |match|
90
+ $1.upcase
91
+ }
92
+ when 'namespace'
93
+ node.target_name ||= node.basename.gsub("::", ".")
94
+ else
95
+ super node
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,14 @@
1
+ module EasySwig
2
+
3
+ class CSharpClassGenerator < ClassGenerator
4
+
5
+ def gen_methods
6
+ swig_file = super
7
+ # Takes the opportunity to also apply partial class typemaps
8
+ if @api_class.features.partial
9
+ swig_file << %Q{\n%typemap(csclassmodifiers) #{@api_class.name} "public partial class"\n}
10
+ end
11
+ swig_file
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,17 @@
1
+ module EasySwig
2
+ module Csharp
3
+
4
+ class CsharpGenerator < EasySwig::Generator
5
+
6
+ def initialize(hfiles, api_namespace, config, log)
7
+ super(hfiles, api_namespace, config, log)
8
+ end
9
+
10
+ def init_generators
11
+ @class_generator = EasySwig::CSharpClassGenerator
12
+ @namespace_generator = EasySwig::CSharpNamespaceGenerator
13
+ @hfile_generator = EasySwig::HFileGenerator
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,33 @@
1
+ module EasySwig
2
+
3
+ class CSharpNamespaceGenerator < NamespaceGenerator
4
+
5
+ protected
6
+
7
+ def generate_functions
8
+ swig_file = ''
9
+ @api_ns.ignored_functions.each { |m|
10
+ swig_file << %Q{%rename("$ignore") #{m.name};} + "\n"
11
+ }
12
+ @api_ns.api_functions.each { |m|
13
+ swig_file << %Q{%rename(#{m.target_name}) #{m.name};} + "\n"
14
+ }
15
+ swig_file
16
+ end
17
+
18
+ def generate_variables
19
+ swig_file = ''
20
+ @api_ns.ignored_variables.each { |m|
21
+ swig_file << %Q{%rename("$ignore") #{m.name};} + "\n"
22
+ }
23
+ @api_ns.api_variables.each { |m|
24
+ if type_is_blacklisted?(m)
25
+ swig_file << %Q{%rename("$ignore") #{m.name};} + "\n"
26
+ else
27
+ swig_file << %Q{%rename(#{m.target_name}) #{m.name};} + "\n"
28
+ end
29
+ }
30
+ swig_file
31
+ end
32
+ end
33
+ end