587
|
1 |
#============================================================================
|
|
2 |
#Name : quality.py
|
|
3 |
#Part of : Helium
|
|
4 |
|
|
5 |
#Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
|
6 |
#All rights reserved.
|
|
7 |
#This component and the accompanying materials are made available
|
|
8 |
#under the terms of the License "Eclipse Public License v1.0"
|
|
9 |
#which accompanies this distribution, and is available
|
|
10 |
#at the URL "http://www.eclipse.org/legal/epl-v10.html".
|
|
11 |
#
|
|
12 |
#Initial Contributors:
|
|
13 |
#Nokia Corporation - initial contribution.
|
|
14 |
#
|
|
15 |
#Contributors:
|
|
16 |
#
|
|
17 |
#Description:
|
|
18 |
#===============================================================================
|
|
19 |
|
|
20 |
"""
|
|
21 |
Symbian log based analyser.
|
|
22 |
|
|
23 |
* Internal export parser
|
|
24 |
* Duplicate generation parser (relying on abld -what)
|
|
25 |
|
|
26 |
Policy validation.
|
|
27 |
"""
|
|
28 |
|
|
29 |
import symbian.log
|
|
30 |
import re
|
|
31 |
import os
|
|
32 |
import csv
|
|
33 |
import fileutils
|
|
34 |
import pathaddition.match
|
|
35 |
import logging
|
|
36 |
|
|
37 |
#logging.basicConfig(level=logging.DEBUG)
|
|
38 |
_logger = logging.getLogger("integration.quality")
|
|
39 |
|
|
40 |
class InternalExportParser(symbian.log.Parser):
|
|
41 |
""" This class extends the Symbian log parser class and implement
|
|
42 |
an "abld -what" analyser which detects file generated/exported inside
|
|
43 |
the source tree.
|
|
44 |
"""
|
|
45 |
def __init__(self, _file):
|
|
46 |
"""The constructor """
|
|
47 |
symbian.log.Parser.__init__(self, _file)
|
|
48 |
self.__match_what = re.compile("abld(\.bat)?(\s+.*)*\s+-(check)?w(hat)?", re.I)
|
|
49 |
self.internalexports = {}
|
|
50 |
|
|
51 |
def task(self, name, _cmd, _dir, content):
|
|
52 |
""" Analyse task log. """
|
|
53 |
if self.__match_what.match(_cmd) != None:
|
|
54 |
for line in content.splitlines():
|
|
55 |
if line.startswith(os.path.sep) \
|
|
56 |
and not os.path.normpath(line.strip().lower()).startswith(os.path.sep+"epoc32"+os.path.sep) \
|
|
57 |
and os.path.splitext(line.strip().lower())[1] != '':
|
|
58 |
if name not in self.internalexports:
|
|
59 |
self.internalexports[name] = []
|
|
60 |
self.internalexports[name].append(line)
|
|
61 |
|
|
62 |
|
|
63 |
class AbldWhatParser(symbian.log.Parser):
|
|
64 |
""" This class extends the Symbian log parser class and implement
|
|
65 |
an "abld -what" analyser which sort the generated files by component.
|
|
66 |
"""
|
|
67 |
def __init__(self, _file):
|
|
68 |
"""The constructor """
|
|
69 |
symbian.log.Parser.__init__(self, _file)
|
|
70 |
self.__match_what = re.compile(r"abld(\.bat)?(\s+.*)*\s+-(check)?w(hat)?", re.I)
|
|
71 |
self.__match_cmaker_what = re.compile(r"cmaker(\.cmd)?(\s+.*)*\s+ACTION=what", re.I)
|
|
72 |
self.files_per_component = {}
|
|
73 |
self.components_per_file = {}
|
|
74 |
|
|
75 |
def task(self, name, _cmd, _dir, content):
|
|
76 |
""" Analyse task log. """
|
|
77 |
if _cmd != None and self.__match_what.match(_cmd) != None:
|
|
78 |
for line in content.splitlines():
|
|
79 |
line = line.strip()
|
|
80 |
if not os.path.normpath(line).startswith(os.path.sep):
|
|
81 |
continue
|
|
82 |
# component per file
|
|
83 |
if line.lower() not in self.components_per_file:
|
|
84 |
self.components_per_file[line.lower()] = []
|
|
85 |
if name not in self.components_per_file[line.lower()]:
|
|
86 |
self.components_per_file[line.lower()].append(name)
|
|
87 |
|
|
88 |
# file per components
|
|
89 |
if name not in self.files_per_component:
|
|
90 |
self.files_per_component[name] = []
|
|
91 |
self.files_per_component[name].append(line)
|
|
92 |
elif _cmd != None and self.__match_cmaker_what.match(_cmd) != None:
|
|
93 |
for line in content.splitlines():
|
|
94 |
line = line.strip()
|
|
95 |
if not line.startswith('"'):
|
|
96 |
continue
|
|
97 |
if not line.endswith('"'):
|
|
98 |
continue
|
|
99 |
line = os.path.normpath(line.strip('"')).lower()
|
|
100 |
# component per file
|
|
101 |
if line not in self.components_per_file:
|
|
102 |
self.components_per_file[line] = []
|
|
103 |
if name not in self.components_per_file[line]:
|
|
104 |
self.components_per_file[line].append(name)
|
|
105 |
|
|
106 |
# file per components
|
|
107 |
if name not in self.files_per_component:
|
|
108 |
self.files_per_component[name] = []
|
|
109 |
self.files_per_component[name].append(line)
|
|
110 |
|
|
111 |
|
|
112 |
class PolicyValidator(object):
|
588
|
113 |
""" Validate policy files on a hierarchy. """
|
|
114 |
def __init__(self, policyfiles=None, csvfile=None, ignoreroot=False, excludes=None):
|
587
|
115 |
"""The constructor """
|
|
116 |
if policyfiles is None:
|
|
117 |
policyfiles = ['distribution.policy.s60']
|
|
118 |
self._policyfiles = policyfiles
|
|
119 |
self._ids = None
|
|
120 |
self._ignoreroot = ignoreroot
|
588
|
121 |
|
|
122 |
if not excludes:
|
|
123 |
self._excludes = []
|
|
124 |
else:
|
|
125 |
self._excludes = excludes
|
587
|
126 |
|
|
127 |
def load_policy_ids(self, csvfile):
|
|
128 |
""" Load the icds from the CSV file.
|
|
129 |
report format by generating array: ['unknownstatus', value, description]
|
|
130 |
"""
|
|
131 |
self._ids = {}
|
|
132 |
reader = csv.reader(open(csvfile, "rU"))
|
588
|
133 |
for row in reader:
|
587
|
134 |
if len(row)>=3 and re.match(r"^\s*\d+\s*$", row[0]):
|
|
135 |
self._ids[row[0]] = row
|
|
136 |
if row[1].lower() != "yes" and row[1].lower() != "no" and row[1].lower() != "bin":
|
|
137 |
yield ["unknownstatus", row[0], row[2]]
|
|
138 |
|
|
139 |
def validate_content(self, filename):
|
|
140 |
""" Validating the policy file content. If it cannot be decoded,
|
588
|
141 |
it reports an 'invalidencoding'.
|
587
|
142 |
Case 'notinidlist': value is not defined under the id list.
|
|
143 |
"""
|
|
144 |
value = None
|
|
145 |
try:
|
|
146 |
value = fileutils.read_policy_content(filename)
|
628
|
147 |
except IOError:
|
587
|
148 |
yield ["invalidencoding", filename, None]
|
588
|
149 |
if value is not None:
|
587
|
150 |
if self._ids != None:
|
|
151 |
if value not in self._ids:
|
|
152 |
yield ["notinidlist", filename, value]
|
|
153 |
|
|
154 |
def find_policy(self, path):
|
|
155 |
""" find the policy file under path using filenames under the list. """
|
|
156 |
for filename in self._policyfiles:
|
|
157 |
if os.sep != '\\':
|
588
|
158 |
for f_file in os.listdir(path):
|
|
159 |
if f_file.lower() == filename.lower():
|
|
160 |
return os.path.join(path, f_file)
|
587
|
161 |
if os.path.exists(os.path.join(path, filename)):
|
|
162 |
return os.path.join(path, filename)
|
|
163 |
return None
|
|
164 |
|
|
165 |
def validate(self, path):
|
|
166 |
""" Return a list couple [errortype, location, policy].
|
|
167 |
errortype: missing, invalidencoding, notinidlist .
|
|
168 |
missing: location is a directory.
|
|
169 |
otherwise the doggie policy file.
|
|
170 |
"""
|
|
171 |
path = os.path.normpath(path)
|
|
172 |
for dirpath, _, _ in os.walk(path):
|
|
173 |
# skipping the root
|
|
174 |
if dirpath == path and self._ignoreroot:
|
|
175 |
continue
|
|
176 |
# skip .svn and .hg dirs
|
|
177 |
if pathaddition.match.ant_match(dirpath, "**/.svn/**"):
|
|
178 |
continue
|
|
179 |
if pathaddition.match.ant_match(dirpath, "**/.hg/**"):
|
|
180 |
continue
|
|
181 |
# Skipping j2me content. Shouln't this be done differently?
|
|
182 |
if pathaddition.match.ant_match(dirpath, "**/j2me/**"):
|
|
183 |
continue
|
|
184 |
filename = self.find_policy(dirpath)
|
|
185 |
if filename != None:
|
|
186 |
for result in self.validate_content(filename):
|
|
187 |
yield result
|
|
188 |
else:
|
|
189 |
# report an error is the directory has no DP file
|
|
190 |
# and any files underneith.
|
|
191 |
for item in os.listdir(dirpath):
|
588
|
192 |
if os.path.isfile(os.path.join(dirpath, item)) \
|
|
193 |
and item not in self._excludes:
|
587
|
194 |
yield ['missing', dirpath, None]
|
|
195 |
break
|
|
196 |
|