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