Hackfix and re-enable strtoull and wcstoull, see bug #3798.
[sdcc.git] / sdcc / support / valdiag / valdiag.py
blob0b45f88e73780073d9a31964037cfb62845a136c
1 #!/usr/bin/env python
2 #---------------------------------------------------------------------------
3 # valdiag.py - Validate diagnostic messages from SDCC/GCC
4 # Written By - Erik Petrich . epetrich@users.sourceforge.net (2003)
6 # This program is free software; you can redistribute it and/or modify it
7 # under the terms of the GNU General Public License as published by the
8 # Free Software Foundation; either version 2, or (at your option) any
9 # later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 # In other words, you are welcome to use, share and improve this program.
21 # You are forbidden to forbid anyone else to use, share and improve
22 # what you give them. Help stamp out software-hoarding!
23 #---------------------------------------------------------------------------
25 from __future__ import print_function
27 import sys, string, os, re, subprocess
28 from subprocess import Popen, PIPE, STDOUT
30 macrodefs = {}
31 extramacrodefs = {}
33 gcc = {
34 "CC":"gcc",
35 "CCFLAGS":"-c -pedantic -Wall -DPORT_HOST=1",
36 "CCDEF":"-D",
37 "CCOUTPUT":"-o",
38 "C89":"-std=c89",
39 "C99":"-std=c99",
40 "defined": {
41 "__GNUC__":"1",
42 "GCC":"1"
44 "ignoremsg": [
48 sdcc = {
49 "CC":"../../bin/sdcc",
50 "CCFLAGS":"-c -m{port}",
51 "CCDEF":"-D",
52 "CCOUTPUT":"-o",
53 "CCINCLUDEDIR":"-I",
54 "C89":"--std-sdcc89",
55 "C99":"--std-sdcc99",
56 "defined": {
57 "SDCC":"1",
58 "SDCC_{port}":"1",
59 "__{port}":"1"
61 "ignoremsg": [
62 "code not generated.*due to previous errors",
63 "unreferenced function argument"
67 testmodes = {
68 "host":{
69 "compiler":gcc,
70 "port":"host",
71 "defined": {
72 "PORT_HOST":"1"
75 "mcs51":{
76 "compiler":sdcc,
77 "port":"mcs51",
78 "extra-defines": {
79 "__has_bit":"1",
80 "__has_data":"1",
81 "__has_xdata":"1",
82 "__has_reentrant":"1"
85 "mcs51-large":{
86 "compiler":sdcc,
87 "port":"mcs51",
88 "flags":"--model-large",
89 "defined": {
90 "SDCC_MODEL_LARGE":"1"
92 "extra-defines" : {
93 "__has_bit":"1",
94 "__has_data":"1",
95 "__has_xdata":"1",
96 "__has_reentrant":"1"
99 "mcs51-stack-auto":{
100 "compiler":sdcc,
101 "port":"mcs51",
102 "flags":"--stack-auto",
103 "defined": {
104 "SDCC_STACK_AUTO":"1"
106 "extra-defines": {
107 "__has_bit":"1",
108 "__has_data":"1",
109 "__has_xdata":"1",
110 "__has_reentrant":"1"
113 "ds390":{
114 "compiler":sdcc,
115 "port":"ds390",
116 "extra-defines": {
117 "__has_bit":"1",
118 "__has_data":"1",
119 "__has_xdata":"1",
120 "__has_reentrant":"1"
123 "z80":{
124 "compiler":sdcc,
125 "port":"z80",
126 "extra-defines": {
127 "__has_z88dk_fastcall":"1"
130 "z180":{
131 "compiler":sdcc,
132 "port":"z180",
133 "extra-defines": {
134 "__has_z88dk_fastcall":"1"
137 "r2k":{
138 "compiler":sdcc,
139 "port":"r2k",
140 "extra-defines": {
141 "__has_z88dk_fastcall":"1"
144 "sm83":{
145 "compiler":sdcc,
146 "port":"sm83",
147 "extra-defines": {
148 "__has_sdcccall":"1"
151 "tlcs90":{
152 "compiler":sdcc,
153 "port":"tlcs90",
154 "extra-defines": {
155 "__has_z88dk_fastcall":"1"
158 "hc08":{
159 "compiler":sdcc,
160 "port":"hc08",
161 "extra-defines": {
162 "__has_data":"1",
163 "__has_xdata":"1",
164 "__has_reentrant":"1"
167 "s08":{
168 "compiler":sdcc,
169 "port":"s08",
170 "extra-defines": {
171 "__has_data":"1",
172 "__has_xdata":"1",
173 "__has_reentrant":"1"
176 "mos6502":{
177 "compiler":sdcc,
178 "port":"mos6502",
179 "extra-defines": {
180 "__has_data":"1",
181 "__has_xdata":"1",
182 "__has_reentrant":"1"
185 "mos65c02":{
186 "compiler":sdcc,
187 "port":"mos65c02",
188 "extra-defines": {
189 "__has_data":"1",
190 "__has_xdata":"1",
191 "__has_reentrant":"1"
194 "stm8":{
195 "compiler":sdcc,
196 "port":"stm8",
197 "extra-defines": {
198 "__has_raisonance":"1",
199 "__has_sdcccall":"1"
202 "pdk13":{
203 "compiler":sdcc,
204 "port":"pdk13"
206 "pdk14":{
207 "compiler":sdcc,
208 "port":"pdk14"
210 "pdk15":{
211 "compiler":sdcc,
212 "port":"pdk15"
214 "pic14":{
215 "compiler":sdcc,
216 "port":"pic14"
218 "pic16":{
219 "compiler":sdcc,
220 "port":"pic16"
225 def evalQualifier(expr):
226 global macrodefs
227 tokens = re.split("([^0-9A-Za-z_])", expr)
228 for tokenindex in range(len(tokens)):
229 token = tokens[tokenindex]
230 if token in macrodefs:
231 tokens[tokenindex] = macrodefs[token]
232 elif token == "defined":
233 tokens[tokenindex] = ""
234 if tokens[tokenindex+2] in macrodefs:
235 tokens[tokenindex+2] = "1"
236 else:
237 tokens[tokenindex+2] = "0"
238 elif len(token)>0:
239 if token[0]=="_" or token[0] in string.ascii_letters:
240 tokens[tokenindex] = "0"
241 #expr = string.join(tokens,"")
242 expr = "".join(tokens)
243 expr = expr.replace("&&"," and ");
244 expr = expr.replace("||"," or ");
245 expr = expr.replace("!"," not ");
246 return eval(expr)
248 def expandPyExpr(expr):
249 tokens = re.split("({|})", expr)
250 for tokenindex in range(1,len(tokens)):
251 if tokens[tokenindex-1]=="{":
252 tokens[tokenindex]=eval(tokens[tokenindex])
253 tokens[tokenindex-1]=""
254 tokens[tokenindex+1]=""
255 expandedExpr = "".join(tokens)
256 return expandedExpr
258 def addDefines(deflist, isExtra):
259 for define in list(deflist.keys()):
260 expandeddef = expandPyExpr(define)
261 macrodefs[expandeddef] = expandPyExpr(deflist[define])
262 if isExtra:
263 extramacrodefs[expandeddef] = macrodefs[expandeddef]
265 def parseInputfile(inputfilename):
266 inputfile = open(inputfilename, "r")
267 testcases = {}
268 testname = ""
269 linenumber = 1
271 # Find the test cases and tests in this file
272 for line in inputfile.readlines():
274 # See if a new testcase is being defined
275 p = line.find("TEST")
276 if p>=0:
277 testname = line[p:].split()[0]
278 if testname not in testcases:
279 testcases[testname] = {}
281 # See if a new test is being defined
282 for testtype in ["ERROR", "WARNING", "IGNORE"]:
283 p = line.find(testtype);
284 if p>=0:
285 # Found a test definition
286 qualifier = line[p+len(testtype):].strip()
287 p = qualifier.find("*/")
288 if p>=0:
289 qualifier = qualifier[:p].strip()
290 if len(qualifier)==0:
291 qualifier="1"
292 qualifier = evalQualifier(qualifier)
293 if qualifier:
294 if not linenumber in testcases[testname]:
295 testcases[testname][linenumber]=[]
296 testcases[testname][linenumber].append(testtype)
298 linenumber = linenumber + 1
300 inputfile.close()
301 return testcases
303 def parseResults(output):
304 results = {}
305 for line in output:
306 print(line, end=' ')
308 if line.count("SIGSEG"):
309 results[0] = ["FAULT", line.strip()]
310 continue
312 # look for something of the form:
313 # filename:line:message
314 msg = line.split(":",2)
315 if len(msg)<3: continue
316 if msg[0]!=inputfilename: continue
317 if len(msg[1])==0: continue
318 if not msg[1][0] in string.digits: continue
320 # it's in the right form; parse it
321 linenumber = int(msg[1])
322 msgtype = "UNKNOWN"
323 uppermsg = msg[2].upper()
324 if uppermsg.count("ERROR"):
325 msgtype = "ERROR"
326 if uppermsg.count("WARNING"):
327 msgtype = "WARNING"
328 msgtext = msg[2].strip()
329 ignore = 0
330 for ignoreExpr in ignoreExprList:
331 if re.search(ignoreExpr,msgtext)!=None:
332 ignore = 1
333 if not ignore:
334 results[linenumber]=[msgtype,msg[2].strip()]
335 return results
337 def showUsage():
338 print("Usage: test testmode cfile [objectfile]")
339 print("Choices for testmode are:")
340 for testmodename in list(testmodes.keys()):
341 print(" %s" % testmodename)
342 sys.exit(1)
344 # Start here
345 if len(sys.argv)<3:
346 showUsage()
348 testmodename = sys.argv[1]
349 if not testmodename in testmodes:
350 print("Unknown test mode '%s'" % testmodename)
351 showUsage()
353 testmode = testmodes[testmodename]
354 compilermode = testmode["compiler"]
355 port = expandPyExpr(testmode["port"])
356 cc = expandPyExpr(compilermode["CC"])
357 ccflags = expandPyExpr(compilermode["CCFLAGS"])
358 if "flags" in testmode:
359 ccflags = " ".join([ccflags,expandPyExpr(testmode["flags"])])
360 if len(sys.argv)>=4:
361 if "CCOUTPUT" in compilermode:
362 ccflags = " ".join([ccflags,expandPyExpr(compilermode["CCOUTPUT"]),sys.argv[3]])
363 if len(sys.argv)>=5:
364 if "CCINCLUDEDIR" in compilermode:
365 ccflags = " ".join([ccflags,expandPyExpr(compilermode["CCINCLUDEDIR"]),sys.argv[4]])
366 if "defined" in compilermode:
367 addDefines(compilermode["defined"], False)
368 if "defined" in testmode:
369 addDefines(testmode["defined"], False)
370 if "extra-defines" in compilermode:
371 addDefines(compilermode["extra-defines"], True)
372 if "extra-defines" in testmode:
373 addDefines(testmode["extra-defines"], True)
374 if "ignoremsg" in compilermode:
375 ignoreExprList = compilermode["ignoremsg"]
376 else:
377 ignoreExprList = []
379 inputfilename = sys.argv[2]
380 inputfilenameshort = os.path.basename(inputfilename)
382 try:
383 testcases = parseInputfile(inputfilename)
384 except IOError:
385 print("Unable to read file '%s'" % inputfilename)
386 sys.exit(1)
388 casecount = len(list(testcases.keys()))
389 testcount = 0
390 failurecount = 0
392 print("--- Running: %s " % inputfilenameshort)
393 for testname in list(testcases.keys()):
394 if testname.find("DISABLED")>=0:
395 continue
396 ccdef = compilermode["CCDEF"]+testname
397 for extradef in list(extramacrodefs.keys()):
398 ccdef = ccdef + " " + compilermode["CCDEF"] + extradef + "=" + extramacrodefs[extradef]
399 if testname[-3:] == "C89":
400 ccstd = compilermode["C89"]
401 elif testname[-3:] == "C99":
402 ccstd = compilermode["C99"]
403 else:
404 ccstd = ""
405 cmd = " ".join([cc,ccflags,ccstd,ccdef,inputfilename])
406 print()
407 print(cmd)
408 spawn = Popen(args=cmd.split(), bufsize=-1, stdout = PIPE, stderr = STDOUT, close_fds=True)
409 (stdoutdata,stderrdata) = spawn.communicate()
410 if not isinstance(stdoutdata, str): # python 3 returns bytes so
411 stdoutdata = str(stdoutdata,"utf-8") # convert to str type first
412 output = stdoutdata.splitlines(True)
414 results = parseResults(output)
416 if len(testcases[testname])==0:
417 testcount = testcount + 1 #implicit test for no errors
419 # Go through the tests of this case and make sure
420 # the compiler gave a diagnostic
421 for checkline in list(testcases[testname].keys()):
422 testcount = testcount + 1
423 if checkline in results:
424 if "IGNORE" in testcases[testname][checkline]:
425 testcount = testcount - 1 #this isn't really a test
426 del results[checkline]
427 else:
428 for wanted in testcases[testname][checkline]:
429 if not wanted=="IGNORE":
430 print("--- FAIL: expected %s" % wanted, end=' ')
431 print("at %s:%d" % (inputfilename, checkline))
432 failurecount = failurecount + 1
434 # Output any unexpected diagnostics
435 for checkline in list(results.keys()):
436 print('--- FAIL: unexpected message "%s" ' % results[checkline][1], end=' ')
437 print("at %s:%d" % (inputfilename, checkline))
438 failurecount = failurecount + 1
440 print()
441 print("--- Summary: %d/%d/%d: " % (failurecount, testcount, casecount), end=' ')
442 print("%d failed of %d tests in %d cases." % (failurecount, testcount, casecount))