forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			249 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			249 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			Python
		
	
	
	
"""Methods for reporting bugs."""
 | 
						|
 | 
						|
import subprocess, sys, os
 | 
						|
 | 
						|
__all__ = ['ReportFailure', 'BugReport', 'getReporters']
 | 
						|
 | 
						|
#
 | 
						|
 | 
						|
class ReportFailure(Exception):
 | 
						|
    """Generic exception for failures in bug reporting."""
 | 
						|
    def __init__(self, value):        
 | 
						|
        self.value = value
 | 
						|
 | 
						|
# Collect information about a bug.
 | 
						|
 | 
						|
class BugReport:
 | 
						|
    def __init__(self, title, description, files):
 | 
						|
        self.title = title
 | 
						|
        self.description = description
 | 
						|
        self.files = files
 | 
						|
 | 
						|
# Reporter interfaces.
 | 
						|
 | 
						|
import os
 | 
						|
 | 
						|
import email, mimetypes, smtplib
 | 
						|
from email import encoders
 | 
						|
from email.message import Message
 | 
						|
from email.mime.base import MIMEBase
 | 
						|
from email.mime.multipart import MIMEMultipart
 | 
						|
from email.mime.text import MIMEText
 | 
						|
 | 
						|
#===------------------------------------------------------------------------===#
 | 
						|
# ReporterParameter
 | 
						|
#===------------------------------------------------------------------------===#
 | 
						|
 | 
						|
class ReporterParameter:
 | 
						|
  def __init__(self, n):
 | 
						|
    self.name = n
 | 
						|
  def getName(self):
 | 
						|
    return self.name
 | 
						|
  def getValue(self,r,bugtype,getConfigOption):
 | 
						|
     return getConfigOption(r.getName(),self.getName())
 | 
						|
  def saveConfigValue(self):
 | 
						|
    return True
 | 
						|
 | 
						|
class TextParameter (ReporterParameter):
 | 
						|
  def getHTML(self,r,bugtype,getConfigOption):
 | 
						|
    return """\
 | 
						|
<tr>
 | 
						|
<td class="form_clabel">%s:</td>
 | 
						|
<td class="form_value"><input type="text" name="%s_%s" value="%s"></td>
 | 
						|
</tr>"""%(self.getName(),r.getName(),self.getName(),self.getValue(r,bugtype,getConfigOption))
 | 
						|
 | 
						|
class SelectionParameter (ReporterParameter):
 | 
						|
  def __init__(self, n, values):
 | 
						|
    ReporterParameter.__init__(self,n)
 | 
						|
    self.values = values
 | 
						|
    
 | 
						|
  def getHTML(self,r,bugtype,getConfigOption):
 | 
						|
    default = self.getValue(r,bugtype,getConfigOption)
 | 
						|
    return """\
 | 
						|
<tr>
 | 
						|
<td class="form_clabel">%s:</td><td class="form_value"><select name="%s_%s">
 | 
						|
%s
 | 
						|
</select></td>"""%(self.getName(),r.getName(),self.getName(),'\n'.join(["""\
 | 
						|
<option value="%s"%s>%s</option>"""%(o[0],
 | 
						|
                                     o[0] == default and ' selected="selected"' or '',
 | 
						|
                                     o[1]) for o in self.values]))
 | 
						|
 | 
						|
#===------------------------------------------------------------------------===#
 | 
						|
# Reporters
 | 
						|
#===------------------------------------------------------------------------===#
 | 
						|
 | 
						|
