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