3 # Copyright (C) 2004, 2005, 2006 Nathaniel Smith
4 # Copyright (C) 2007 Holger Hans Peter Freyther
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
10 # 1. Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # 2. Redistributions in binary form must reproduce the above copyright
13 # notice, this list of conditions and the following disclaimer in the
14 # documentation and/or other materials provided with the distribution.
15 # 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
16 # its contributors may be used to endorse or promote products derived
17 # from this software without specific prior written permission.
19 # THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 # DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 """Create a directory like 'mkdir -p', but does not complain if
35 directory already exists like os.makedirs
40 if e.errno != 17: raise e
42 def collect_base(src,match_array):
44 Collect all files that match the match_array.
48 for root, dirs, files in os.walk(src):
53 base,ext = os.path.splitext(file)
54 if ext in match_array:
55 sources.append( os.path.join(root, file) )
59 def collect_depends(src):
60 return collect_base(src, [".d"])
62 def parse_dependency_file(src, base_dir, black_list):
64 Parse the .d files of the gcc
66 Wow, the first time os.path.join is doing the right thing. We might
67 have a relative path in the depends using os.path.join(dirname of .d, dep)
72 file = file.replace('\\', '').replace('\n', '')
74 # We now have object: dependencies splitted
75 ar = file.split(':', 1)
77 dir = os.path.dirname(obj)
78 deps = ar[1].split(' ')
80 # Remove files outside WebKit, make path absolute
81 deps = filter(lambda x: base_dir in x, deps)
82 deps = map(lambda x: os.path.abspath(os.path.join(dir, x)), deps)
83 return (obj, dir, deps)
85 def collect_cov(base_path,targets):
87 Collect gcov files, collect_sources is not used as it also creates
88 dirs and needs to do substituting.
89 Actually we will build a mapping from source file to gcov files of
90 interest. This is because we could have bytestream.h in many different
91 subdirectories. And we would endup with bla.cpp##bytestream.h and we
92 do not know which bytestream file was tested
94 def find_source_file(root,cov_file):
95 """ Find a Source line or crash
97 '#Users#ich#projekte#src#threadmessage.cpp###space#dports#include#qt3#qstring.h.gcov'
98 '#Users#ich#projekte#src#threadmessage.cpp##..#^#src#threadmessage.cpp.gcov'
101 ##..#^# is relative path... well a gcov bug as well
102 ## normal split file in the same directory
104 if '###' in cov_file:
105 split = cov_file.split('###')
106 if not len(split) == 2:
107 raise "Unexpected split result"
108 filepath = split[1][:-5].replace('#',os.path.sep)
109 return os.path.join(os.path.sep,filepath)
110 elif '##..#^#' in cov_file:
111 split = cov_file.split('##..#^#')
112 if not len(split) == 2:
113 raise "Unexpected split result"
114 filepath = split[1][:-5].replace('#',os.path.sep)
115 return os.path.abspath(os.path.join(root,os.path.pardir,os.path.pardir,filepath))
116 elif '##' in cov_file:
117 split = cov_file.split('##')
118 if not len(split) == 2:
119 raise "Unexpected split result"
120 filepath = split[1][:-5].replace('#',os.path.sep)
121 return os.path.abspath(os.path.join(root,filepath))
122 elif '#' in cov_file:
123 # wow a not broken gcov on OSX
124 basename=os.path.basename(cov_file).replace('#',os.path.sep)[:-5]
125 return os.path.abspath(os.path.join(root,basename))
128 raise "No source found %s" % cov_file
130 def sanitize_path(path):
132 Well fix up paths once again /usr/lib/gcc/i486-linux-gnu/4.1.2/^/^/^/^/include/c++/4.1.2/bits/stl_pair.h
133 according to gcov '^' is a relative path, we will now build one from this one. Somehow it depends
134 on the gcov version if .. really gets replaced to ^....
137 split = path.split(os.path.sep)
143 str = "%s..%s" % (str,os.path.sep)
145 str = "%s%s%s" % (str,part,os.path.sep)
146 return os.path.abspath(str)
150 for root, dirs, files in os.walk(base_path):
154 base,ext = os.path.splitext(file)
157 cov = os.path.join(root, file)
158 src = find_source_file( root, cov )
159 src = sanitize_path( src )
163 gcov[src].append( cov )
165 print "Exception on ", e
173 def generate_covs(candidates):
175 Generate gcov files in the right directory
177 candidtaes contains the directories we have used when
178 building. Each directory contains a set of files we will
179 try to generate gcov files for.
181 print candidates.keys()
182 for dir in candidates.keys():
183 print "Trying in %s" % (dir)
184 for dep in candidates[dir].keys():
185 cmd = "cd %s; gcov -p -l %s" % (dir, dep)
186 os.system("%s > /dev/null 2>&1 " % cmd)
189 def analyze_coverage(sources,data,dirs,runid,base):
191 sources actual source files relative to src_dir e.g kdelibs/kdecore/klibloader.cpp
192 data Where to put the stuff
193 dirs Where to take a look for gcov files
194 base The base directory for files. All files not inside base will be ignored
198 gcov = collect_cov(base,dirs)
199 result = cov.analyze_coverage(gcov, sources, runid, data, base)
202 if __name__ == "__main__":
204 if not len(sys.argv) == 3:
205 print "This script needs three parameters"
206 print "Call it with generate_cov RUNID ResultsDir"
209 results = sys.argv[2]
211 # create directories for out result
214 print "Collection Sources and preparing data tree"
215 base_dir = os.path.abspath(os.path.curdir)
216 depends = collect_depends(base_dir)
217 candidates = map(lambda x: parse_dependency_file(x,base_dir,[]), depends)
219 # Build a number of sources from the candidates. This is a Set for the poor
220 # Two level dict. One for
223 for (_,dir,deps) in candidates:
227 if not dep in dirs[dir]:
232 sources = files.keys()
234 print "Found %d candidates" % (len(sources))
235 print "Will run inefficient generation of gcov files now"
238 print "Analyzing Gcov"
239 analyze_coverage(sources, results, dirs.keys(), runid, base_dir)