class EmailReporter:
 | 
						|
    def getName(self):
 | 
						|
        return 'Email'
 | 
						|
 | 
						|
    def getParameters(self):
 | 
						|
        return map(lambda x:TextParameter(x),['To', 'From', 'SMTP Server', 'SMTP Port'])
 | 
						|
 | 
						|
    # Lifted from python email module examples.
 | 
						|
    def attachFile(self, outer, path):
 | 
						|
        # Guess the content type based on the file's extension.  Encoding
 | 
						|
        # will be ignored, although we should check for simple things like
 | 
						|
        # gzip'd or compressed files.
 | 
						|
        ctype, encoding = mimetypes.guess_type(path)
 | 
						|
        if ctype is None or encoding is not None:
 | 
						|
            # No guess could be made, or the file is encoded (compressed), so
 | 
						|
            # use a generic bag-of-bits type.
 | 
						|
            ctype = 'application/octet-stream'
 | 
						|
        maintype, subtype = ctype.split('/', 1)
 | 
						|
        if maintype == 'text':
 | 
						|
            fp = open(path)
 | 
						|
            # Note: we should handle calculating the charset
 | 
						|
            msg = MIMEText(fp.read(), _subtype=subtype)
 | 
						|
            fp.close()
 | 
						|
        else:
 | 
						|
            fp = open(path, 'rb')
 | 
						|
            msg = MIMEBase(maintype, subtype)
 | 
						|
            msg.set_payload(fp.read())
 | 
						|
            fp.close()
 | 
						|
            # Encode the payload using Base64
 | 
						|
            encoders.encode_base64(msg)
 | 
						|
        # Set the filename parameter
 | 
						|
        msg.add_header('Content-Disposition', 'attachment', filename=os.path.basename(path))
 | 
						|
        outer.attach(msg)
 | 
						|
 | 
						|
    def fileReport(self, report, parameters):
 | 
						|
        mainMsg = """\
 | 
						|
BUG REPORT
 | 
						|
---
 | 
						|
Title: %s
 | 
						|
Description: %s
 | 
						|
"""%(report.title, report.description)
 | 
						|
 | 
						|
        if not parameters.get('To'):
 | 
						|
            raise ReportFailure('No "To" address specified.')
 | 
						|
        if not parameters.get('From'):
 | 
						|
            raise ReportFailure('No "From" address specified.')
 | 
						|
 | 
						|
        msg = MIMEMultipart()
 | 
						|
        msg['Subject'] = 'BUG REPORT: %s'%(report.title)
 | 
						|
        # FIXME: Get config parameters
 | 
						|
        msg['To'] = parameters.get('To')
 | 
						|
        msg['From'] = parameters.get('From')
 | 
						|
        msg.preamble = mainMsg
 | 
						|
 | 
						|
        msg.attach(MIMEText(mainMsg, _subtype='text/plain'))
 | 
						|
        for file in report.files:
 | 
						|
            self.attachFile(msg, file)
 | 
						|
 | 
						|
        try:
 | 
						|
            s = smtplib.SMTP(host=parameters.get('SMTP Server'),
 | 
						|
                             port=parameters.get('SMTP Port'))
 | 
						|
            s.sendmail(msg['From'], msg['To'], msg.as_string())
 | 
						|
            s.close()
 | 
						|
        except:
 | 
						|
            raise ReportFailure('Unable to send message via SMTP.')
 | 
						|
 | 
						|
        return "Message sent!"
 | 
						|
 | 
						|
class BugzillaReporter:
 | 
						|
    def getName(self):
 | 
						|
        return 'Bugzilla'
 | 
						|
    
 | 
						|
    def getParameters(self):
 | 
						|
        return map(lambda x:TextParameter(x),['URL','Product'])
 | 
						|
 | 
						|
    def fileReport(self, report, parameters):
 | 
						|
        raise NotImplementedError
 | 
						|
 
 | 
						|
 | 
						|
class RadarClassificationParameter(SelectionParameter):
 | 
						|
  def __init__(self):
 | 
						|
    SelectionParameter.__init__(self,"Classification",
 | 
						|
            [['1', 'Security'], ['2', 'Crash/Hang/Data Loss'],
 | 
						|
             ['3', 'Performance'], ['4', 'UI/Usability'], 
 | 
						|
             ['6', 'Serious Bug'], ['7', 'Other']])
 | 
						|
 | 
						|
  def saveConfigValue(self):
 | 
						|
    return False
 | 
						|
    
 | 
						|
  def getValue(self,r,bugtype,getConfigOption):
 | 
						|
    if bugtype.find("leak") != -1:
 | 
						|
      return '3'
 | 
						|
    elif bugtype.find("dereference") != -1:
 | 
						|
      return '2'
 | 
						|
    elif bugtype.find("missing ivar release") != -1:
 | 
						|
      return '3'
 | 
						|
    else:
 | 
						|
      return '7'
 | 
						|
 | 
						|
