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,28 @@
1
+ module EasySwig
2
+
3
+ class DoxygenTask
4
+ include EasySwig
5
+
6
+ def initialize config
7
+ @config = config
8
+ if Dir.exists?(@config.doxy_dir)
9
+ FileUtils.rm_r @config.doxy_dir
10
+ end
11
+ FileUtils.mkdir_p @config.doxy_dir
12
+
13
+ @log = EasySwig::Logger.doxy_log(@config.doxy_dir)
14
+ @log.info { "Created Doxygen directory in #{@config.doxy_dir}" }
15
+ end
16
+
17
+ def generate
18
+ @log.info { "Creating Doxygen documentation in directory: #{@config.doxy_dir} ..." }
19
+ headers_dirs = [@config.headers_dir]
20
+ output = Doxyparser::gen_xml_docs(headers_dirs, @config.doxy_dir, true, @config.includes_dir, @config.html, @config.stl_support)
21
+ @log.info { 'Doxygen documentation created at: '+ @config.doxy_dir}
22
+ end
23
+
24
+ def dispose
25
+ @log.close
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,85 @@
1
+ module EasySwig
2
+
3
+ class GenerateTask
4
+ include Util
5
+ include Print
6
+
7
+ def initialize(config)
8
+ @config = config
9
+ @gen_dir = @config.generate_dir+'/'+@config.lang
10
+ @generators = {}
11
+
12
+ FileUtils.rm_r @gen_dir if Dir.exists?(@gen_dir)
13
+ FileUtils.mkdir_p @gen_dir
14
+ FileUtils.cp "#{home_dir}/resources/#{@config.lang}/standard_header.i", "#{@gen_dir}/standard_header.i"
15
+ FileUtils.cp @config.custom_file, "#{@gen_dir}/custom_config.i" if @config.custom_file
16
+ FileUtils.cp "#{home_dir}/resources/swig-patches/attribute.i", "#{@gen_dir}/attribute.i"
17
+
18
+ @log=EasySwig::Logger.gen_log(@gen_dir)
19
+ @log.info { "Created Generator directory for #{@native_name} in #{@node_gen_dir}" }
20
+ end
21
+
22
+ def generate
23
+ module_file = <<-IFILE.escape_heredoc
24
+ %module #{@config.module_name}
25
+
26
+ %include "standard_header.i"
27
+ %include "#{@config.custom_file}"
28
+ %include "attribute.i"
29
+
30
+ IFILE
31
+
32
+ @log.info { "Parsing CSV file #{@csv_file} ..." }
33
+ csv_parser = EasySwig::Readers::CsvParser.new(@config.csv_file, @config.lang)
34
+
35
+ @log.info { "Parsed CSV file. Number of entries: #{csv_parser.table.size}" }
36
+
37
+ @log.info { "Generating Namespaces from CSV file #{@csv_file}..." }
38
+ api_namespaces = csv_parser.namespaces_from_csv
39
+ @log.info { "Namespaces generated in memory: \n\t\tNamespaces: " + (api_namespaces.map{|n| n.basename}.join(', ')) }
40
+ @log.debug { "Namespace contents:\n\t\t\t\t" + print_api_namespaces(api_namespaces) }
41
+
42
+ api_namespaces.each do |api_namespace|
43
+
44
+ @log.info { "Parsing namespace from C++ files..." }
45
+ namespace = Doxyparser::parse_namespace(api_namespace.basename, @config.doxy_dir+'/xml')
46
+ @log.info { "C++ Namespace parsed" }
47
+
48
+ @log.info { "Associating namespace tree (classes, structs, enums...) with the corresponding Doxygen tree..." }
49
+ api_namespace.assoc_with_node(namespace)
50
+ @log.info { "Namespace Tree associated" }
51
+ @log.debug { print_api_namespace(api_namespace) }
52
+
53
+ # If there are no classes in this namespace things get a bit tricky
54
+ conf_doxy_dir = @config.doxy_dir
55
+ if api_namespace.api_classes.empty?
56
+ api_namespace.define_singleton_method(:file) {
57
+ Doxyparser::parse_file(api_namespace.basename + '.h', conf_doxy_dir+"/xml")
58
+ }
59
+ end
60
+
61
+ @log.info { "Associating and sorting header files for all sub-nodes of: #{api_namespace.node_type} #{api_namespace.name}..." }
62
+ hfiles = EasySwig::HFilesManager.assoc_headers(api_namespace)
63
+ @log.info { "Header files associated" }
64
+ hfiles = EasySwig::HFilesManager.sort_headers(hfiles)
65
+ @log.info { "Header files sorted" }
66
+ @log.debug { print_incl_files(hfiles) }
67
+
68
+ native_name = api_namespace.basename # Name of module (C++ namespace)
69
+ target_name = api_namespace.target_name # Name of target namespace
70
+
71
+ @generators[api_namespace] = Generator.create_instance(hfiles, api_namespace, @config, @log)
72
+
73
+ @log.info { "Generating SWIG configuration files..." }
74
+ gen_node_name = @generators[api_namespace].generate
75
+ @log.info { "SWIG configuration files generated" }
76
+ module_file << %Q{%include "#{gen_node_name}.i"\n}
77
+ end
78
+ write_file(%Q{#{@gen_dir}/#{@config.module_name}.i}, module_file)
79
+ end
80
+
81
+ def dispose
82
+ @log.close
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,86 @@
1
+ module EasySwig
2
+ module HFilesManager
3
+
4
+ class << self
5
+
6
+ def assoc_headers(api_node)
7
+
8
+ api_nodes = api_node.api_nodes
9
+ hfile_hash = Hash.new
10
+
11
+ api_nodes.each { |api_n|
12
+ hfile = api_n.file
13
+
14
+ next if hfile.nil?
15
+
16
+ filename = hfile.name
17
+
18
+ if hfile_hash[filename] == nil
19
+ params = {'node_type' => 'file', 'features' => api_n.features }
20
+ hfile_hash[filename] = IncFile.new(params).assoc_with_node(hfile)
21
+ hfile_hash[filename].assoc_inner_node(api_node)
22
+ end
23
+ api_n.header_file = hfile_hash[filename]
24
+ hfile_hash[filename].assoc_inner_node(api_n)
25
+ }
26
+ hfile_hash.each_value { |f|
27
+ f.ignore_inner_nodes
28
+ puts f.name
29
+ }
30
+ hfiles = hfile_hash.values
31
+ hfiles
32
+ end
33
+
34
+ def sort_headers(files)
35
+ sorted = []
36
+ includes = {}
37
+ while true
38
+
39
+ break if files.empty?
40
+
41
+ files_aux = files.map { |f| f.name }
42
+ files = files.sort { |fx,fy| compare(fx, fy, files_aux) }
43
+ includes = {}
44
+ while true
45
+ f = files.shift
46
+ break if f.nil?
47
+
48
+ incs = f.files_included.map { |x| x.name }
49
+ t1 = files.select { |h| incs.include?(h.name) }
50
+ t2 = sorted.select { |h| incs.include?(h.name) }
51
+ t2.select! { |h| compare(h, f, files_aux) > 0}
52
+ includes[f] = t1
53
+ includes[f] << t2.flatten
54
+ t1.each { |v| files.delete(v) }
55
+ t2.each { |v| sorted.delete(v) }
56
+ files -= includes[f]
57
+ end
58
+ sorted.push(*includes.keys)
59
+ files = includes.values.flatten
60
+ end
61
+ sorted.reverse
62
+ end
63
+
64
+ def compare(fx, fy, files_set)
65
+ num_incl_by_fx = num_included_by(fx, files_set)
66
+ num_incl_by_fy = num_included_by(fy, files_set)
67
+ num_incl_fx = num_includes(fx, files_set)
68
+ num_incl_fy = num_includes(fy, files_set)
69
+ if num_incl_by_fx == 0 && num_incl_by_fy == 0
70
+ return num_incl_fy <=> num_incl_fx
71
+ end
72
+ return -1 if num_incl_by_fx == 0
73
+ return 1 if num_incl_by_fy == 0
74
+ return (num_incl_fy - num_incl_by_fy) <=> (num_incl_fx - num_incl_by_fx)
75
+ end
76
+
77
+ def num_includes(file, files_set)
78
+ file.files_included.map { |f| f.name }.select { |f| files_set.include?(f) }.size
79
+ end
80
+
81
+ def num_included_by(file, files_set)
82
+ file.files_including.map { |f| f.name }.select{ |f| files_set.include?(f) }.size
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,69 @@
1
+ module EasySwig
2
+ class SwigTask
3
+
4
+ def initialize(config)
5
+ @config = config
6
+ @target = @config.target_file
7
+ @header_dir = @config.headers_dir
8
+ @out_dir = @config.output_dir
9
+ @lang = @config.lang
10
+ @inc_dirs = @config.includes_dir
11
+
12
+ if Dir.exists? @out_dir
13
+ FileUtils.rm_r @out_dir
14
+ end
15
+ FileUtils.mkdir_p(@out_dir)
16
+
17
+ @log=EasySwig::Logger.swig_log @out_dir
18
+ @log.info { "Created SWIG output directory in #{@out_dir}" }
19
+ end
20
+
21
+ def run_swig
22
+
23
+ swig_opts=%Q{-c++ -#{@lang} -v -Wall -debug-classes} #
24
+ saved_dir = Dir.pwd
25
+ output_dir = @out_dir.clone
26
+ ifiles = []
27
+ current_dir = nil
28
+ if File.directory? @target
29
+ ifiles = Dir.entries(@target).select { |entry| File.extname(entry)=='.i' }
30
+ output_dir << "/#{File.basename(@target)}"
31
+ current_dir = @target
32
+ else
33
+ ifiles << File.basename(@target)
34
+ current_dir = ::File.dirname(@target)
35
+ end
36
+
37
+ ifiles.each { |f|
38
+ @log.info "\nExecuting SWIG command for file: " + f
39
+ target_name = File.basename(f, '.i')
40
+ odir = output_dir + "/#{target_name}"
41
+ FileUtils.mkdir_p(odir)
42
+ Dir.chdir current_dir
43
+ incs = ""
44
+ Dir.foreach(@header_dir) { |e|
45
+ subdir = File.absolute_path(e, @header_dir)
46
+ if File.directory?(subdir)
47
+ incs << "-I#{subdir} "
48
+ end
49
+ }
50
+ @inc_dirs.each { |idir|
51
+ incs << "-I#{idir} "
52
+ }
53
+ command=%Q{swig #{swig_opts} -namespace #{target_name} -outdir #{odir} #{incs} #{f} > swig_output 2>&1}
54
+ @log.info command
55
+ output=IO.popen(command)
56
+ @log.debug { 'SWIG output:' }
57
+ @log.debug { output.readlines.join }
58
+ output.close
59
+ FileUtils.mv("#{File.basename(f, '.i')}_wrap.cxx", output_dir)
60
+ }
61
+ Dir.chdir saved_dir
62
+ end
63
+
64
+ def dispose
65
+ @log.close
66
+ end
67
+
68
+ end
69
+ end
@@ -0,0 +1,36 @@
1
+ module EasySwig
2
+
3
+ class Logger
4
+ class << self
5
+
6
+ def doxy_log dir
7
+ file = File.open("#{dir}/doxygen.log", File::WRONLY | File::APPEND | File::CREAT)
8
+ @doxy_log ||= ::Logger.new(file)
9
+ end
10
+
11
+ def log msg
12
+ @gen_log.info msg
13
+ end
14
+
15
+ def gen_log dir=nil
16
+ @gen_log ||= ::Logger.new(File.open("#{dir}/generate.log", File::WRONLY | File::APPEND | File::CREAT))
17
+ end
18
+
19
+ def node_log dir=nil
20
+ @gen_log
21
+ end
22
+
23
+ def csv_log dir
24
+ @csv_log ||= ::Logger.new(File.open("#{dir}/generate.log", File::WRONLY | File::APPEND | File::CREAT))
25
+ end
26
+
27
+ def swig_log dir
28
+ @swig_log ||= ::Logger.new(File.open("#{dir}/swig.log", File::WRONLY | File::APPEND | File::CREAT))
29
+ end
30
+
31
+ def close_logs
32
+ end
33
+ end
34
+
35
+ end
36
+ end
data/lib/util/print.rb ADDED
@@ -0,0 +1,72 @@
1
+ module EasySwig
2
+ module Print
3
+
4
+ def print_incl_files incl_files
5
+ ret = ''
6
+ ret << "\nGenerated Include Files: "
7
+ incl_files.each { |f|
8
+ ret << "\nincl_file:" + f.basename
9
+ f.api_classes.each { |c|
10
+ ret << "\t"+"Api Class:" + c.basename
11
+ c.api_methods.each { |m|
12
+ ret << "\t"+"\t"+"Api Method:" + m.basename
13
+ }
14
+ c.ignored_methods.each { |m|
15
+ ret << "\t"+"\t"+"Ignored Method:" + m.basename
16
+ }
17
+ }
18
+ f.ignored_classes.each { |c|
19
+ ret << "\t"+"Ignored Class:" + c.basename
20
+ # c.methods.each { |m|
21
+ # log "\t"+"\t"+"Ignored Method:" + m.name
22
+ #}
23
+ }
24
+ }
25
+ ret
26
+ end
27
+
28
+ def print_api_namespaces api_namespaces
29
+ ret = ''
30
+ api_namespaces.each { |api_ns|
31
+ ret << print_api_namespace(api_ns)
32
+ }
33
+ ret
34
+ end
35
+
36
+ def print_api_namespace api_ns
37
+ ret = '\n'
38
+ ret << "\tNamespace: "+ api_ns+"\n"
39
+ api_ns.api_functions.each { |f|
40
+ ret << "\t\tFunction: "+f.basename+" Assoc: "+f.wrapped_node.to_s + '\n'
41
+ }
42
+ api_ns.api_variables.each { |f|
43
+ ret << "\t\tVariable: "+f.basename+" Assoc: "+f.wrapped_node.to_s + '\n'
44
+ }
45
+ api_ns.api_enums.each { |f|
46
+ ret << "\t\tEnum: "+f.basename+" Assoc: "+f.wrapped_node.to_s + '\n'
47
+ }
48
+ api_ns.api_classes.each { |c|
49
+ ret << print_api_class(c)
50
+ }
51
+ ret
52
+ end
53
+
54
+ def print_api_class c
55
+ ret = ''
56
+ ret << "\t\tClass: " + c + "\n"
57
+ c.api_methods.each { |m|
58
+ ret << "\t\t\tMethod: "+m.basename+" Assoc: "+m.wrapped_node.to_s + '\n'
59
+ }
60
+ c.api_attributes.each { |m|
61
+ ret << "\t\t\tAttribute : "+m.basename+" Assoc: "+m.wrapped_node.to_s + '\n'
62
+ }
63
+ c.api_enums.each { |m|
64
+ ret << "\t\t\tEnum: "+m.basename+" Assoc: "+m.wrapped_node.to_s + '\n'
65
+ }
66
+ c.api_innerclasses.each { |m|
67
+ ret << "\t\t\tInnerClass: "+m.basename+" Assoc: "+m.wrapped_node.to_s + '\n'
68
+ }
69
+ ret
70
+ end
71
+ end
72
+ end
data/lib/util/query.rb ADDED
@@ -0,0 +1,136 @@
1
+ module EasySwig
2
+ module Query
3
+
4
+ def get_reference_type(type, klass)
5
+ if is_primitive?(type.name)
6
+ return :ptr
7
+ elsif type.name.end_with?('&')
8
+ return :ref
9
+ elsif type.name == 'std::string'
10
+ return :str
11
+ else
12
+ looked_up = klass.lookup_node(type)
13
+ return nil if looked_up.nil?
14
+ if type.name.end_with?('*')
15
+ if is_primitive?(looked_up.name)
16
+ return :prim_ptr
17
+ end
18
+ return :ptr
19
+ end
20
+ if looked_up.class == Doxyparser::Enum
21
+ return :ptr
22
+ else
23
+ return :val
24
+ end
25
+ end
26
+ end
27
+
28
+ def lookup_typename(type, klass) # Type should not have * & const
29
+ escaped_typename = escape_template(type.escaped_name)
30
+ return type.escaped_name if escaped_typename.include?('::') || is_primitive?(escaped_typename)
31
+ return type.escaped_name.gsub(escaped_typename, 'std::' + escaped_typename) if is_std?(escaped_typename)
32
+ lookedup = klass.lookup_node(type)
33
+ return nil if lookedup.nil?
34
+ lookedup.name
35
+ end
36
+
37
+ def nested_typenames_for(typename)
38
+ Doxyparser::Type.nested_typenames(typename)
39
+ end
40
+
41
+ def no_public_constructors?(klass)
42
+ klass.constructors(:public).empty? && !klass.constructors(:all).empty?
43
+ end
44
+
45
+ def no_public_destructors?(klass)
46
+ klass.destructors(:public).empty? && !klass.destructors(:all).empty?
47
+ end
48
+
49
+ def name_for_template(template_name)
50
+ nested_typenames_for(template_name).map{ |typename|
51
+ del_prefix_class(typename.split(' ').join('')).capitalize
52
+ }.join('')
53
+ end
54
+
55
+ def name_for_operator(typename, num_args)
56
+ ret = case typename
57
+ when 'operator+'
58
+ return num_args == 2 ? '__add__' : '__pos__'
59
+ when 'operator-'
60
+ return num_args == 2 ? '__sub__' : '__neg__'
61
+ when 'operator*'
62
+ return '__mul__'
63
+ when 'operator/'
64
+ return '__div__'
65
+ when 'operator%'
66
+ return '__mod__'
67
+ when 'operator<<'
68
+ return '__lshift__'
69
+ when 'operator>>'
70
+ return '__rshift__'
71
+ when 'operator&'
72
+ return '__and__'
73
+ when 'operator||'
74
+ return '__or__'
75
+ when 'operator^'
76
+ return '__xor__'
77
+ when 'operator~'
78
+ return '__invert__'
79
+ when 'operator<'
80
+ return '__lt__'
81
+ when 'operator<='
82
+ return '__le__'
83
+ when 'operator>'
84
+ return '__gt__'
85
+ when 'operator>='
86
+ return '__ge__'
87
+ when 'operator=='
88
+ return '__eq__'
89
+ when 'operator()'
90
+ return '__call__'
91
+ else
92
+ return nil
93
+ end
94
+ end
95
+
96
+ def name_for_friend(typename, num_args = 0)
97
+ return '_friend_' + typename if num_args == 0
98
+ result = name_for_operator(typename, num_args)
99
+ return result.nil? ? '_friend_' + typename : result
100
+ end
101
+
102
+ def is_template_param?(typename, klass)
103
+ class_template_params = klass.template_params.map{|tp| tp.declname}
104
+ return if nested_typenames_for(typename).any?{ |t|
105
+ class_template_params.include?(t)
106
+ }
107
+ end
108
+
109
+ def is_template?(typename)
110
+ Doxyparser::Type.template?(typename)
111
+ end
112
+
113
+ def is_operator?(name)
114
+ name =~ /operator\W+/
115
+ end
116
+
117
+ def method_has_blacklisted_types?(method)
118
+ return true if type_is_blacklisted?(method)
119
+ return true if method.params.any?{ |p| type_is_blacklisted?(p) }
120
+ return false
121
+ end
122
+
123
+ def type_is_blacklisted?(p)
124
+ return p.type.nested_typenames.any?{ |t| typename_is_blacklisted?(t)}
125
+ end
126
+
127
+ def typename_is_blacklisted?(typename)
128
+ return typename == 'multimap' || typename == 'std::multimap'
129
+ end
130
+
131
+ def anonymous_enum?(enum)
132
+ enum.basename =~ /_Enum\d*$/
133
+ end
134
+
135
+ end
136
+ end