* src/SDCCval.c (checkConstantRange): added
[fw/sdcc] / support / valdiag / valdiag.py
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)
5 #
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.
10 #   
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.
15 #   
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.
19 #   
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 #---------------------------------------------------------------------------
24
25 import sys, string, os, popen2, re
26
27 macrodefs = {}
28
29 gcc = {
30     "CC":"gcc",
31     "CCFLAGS":"-c -Wall -DPORT_HOST=1",
32     "CCDEF":"-D",
33     "CCOUTPUT":"-o",
34     "defined": {
35         "__GNUC__":"1",
36         "GCC":"1"
37     },
38     "ignoremsg": [
39     ]
40 }
41
42 sdcc = {
43     "CC":"../../bin/sdcc",
44     "CCFLAGS":"-c -m{port}",
45     "CCDEF":"-D",
46     "CCOUTPUT":"-o",
47     "defined": {
48         "SDCC":"1",
49         "SDCC_{port}":"1",
50         "__{port}":"1"
51     },
52     "ignoremsg": [
53         "code not generated.*due to previous errors",
54         "unreferenced function argument"
55     ]
56 }
57
58 testmodes = {
59     "host":{
60         "compiler":gcc,
61         "port":"host",
62         "defined": {
63             "PORT_HOST":"1"
64         }
65     },
66     "mcs51":{
67         "compiler":sdcc,
68         "port":"mcs51"
69     },
70     "mcs51-large":{
71         "compiler":sdcc,
72         "port":"mcs51",
73         "flags":"--model-large",
74         "defined": {
75             "SDCC_MODEL_LARGE":"1"
76         }
77     },
78     "mcs51-stack-auto":{
79         "compiler":sdcc,
80         "port":"mcs51",
81         "flags":"--stack-auto",
82         "defined": {
83             "SDCC_STACK_AUTO":"1"
84         }
85     },
86     "ds390":{
87         "compiler":sdcc,
88         "port":"ds390"
89     },
90     "z80":{
91         "compiler":sdcc,
92         "port":"z80"
93     },
94     "gbz80":{
95         "compiler":sdcc,
96         "port":"gbz80"
97     },
98     "hc08":{
99         "compiler":sdcc,
100         "port":"hc08"
101     },
102     "pic14":{
103         "compiler":sdcc,
104         "port":"pic14"
105     },
106     "pic16":{
107         "compiler":sdcc,
108         "port":"pic16"
109     }
110 }
111     
112
113 def evalQualifier(expr):
114     global macrodefs
115     tokens = re.split("([^0-9A-Za-z_])", expr)
116     for tokenindex in range(len(tokens)):
117         token = tokens[tokenindex]
118         if token in macrodefs:
119             tokens[tokenindex] = macrodefs[token]
120         elif token == "defined":
121             tokens[tokenindex] = ""
122             if tokens[tokenindex+2] in macrodefs:
123                 tokens[tokenindex+2] = "1"
124             else:
125                 tokens[tokenindex+2] = "0"
126         elif len(token)>0:
127             if token[0]=="_" or token[0] in string.ascii_letters:
128                 tokens[tokenindex] = "0"
129     expr = string.join(tokens,"")
130     expr = string.replace(expr,"&&"," and ");
131     expr = string.replace(expr,"||"," or ");
132     expr = string.replace(expr,"!"," not ");
133     return eval(expr)
134     
135 def expandPyExpr(expr):
136     tokens = re.split("({|})", expr)
137     for tokenindex in range(1,len(tokens)):
138         if tokens[tokenindex-1]=="{":
139             tokens[tokenindex]=eval(tokens[tokenindex])
140             tokens[tokenindex-1]=""
141             tokens[tokenindex+1]=""
142     expandedExpr = string.join(tokens,"")
143     return expandedExpr
144     
145 def addDefines(deflist):
146     for define in deflist.keys():
147         expandeddef = expandPyExpr(define)
148         macrodefs[expandeddef] = expandPyExpr(deflist[define])
149
150 def parseInputfile(inputfilename):
151     inputfile = open(inputfilename, "r")
152     testcases = {}
153     testname = ""
154     linenumber = 1
155     
156     # Find the test cases and tests in this file
157     for line in inputfile.readlines():
158         
159         # See if a new testcase is being defined
160         p = string.find(line, "TEST")
161         if p>=0:
162             testname = string.split(line[p:])[0]
163             if not testcases.has_key(testname):
164                 testcases[testname] = {}
165         
166         # See if a new test is being defined
167         for testtype in ["ERROR", "WARNING", "IGNORE"]:
168             p = string.find(line, testtype);
169             if p>=0:
170                 # Found a test definition
171                 qualifier = string.strip(line[p+len(testtype):])
172                 p = string.find(qualifier, "*/")
173                 if p>=0:
174                     qualifier = string.strip(qualifier[:p])
175                 if len(qualifier)==0:
176                     qualifier="1"
177                 qualifier = evalQualifier(qualifier)
178                 if qualifier:
179                     if not linenumber in testcases[testname]:
180                         testcases[testname][linenumber]=[]
181                     testcases[testname][linenumber].append(testtype)
182         
183         linenumber = linenumber + 1
184     
185     inputfile.close()
186     return testcases
187
188 def parseResults(output):
189     results = {}
190     for line in output:
191         print line,
192         
193         if string.count(line, "SIGSEG"):
194             results[0] = ["FAULT", string.strip(line)]
195             continue
196         
197         # look for something of the form:
198         #   filename:line:message
199         msg = string.split(line,":",2)
200         if len(msg)<3: continue
201         if msg[0]!=inputfilename: continue
202         if len(msg[1])==0: continue
203         if not msg[1][0] in string.digits: continue
204         
205         # it's in the right form; parse it
206         linenumber = int(msg[1])
207         msgtype = "UNKNOWN"
208         uppermsg = string.upper(msg[2])
209         if string.count(uppermsg,"ERROR"):
210             msgtype = "ERROR"
211         if string.count(uppermsg,"WARNING"):
212             msgtype = "WARNING"
213         msgtext = string.strip(msg[2])
214         ignore = 0
215         for ignoreExpr in ignoreExprList:
216            if re.search(ignoreExpr,msgtext)!=None:
217                ignore = 1
218         if not ignore:
219             results[linenumber]=[msgtype,string.strip(msg[2])]
220     return results
221
222 def showUsage():
223     print "Usage: test testmode cfile [objectfile]"
224     print "Choices for testmode are:"
225     for testmodename in testmodes.keys():
226         print "   %s" % testmodename
227     sys.exit(1)
228       
229 # Start here         
230 if len(sys.argv)<3:
231     showUsage()
232     
233 testmodename = sys.argv[1]
234 if not testmodename in testmodes:
235     print "Unknown test mode '%s'" % testmodename
236     showUsage()
237
238 testmode = testmodes[testmodename]
239 compilermode = testmode["compiler"]
240 port = expandPyExpr(testmode["port"])
241 cc = expandPyExpr(compilermode["CC"])
242 ccflags = expandPyExpr(compilermode["CCFLAGS"])
243 if "flags" in testmode:
244     ccflags = string.join([ccflags,expandPyExpr(testmode["flags"])])
245 if len(sys.argv)>=4:
246     if "CCOUTPUT" in compilermode:
247         ccflags = string.join([ccflags,expandPyExpr(compilermode["CCOUTPUT"]),sys.argv[3]])
248 if "defined" in compilermode:
249     addDefines(compilermode["defined"])
250 if "defined" in testmode:
251     addDefines(testmode["defined"])
252 if "ignoremsg" in compilermode:
253     ignoreExprList = compilermode["ignoremsg"]
254 else:
255     ignoreExprList = []
256
257 inputfilename = sys.argv[2]
258 try:
259     testcases = parseInputfile(inputfilename)
260 except IOError:
261     print "Unable to read file '%s'" % inputfilename
262     sys.exit(1)
263
264 casecount = len(testcases.keys())
265 testcount = 0
266 failurecount = 0
267
268 for testname in testcases.keys():
269     ccdef = compilermode["CCDEF"]+testname
270     cmd = string.join([cc,ccflags,ccdef,inputfilename])
271     print
272     print cmd
273     spawn = popen2.Popen4(cmd)
274     spawn.wait()
275     output = spawn.fromchild.readlines()
276     
277     results = parseResults(output)
278     
279     if len(testcases[testname])==0:
280         testcount = testcount + 1 #implicit test for no errors
281
282     # Go through the tests of this case and make sure
283     # the compiler gave a diagnostic
284     for checkline in testcases[testname].keys():
285         testcount = testcount + 1
286         if checkline in results:
287             if "IGNORE" in testcases[testname][checkline]:
288                 testcount = testcount - 1  #this isn't really a test
289             del results[checkline]
290         else:
291             for wanted in testcases[testname][checkline]:
292                 if not wanted=="IGNORE":
293                     print "--- FAIL: expected %s" % wanted,
294                     print "at %s:%d" % (inputfilename, checkline)
295                     failurecount = failurecount + 1
296
297     # Output any unexpected diagnostics    
298     for checkline in results.keys():
299         print '--- FAIL: unexpected message "%s" ' % results[checkline][1],
300         print "at %s:%d" % (inputfilename, checkline)
301         failurecount = failurecount + 1
302
303 print
304 print "--- Summary: %d/%d/%d: " % (failurecount, testcount, casecount),
305 print "%d failed of %d tests in %d cases." % (failurecount, testcount, casecount)