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
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
35 "CCFLAGS":"-c -pedantic -Wall -DPORT_HOST=1",
49 "CC":"../../bin/sdcc",
50 "CCFLAGS":"-c -m{port}",
62 "code not generated.*due to previous errors",
63 "unreferenced function argument"
88 "flags":"--model-large",
90 "SDCC_MODEL_LARGE":"1"
102 "flags":"--stack-auto",
104 "SDCC_STACK_AUTO":"1"
110 "__has_reentrant":"1"
120 "__has_reentrant":"1"
127 "__has_z88dk_fastcall":"1"
134 "__has_z88dk_fastcall":"1"
141 "__has_z88dk_fastcall":"1"
155 "__has_z88dk_fastcall":"1"
164 "__has_reentrant":"1"
173 "__has_reentrant":"1"
182 "__has_reentrant":"1"
191 "__has_reentrant":"1"
198 "__has_raisonance":"1",
225 def evalQualifier(expr
):
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"
237 tokens
[tokenindex
+2] = "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 ");
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
)
258 def addDefines(deflist
, isExtra
):
259 for define
in list(deflist
.keys()):
260 expandeddef
= expandPyExpr(define
)
261 macrodefs
[expandeddef
] = expandPyExpr(deflist
[define
])
263 extramacrodefs
[expandeddef
] = macrodefs
[expandeddef
]
265 def parseInputfile(inputfilename
):
266 inputfile
= open(inputfilename
, "r")
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")
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
);
285 # Found a test definition
286 qualifier
= line
[p
+len(testtype
):].strip()
287 p
= qualifier
.find("*/")
289 qualifier
= qualifier
[:p
].strip()
290 if len(qualifier
)==0:
292 qualifier
= evalQualifier(qualifier
)
294 if not linenumber
in testcases
[testname
]:
295 testcases
[testname
][linenumber
]=[]
296 testcases
[testname
][linenumber
].append(testtype
)
298 linenumber
= linenumber
+ 1
303 def parseResults(output
):
308 if line
.count("SIGSEG"):
309 results
[0] = ["FAULT", line
.strip()]
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])
323 uppermsg
= msg
[2].upper()
324 if uppermsg
.count("ERROR"):
326 if uppermsg
.count("WARNING"):
328 msgtext
= msg
[2].strip()
330 for ignoreExpr
in ignoreExprList
:
331 if re
.search(ignoreExpr
,msgtext
)!=None:
334 results
[linenumber
]=[msgtype
,msg
[2].strip()]
338 print("Usage: test testmode cfile [objectfile]")
339 print("Choices for testmode are:")
340 for testmodename
in list(testmodes
.keys()):
341 print(" %s" % testmodename
)
348 testmodename
= sys
.argv
[1]
349 if not testmodename
in testmodes
:
350 print("Unknown test mode '%s'" % testmodename
)
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"])])
361 if "CCOUTPUT" in compilermode
:
362 ccflags
= " ".join([ccflags
,expandPyExpr(compilermode
["CCOUTPUT"]),sys
.argv
[3]])
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"]
379 inputfilename
= sys
.argv
[2]
380 inputfilenameshort
= os
.path
.basename(inputfilename
)
383 testcases
= parseInputfile(inputfilename
)
385 print("Unable to read file '%s'" % inputfilename
)
388 casecount
= len(list(testcases
.keys()))
392 print("--- Running: %s " % inputfilenameshort
)
393 for testname
in list(testcases
.keys()):
394 if testname
.find("DISABLED")>=0:
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"]
405 cmd
= " ".join([cc
,ccflags
,ccstd
,ccdef
,inputfilename
])
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
]
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
441 print("--- Summary: %d/%d/%d: " % (failurecount
, testcount
, casecount
), end
=' ')
442 print("%d failed of %d tests in %d cases." % (failurecount
, testcount
, casecount
))