xref: /minix3/external/bsd/llvm/dist/clang/tools/scan-view/Reporter.py (revision f4a2713ac843a11c696ec80c0a5e3e5d80b4d338)
1*f4a2713aSLionel Sambuc"""Methods for reporting bugs."""
2*f4a2713aSLionel Sambuc
3*f4a2713aSLionel Sambucimport subprocess, sys, os
4*f4a2713aSLionel Sambuc
5*f4a2713aSLionel Sambuc__all__ = ['ReportFailure', 'BugReport', 'getReporters']
6*f4a2713aSLionel Sambuc
7*f4a2713aSLionel Sambuc#
8*f4a2713aSLionel Sambuc
9*f4a2713aSLionel Sambucclass ReportFailure(Exception):
10*f4a2713aSLionel Sambuc    """Generic exception for failures in bug reporting."""
11*f4a2713aSLionel Sambuc    def __init__(self, value):
12*f4a2713aSLionel Sambuc        self.value = value
13*f4a2713aSLionel Sambuc
14*f4a2713aSLionel Sambuc# Collect information about a bug.
15*f4a2713aSLionel Sambuc
16*f4a2713aSLionel Sambucclass BugReport:
17*f4a2713aSLionel Sambuc    def __init__(self, title, description, files):
18*f4a2713aSLionel Sambuc        self.title = title
19*f4a2713aSLionel Sambuc        self.description = description
20*f4a2713aSLionel Sambuc        self.files = files
21*f4a2713aSLionel Sambuc
22*f4a2713aSLionel Sambuc# Reporter interfaces.
23*f4a2713aSLionel Sambuc
24*f4a2713aSLionel Sambucimport os
25*f4a2713aSLionel Sambuc
26*f4a2713aSLionel Sambucimport email, mimetypes, smtplib
27*f4a2713aSLionel Sambucfrom email import encoders
28*f4a2713aSLionel Sambucfrom email.message import Message
29*f4a2713aSLionel Sambucfrom email.mime.base import MIMEBase
30*f4a2713aSLionel Sambucfrom email.mime.multipart import MIMEMultipart
31*f4a2713aSLionel Sambucfrom email.mime.text import MIMEText
32*f4a2713aSLionel Sambuc
33*f4a2713aSLionel Sambuc#===------------------------------------------------------------------------===#
34*f4a2713aSLionel Sambuc# ReporterParameter
35*f4a2713aSLionel Sambuc#===------------------------------------------------------------------------===#
36*f4a2713aSLionel Sambuc
37*f4a2713aSLionel Sambucclass ReporterParameter:
38*f4a2713aSLionel Sambuc  def __init__(self, n):
39*f4a2713aSLionel Sambuc    self.name = n
40*f4a2713aSLionel Sambuc  def getName(self):
41*f4a2713aSLionel Sambuc    return self.name
42*f4a2713aSLionel Sambuc  def getValue(self,r,bugtype,getConfigOption):
43*f4a2713aSLionel Sambuc     return getConfigOption(r.getName(),self.getName())
44*f4a2713aSLionel Sambuc  def saveConfigValue(self):
45*f4a2713aSLionel Sambuc    return True
46*f4a2713aSLionel Sambuc
47*f4a2713aSLionel Sambucclass TextParameter (ReporterParameter):
48*f4a2713aSLionel Sambuc  def getHTML(self,r,bugtype,getConfigOption):
49*f4a2713aSLionel Sambuc    return """\
50*f4a2713aSLionel Sambuc<tr>
51*f4a2713aSLionel Sambuc<td class="form_clabel">%s:</td>
52*f4a2713aSLionel Sambuc<td class="form_value"><input type="text" name="%s_%s" value="%s"></td>
53*f4a2713aSLionel Sambuc</tr>"""%(self.getName(),r.getName(),self.getName(),self.getValue(r,bugtype,getConfigOption))
54*f4a2713aSLionel Sambuc
55*f4a2713aSLionel Sambucclass SelectionParameter (ReporterParameter):
56*f4a2713aSLionel Sambuc  def __init__(self, n, values):
57*f4a2713aSLionel Sambuc    ReporterParameter.__init__(self,n)
58*f4a2713aSLionel Sambuc    self.values = values
59*f4a2713aSLionel Sambuc
60*f4a2713aSLionel Sambuc  def getHTML(self,r,bugtype,getConfigOption):
61*f4a2713aSLionel Sambuc    default = self.getValue(r,bugtype,getConfigOption)
62*f4a2713aSLionel Sambuc    return """\
63*f4a2713aSLionel Sambuc<tr>
64*f4a2713aSLionel Sambuc<td class="form_clabel">%s:</td><td class="form_value"><select name="%s_%s">
65*f4a2713aSLionel Sambuc%s
66*f4a2713aSLionel Sambuc</select></td>"""%(self.getName(),r.getName(),self.getName(),'\n'.join(["""\
67*f4a2713aSLionel Sambuc<option value="%s"%s>%s</option>"""%(o[0],
68*f4a2713aSLionel Sambuc                                     o[0] == default and ' selected="selected"' or '',
69*f4a2713aSLionel Sambuc                                     o[1]) for o in self.values]))
70*f4a2713aSLionel Sambuc
71*f4a2713aSLionel Sambuc#===------------------------------------------------------------------------===#
72*f4a2713aSLionel Sambuc# Reporters
73*f4a2713aSLionel Sambuc#===------------------------------------------------------------------------===#
74*f4a2713aSLionel Sambuc
75*f4a2713aSLionel Sambucclass EmailReporter:
76*f4a2713aSLionel Sambuc    def getName(self):
77*f4a2713aSLionel Sambuc        return 'Email'
78*f4a2713aSLionel Sambuc
79*f4a2713aSLionel Sambuc    def getParameters(self):
80*f4a2713aSLionel Sambuc        return map(lambda x:TextParameter(x),['To', 'From', 'SMTP Server', 'SMTP Port'])
81*f4a2713aSLionel Sambuc
82*f4a2713aSLionel Sambuc    # Lifted from python email module examples.
83*f4a2713aSLionel Sambuc    def attachFile(self, outer, path):
84*f4a2713aSLionel Sambuc        # Guess the content type based on the file's extension.  Encoding
85*f4a2713aSLionel Sambuc        # will be ignored, although we should check for simple things like
86*f4a2713aSLionel Sambuc        # gzip'd or compressed files.
87*f4a2713aSLionel Sambuc        ctype, encoding = mimetypes.guess_type(path)
88*f4a2713aSLionel Sambuc        if ctype is None or encoding is not None:
89*f4a2713aSLionel Sambuc            # No guess could be made, or the file is encoded (compressed), so
90*f4a2713aSLionel Sambuc            # use a generic bag-of-bits type.
91*f4a2713aSLionel Sambuc            ctype = 'application/octet-stream'
92*f4a2713aSLionel Sambuc        maintype, subtype = ctype.split('/', 1)
93*f4a2713aSLionel Sambuc        if maintype == 'text':
94*f4a2713aSLionel Sambuc            fp = open(path)
95*f4a2713aSLionel Sambuc            # Note: we should handle calculating the charset
96*f4a2713aSLionel Sambuc            msg = MIMEText(fp.read(), _subtype=subtype)
97*f4a2713aSLionel Sambuc            fp.close()
98*f4a2713aSLionel Sambuc        else:
99*f4a2713aSLionel Sambuc            fp = open(path, 'rb')
100*f4a2713aSLionel Sambuc            msg = MIMEBase(maintype, subtype)
101*f4a2713aSLionel Sambuc            msg.set_payload(fp.read())
102*f4a2713aSLionel Sambuc            fp.close()
103*f4a2713aSLionel Sambuc            # Encode the payload using Base64
104*f4a2713aSLionel Sambuc            encoders.encode_base64(msg)
105*f4a2713aSLionel Sambuc        # Set the filename parameter
106*f4a2713aSLionel Sambuc        msg.add_header('Content-Disposition', 'attachment', filename=os.path.basename(path))
107*f4a2713aSLionel Sambuc        outer.attach(msg)
108*f4a2713aSLionel Sambuc
109*f4a2713aSLionel Sambuc    def fileReport(self, report, parameters):
110*f4a2713aSLionel Sambuc        mainMsg = """\
111*f4a2713aSLionel SambucBUG REPORT
112*f4a2713aSLionel Sambuc---
113*f4a2713aSLionel SambucTitle: %s
114*f4a2713aSLionel SambucDescription: %s
115*f4a2713aSLionel Sambuc"""%(report.title, report.description)
116*f4a2713aSLionel Sambuc
117*f4a2713aSLionel Sambuc        if not parameters.get('To'):
118*f4a2713aSLionel Sambuc            raise ReportFailure('No "To" address specified.')
119*f4a2713aSLionel Sambuc        if not parameters.get('From'):
120*f4a2713aSLionel Sambuc            raise ReportFailure('No "From" address specified.')
121*f4a2713aSLionel Sambuc
122*f4a2713aSLionel Sambuc        msg = MIMEMultipart()
123*f4a2713aSLionel Sambuc        msg['Subject'] = 'BUG REPORT: %s'%(report.title)
124*f4a2713aSLionel Sambuc        # FIXME: Get config parameters
125*f4a2713aSLionel Sambuc        msg['To'] = parameters.get('To')
126*f4a2713aSLionel Sambuc        msg['From'] = parameters.get('From')
127*f4a2713aSLionel Sambuc        msg.preamble = mainMsg
128*f4a2713aSLionel Sambuc
129*f4a2713aSLionel Sambuc        msg.attach(MIMEText(mainMsg, _subtype='text/plain'))
130*f4a2713aSLionel Sambuc        for file in report.files:
131*f4a2713aSLionel Sambuc            self.attachFile(msg, file)
132*f4a2713aSLionel Sambuc
133*f4a2713aSLionel Sambuc        try:
134*f4a2713aSLionel Sambuc            s = smtplib.SMTP(host=parameters.get('SMTP Server'),
135*f4a2713aSLionel Sambuc                             port=parameters.get('SMTP Port'))
136*f4a2713aSLionel Sambuc            s.sendmail(msg['From'], msg['To'], msg.as_string())
137*f4a2713aSLionel Sambuc            s.close()
138*f4a2713aSLionel Sambuc        except:
139*f4a2713aSLionel Sambuc            raise ReportFailure('Unable to send message via SMTP.')
140*f4a2713aSLionel Sambuc
141*f4a2713aSLionel Sambuc        return "Message sent!"
142*f4a2713aSLionel Sambuc
143*f4a2713aSLionel Sambucclass BugzillaReporter:
144*f4a2713aSLionel Sambuc    def getName(self):
145*f4a2713aSLionel Sambuc        return 'Bugzilla'
146*f4a2713aSLionel Sambuc
147*f4a2713aSLionel Sambuc    def getParameters(self):
148*f4a2713aSLionel Sambuc        return map(lambda x:TextParameter(x),['URL','Product'])
149*f4a2713aSLionel Sambuc
150*f4a2713aSLionel Sambuc    def fileReport(self, report, parameters):
151*f4a2713aSLionel Sambuc        raise NotImplementedError
152*f4a2713aSLionel Sambuc
153*f4a2713aSLionel Sambuc
154*f4a2713aSLionel Sambucclass RadarClassificationParameter(SelectionParameter):
155*f4a2713aSLionel Sambuc  def __init__(self):
156*f4a2713aSLionel Sambuc    SelectionParameter.__init__(self,"Classification",
157*f4a2713aSLionel Sambuc            [['1', 'Security'], ['2', 'Crash/Hang/Data Loss'],
158*f4a2713aSLionel Sambuc             ['3', 'Performance'], ['4', 'UI/Usability'],
159*f4a2713aSLionel Sambuc             ['6', 'Serious Bug'], ['7', 'Other']])
160*f4a2713aSLionel Sambuc
161*f4a2713aSLionel Sambuc  def saveConfigValue(self):
162*f4a2713aSLionel Sambuc    return False
163*f4a2713aSLionel Sambuc
164*f4a2713aSLionel Sambuc  def getValue(self,r,bugtype,getConfigOption):
165*f4a2713aSLionel Sambuc    if bugtype.find("leak") != -1:
166*f4a2713aSLionel Sambuc      return '3'
167*f4a2713aSLionel Sambuc    elif bugtype.find("dereference") != -1:
168*f4a2713aSLionel Sambuc      return '2'
169*f4a2713aSLionel Sambuc    elif bugtype.find("missing ivar release") != -1:
170*f4a2713aSLionel Sambuc      return '3'
171*f4a2713aSLionel Sambuc    else:
172*f4a2713aSLionel Sambuc      return '7'
173*f4a2713aSLionel Sambuc
174*f4a2713aSLionel Sambucclass RadarReporter:
175*f4a2713aSLionel Sambuc    @staticmethod
176*f4a2713aSLionel Sambuc    def isAvailable():
177*f4a2713aSLionel Sambuc        # FIXME: Find this .scpt better
178*f4a2713aSLionel Sambuc        path = os.path.join(os.path.dirname(__file__),'Resources/GetRadarVersion.scpt')
179*f4a2713aSLionel Sambuc        try:
180*f4a2713aSLionel Sambuc          p = subprocess.Popen(['osascript',path],
181*f4a2713aSLionel Sambuc          stdout=subprocess.PIPE, stderr=subprocess.PIPE)
182*f4a2713aSLionel Sambuc        except:
183*f4a2713aSLionel Sambuc            return False
184*f4a2713aSLionel Sambuc        data,err = p.communicate()
185*f4a2713aSLionel Sambuc        res = p.wait()
186*f4a2713aSLionel Sambuc        # FIXME: Check version? Check for no errors?
187*f4a2713aSLionel Sambuc        return res == 0
188*f4a2713aSLionel Sambuc
189*f4a2713aSLionel Sambuc    def getName(self):
190*f4a2713aSLionel Sambuc        return 'Radar'
191*f4a2713aSLionel Sambuc
192*f4a2713aSLionel Sambuc    def getParameters(self):
193*f4a2713aSLionel Sambuc        return [ TextParameter('Component'), TextParameter('Component Version'),
194*f4a2713aSLionel Sambuc                 RadarClassificationParameter() ]
195*f4a2713aSLionel Sambuc
196*f4a2713aSLionel Sambuc    def fileReport(self, report, parameters):
197*f4a2713aSLionel Sambuc        component = parameters.get('Component', '')
198*f4a2713aSLionel Sambuc        componentVersion = parameters.get('Component Version', '')
199*f4a2713aSLionel Sambuc        classification = parameters.get('Classification', '')
200*f4a2713aSLionel Sambuc        personID = ""
201*f4a2713aSLionel Sambuc        diagnosis = ""
202*f4a2713aSLionel Sambuc        config = ""
203*f4a2713aSLionel Sambuc
204*f4a2713aSLionel Sambuc        if not component.strip():
205*f4a2713aSLionel Sambuc            component = 'Bugs found by clang Analyzer'
206*f4a2713aSLionel Sambuc        if not componentVersion.strip():
207*f4a2713aSLionel Sambuc            componentVersion = 'X'
208*f4a2713aSLionel Sambuc
209*f4a2713aSLionel Sambuc        script = os.path.join(os.path.dirname(__file__),'Resources/FileRadar.scpt')
210*f4a2713aSLionel Sambuc        args = ['osascript', script, component, componentVersion, classification, personID, report.title,
211*f4a2713aSLionel Sambuc                report.description, diagnosis, config] + map(os.path.abspath, report.files)
212*f4a2713aSLionel Sambuc#        print >>sys.stderr, args
213*f4a2713aSLionel Sambuc        try:
214*f4a2713aSLionel Sambuc          p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
215*f4a2713aSLionel Sambuc        except:
216*f4a2713aSLionel Sambuc            raise ReportFailure("Unable to file radar (AppleScript failure).")
217*f4a2713aSLionel Sambuc        data, err = p.communicate()
218*f4a2713aSLionel Sambuc        res = p.wait()
219*f4a2713aSLionel Sambuc
220*f4a2713aSLionel Sambuc        if res:
221*f4a2713aSLionel Sambuc            raise ReportFailure("Unable to file radar (AppleScript failure).")
222*f4a2713aSLionel Sambuc
223*f4a2713aSLionel Sambuc        try:
224*f4a2713aSLionel Sambuc            values = eval(data)
225*f4a2713aSLionel Sambuc        except:
226*f4a2713aSLionel Sambuc            raise ReportFailure("Unable to process radar results.")
227*f4a2713aSLionel Sambuc
228*f4a2713aSLionel Sambuc        # We expect (int: bugID, str: message)
229*f4a2713aSLionel Sambuc        if len(values) != 2 or not isinstance(values[0], int):
230*f4a2713aSLionel Sambuc            raise ReportFailure("Unable to process radar results.")
231*f4a2713aSLionel Sambuc
232*f4a2713aSLionel Sambuc        bugID,message = values
233*f4a2713aSLionel Sambuc        bugID = int(bugID)
234*f4a2713aSLionel Sambuc
235*f4a2713aSLionel Sambuc        if not bugID:
236*f4a2713aSLionel Sambuc            raise ReportFailure(message)
237*f4a2713aSLionel Sambuc
238*f4a2713aSLionel Sambuc        return "Filed: <a href=\"rdar://%d/\">%d</a>"%(bugID,bugID)
239*f4a2713aSLionel Sambuc
240*f4a2713aSLionel Sambuc###
241*f4a2713aSLionel Sambuc
242*f4a2713aSLionel Sambucdef getReporters():
243*f4a2713aSLionel Sambuc    reporters = []
244*f4a2713aSLionel Sambuc    if RadarReporter.isAvailable():
245*f4a2713aSLionel Sambuc        reporters.append(RadarReporter())
246*f4a2713aSLionel Sambuc    reporters.append(EmailReporter())
247*f4a2713aSLionel Sambuc    return reporters
248*f4a2713aSLionel Sambuc
249