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