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