587
|
1 |
# -*- encoding: latin-1 -*-
|
|
2 |
|
|
3 |
#============================================================================
|
|
4 |
#Name : parsers.py
|
|
5 |
#Part of : Helium
|
|
6 |
|
|
7 |
#Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
|
8 |
#All rights reserved.
|
|
9 |
#This component and the accompanying materials are made available
|
|
10 |
#under the terms of the License "Eclipse Public License v1.0"
|
|
11 |
#which accompanies this distribution, and is available
|
|
12 |
#at the URL "http://www.eclipse.org/legal/epl-v10.html".
|
|
13 |
#
|
|
14 |
#Initial Contributors:
|
|
15 |
#Nokia Corporation - initial contribution.
|
|
16 |
#
|
|
17 |
#Contributors:
|
|
18 |
#
|
|
19 |
#Description:
|
|
20 |
#===============================================================================
|
|
21 |
|
|
22 |
""" The ATS related parsers """
|
|
23 |
|
|
24 |
|
|
25 |
# pylint: disable-msg=W0142,W0102
|
|
26 |
# pylint: disable-msg=C0302
|
|
27 |
# pylint: disable-msg=R0201,R0912,R0915,R0911,R0902
|
|
28 |
|
|
29 |
#W0142 => * and ** were used
|
|
30 |
#W0102 => Dangerous default value [] as argument
|
|
31 |
#C0302 => Too many lines
|
|
32 |
#R* remove during refactoring
|
|
33 |
|
|
34 |
import os
|
|
35 |
import re
|
|
36 |
import logging
|
|
37 |
from path import path # pylint: disable-msg=F0401
|
|
38 |
import fnmatch
|
|
39 |
import subprocess
|
|
40 |
import codecs
|
|
41 |
import traceback
|
|
42 |
|
|
43 |
_logger = logging.getLogger('ats-parser')
|
|
44 |
|
|
45 |
import configuration
|
|
46 |
def split_config_to_attributes_and_properties(specfile):
|
|
47 |
"""Split the specfile to its parts"""
|
|
48 |
attributes = {}
|
|
49 |
properties = {}
|
|
50 |
builder = configuration.NestedConfigurationBuilder(specfile)
|
|
51 |
configs = builder.getConfigurations()
|
|
52 |
# the supported configs are either attributes or properties
|
|
53 |
# collect each in a dictionary and return them.
|
|
54 |
for config in configs:
|
|
55 |
if config.name == 'attributes' :
|
|
56 |
for attr in config:
|
|
57 |
attributes[attr] = config[attr]
|
|
58 |
if config.name == 'properties' :
|
|
59 |
for prop in config:
|
|
60 |
properties[prop] = config[prop]
|
|
61 |
return (properties, attributes)
|
|
62 |
|
|
63 |
|
|
64 |
class CppParser(object):
|
|
65 |
"""
|
|
66 |
Parser for CPP tool output. Returns cleaned output from the execution
|
|
67 |
of CPP with or without parent paths included in the output.
|
|
68 |
"""
|
|
69 |
|
|
70 |
def __init__(self):
|
|
71 |
self.path_to_build = ""
|
|
72 |
|
|
73 |
def get_cpp_output(self, bld_path = None, output_parameter = "n", imacros = None):
|
|
74 |
"""
|
|
75 |
To clean out conditionals from the compilation it is necessary to
|
|
76 |
use C preprocessing to clean out those.
|
|
77 |
|
|
78 |
If ('n' - normal) output is chosen, parser returns list of paths
|
|
79 |
If ('e' - extended) output is chosen parser returns list of (path, parent_path) tuples
|
|
80 |
If ('d' - dependency) output is chosen parser returns a dicitionary (can be a nested dictionary)
|
|
81 |
of paths dependency (-ies).
|
|
82 |
|
|
83 |
'imacros' can also be given as parameters for CPP options.
|
|
84 |
|
|
85 |
if bld file is not given, the function will try to find the file(s) on the given location with extension ".inf"
|
|
86 |
"""
|
|
87 |
temp_path = os.getcwd()
|
|
88 |
if "bld.inf" in str(bld_path).lower():
|
|
89 |
os.chdir(os.path.normpath(os.path.join(bld_path, os.pardir)))
|
|
90 |
else:
|
|
91 |
os.chdir(os.path.normpath(os.path.join(bld_path)))
|
|
92 |
|
|
93 |
if imacros is not None:
|
|
94 |
includedir = os.path.join(os.path.splitdrive(bld_path)[0] + os.sep, 'epoc32', 'include')
|
|
95 |
command = r"cpp -imacros %s -I %s bld.inf" % (str(imacros), includedir)
|
|
96 |
else:
|
|
97 |
command = u"cpp bld.inf"
|
|
98 |
|
|
99 |
process = subprocess.Popen(command, shell = True, stdout = subprocess.PIPE)
|
|
100 |
pipe = process.stdout
|
|
101 |
|
|
102 |
if output_parameter == "d":
|
|
103 |
return self.create_dependency_dictionary(pipe, bld_path)
|
|
104 |
|
|
105 |
#If not depdendency dictiontionary then create normal or extended list
|
|
106 |
#Creates dictionary for 'n' (normal) and 'e' extended paths
|
|
107 |
|
|
108 |
clean_path_list = []
|
|
109 |
path_list = []
|
|
110 |
for line in pipe.readlines():
|
|
111 |
#_logger.debug(line.strip())
|
|
112 |
if re.search(r"\A#\s.*?", line.strip()) or re.search(r"\A#.*?[0-9]", line.strip()):
|
|
113 |
if line.strip() not in path_list:
|
|
114 |
path_list.append(line.strip())
|
|
115 |
process.wait()
|
|
116 |
if process.returncode == 1:
|
|
117 |
_logger.error('CPP failed: ' + command + ' in: ' + os.getcwd())
|
|
118 |
pipe.close()
|
|
119 |
|
|
120 |
os.chdir(temp_path)
|
|
121 |
if output_parameter is "n":
|
|
122 |
for _path in self.clean_cpp_output(bld_path, path_list):
|
|
123 |
clean_path_list.append(_path[0])
|
|
124 |
|
|
125 |
elif output_parameter is "e":
|
|
126 |
clean_path_list = self.clean_cpp_output(bld_path, path_list)
|
|
127 |
|
|
128 |
clean_path_list = list(set(clean_path_list))
|
|
129 |
|
|
130 |
bfp = BldFileParser()
|
|
131 |
|
|
132 |
for tsrc in clean_path_list:
|
|
133 |
mmp_path = bfp.get_test_mmp_files(tsrc[0])
|
|
134 |
if tsrc[0] == tsrc[1]:
|
|
135 |
if mmp_path == None or mmp_path == []:
|
|
136 |
clean_path_list.remove(tsrc)
|
|
137 |
|
|
138 |
return clean_path_list
|
|
139 |
|
|
140 |
def create_dependency_dictionary(self, _pipe_, path_to_bld):
|
|
141 |
"""
|
|
142 |
The output from CPP is cleaned in a fashion that the output is
|
|
143 |
a dictionary (or nested dictionary) of paths and their dependencies.
|
|
144 |
"""
|
|
145 |
bld_parser = BldFileParser()
|
|
146 |
pkg_parser = PkgFileParser()
|
|
147 |
mmp_parser = MmpFileParser()
|
|
148 |
|
|
149 |
temp_path = os.getcwd()
|
|
150 |
parent = os.getcwd()
|
|
151 |
self.path_to_build = path_to_bld
|
|
152 |
|
|
153 |
test_sets = {}
|
|
154 |
harness = ""
|
|
155 |
main_level = ""
|
|
156 |
test_cases = []
|
|
157 |
output_list = []
|
|
158 |
for line in _pipe_.readlines():
|
|
159 |
if re.match(r"#.*", line.lower()):
|
|
160 |
#_logger.debug(line)
|
|
161 |
tpat = re.findall(r'"(.*bld.inf?)"', line.lower())
|
|
162 |
if tpat != []:
|
|
163 |
output_list.append((line, os.path.dirname(os.path.normpath(os.path.join(self.path_to_build, tpat[0])))))
|
|
164 |
_pipe_.close()
|
|
165 |
|
|
166 |
#Creating dependencies
|
|
167 |
for case in output_list:
|
|
168 |
if re.match(r".*[bld.inf][^0-9]\Z", case[0].lower().strip()):
|
|
169 |
|
|
170 |
if main_level == "":
|
|
171 |
main_level = case[1]
|
|
172 |
parent = case[1]
|
|
173 |
os.chdir(case[1])
|
|
174 |
test_cases.append((parent, case[1]))
|
|
175 |
elif re.match(r".*[1]\Z", case[0].lower().strip()):
|
|
176 |
parent = os.getcwd()
|
|
177 |
os.chdir(case[1])
|
|
178 |
|
|
179 |
test_cases.append((parent, case[1]))
|
|
180 |
elif re.match(r".*[2]\Z", case[0].lower().strip()):
|
|
181 |
if test_cases:
|
|
182 |
for tcase in test_cases:
|
|
183 |
if parent in tcase[1]:
|
|
184 |
parent = tcase[0]
|
|
185 |
os.chdir(tcase[1])
|
|
186 |
break
|
|
187 |
|
|
188 |
for t_case in test_cases:
|
|
189 |
if t_case[0] == t_case[1] and (not bld_parser.get_test_mmp_files(t_case[1])):
|
|
190 |
del t_case
|
|
191 |
elif t_case[0] in main_level:
|
|
192 |
test_sets[t_case[1]] = {}
|
|
193 |
test_sets[t_case[1]]['content'] = {}
|
|
194 |
test_sets[t_case[1]]['content'][t_case[1]] = {}
|
|
195 |
harness = mmp_parser.get_harness(t_case[1])
|
|
196 |
#if harness == "": harness = None
|
|
197 |
test_sets[t_case[1]]['content'][t_case[1]]['type'] = mmp_parser.get_dll_type(t_case[1])
|
|
198 |
test_sets[t_case[1]]['content'][t_case[1]]['harness'] = harness
|
|
199 |
test_sets[t_case[1]]['content'][t_case[1]]['pkg_files'] = pkg_parser.get_pkg_files(t_case[1], False)
|
|
200 |
test_sets[t_case[1]]['content'][t_case[1]]['mmp_files'] = bld_parser.get_test_mmp_files(t_case[1], False)
|
|
201 |
else:
|
|
202 |
for key, value in test_sets.items():
|
|
203 |
if t_case[0] in value['content'].keys():
|
|
204 |
harness = mmp_parser.get_harness(t_case[1])
|
|
205 |
if harness is "" or harness in test_sets[key]['content'][t_case[0]]['harness']:
|
|
206 |
test_sets[key]['content'][t_case[1]] = {}
|
|
207 |
test_sets[key]['content'][t_case[1]]['type'] = mmp_parser.get_dll_type(t_case[1])
|
|
208 |
test_sets[key]['content'][t_case[1]]['harness'] = harness
|
|
209 |
test_sets[key]['content'][t_case[1]]['pkg_files'] = pkg_parser.get_pkg_files(t_case[1], False)
|
|
210 |
test_sets[key]['content'][t_case[1]]['mmp_files'] = bld_parser.get_test_mmp_files(t_case[1], False)
|
|
211 |
else:
|
|
212 |
test_sets[t_case[1]] = {}
|
|
213 |
test_sets[t_case[1]]['content'] = {}
|
|
214 |
test_sets[t_case[1]]['content'][t_case[1]] = {}
|
|
215 |
test_sets[t_case[1]]['content'][t_case[1]]['type'] = mmp_parser.get_dll_type(t_case[1])
|
|
216 |
test_sets[t_case[1]]['content'][t_case[1]]['harness'] = harness
|
|
217 |
test_sets[t_case[1]]['content'][t_case[1]]['pkg_files'] = pkg_parser.get_pkg_files(t_case[1], False)
|
|
218 |
test_sets[t_case[1]]['content'][t_case[1]]['mmp_files'] = bld_parser.get_test_mmp_files(t_case[1], False)
|
|
219 |
|
|
220 |
os.chdir(temp_path)
|
|
221 |
if test_sets == {}:
|
|
222 |
for itm in output_list:
|
|
223 |
_logger.debug(itm)
|
|
224 |
for itm in test_cases:
|
|
225 |
_logger.debug(itm)
|
|
226 |
_logger.error(path_to_bld + ' test_sets are empty')
|
|
227 |
return test_sets
|
|
228 |
|
|
229 |
|
|
230 |
def clean_cpp_output(self, bld_path, path_list):
|
|
231 |
"""
|
|
232 |
The output from CPP needs to be "cleaned" so that extra chars needs
|
|
233 |
to be removed and also hierarchy which cpp is following is preserved
|
|
234 |
and returned as an output.
|
|
235 |
"""
|
|
236 |
|
|
237 |
pat = ""
|
|
238 |
value = ""
|
|
239 |
cleaned_output = []
|
|
240 |
if "bld.inf" in bld_path:
|
|
241 |
path_to_parent = os.path.dirname(bld_path)
|
|
242 |
else:
|
|
243 |
path_to_parent = bld_path
|
|
244 |
pat = re.compile(r'\A#\s*?.*?[\"](.*?)[\"].*?')
|
|
245 |
for _path in path_list:
|
|
246 |
if re.match(r".*[bld.inf]\s*?[^0-9]\Z", _path.lower().strip()):
|
|
247 |
value = pat.match(_path.strip())
|
|
248 |
path_to_tc = os.path.dirname(os.path.normpath(os.path.join((bld_path), value.group(1))))
|
|
249 |
cleaned_output.append((path_to_tc, path_to_parent))
|
|
250 |
if re.match(r".*[1]\s*?\Z", _path.lower().strip()):
|
|
251 |
value = pat.match(_path.strip())
|
|
252 |
path_to_tc = os.path.dirname(os.path.normpath(os.path.join(bld_path, value.group(1))))
|
|
253 |
cleaned_output.append((path_to_tc, path_to_parent))
|
|
254 |
if re.match(r".*[2]\s*?\Z", _path.lower().strip()):
|
|
255 |
if cleaned_output:
|
|
256 |
for cout in cleaned_output:
|
|
257 |
if path_to_parent.lower() == cout[0].lower():
|
|
258 |
path_to_tc = cout[1]
|
|
259 |
path_to_parent = path_to_tc
|
|
260 |
return cleaned_output
|
|
261 |
|
|
262 |
|
|
263 |
class BldFileParser(object):
|
|
264 |
"""
|
|
265 |
Parser for bld.inf files. Returns MACRO values.
|
|
266 |
Parsing Paths can be done using CPP parser
|
|
267 |
"""
|
|
268 |
def __init__(self):
|
|
269 |
self.mmp_files = []
|
|
270 |
|
|
271 |
#def get_mmp_files():
|
|
272 |
# """
|
|
273 |
# returns mmp files from PRJ_MMPFILES macro
|
|
274 |
# """
|
|
275 |
|
|
276 |
def get_test_mmp_files(self, bld_file_path = None, with_full_path = True):
|
|
277 |
"""
|
|
278 |
returns a list of test mmp files
|
|
279 |
Usage: if "x:\abc\bldfile", "PRJ_TESTMMPFILES".
|
|
280 |
1. get_test_mmp_files("x:\abc\bldfile") - with full paths e.g. ["x:\abc\abc.mmp"]
|
|
281 |
2. get_test_mmp_files("x:\abc\bldfile", False) - without full paths e.g. ["abc.mmp"]
|
|
282 |
|
|
283 |
if bld file is not given, the function will try to find the file(s) on the given location with extension ".inf"
|
|
284 |
"""
|
|
285 |
|
|
286 |
if bld_file_path == None:
|
|
287 |
_logger.warning("Incorrect bld file")
|
|
288 |
return None
|
|
289 |
else:
|
|
290 |
bld_file_path = path(bld_file_path)
|
|
291 |
if not "bld.inf" in str(bld_file_path).lower():
|
|
292 |
bld_file_path = os.path.join(os.path.normpath(bld_file_path), "bld.inf")
|
|
293 |
|
|
294 |
if not os.path.exists(bld_file_path):
|
|
295 |
_logger.error(r"bld file path does not exist: '%s'" % bld_file_path)
|
|
296 |
return None
|
|
297 |
|
|
298 |
return self.get_files(path(bld_file_path), "PRJ_TESTMMPFILES", with_full_path)
|
|
299 |
|
|
300 |
|
|
301 |
def get_files(self, bld_inf_path, bld_macro, with_full_path = True):
|
|
302 |
"""
|
|
303 |
Component's MMP files, as stored in BLD.INF.
|
|
304 |
"""
|
|
305 |
|
|
306 |
bld_inf_path = path(bld_inf_path)
|
|
307 |
bld_inf = bld_inf_path.text()
|
|
308 |
if bld_inf.count(bld_macro) > 1:
|
|
309 |
_logger.error(bld_macro + ' in ' + bld_inf_path + ' more than once')
|
|
310 |
try:
|
|
311 |
bld_inf = re.compile(r"%s" % bld_macro).split(bld_inf)[1].strip()
|
|
312 |
bld_inf = re.compile(r"PRJ_+\S").split(bld_inf)[0].strip()
|
|
313 |
|
|
314 |
except IndexError:
|
|
315 |
try:
|
|
316 |
bld_inf = re.compile(r"%s" % bld_macro).split(bld_inf)[0].strip()
|
|
317 |
bld_inf = re.compile(r"PRJ_+\S").split(bld_inf)[0].strip()
|
|
318 |
|
|
319 |
except IndexError:
|
|
320 |
_logger.warning("Index Error while parsing bld.inf file")
|
|
321 |
|
|
322 |
comments_free_text = self.ignore_comments_from_input(bld_inf)
|
|
323 |
|
|
324 |
self.mmp_files = re.findall(r"(\S+?[.]mmp)", comments_free_text, re.IGNORECASE)
|
|
325 |
|
|
326 |
|
|
327 |
|
|
328 |
if with_full_path:
|
|
329 |
bld_dir = bld_inf_path.dirname()
|
|
330 |
return [path.joinpath(bld_dir, mmp).normpath()
|
|
331 |
for mmp in self.mmp_files]
|
|
332 |
else:
|
|
333 |
return self.mmp_files
|
|
334 |
|
|
335 |
def ignore_comments_from_input(self, input_str = ""):
|
|
336 |
"""
|
|
337 |
Removes comments from the input string. Enables the use of examples
|
|
338 |
in bld.inf.
|
|
339 |
"""
|
|
340 |
_input = ""
|
|
341 |
for i in input_str.split("\n"):
|
|
342 |
_input += "\n" + i.split("//")[0]
|
|
343 |
|
|
344 |
if not _input == "":
|
|
345 |
input_str = _input
|
|
346 |
count = input_str.count("/*")
|
|
347 |
count2 = input_str.count("*/")
|
|
348 |
if (count == count2):
|
|
349 |
idx_1 = input_str.find('/*')
|
|
350 |
idx_2 = input_str.find('*/') + 2
|
|
351 |
while count > 0:
|
|
352 |
substr_1 = input_str[:idx_1].strip()
|
|
353 |
substr_2 = input_str[idx_2:].strip()
|
|
354 |
input_str = substr_1 + " " + substr_2
|
|
355 |
idx_1 = input_str.find('/*')
|
|
356 |
idx_2 = input_str.find('*/') + 2
|
|
357 |
count = input_str.count('/*')
|
|
358 |
return input_str.strip()
|
|
359 |
else:
|
|
360 |
_logger.warning("Comments in bld.inf-file inconsistent. "
|
|
361 |
"Check comments in bld.inf.")
|
|
362 |
return input_str.strip()
|
|
363 |
|
|
364 |
|
|
365 |
#def get_exports():
|
|
366 |
# """
|
|
367 |
# returns exports from the macro PRJ_EXPORTS
|
|
368 |
# """
|
|
369 |
|
|
370 |
class MmpFileParser(object):
|
|
371 |
"""
|
|
372 |
Parser for .mmp files. Returns wanted information from the mmp-file
|
|
373 |
- file type (executable dll, plugin, exe, etc)
|
|
374 |
- test harness (STIF, EUNIT) if mmp is related to the test component
|
|
375 |
- file name
|
|
376 |
- libraries listed in the mmp
|
|
377 |
"""
|
|
378 |
|
|
379 |
def __init__(self):
|
|
380 |
self.mmp_files = []
|
|
381 |
self.path_to_mmp = ""
|
|
382 |
|
|
383 |
def get_target_filetype(self, path_to_mmp = None):
|
|
384 |
"""
|
|
385 |
Filetype given using TARGETTYPE in .mmp file is returned.
|
|
386 |
If "c:\path\to\mmp" is a location where mmp file is stored
|
|
387 |
get_target_filetype("c:\path\to\mmp")
|
|
388 |
|
|
389 |
if mmp file is not given, the function will try to find the file(s) on the given location with extension ".mmp"
|
|
390 |
"""
|
|
391 |
return self.read_information_from_mmp(path_to_mmp, 4)
|
|
392 |
|
|
393 |
def get_target_filename(self, path_to_mmp = None):
|
|
394 |
"""
|
|
395 |
Filename given using TARGET in .mmp file is returned
|
|
396 |
If "c:\path\to\mmp" is a location where mmp file is stored
|
|
397 |
get_target_filename("c:\path\to\mmp")
|
|
398 |
|
|
399 |
if mmp file is not given, the function will try to find the file(s) on the given location with extension ".mmp"
|
|
400 |
"""
|
|
401 |
return self.read_information_from_mmp(path_to_mmp, 3)
|
|
402 |
|
|
403 |
def get_libraries(self, path_to_mmp = None):
|
|
404 |
"""
|
|
405 |
Libraries listed in the MMP file are returned in a list
|
|
406 |
If "c:\path\to\mmp" is a location where mmp file is stored
|
|
407 |
get_libraries("c:\path\to\mmp")
|
|
408 |
|
|
409 |
if mmp file is not given, the function will try to find the file(s) on the given location with extension ".mmp"
|
|
410 |
"""
|
|
411 |
return self.read_information_from_mmp(path_to_mmp, 5)
|
|
412 |
|
|
413 |
def get_harness(self, path_to_mmp = None):
|
|
414 |
"""
|
|
415 |
Returns harness of test component
|
|
416 |
If "c:\path\to\mmp" is a location where mmp file is stored
|
|
417 |
get_harness("c:\path\to\mmp")
|
|
418 |
|
|
419 |
if mmp file is not given, the function will try to find the file(s) on the given location with extension ".mmp"
|
|
420 |
"""
|
|
421 |
return self.read_information_from_mmp(path_to_mmp, 6)
|
|
422 |
|
|
423 |
def get_dll_type(self, path_to_mmp = None):
|
|
424 |
"""
|
|
425 |
Returns type of test whether 'executable' or 'dependent' (dependent can be a stub or plugin)
|
|
426 |
If "c:\path\to\mmp" is a location where mmp file is stored
|
|
427 |
get_dll_type("c:\path\to\mmp")
|
|
428 |
|
|
429 |
if mmp file is not given, the function will try to find the file(s) on the given location with extension ".mmp"
|
|
430 |
"""
|
|
431 |
return self.read_information_from_mmp(path_to_mmp, 7)
|
|
432 |
|
|
433 |
def read_information_from_mmp(self, path_to_mmp, flag = 0):
|
|
434 |
"""
|
|
435 |
Returns wanted information - user can define
|
|
436 |
the wanted information level by setting a flag
|
|
437 |
value following way:
|
|
438 |
0 - (targetfilename, filetype, libraries, harness)
|
|
439 |
1 - (targetfilename, filetype, libraries)
|
|
440 |
2 - (targetfilename, filetype)
|
|
441 |
3 - targetfilename
|
|
442 |
4 - filetype
|
|
443 |
5 - libraries
|
|
444 |
6 - harness (in case of test component)
|
|
445 |
7 - mmpfilename
|
|
446 |
"""
|
|
447 |
|
|
448 |
|
|
449 |
filename = ""
|
|
450 |
filetype = ""
|
|
451 |
dll_type = ""
|
|
452 |
libraries = []
|
|
453 |
lst_mmp_paths = []
|
|
454 |
harness = ""
|
|
455 |
stif = False
|
|
456 |
eunit = False
|
|
457 |
stifunit = False
|
|
458 |
tef = False
|
|
459 |
self.path_to_mmp = path_to_mmp
|
|
460 |
try:
|
|
461 |
if isinstance(path_to_mmp, list):
|
|
462 |
lst_mmp_paths = self.path_to_mmp
|
|
463 |
else:
|
|
464 |
self.path_to_mmp = path(self.path_to_mmp)
|
|
465 |
if not ".mmp" in str(self.path_to_mmp).lower():
|
|
466 |
bld_parser = BldFileParser()
|
|
467 |
self.mmp_files = bld_parser.get_test_mmp_files(self.path_to_mmp, False)
|
|
468 |
|
|
469 |
for mpath in self.mmp_files:
|
|
470 |
lst_mmp_paths.append(os.path.join(self.path_to_mmp, mpath))
|
|
471 |
else:
|
|
472 |
lst_mmp_paths.append(self.path_to_mmp)
|
|
473 |
|
|
474 |
for mmp in lst_mmp_paths:
|
|
475 |
mmp_file = open(mmp, 'r')
|
|
476 |
for line in mmp_file:
|
|
477 |
if re.match(r"\A(target\s).*([.]\w+)", line.lower().strip()):
|
|
478 |
found = re.findall(r"\Atarget[\s]*(\w+[.]\w+)", line.lower())
|
|
479 |
if found:
|
|
480 |
filename = found[0]
|
|
481 |
elif re.match(r"\A(targettype\s).*", line.lower().strip()):
|
|
482 |
found = re.findall(r"\Atargettype[\s]*(\w+)", line.lower())
|
|
483 |
if found:
|
|
484 |
filetype = found[0]
|
|
485 |
|
|
486 |
libraries = libraries + re.findall(r"\b(\w+[.]lib)\b", mmp.text().lower())
|
|
487 |
if '//rtest' in mmp.text().lower() or '* rtest' in mmp.text().lower() or '// rtest' in mmp.text().lower():
|
|
488 |
libraries.append('rtest')
|
|
489 |
|
|
490 |
if libraries:
|
|
491 |
if "stiftestinterface.lib" in libraries:
|
|
492 |
stif = True
|
|
493 |
if "eunit.lib" in libraries or "qttest.lib" in libraries:
|
|
494 |
eunit = True
|
|
495 |
if "stifunit.lib" in libraries:
|
|
496 |
stifunit = True
|
|
497 |
elif "testexecuteutils.lib" in libraries or 'testframeworkclient.lib' in libraries or 'rtest' in libraries:
|
|
498 |
tef = True
|
|
499 |
|
|
500 |
if tef:
|
|
501 |
harness = "GENERIC"
|
|
502 |
elif stif and eunit:
|
|
503 |
#_logger.warning("both eunit.lib and stiftestinterface.lib listed in mmp file - choosing STIF.")
|
|
504 |
harness = "STIF"
|
|
505 |
elif stif and not eunit:
|
|
506 |
harness = "STIF"
|
|
507 |
elif eunit and not stif:
|
|
508 |
harness = "EUNIT"
|
|
509 |
elif stifunit and not stif and not eunit:
|
|
510 |
harness = "STIFUNIT"
|
|
511 |
|
|
512 |
if harness is "":
|
|
513 |
dll_type = "dependent"
|
|
514 |
elif harness is "EUNIT":
|
|
515 |
dll_type = "executable"
|
|
516 |
elif harness is "STIF":
|
|
517 |
dll_type = "executable"
|
|
518 |
|
|
519 |
except:
|
|
520 |
traceback.print_exc()
|
|
521 |
finally:
|
|
522 |
if flag == 0:
|
|
523 |
return (filename, filetype, libraries, harness)
|
|
524 |
elif flag == 1:
|
|
525 |
return (filename, filetype, libraries)
|
|
526 |
elif flag == 2:
|
|
527 |
return (filename, filetype)
|
|
528 |
elif flag == 3:
|
|
529 |
return filename
|
|
530 |
elif flag == 4:
|
|
531 |
return filetype
|
|
532 |
elif flag == 5:
|
|
533 |
return libraries
|
|
534 |
elif flag == 6:
|
|
535 |
return harness
|
|
536 |
elif flag == 7:
|
|
537 |
return dll_type
|
|
538 |
|
|
539 |
class PkgFileParser(object):
|
|
540 |
"""
|
|
541 |
Parses .pkg files. Returns a list of:
|
|
542 |
a. src path of the file
|
|
543 |
b. dst path on the phone
|
|
544 |
c. type of the file
|
|
545 |
for every file in the pkg file
|
|
546 |
"""
|
|
547 |
|
618
|
548 |
def __init__(self, platform = None, specific_pkg = None, drive=''):
|
587
|
549 |
self.platform = platform
|
|
550 |
self.build_platform = None
|
|
551 |
if self.platform is not None and "_" in self.platform:
|
|
552 |
plat_tar = re.search(r"(.*)_(.*).pkg", self.platform)
|
|
553 |
self.build_platform, self.build_target = plat_tar.groups()
|
618
|
554 |
self.drive = drive
|
587
|
555 |
self._files = []
|
|
556 |
self.pkg_files = []
|
|
557 |
self.pkg_file_path = None
|
|
558 |
self.exclude = ""
|
|
559 |
self.location = None
|
|
560 |
self.specific_pkg = specific_pkg
|
|
561 |
if specific_pkg:
|
|
562 |
self.platform = specific_pkg + '.pkg'
|
|
563 |
|
|
564 |
def get_pkg_files(self, location = None, with_full_path = True):
|
|
565 |
"""
|
|
566 |
Returns list of PKG files on the given location. If True, full path is returned
|
|
567 |
otherwise only filenames. Default is set to True
|
|
568 |
|
|
569 |
Assume at location "c:\abd\files", two pkg file '1.pkg' and '2.pkg', then the funtion
|
|
570 |
can be called as:
|
|
571 |
1. get_pkg_files("c:\abd\files") - will return a list of pkg files with full paths.
|
|
572 |
like ['c:\abd\files\1.pkg', 'c:\abd\files\2.pkg']
|
|
573 |
2. get_pkg_files("c:\abd\files", False) - will return a list of pkg files only.
|
|
574 |
like ['1.pkg', '2.pkg']
|
|
575 |
"""
|
|
576 |
self.location = path(location)
|
|
577 |
self.pkg_files = []
|
|
578 |
if not self.location.exists():
|
|
579 |
return None
|
|
580 |
|
|
581 |
for pths, _, files in os.walk(self.location):
|
|
582 |
pfiles = [f for f in files if self.platform != None and f.endswith(self.platform)]
|
|
583 |
if self.platform != None and len(pfiles)>0:
|
|
584 |
if with_full_path:
|
|
585 |
self.pkg_files.append(os.path.join(pths, pfiles[0]))
|
|
586 |
else:
|
|
587 |
self.pkg_files.append(str(pfiles[0]))
|
|
588 |
elif self.specific_pkg == None:
|
|
589 |
for name in files:
|
|
590 |
if fnmatch.fnmatch(name, "*.pkg"):
|
|
591 |
if with_full_path:
|
|
592 |
self.pkg_files.append(os.path.join(pths, name))
|
|
593 |
else:
|
|
594 |
self.pkg_files.append(str(name))
|
|
595 |
|
|
596 |
return self.pkg_files
|
|
597 |
|
|
598 |
def get_data_files(self, location = [], drive = "", exclude = ""):
|
|
599 |
"""
|
|
600 |
Returns data files, source and destination of the files to be installed
|
|
601 |
on the phone
|
|
602 |
e.g. location = tsrc\testComponent\group
|
|
603 |
|
|
604 |
Function can be called in any of the following ways:
|
|
605 |
1. get_data_files("c:\abc\abc.pkg") - only data files' paths are returnd
|
|
606 |
as they are mention in the pkg file
|
|
607 |
2. get_data_files("c:\abc\abc.pkg", "x:") - Proper data files' paths are returnd
|
|
608 |
with drive letter included
|
|
609 |
3. get_data_files("c:\abc\abc.pkg", "x:", "\.dll") - Data files' paths are returnd with
|
|
610 |
drive letter included but the dll
|
|
611 |
files will be excluded if found in
|
|
612 |
the pkg file
|
|
613 |
|
|
614 |
if pkg file is not given, the function will try to find the file(s) on the given location with extension ".pkg"
|
|
615 |
"""
|
|
616 |
|
|
617 |
self.drive = drive
|
|
618 |
self.exclude = exclude
|
|
619 |
self._files = []
|
|
620 |
|
|
621 |
if type(location) is not list:
|
|
622 |
locations = [location]
|
|
623 |
else:
|
|
624 |
locations = location
|
|
625 |
|
|
626 |
for _file_ in locations:
|
|
627 |
|
|
628 |
#if location is already a file
|
|
629 |
if ".pkg" in str(_file_).lower():
|
|
630 |
self._files = _file_
|
|
631 |
else:
|
|
632 |
self.location = path(_file_)
|
|
633 |
|
|
634 |
if not self.location.exists():
|
|
635 |
continue
|
|
636 |
for p_file in self.get_pkg_files(self.location, True):
|
|
637 |
self._files.append(p_file)
|
|
638 |
|
618
|
639 |
return self.read_pkg_file(self._files)
|
587
|
640 |
|
|
641 |
def __map_pkg_path(self, pkg_line, pkg_file_path, pkg_file):
|
|
642 |
"""Parse package file to get the src and dst paths" for installing files"""
|
|
643 |
mmp_parser = MmpFileParser()
|
|
644 |
ext = ""
|
|
645 |
val1 = ""
|
|
646 |
val2 = ""
|
|
647 |
map_src = ""
|
|
648 |
map_dst = ""
|
|
649 |
self.pkg_file_path = pkg_file_path
|
|
650 |
|
|
651 |
if not self.exclude == "":
|
|
652 |
if re.search(r'%s' % self.exclude, pkg_line) is not None:
|
|
653 |
return None
|
|
654 |
#searches for the file path (src and dst) in the pkg file
|
|
655 |
#e.g.: "..\conf\VCXErrors.inc"-"C:\TestFramework\VCXErrors.inc"
|
|
656 |
result = re.search(r'^\s*"(.*?)".*?-.*?"(.*?)"', pkg_line)
|
|
657 |
|
|
658 |
if result is None:
|
|
659 |
return None
|
|
660 |
val1, val2 = result.groups()
|
|
661 |
|
|
662 |
if val1 != "":
|
|
663 |
|
|
664 |
#replacing delimiters (${platform} and ${target}) in PKG file templates,
|
|
665 |
#for instance, QT tests PKG files have delimeters
|
|
666 |
if "$(platform)" in val1.lower() and self.build_platform is not None:
|
|
667 |
val1 = val1.lower().replace("$(platform)", self.build_platform)
|
|
668 |
if "$(target)" in val1.lower() and self.build_target is not None:
|
|
669 |
val1 = val1.lower().replace("$(target)", self.build_target)
|
|
670 |
|
|
671 |
if path.isabs(path(val1).normpath()):
|
618
|
672 |
map_src = os.path.normpath(os.path.join(self.drive, val1))
|
587
|
673 |
elif re.search(r"\A\w", val1, 1):
|
|
674 |
map_src = str(path.joinpath(self.pkg_file_path + os.sep, os.path.normpath(val1)).normpath())
|
|
675 |
else:
|
|
676 |
map_src = str(path.joinpath(self.pkg_file_path, path(val1)).normpath())
|
|
677 |
map_dst = str(path(val2).normpath())
|
|
678 |
else:
|
|
679 |
map_src, map_dst = val1, val2
|
|
680 |
map_src = map_src.strip()
|
|
681 |
|
|
682 |
#replaces the characters with the drive letters
|
|
683 |
map_dst = map_dst.replace("!:", "c:")
|
|
684 |
map_dst = map_dst.replace("$:", "c:")
|
|
685 |
map_dst = re.sub(r'^(\w)', r'\1', map_dst).strip()
|
|
686 |
indx = map_dst.rsplit(".")
|
|
687 |
try:
|
|
688 |
ext = indx[1]
|
|
689 |
except IndexError:
|
|
690 |
_logger.warning("Index Error in map_pkg_path()")
|
|
691 |
|
|
692 |
_test_type_ = ""
|
|
693 |
_target_filename_ = ""
|
|
694 |
|
|
695 |
_target_filename_ = mmp_parser.get_target_filename(self.pkg_file_path)
|
|
696 |
_test_type_ = mmp_parser.get_dll_type(self.pkg_file_path)
|
|
697 |
_harness_ = mmp_parser.get_harness(self.pkg_file_path)
|
|
698 |
_libraries_ = mmp_parser.get_libraries(self.pkg_file_path)
|
|
699 |
|
|
700 |
if ext == "ini":
|
|
701 |
file_type = "engine_ini"
|
|
702 |
elif ext == "cfg":
|
|
703 |
file_type = "conf"
|
|
704 |
elif ext == "dll":
|
|
705 |
#adding type of dll (executable or dependent), if file type is dll
|
|
706 |
if _test_type_ == "dependent":
|
|
707 |
file_type = "data" + ":%s" % _test_type_
|
|
708 |
else:
|
|
709 |
if "qttest.lib" in _libraries_:
|
|
710 |
file_type = "data" + ":qt:dependent"
|
|
711 |
else:
|
|
712 |
file_type = "testmodule"
|
|
713 |
|
|
714 |
elif ext == 'exe' and 'rtest' in _libraries_:
|
|
715 |
file_type = "testmodule:rtest"
|
|
716 |
elif ext == "exe":
|
|
717 |
if _test_type_ == "dependent":
|
|
718 |
file_type = "data" + ":%s" % _test_type_
|
|
719 |
else:
|
|
720 |
if "qttest.lib" in _libraries_:
|
|
721 |
file_type = "testmodule:qt"
|
|
722 |
else:
|
|
723 |
file_type = "testmodule"
|
|
724 |
|
|
725 |
elif ext == "sisx":
|
|
726 |
file_type = ""
|
|
727 |
elif ext == "xml":
|
|
728 |
file_type = "trace_init"
|
|
729 |
elif ext == "pmd":
|
|
730 |
file_type = "pmd"
|
|
731 |
elif ext == "script":
|
|
732 |
if "testframeworkclient.lib" in _libraries_:
|
|
733 |
file_type = "testscript:mtf"
|
|
734 |
else:
|
|
735 |
file_type = "testscript"
|
|
736 |
else:
|
|
737 |
file_type = "data"
|
|
738 |
|
|
739 |
if not map_src or map_src == "." or not map_dst or map_dst == ".":
|
|
740 |
return None
|
618
|
741 |
if not os.path.exists(map_src):
|
|
742 |
_logger.error(map_src + ' not found')
|
|
743 |
return None
|
587
|
744 |
return path(map_src).normpath(), path(map_dst).normpath(), file_type, pkg_file
|
|
745 |
|
618
|
746 |
def read_pkg_file(self, pkg_files):
|
587
|
747 |
"""Reads contents of PKG file"""
|
|
748 |
pkg_paths = []
|
|
749 |
for pkg_file in pkg_files:
|
|
750 |
if not os.path.exists( pkg_file ):
|
|
751 |
_logger.error("No PKG -file in path specified")
|
|
752 |
continue
|
|
753 |
else:
|
|
754 |
file1 = codecs.open(pkg_file, 'r', 'utf16')
|
|
755 |
try:
|
|
756 |
lines = file1.readlines()
|
|
757 |
except UnicodeError:
|
|
758 |
file1 = open(pkg_file, 'r')
|
|
759 |
lines = file1.readlines()
|
618
|
760 |
pkg_file_path = path(os.path.dirname(pkg_file))
|
587
|
761 |
for line in lines:
|
|
762 |
pkg_path = self.__map_pkg_path(line, pkg_file_path, os.path.basename(pkg_file))
|
|
763 |
if pkg_path is None:
|
|
764 |
continue
|
|
765 |
else:
|
|
766 |
pkg_paths.append(pkg_path)
|
|
767 |
|
|
768 |
return pkg_paths |