cxxproject_gcov 0.0.1

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.
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Christian Köstlin
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # CxxprojectStats
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'cxxproject_stats'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install cxxproject_stats
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
@@ -0,0 +1,28 @@
1
+ body {
2
+ color: #000000;
3
+ background-color: #fff;
4
+ }
5
+
6
+ .source {
7
+ font-family: monospaced;
8
+ background-color: #fff;
9
+ }
10
+
11
+ .dead_code {
12
+ background-color: #FF6230;
13
+ }
14
+
15
+ .ignored_code {
16
+ }
17
+
18
+ .used_code {
19
+ background-color: #CAD7FE;;
20
+ }
21
+
22
+
23
+ .not_covered {
24
+ background-color: #FF6230;
25
+ }
26
+
27
+ .covered {
28
+ }
@@ -0,0 +1,167 @@
1
+ require 'ostruct'
2
+
3
+ module Gcov
4
+ @@excludes = []
5
+ class << self
6
+ def excludes
7
+ @excludes = [] unless @excludes
8
+ return @excludes
9
+ end
10
+ def excludes=(e)
11
+ @excludes = e
12
+ end
13
+ end
14
+
15
+ class FileStatistics
16
+ attr_reader :total_lines_of_code
17
+ def initialize(lines)
18
+ @total_lines_of_code = lines.select{|i|i.hit_count.counts?}.size
19
+ @dead_lines_of_code = lines.select{|i|i.hit_count.count == :DEAD_CODE}.size
20
+ @ignored_lines_of_code = lines.select{|i|i.hit_count.count == :IGNORED}.size
21
+ end
22
+ def covered_lines
23
+ return @total_lines_of_code - @dead_lines_of_code
24
+ end
25
+ def coverage
26
+ covered = covered_lines()
27
+ result = (100 * (covered.to_f / @total_lines_of_code)).to_i
28
+ if covered < @total_lines_of_code
29
+ if result == 100
30
+ result = 99
31
+ end
32
+ end
33
+ return result
34
+ end
35
+ end
36
+
37
+ class Hitcount
38
+ attr_reader :count
39
+ def counts?
40
+ if @count == :DEAD_CODE
41
+ return true
42
+ elsif @count == :IGNORED
43
+ return false
44
+ else
45
+ return true
46
+ end
47
+ end
48
+
49
+ def initialize(s)
50
+ if s.match('#+')
51
+ @count = :DEAD_CODE
52
+ elsif s == '-'
53
+ @count = :IGNORED
54
+ else
55
+ @count = s.to_i
56
+ end
57
+ end
58
+ def join(other)
59
+ if @count == :DEAD_CODE
60
+ if other.count == :DEAD_CODE
61
+ elsif other.count == :IGNORED
62
+ else
63
+ @count = other.count
64
+ end
65
+ elsif @count == :IGNORED
66
+ if other.count == :DEAD_CODE
67
+ @count = :DEAD_CODE
68
+ elsif other.count == :IGNORED
69
+ else
70
+ @count = other.count
71
+ end
72
+ else
73
+ if other.count == :DEAD_CODE
74
+ elsif other.count == :IGNORED
75
+ else
76
+ @count += other.count
77
+ end
78
+ end
79
+ self
80
+ end
81
+ end
82
+
83
+ def self.match_to_line(match)
84
+ =begin
85
+ lines look like this:
86
+ 4: 30: }
87
+ -: 31:
88
+ #####: 32: void Timestamp::unused() {
89
+ =end
90
+ hit_count = Hitcount.new(match[1].strip)
91
+ line_number = match[2].strip.to_i
92
+ code = match[3]
93
+ OpenStruct.new({:hit_count => hit_count, :line_number => line_number, :code => code})
94
+ end
95
+
96
+ def self.parse_gcov_string(str)
97
+ line_regexp = Regexp.new('(.*?):(.*?):(.*)')
98
+ matches = str.each_line.map{|i|i.match(line_regexp)}
99
+ return matches.map{|match|match_to_line(match)}.delete_if{|line|line.line_number == 0}
100
+ end
101
+
102
+ def self.simplify_path(p)
103
+ path_elements = p.split('/')
104
+ path_elements = path_elements.delete_if{|i|i == '.'}
105
+ new_elements = []
106
+ path_elements.each do |i|
107
+ if i == '..'
108
+ if new_elements.size > 0
109
+ new_elements.pop
110
+ else
111
+ new_elements << i
112
+ end
113
+ else
114
+ new_elements << i
115
+ end
116
+ end
117
+ return new_elements.join('/')
118
+ end
119
+
120
+ def self.replace_hash_with_slash(f)
121
+ f.gsub('#', '/')
122
+ end
123
+
124
+ def self.get_source_file(file)
125
+ res = ''
126
+ if not file.index('#')
127
+ # no #
128
+ res = file
129
+ elsif file.index('##')
130
+ # two or more ##
131
+ res = replace_hash_with_slash(file[0...file.index('##')])
132
+ else
133
+ # one #
134
+ res = replace_hash_with_slash(file)
135
+ end
136
+ return simplify_path(res.gsub('.gcov', ''))
137
+ end
138
+
139
+ def self.get_other_file(file)
140
+ res = ''
141
+ if not file.index('#')
142
+ # no #
143
+ return nil
144
+ elsif file.index('##')
145
+ # 2 or 3 ###
146
+ res = simplify_path(replace_hash_with_slash(file[file.index('##')+2..-1]).gsub('.gcov', ''))
147
+ else
148
+ return nil
149
+ end
150
+ end
151
+
152
+ def self.calc_source_and_visited(s)
153
+ return [get_source_file(s), get_other_file(s)]
154
+ end
155
+
156
+ # gcovs is array of gov-files
157
+ # gcov-files are arrays of lines
158
+ # lines are [Hitcount, linenr, content]
159
+ def self.merge_gcovs(gcovs)
160
+ gcovs.inject do |one,other|
161
+ one.zip(other).map do |a,b|
162
+ OpenStruct.new({:hit_count => a.hit_count.join(b.hit_count), :line_number => a.line_number, :code => a.code})
163
+ end
164
+ end
165
+ end
166
+
167
+ end
@@ -0,0 +1,91 @@
1
+ require 'cgi'
2
+ require 'cxxproject_gcov/gcov'
3
+ class HtmlExport
4
+
5
+ def initialize(out_dir, gcov_for_files, base_dir)
6
+ @out_dir = out_dir
7
+ @gcov_for_files = gcov_for_files
8
+ @base_dir = base_dir
9
+ export_css()
10
+ export_toc()
11
+ export_sources()
12
+ end
13
+ def export_css()
14
+ open_for_write(File.join(@out_dir, 'gcov.css')) do |out|
15
+ data = File.read(File.join(File.dirname(__FILE__), 'gcov.css'))
16
+ out.puts(data)
17
+ end
18
+ end
19
+ def export_toc
20
+ output_file_name = File.join(@out_dir, 'index.html')
21
+ open_for_write(output_file_name) do |out|
22
+ out.puts("<html><head><link rel=\"stylesheet\" type=\"text/css\" href=\"gcov.css\"></head><body>")
23
+
24
+ out.puts("<table><tr><th>Sourcefile</th><th>Loc</th><th>Coverage in %</th></tr>")
25
+ total_lines = 0
26
+ total_covered_lines = 0
27
+ @gcov_for_files.each do |file, coverage|
28
+ puts "working on #{file}"
29
+ relative_file = file.gsub(@base_dir, '')
30
+ stats = Gcov::FileStatistics.new(coverage)
31
+ total_lines += stats.total_lines_of_code
32
+ total_covered_lines += stats.covered_lines
33
+ coverage = stats.coverage
34
+ covered_style = coverage == 100 ? 'covered' : 'not_covered'
35
+ out.puts("<tr class=\"#{covered_style}\"><td><a href=\"#{relative_file}.html\">#{relative_file}</a></td><td>#{stats.total_lines_of_code}</td><td>#{coverage}</td></tr>")
36
+ end
37
+ covered = (100 * (total_covered_lines.to_f / total_lines)).to_i
38
+ out.puts("<tr><td>TOTAL</td><td>#{total_lines}</td><td>#{covered}</td></tr>")
39
+ out.puts("</table>")
40
+ out.puts("</body></html>")
41
+ end
42
+ end
43
+
44
+ def export_sources
45
+ @gcov_for_files.each do |file, coverage|
46
+ relative_file = file.gsub(@base_dir, '')
47
+ output_file_name = File.join(@out_dir, relative_file + '.html')
48
+ path_to_stylesheet = calc_path_to_stylesheet(output_file_name, @out_dir)
49
+ if path_to_stylesheet.size > 0
50
+ path_to_stylesheet = File.join(path_to_stylesheet, 'gcov.css')
51
+ else
52
+ path_to_stylesheet = 'gcov.css'
53
+ end
54
+ open_for_write(output_file_name) do |out|
55
+ out.puts("<html><head><link rel=\"stylesheet\" type=\"text/css\" href=\"#{path_to_stylesheet}\"></head><body><pre>")
56
+ coverage.each do |line|
57
+ line_number = sprintf("%5d:", line.line_number)
58
+ count_state = count_state_to_css_class(line.hit_count)
59
+ out.puts("<span class=\"#{count_state} linenumber\">#{line_number}</span><span class=\"#{count_state}\">#{CGI.escape_html(line.code)}</span>")
60
+ end
61
+ out.puts("</pre></body></html>")
62
+ end
63
+ end
64
+ end
65
+
66
+ def ensure_dir_for_file(output_file_name)
67
+ FileUtils.mkdir_p(File.dirname(output_file_name))
68
+ end
69
+
70
+ def open_for_write(output_file_name)
71
+ ensure_dir_for_file(output_file_name)
72
+ File.open(output_file_name, 'w') do |out|
73
+ yield out
74
+ end
75
+ end
76
+
77
+ def count_state_to_css_class(hit_count)
78
+ mapping = {:DEAD_CODE => 'dead_code', :IGNORED => 'ignored_code'}
79
+ if mapping.has_key?(hit_count.count)
80
+ return mapping[hit_count.count]
81
+ else
82
+ return 'used_code'
83
+ end
84
+ end
85
+
86
+ def calc_path_to_stylesheet(output, base)
87
+ count = output.split('/').size - base.split('/').size - 1
88
+ return ('../' * count)[0...-1]
89
+ end
90
+
91
+ end
@@ -0,0 +1,93 @@
1
+ require 'cxxproject_gcov/gcov'
2
+ require 'cxxproject_gcov/html_export'
3
+
4
+ cxx_plugin do |ruby_dsl, building_blocks, log|
5
+
6
+ def handle_has_sources(building_block)
7
+ if building_block.kind_of?(Cxxproject::HasSources)
8
+ building_block.collect_sources_and_toolchains.each do | source, toolchain |
9
+ cd(building_block.project_dir, :verbose => false) do
10
+ sh "gcov -l -p -o \"#{File.join(building_block.project_dir, building_block.get_object_file(source))}\" \"#{source}\""
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ def find_absolute_files(gcov_files)
17
+ return gcov_files.select{|f|f.index('###')}.map{|f|'/' + f.match(/.*###(.*)/)[1].gsub('#', '/').gsub('.gcov', '')}
18
+ end
19
+
20
+ def is_absolute?(file)
21
+ return file[0] == '/'
22
+ end
23
+
24
+ def find_building_block_for_source(building_blocks, to_find)
25
+ building_blocks.each do |building_block|
26
+ building_block.collect_sources_and_toolchains.each do |source, toolchain|
27
+ h = File.join(building_block.project_dir, source)
28
+ if h === to_find
29
+ return building_block
30
+ end
31
+ end
32
+ end
33
+ return nil
34
+ end
35
+
36
+ def find_file(file, building_blocks)
37
+ with_sources = building_blocks.select {|bb|bb.kind_of?(Cxxproject::HasSources)}
38
+ source, visited = Gcov.calc_source_and_visited(file)
39
+ if not visited
40
+ bb = find_building_block_for_source(with_sources, source)
41
+ return [source, bb]
42
+ else
43
+ if is_absolute?(visited)
44
+ return [visited, nil]
45
+ else
46
+ bb = find_building_block_for_source(with_sources, source)
47
+ if bb
48
+ return [Gcov.simplify_path(File.join(bb.project_dir, visited)), bb]
49
+ else
50
+ raise "could not find building block for #{source}"
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ directory ruby_dsl.build_dir
57
+
58
+ desc 'run gcov'
59
+ task :gcov => ruby_dsl.build_dir do
60
+ excludes = Gcov.excludes
61
+ sh 'find . -name "*.gcov" -delete'
62
+ Cxxproject::sorted_building_blocks.each do |building_block|
63
+ handle_has_sources(building_block)
64
+ end
65
+
66
+ gcovs_for_files = {}
67
+ bb_for_files = {}
68
+ gcov_files = Dir.glob('**/*.gcov')
69
+ gcov_files.each do |f|
70
+ f = File.join(Dir.pwd, f)
71
+ file, bb = find_file(f, Cxxproject::sorted_building_blocks)
72
+ raise "could not find file for #{f}" unless file
73
+
74
+ bb_for_files[file] = bb
75
+ gcovs_for_files[file] = [] unless gcovs_for_files.has_key?(file)
76
+ gcovs_for_files[file] << f
77
+ end
78
+
79
+ gcovs_for_files = gcovs_for_files.delete_if do | key, value |
80
+ excludes.any? do | i |
81
+ key.match(i)
82
+ end
83
+ end
84
+
85
+ gcovs_for_files.each do | file, gcovs |
86
+ gcovs_for_files[file] = Gcov.merge_gcovs(gcovs.map{|i|Gcov.parse_gcov_string(File.read(i))})
87
+ end
88
+
89
+
90
+ HtmlExport.new('out/gcov/html', gcovs_for_files, Dir.pwd + '/')
91
+ end
92
+
93
+ end
@@ -0,0 +1,3 @@
1
+ module CxxprojectGcov
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,5 @@
1
+ require "cxxproject_gcov/version"
2
+
3
+ module CxxprojectGcov
4
+ # Your code goes here...
5
+ end
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cxxproject_gcov
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Christian Köstlin
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-11-14 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: cxx
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ description: create gcvo files after a run
31
+ email:
32
+ - christian.koestlin@esrlabs.com
33
+ executables: []
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - lib/cxxproject_gcov.rb
38
+ - lib/cxxproject_gcov/gcov.css
39
+ - lib/cxxproject_gcov/gcov.rb
40
+ - lib/cxxproject_gcov/plugin.rb
41
+ - lib/cxxproject_gcov/version.rb
42
+ - lib/cxxproject_gcov/html_export.rb
43
+ - LICENSE
44
+ - README.md
45
+ homepage: http://github.com
46
+ licenses:
47
+ - MIT
48
+ post_install_message:
49
+ rdoc_options: []
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ! '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ! '>='
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ requirements: []
65
+ rubyforge_project:
66
+ rubygems_version: 1.8.25
67
+ signing_key:
68
+ specification_version: 3
69
+ summary: ! '...'
70
+ test_files: []