class RadarReporter:
 | 
						|
    @staticmethod
 | 
						|
    def isAvailable():
 | 
						|
        # FIXME: Find this .scpt better
 | 
						|
        path = os.path.join(os.path.dirname(__file__),'Resources/GetRadarVersion.scpt')
 | 
						|
        try:
 | 
						|
          p = subprocess.Popen(['osascript',path], 
 | 
						|
          stdout=subprocess.PIPE, stderr=subprocess.PIPE)
 | 
						|
        except:
 | 
						|
            return False
 | 
						|
        data,err = p.communicate()
 | 
						|
        res = p.wait()
 | 
						|
        # FIXME: Check version? Check for no errors?
 | 
						|
        return res == 0
 | 
						|
 | 
						|
    def getName(self):
 | 
						|
        return 'Radar'
 | 
						|
 | 
						|
    def getParameters(self):
 | 
						|
        return [ TextParameter('Component'), TextParameter('Component Version'),
 | 
						|
                 RadarClassificationParameter() ]
 | 
						|
 | 
						|
    def fileReport(self, report, parameters):
 | 
						|
        component = parameters.get('Component', '')
 | 
						|
        componentVersion = parameters.get('Component Version', '')
 | 
						|
        classification = parameters.get('Classification', '')
 | 
						|
        personID = ""
 | 
						|
        diagnosis = ""
 | 
						|
        config = ""
 | 
						|
 | 
						|
        if not component.strip():
 | 
						|
            component = 'Bugs found by clang Analyzer'
 | 
						|
        if not componentVersion.strip():
 | 
						|
            componentVersion = 'X'
 | 
						|
 | 
						|
        script = os.path.join(os.path.dirname(__file__),'Resources/FileRadar.scpt')
 | 
						|
        args = ['osascript', script, component, componentVersion, classification, personID, report.title,
 | 
						|
                report.description, diagnosis, config] + map(os.path.abspath, report.files)
 | 
						|
#        print >>sys.stderr, args
 | 
						|
        try:
 | 
						|
          p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
 | 
						|
        except:
 | 
						|
            raise ReportFailure("Unable to file radar (AppleScript failure).")
 | 
						|
        data, err = p.communicate()
 | 
						|
        res = p.wait()
 | 
						|
 | 
						|
        if res:
 | 
						|
            raise ReportFailure("Unable to file radar (AppleScript failure).")
 | 
						|
 | 
						|
        try:
 | 
						|
            values = eval(data)
 | 
						|
        except:
 | 
						|
            raise ReportFailure("Unable to process radar results.")
 | 
						|
 | 
						|
        # We expect (int: bugID, str: message)
 | 
						|
        if len(values) != 2 or not isinstance(values[0], int):
 | 
						|
            raise ReportFailure("Unable to process radar results.")
 | 
						|
 | 
						|
        bugID,message = values
 | 
						|
        bugID = int(bugID)
 | 
						|
        
 | 
						|
        if not bugID:
 | 
						|
            raise ReportFailure(message)
 | 
						|
        
 | 
						|
        return "Filed: <a href=\"rdar://%d/\">%d</a>"%(bugID,bugID)
 | 
						|
 | 
						|
###
 | 
						|
 | 
						|
def getReporters():
 | 
						|
    reporters = []
 | 
						|
    if RadarReporter.isAvailable():
 | 
						|
        reporters.append(RadarReporter())
 | 
						|
    reporters.append(EmailReporter())
 | 
						|
    return reporters
 | 
						|
 |