easy-swig 1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/MIT_LICENSE +19 -0
- data/README.md +86 -0
- data/lib/apinodes/api_attribute.rb +6 -0
- data/lib/apinodes/api_class.rb +207 -0
- data/lib/apinodes/api_enum.rb +10 -0
- data/lib/apinodes/api_function.rb +13 -0
- data/lib/apinodes/api_group.rb +13 -0
- data/lib/apinodes/api_method.rb +12 -0
- data/lib/apinodes/api_namespace.rb +104 -0
- data/lib/apinodes/api_node.rb +109 -0
- data/lib/apinodes/api_variable.rb +12 -0
- data/lib/apinodes/inc_file.rb +118 -0
- data/lib/config.rb +56 -0
- data/lib/csharp/csharp_features.rb +100 -0
- data/lib/csharp/generators/csharp_class_generator.rb +14 -0
- data/lib/csharp/generators/csharp_generator.rb +17 -0
- data/lib/csharp/generators/csharp_namespace_generator.rb +33 -0
- data/lib/easy-swig-cli.rb +143 -0
- data/lib/easy-swig.rb +71 -0
- data/lib/features.rb +39 -0
- data/lib/generators/class_generator.rb +209 -0
- data/lib/generators/generator.rb +124 -0
- data/lib/generators/generator_util.rb +135 -0
- data/lib/generators/hfile_generator.rb +56 -0
- data/lib/generators/namespace_generator.rb +58 -0
- data/lib/generators/properties.rb +13 -0
- data/lib/readers/csv_parser.rb +127 -0
- data/lib/tasks/doxygen_task.rb +28 -0
- data/lib/tasks/generate_task.rb +85 -0
- data/lib/tasks/hfiles_manager.rb +86 -0
- data/lib/tasks/swig_task.rb +69 -0
- data/lib/util/logger.rb +36 -0
- data/lib/util/print.rb +72 -0
- data/lib/util/query.rb +136 -0
- data/lib/util/utilities.rb +101 -0
- data/spec/accessors/accessors_spec.rb +45 -0
- data/spec/anonymousEnums/anonymous_enums_spec.rb +49 -0
- data/spec/friends/friends_spec.rb +45 -0
- data/spec/innerclasses/innerclasses_spec.rb +48 -0
- data/spec/namespacePrefixes/namespace_prefixes_spec.rb +39 -0
- data/spec/squish/squish_spec.rb +40 -0
- data/spec/subdirectories/subdirectories_spec.rb +41 -0
- data/spec/templates/templates_spec.rb +34 -0
- 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,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
|