mirror of
https://github.com/zhigang1992/RubyMotion.git
synced 2026-06-18 15:39:10 +08:00
[rake] Add analyze task which performs a static analysis on the codebase.
Vendored relavant part from http://clang-analyzer.llvm.org/downloads/checker-275.tar.bz2
This commit is contained in:
7
Rakefile
7
Rakefile
@@ -56,6 +56,13 @@ end
|
||||
desc "Build all targets"
|
||||
task :build => targets.map { |x| "build:#{x}" }
|
||||
|
||||
desc "Run the clang static analyzer against the source"
|
||||
task :analyze do
|
||||
sh './static-analyzer/scan-build --keep-empty --use-analyzer=/usr/bin/clang -o ./static-analysis rake'
|
||||
results = Dir.glob('static-analysis/*').sort_by { |path| File.mtime(path) }.last
|
||||
sh "./static-analyzer/scan-view #{results}"
|
||||
end
|
||||
|
||||
targets.each do |target|
|
||||
desc "Clean target #{target}"
|
||||
task "clean:#{target}" do
|
||||
|
||||
248
static-analyzer/Reporter.py
Normal file
248
static-analyzer/Reporter.py
Normal file
@@ -0,0 +1,248 @@
|
||||
"""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
|
||||
|
||||
BIN
static-analyzer/Reporter.pyc
Normal file
BIN
static-analyzer/Reporter.pyc
Normal file
Binary file not shown.
BIN
static-analyzer/Resources/FileRadar.scpt
Normal file
BIN
static-analyzer/Resources/FileRadar.scpt
Normal file
Binary file not shown.
0
static-analyzer/Resources/GetRadarVersion.scpt
Normal file
0
static-analyzer/Resources/GetRadarVersion.scpt
Normal file
BIN
static-analyzer/Resources/bugcatcher.ico
Normal file
BIN
static-analyzer/Resources/bugcatcher.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 318 B |
767
static-analyzer/ScanView.py
Normal file
767
static-analyzer/ScanView.py
Normal file
@@ -0,0 +1,767 @@
|
||||
import BaseHTTPServer
|
||||
import SimpleHTTPServer
|
||||
import os
|
||||
import sys
|
||||
import urllib, urlparse
|
||||
import posixpath
|
||||
import StringIO
|
||||
import re
|
||||
import shutil
|
||||
import threading
|
||||
import time
|
||||
import socket
|
||||
import itertools
|
||||
|
||||
import Reporter
|
||||
import ConfigParser
|
||||
|
||||
###
|
||||
# Various patterns matched or replaced by server.
|
||||
|
||||
kReportFileRE = re.compile('(.*/)?report-(.*)\\.html')
|
||||
|
||||
kBugKeyValueRE = re.compile('<!-- BUG([^ ]*) (.*) -->')
|
||||
|
||||
# <!-- REPORTPROBLEM file="crashes/clang_crash_ndSGF9.mi" stderr="crashes/clang_crash_ndSGF9.mi.stderr.txt" info="crashes/clang_crash_ndSGF9.mi.info" -->
|
||||
|
||||
kReportCrashEntryRE = re.compile('<!-- REPORTPROBLEM (.*?)-->')
|
||||
kReportCrashEntryKeyValueRE = re.compile(' ?([^=]+)="(.*?)"')
|
||||
|
||||
kReportReplacements = []
|
||||
|
||||
# Add custom javascript.
|
||||
kReportReplacements.append((re.compile('<!-- SUMMARYENDHEAD -->'), """\
|
||||
<script language="javascript" type="text/javascript">
|
||||
function load(url) {
|
||||
if (window.XMLHttpRequest) {
|
||||
req = new XMLHttpRequest();
|
||||
} else if (window.ActiveXObject) {
|
||||
req = new ActiveXObject("Microsoft.XMLHTTP");
|
||||
}
|
||||
if (req != undefined) {
|
||||
req.open("GET", url, true);
|
||||
req.send("");
|
||||
}
|
||||
}
|
||||
</script>"""))
|
||||
|
||||
# Insert additional columns.
|
||||
kReportReplacements.append((re.compile('<!-- REPORTBUGCOL -->'),
|
||||
'<td></td><td></td>'))
|
||||
|
||||
# Insert report bug and open file links.
|
||||
kReportReplacements.append((re.compile('<!-- REPORTBUG id="report-(.*)\\.html" -->'),
|
||||
('<td class="Button"><a href="report/\\1">Report Bug</a></td>' +
|
||||
'<td class="Button"><a href="javascript:load(\'open/\\1\')">Open File</a></td>')))
|
||||
|
||||
kReportReplacements.append((re.compile('<!-- REPORTHEADER -->'),
|
||||
'<h3><a href="/">Summary</a> > Report %(report)s</h3>'))
|
||||
|
||||
kReportReplacements.append((re.compile('<!-- REPORTSUMMARYEXTRA -->'),
|
||||
'<td class="Button"><a href="report/%(report)s">Report Bug</a></td>'))
|
||||
|
||||
# Insert report crashes link.
|
||||
|
||||
# Disabled for the time being until we decide exactly when this should
|
||||
# be enabled. Also the radar reporter needs to be fixed to report
|
||||
# multiple files.
|
||||
|
||||
#kReportReplacements.append((re.compile('<!-- REPORTCRASHES -->'),
|
||||
# '<br>These files will automatically be attached to ' +
|
||||
# 'reports filed here: <a href="report_crashes">Report Crashes</a>.'))
|
||||
|
||||
###
|
||||
# Other simple parameters
|
||||
|
||||
kResources = posixpath.join(posixpath.dirname(__file__), 'Resources')
|
||||
kConfigPath = os.path.expanduser('~/.scanview.cfg')
|
||||
|
||||
###
|
||||
|
||||
__version__ = "0.1"
|
||||
|
||||
__all__ = ["create_server"]
|
||||
|
||||
class ReporterThread(threading.Thread):
|
||||
def __init__(self, report, reporter, parameters, server):
|
||||
threading.Thread.__init__(self)
|
||||
self.report = report
|
||||
self.server = server
|
||||
self.reporter = reporter
|
||||
self.parameters = parameters
|
||||
self.success = False
|
||||
self.status = None
|
||||
|
||||
def run(self):
|
||||
result = None
|
||||
try:
|
||||
if self.server.options.debug:
|
||||
print >>sys.stderr, "%s: SERVER: submitting bug."%(sys.argv[0],)
|
||||
self.status = self.reporter.fileReport(self.report, self.parameters)
|
||||
self.success = True
|
||||
time.sleep(3)
|
||||
if self.server.options.debug:
|
||||
print >>sys.stderr, "%s: SERVER: submission complete."%(sys.argv[0],)
|
||||
except Reporter.ReportFailure,e:
|
||||
self.status = e.value
|
||||
except Exception,e:
|
||||
s = StringIO.StringIO()
|
||||
import traceback
|
||||
print >>s,'<b>Unhandled Exception</b><br><pre>'
|
||||
traceback.print_exc(e,file=s)
|
||||
print >>s,'</pre>'
|
||||
self.status = s.getvalue()
|
||||
|
||||
class ScanViewServer(BaseHTTPServer.HTTPServer):
|
||||
def __init__(self, address, handler, root, reporters, options):
|
||||
BaseHTTPServer.HTTPServer.__init__(self, address, handler)
|
||||
self.root = root
|
||||
self.reporters = reporters
|
||||
self.options = options
|
||||
self.halted = False
|
||||
self.config = None
|
||||
self.load_config()
|
||||
|
||||
def load_config(self):
|
||||
self.config = ConfigParser.RawConfigParser()
|
||||
|
||||
# Add defaults
|
||||
self.config.add_section('ScanView')
|
||||
for r in self.reporters:
|
||||
self.config.add_section(r.getName())
|
||||
for p in r.getParameters():
|
||||
if p.saveConfigValue():
|
||||
self.config.set(r.getName(), p.getName(), '')
|
||||
|
||||
# Ignore parse errors
|
||||
try:
|
||||
self.config.read([kConfigPath])
|
||||
except:
|
||||
pass
|
||||
|
||||
# Save on exit
|
||||
import atexit
|
||||
atexit.register(lambda: self.save_config())
|
||||
|
||||
def save_config(self):
|
||||
# Ignore errors (only called on exit).
|
||||
try:
|
||||
f = open(kConfigPath,'w')
|
||||
self.config.write(f)
|
||||
f.close()
|
||||
except:
|
||||
pass
|
||||
|
||||
def halt(self):
|
||||
self.halted = True
|
||||
if self.options.debug:
|
||||
print >>sys.stderr, "%s: SERVER: halting." % (sys.argv[0],)
|
||||
|
||||
def serve_forever(self):
|
||||
while not self.halted:
|
||||
if self.options.debug > 1:
|
||||
print >>sys.stderr, "%s: SERVER: waiting..." % (sys.argv[0],)
|
||||
try:
|
||||
self.handle_request()
|
||||
except OSError,e:
|
||||
print 'OSError',e.errno
|
||||
|
||||
def finish_request(self, request, client_address):
|
||||
if self.options.autoReload:
|
||||
import ScanView
|
||||
self.RequestHandlerClass = reload(ScanView).ScanViewRequestHandler
|
||||
BaseHTTPServer.HTTPServer.finish_request(self, request, client_address)
|
||||
|
||||
def handle_error(self, request, client_address):
|
||||
# Ignore socket errors
|
||||
info = sys.exc_info()
|
||||
if info and isinstance(info[1], socket.error):
|
||||
if self.options.debug > 1:
|
||||
print >>sys.stderr, "%s: SERVER: ignored socket error." % (sys.argv[0],)
|
||||
return
|
||||
BaseHTTPServer.HTTPServer.handle_error(self, request, client_address)
|
||||
|
||||
# Borrowed from Quixote, with simplifications.
|
||||
def parse_query(qs, fields=None):
|
||||
if fields is None:
|
||||
fields = {}
|
||||
for chunk in filter(None, qs.split('&')):
|
||||
if '=' not in chunk:
|
||||
name = chunk
|
||||
value = ''
|
||||
else:
|
||||
name, value = chunk.split('=', 1)
|
||||
name = urllib.unquote(name.replace('+', ' '))
|
||||
value = urllib.unquote(value.replace('+', ' '))
|
||||
item = fields.get(name)
|
||||
if item is None:
|
||||
fields[name] = [value]
|
||||
else:
|
||||
item.append(value)
|
||||
return fields
|
||||
|
||||
class ScanViewRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
||||
server_version = "ScanViewServer/" + __version__
|
||||
dynamic_mtime = time.time()
|
||||
|
||||
def do_HEAD(self):
|
||||
try:
|
||||
SimpleHTTPServer.SimpleHTTPRequestHandler.do_HEAD(self)
|
||||
except Exception,e:
|
||||
self.handle_exception(e)
|
||||
|
||||
def do_GET(self):
|
||||
try:
|
||||
SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
|
||||
except Exception,e:
|
||||
self.handle_exception(e)
|
||||
|
||||
def do_POST(self):
|
||||
"""Serve a POST request."""
|
||||
try:
|
||||
length = self.headers.getheader('content-length') or "0"
|
||||
try:
|
||||
length = int(length)
|
||||
except:
|
||||
length = 0
|
||||
content = self.rfile.read(length)
|
||||
fields = parse_query(content)
|
||||
f = self.send_head(fields)
|
||||
if f:
|
||||
self.copyfile(f, self.wfile)
|
||||
f.close()
|
||||
except Exception,e:
|
||||
self.handle_exception(e)
|
||||
|
||||
def log_message(self, format, *args):
|
||||
if self.server.options.debug:
|
||||
sys.stderr.write("%s: SERVER: %s - - [%s] %s\n" %
|
||||
(sys.argv[0],
|
||||
self.address_string(),
|
||||
self.log_date_time_string(),
|
||||
format%args))
|
||||
|
||||
def load_report(self, report):
|
||||
path = os.path.join(self.server.root, 'report-%s.html'%report)
|
||||
data = open(path).read()
|
||||
keys = {}
|
||||
for item in kBugKeyValueRE.finditer(data):
|
||||
k,v = item.groups()
|
||||
keys[k] = v
|
||||
return keys
|
||||
|
||||
def load_crashes(self):
|
||||
path = posixpath.join(self.server.root, 'index.html')
|
||||
data = open(path).read()
|
||||
problems = []
|
||||
for item in kReportCrashEntryRE.finditer(data):
|
||||
fieldData = item.group(1)
|
||||
fields = dict([i.groups() for i in
|
||||
kReportCrashEntryKeyValueRE.finditer(fieldData)])
|
||||
problems.append(fields)
|
||||
return problems
|
||||
|
||||
def handle_exception(self, exc):
|
||||
import traceback
|
||||
s = StringIO.StringIO()
|
||||
print >>s, "INTERNAL ERROR\n"
|
||||
traceback.print_exc(exc, s)
|
||||
f = self.send_string(s.getvalue(), 'text/plain')
|
||||
if f:
|
||||
self.copyfile(f, self.wfile)
|
||||
f.close()
|
||||
|
||||
def get_scalar_field(self, name):
|
||||
if name in self.fields:
|
||||
return self.fields[name][0]
|
||||
else:
|
||||
return None
|
||||
|
||||
def submit_bug(self, c):
|
||||
title = self.get_scalar_field('title')
|
||||
description = self.get_scalar_field('description')
|
||||
report = self.get_scalar_field('report')
|
||||
reporterIndex = self.get_scalar_field('reporter')
|
||||
files = []
|
||||
for fileID in self.fields.get('files',[]):
|
||||
try:
|
||||
i = int(fileID)
|
||||
except:
|
||||
i = None
|
||||
if i is None or i<0 or i>=len(c.files):
|
||||
return (False, 'Invalid file ID')
|
||||
files.append(c.files[i])
|
||||
|
||||
if not title:
|
||||
return (False, "Missing title.")
|
||||
if not description:
|
||||
return (False, "Missing description.")
|
||||
try:
|
||||
reporterIndex = int(reporterIndex)
|
||||
except:
|
||||
return (False, "Invalid report method.")
|
||||
|
||||
# Get the reporter and parameters.
|
||||
reporter = self.server.reporters[reporterIndex]
|
||||
parameters = {}
|
||||
for o in reporter.getParameters():
|
||||
name = '%s_%s'%(reporter.getName(),o.getName())
|
||||
if name not in self.fields:
|
||||
return (False,
|
||||
'Missing field "%s" for %s report method.'%(name,
|
||||
reporter.getName()))
|
||||
parameters[o.getName()] = self.get_scalar_field(name)
|
||||
|
||||
# Update config defaults.
|
||||
if report != 'None':
|
||||
self.server.config.set('ScanView', 'reporter', reporterIndex)
|
||||
for o in reporter.getParameters():
|
||||
if o.saveConfigValue():
|
||||
name = o.getName()
|
||||
self.server.config.set(reporter.getName(), name, parameters[name])
|
||||
|
||||
# Create the report.
|
||||
bug = Reporter.BugReport(title, description, files)
|
||||
|
||||
# Kick off a reporting thread.
|
||||
t = ReporterThread(bug, reporter, parameters, self.server)
|
||||
t.start()
|
||||
|
||||
# Wait for thread to die...
|
||||
while t.isAlive():
|
||||
time.sleep(.25)
|
||||
submitStatus = t.status
|
||||
|
||||
return (t.success, t.status)
|
||||
|
||||
def send_report_submit(self):
|
||||
report = self.get_scalar_field('report')
|
||||
c = self.get_report_context(report)
|
||||
if c.reportSource is None:
|
||||
reportingFor = "Report Crashes > "
|
||||
fileBug = """\
|
||||
<a href="/report_crashes">File Bug</a> > """%locals()
|
||||
else:
|
||||
reportingFor = '<a href="/%s">Report %s</a> > ' % (c.reportSource,
|
||||
report)
|
||||
fileBug = '<a href="/report/%s">File Bug</a> > ' % report
|
||||
title = self.get_scalar_field('title')
|
||||
description = self.get_scalar_field('description')
|
||||
|
||||
res,message = self.submit_bug(c)
|
||||
|
||||
if res:
|
||||
statusClass = 'SubmitOk'
|
||||
statusName = 'Succeeded'
|
||||
else:
|
||||
statusClass = 'SubmitFail'
|
||||
statusName = 'Failed'
|
||||
|
||||
result = """
|
||||
<head>
|
||||
<title>Bug Submission</title>
|
||||
<link rel="stylesheet" type="text/css" href="/scanview.css" />
|
||||
</head>
|
||||
<body>
|
||||
<h3>
|
||||
<a href="/">Summary</a> >
|
||||
%(reportingFor)s
|
||||
%(fileBug)s
|
||||
Submit</h3>
|
||||
<form name="form" action="">
|
||||
<table class="form">
|
||||
<tr><td>
|
||||
<table class="form_group">
|
||||
<tr>
|
||||
<td class="form_clabel">Title:</td>
|
||||
<td class="form_value">
|
||||
<input type="text" name="title" size="50" value="%(title)s" disabled>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="form_label">Description:</td>
|
||||
<td class="form_value">
|
||||
<textarea rows="10" cols="80" name="description" disabled>
|
||||
%(description)s
|
||||
</textarea>
|
||||
</td>
|
||||
</table>
|
||||
</td></tr>
|
||||
</table>
|
||||
</form>
|
||||
<h1 class="%(statusClass)s">Submission %(statusName)s</h1>
|
||||
%(message)s
|
||||
<p>
|
||||
<hr>
|
||||
<a href="/">Return to Summary</a>
|
||||
</body>
|
||||
</html>"""%locals()
|
||||
return self.send_string(result)
|
||||
|
||||
def send_open_report(self, report):
|
||||
try:
|
||||
keys = self.load_report(report)
|
||||
except IOError:
|
||||
return self.send_error(400, 'Invalid report.')
|
||||
|
||||
file = keys.get('FILE')
|
||||
if not file or not posixpath.exists(file):
|
||||
return self.send_error(400, 'File does not exist: "%s"' % file)
|
||||
|
||||
import startfile
|
||||
if self.server.options.debug:
|
||||
print >>sys.stderr, '%s: SERVER: opening "%s"'%(sys.argv[0],
|
||||
file)
|
||||
|
||||
status = startfile.open(file)
|
||||
if status:
|
||||
res = 'Opened: "%s"' % file
|
||||
else:
|
||||
res = 'Open failed: "%s"' % file
|
||||
|
||||
return self.send_string(res, 'text/plain')
|
||||
|
||||
def get_report_context(self, report):
|
||||
class Context:
|
||||
pass
|
||||
if report is None or report == 'None':
|
||||
data = self.load_crashes()
|
||||
# Don't allow empty reports.
|
||||
if not data:
|
||||
raise ValueError, 'No crashes detected!'
|
||||
c = Context()
|
||||
c.title = 'clang static analyzer failures'
|
||||
|
||||
stderrSummary = ""
|
||||
for item in data:
|
||||
if 'stderr' in item:
|
||||
path = posixpath.join(self.server.root, item['stderr'])
|
||||
if os.path.exists(path):
|
||||
lns = itertools.islice(open(path), 0, 10)
|
||||
stderrSummary += '%s\n--\n%s' % (item.get('src',
|
||||
'<unknown>'),
|
||||
''.join(lns))
|
||||
|
||||
c.description = """\
|
||||
The clang static analyzer failed on these inputs:
|
||||
%s
|
||||
|
||||
STDERR Summary
|
||||
--------------
|
||||
%s
|
||||
""" % ('\n'.join([item.get('src','<unknown>') for item in data]),
|
||||
stderrSummary)
|
||||
c.reportSource = None
|
||||
c.navMarkup = "Report Crashes > "
|
||||
c.files = []
|
||||
for item in data:
|
||||
c.files.append(item.get('src',''))
|
||||
c.files.append(posixpath.join(self.server.root,
|
||||
item.get('file','')))
|
||||
c.files.append(posixpath.join(self.server.root,
|
||||
item.get('clangfile','')))
|
||||
c.files.append(posixpath.join(self.server.root,
|
||||
item.get('stderr','')))
|
||||
c.files.append(posixpath.join(self.server.root,
|
||||
item.get('info','')))
|
||||
# Just in case something failed, ignore files which don't
|
||||
# exist.
|
||||
c.files = [f for f in c.files
|
||||
if os.path.exists(f) and os.path.isfile(f)]
|
||||
else:
|
||||
# Check that this is a valid report.
|
||||
path = posixpath.join(self.server.root, 'report-%s.html' % report)
|
||||
if not posixpath.exists(path):
|
||||
raise ValueError, 'Invalid report ID'
|
||||
keys = self.load_report(report)
|
||||
c = Context()
|
||||
c.title = keys.get('DESC','clang error (unrecognized')
|
||||
c.description = """\
|
||||
Bug reported by the clang static analyzer.
|
||||
|
||||
Description: %s
|
||||
File: %s
|
||||
Line: %s
|
||||
"""%(c.title, keys.get('FILE','<unknown>'), keys.get('LINE', '<unknown>'))
|
||||
c.reportSource = 'report-%s.html' % report
|
||||
c.navMarkup = """<a href="/%s">Report %s</a> > """ % (c.reportSource,
|
||||
report)
|
||||
|
||||
c.files = [path]
|
||||
return c
|
||||
|
||||
def send_report(self, report, configOverrides=None):
|
||||
def getConfigOption(section, field):
|
||||
if (configOverrides is not None and
|
||||
section in configOverrides and
|
||||
field in configOverrides[section]):
|
||||
return configOverrides[section][field]
|
||||
return self.server.config.get(section, field)
|
||||
|
||||
# report is None is used for crashes
|
||||
try:
|
||||
c = self.get_report_context(report)
|
||||
except ValueError, e:
|
||||
return self.send_error(400, e.message)
|
||||
|
||||
title = c.title
|
||||
description= c.description
|
||||
reportingFor = c.navMarkup
|
||||
if c.reportSource is None:
|
||||
extraIFrame = ""
|
||||
else:
|
||||
extraIFrame = """\
|
||||
<iframe src="/%s" width="100%%" height="40%%"
|
||||
scrolling="auto" frameborder="1">
|
||||
<a href="/%s">View Bug Report</a>
|
||||
</iframe>""" % (c.reportSource, c.reportSource)
|
||||
|
||||
reporterSelections = []
|
||||
reporterOptions = []
|
||||
|
||||
try:
|
||||
active = int(getConfigOption('ScanView','reporter'))
|
||||
except:
|
||||
active = 0
|
||||
for i,r in enumerate(self.server.reporters):
|
||||
selected = (i == active)
|
||||
if selected:
|
||||
selectedStr = ' selected'
|
||||
else:
|
||||
selectedStr = ''
|
||||
reporterSelections.append('<option value="%d"%s>%s</option>'%(i,selectedStr,r.getName()))
|
||||
options = '\n'.join([ o.getHTML(r,title,getConfigOption) for o in r.getParameters()])
|
||||
display = ('none','')[selected]
|
||||
reporterOptions.append("""\
|
||||
<tr id="%sReporterOptions" style="display:%s">
|
||||
<td class="form_label">%s Options</td>
|
||||
<td class="form_value">
|
||||
<table class="form_inner_group">
|
||||
%s
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
"""%(r.getName(),display,r.getName(),options))
|
||||
reporterSelections = '\n'.join(reporterSelections)
|
||||
reporterOptionsDivs = '\n'.join(reporterOptions)
|
||||
reportersArray = '[%s]'%(','.join([`r.getName()` for r in self.server.reporters]))
|
||||
|
||||
if c.files:
|
||||
fieldSize = min(5, len(c.files))
|
||||
attachFileOptions = '\n'.join(["""\
|
||||
<option value="%d" selected>%s</option>""" % (i,v) for i,v in enumerate(c.files)])
|
||||
attachFileRow = """\
|
||||
<tr>
|
||||
<td class="form_label">Attach:</td>
|
||||
<td class="form_value">
|
||||
<select style="width:100%%" name="files" multiple size=%d>
|
||||
%s
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
""" % (min(5, len(c.files)), attachFileOptions)
|
||||
else:
|
||||
attachFileRow = ""
|
||||
|
||||
result = """<html>
|
||||
<head>
|
||||
<title>File Bug</title>
|
||||
<link rel="stylesheet" type="text/css" href="/scanview.css" />
|
||||
</head>
|
||||
<script language="javascript" type="text/javascript">
|
||||
var reporters = %(reportersArray)s;
|
||||
function updateReporterOptions() {
|
||||
index = document.getElementById('reporter').selectedIndex;
|
||||
for (var i=0; i < reporters.length; ++i) {
|
||||
o = document.getElementById(reporters[i] + "ReporterOptions");
|
||||
if (i == index) {
|
||||
o.style.display = "";
|
||||
} else {
|
||||
o.style.display = "none";
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<body onLoad="updateReporterOptions()">
|
||||
<h3>
|
||||
<a href="/">Summary</a> >
|
||||
%(reportingFor)s
|
||||
File Bug</h3>
|
||||
<form name="form" action="/report_submit" method="post">
|
||||
<input type="hidden" name="report" value="%(report)s">
|
||||
|
||||
<table class="form">
|
||||
<tr><td>
|
||||
<table class="form_group">
|
||||
<tr>
|
||||
<td class="form_clabel">Title:</td>
|
||||
<td class="form_value">
|
||||
<input type="text" name="title" size="50" value="%(title)s">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="form_label">Description:</td>
|
||||
<td class="form_value">
|
||||
<textarea rows="10" cols="80" name="description">
|
||||
%(description)s
|
||||
</textarea>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
%(attachFileRow)s
|
||||
|
||||
</table>
|
||||
<br>
|
||||
<table class="form_group">
|
||||
<tr>
|
||||
<td class="form_clabel">Method:</td>
|
||||
<td class="form_value">
|
||||
<select id="reporter" name="reporter" onChange="updateReporterOptions()">
|
||||
%(reporterSelections)s
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
%(reporterOptionsDivs)s
|
||||
</table>
|
||||
<br>
|
||||
</td></tr>
|
||||
<tr><td class="form_submit">
|
||||
<input align="right" type="submit" name="Submit" value="Submit">
|
||||
</td></tr>
|
||||
</table>
|
||||
</form>
|
||||
|
||||
%(extraIFrame)s
|
||||
|
||||
</body>
|
||||
</html>"""%locals()
|
||||
|
||||
return self.send_string(result)
|
||||
|
||||
def send_head(self, fields=None):
|
||||
if (self.server.options.onlyServeLocal and
|
||||
self.client_address[0] != '127.0.0.1'):
|
||||
return self.send_error(401, 'Unauthorized host.')
|
||||
|
||||
if fields is None:
|
||||
fields = {}
|
||||
self.fields = fields
|
||||
|
||||
o = urlparse.urlparse(self.path)
|
||||
self.fields = parse_query(o.query, fields)
|
||||
path = posixpath.normpath(urllib.unquote(o.path))
|
||||
|
||||
# Split the components and strip the root prefix.
|
||||
components = path.split('/')[1:]
|
||||
|
||||
# Special case some top-level entries.
|
||||
if components:
|
||||
name = components[0]
|
||||
if len(components)==2:
|
||||
if name=='report':
|
||||
return self.send_report(components[1])
|
||||
elif name=='open':
|
||||
return self.send_open_report(components[1])
|
||||
elif len(components)==1:
|
||||
if name=='quit':
|
||||
self.server.halt()
|
||||
return self.send_string('Goodbye.', 'text/plain')
|
||||
elif name=='report_submit':
|
||||
return self.send_report_submit()
|
||||
elif name=='report_crashes':
|
||||
overrides = { 'ScanView' : {},
|
||||
'Radar' : {},
|
||||
'Email' : {} }
|
||||
for i,r in enumerate(self.server.reporters):
|
||||
if r.getName() == 'Radar':
|
||||
overrides['ScanView']['reporter'] = i
|
||||
break
|
||||
overrides['Radar']['Component'] = 'llvm - checker'
|
||||
overrides['Radar']['Component Version'] = 'X'
|
||||
return self.send_report(None, overrides)
|
||||
elif name=='favicon.ico':
|
||||
return self.send_path(posixpath.join(kResources,'bugcatcher.ico'))
|
||||
|
||||
# Match directory entries.
|
||||
if components[-1] == '':
|
||||
components[-1] = 'index.html'
|
||||
|
||||
relpath = '/'.join(components)
|
||||
path = posixpath.join(self.server.root, relpath)
|
||||
|
||||
if self.server.options.debug > 1:
|
||||
print >>sys.stderr, '%s: SERVER: sending path "%s"'%(sys.argv[0],
|
||||
path)
|
||||
return self.send_path(path)
|
||||
|
||||
def send_404(self):
|
||||
self.send_error(404, "File not found")
|
||||
return None
|
||||
|
||||
def send_path(self, path):
|
||||
# If the requested path is outside the root directory, do not open it
|
||||
rel = os.path.abspath(path)
|
||||
if not rel.startswith(os.path.abspath(self.server.root)):
|
||||
return self.send_404()
|
||||
|
||||
ctype = self.guess_type(path)
|
||||
if ctype.startswith('text/'):
|
||||
# Patch file instead
|
||||
return self.send_patched_file(path, ctype)
|
||||
else:
|
||||
mode = 'rb'
|
||||
try:
|
||||
f = open(path, mode)
|
||||
except IOError:
|
||||
return self.send_404()
|
||||
return self.send_file(f, ctype)
|
||||
|
||||
def send_file(self, f, ctype):
|
||||
# Patch files to add links, but skip binary files.
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", ctype)
|
||||
fs = os.fstat(f.fileno())
|
||||
self.send_header("Content-Length", str(fs[6]))
|
||||
self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
|
||||
self.end_headers()
|
||||
return f
|
||||
|
||||
def send_string(self, s, ctype='text/html', headers=True, mtime=None):
|
||||
if headers:
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", ctype)
|
||||
self.send_header("Content-Length", str(len(s)))
|
||||
if mtime is None:
|
||||
mtime = self.dynamic_mtime
|
||||
self.send_header("Last-Modified", self.date_time_string(mtime))
|
||||
self.end_headers()
|
||||
return StringIO.StringIO(s)
|
||||
|
||||
def send_patched_file(self, path, ctype):
|
||||
# Allow a very limited set of variables. This is pretty gross.
|
||||
variables = {}
|
||||
variables['report'] = ''
|
||||
m = kReportFileRE.match(path)
|
||||
if m:
|
||||
variables['report'] = m.group(2)
|
||||
|
||||
try:
|
||||
f = open(path,'r')
|
||||
except IOError:
|
||||
return self.send_404()
|
||||
fs = os.fstat(f.fileno())
|
||||
data = f.read()
|
||||
for a,b in kReportReplacements:
|
||||
data = a.sub(b % variables, data)
|
||||
return self.send_string(data, ctype, mtime=fs.st_mtime)
|
||||
|
||||
|
||||
def create_server(address, options, root):
|
||||
import Reporter
|
||||
|
||||
reporters = Reporter.getReporters()
|
||||
|
||||
return ScanViewServer(address, ScanViewRequestHandler,
|
||||
root,
|
||||
reporters,
|
||||
options)
|
||||
BIN
static-analyzer/ScanView.pyc
Normal file
BIN
static-analyzer/ScanView.pyc
Normal file
Binary file not shown.
8
static-analyzer/libexec/c++-analyzer
Executable file
8
static-analyzer/libexec/c++-analyzer
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
use Cwd qw/ abs_path /;
|
||||
use File::Basename qw/ dirname /;
|
||||
# Add scan-build dir to the list of places where perl looks for modules.
|
||||
use lib dirname(abs_path($0));
|
||||
|
||||
do 'ccc-analyzer';
|
||||
710
static-analyzer/libexec/ccc-analyzer
Executable file
710
static-analyzer/libexec/ccc-analyzer
Executable file
@@ -0,0 +1,710 @@
|
||||
#!/usr/bin/env perl
|
||||
#
|
||||
# The LLVM Compiler Infrastructure
|
||||
#
|
||||
# This file is distributed under the University of Illinois Open Source
|
||||
# License. See LICENSE.TXT for details.
|
||||
#
|
||||
##===----------------------------------------------------------------------===##
|
||||
#
|
||||
# A script designed to interpose between the build system and gcc. It invokes
|
||||
# both gcc and the static analyzer.
|
||||
#
|
||||
##===----------------------------------------------------------------------===##
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use FindBin;
|
||||
use Cwd qw/ getcwd abs_path /;
|
||||
use File::Temp qw/ tempfile /;
|
||||
use File::Path qw / mkpath /;
|
||||
use File::Basename;
|
||||
use Text::ParseWords;
|
||||
|
||||
##===----------------------------------------------------------------------===##
|
||||
# Compiler command setup.
|
||||
##===----------------------------------------------------------------------===##
|
||||
|
||||
my $Compiler;
|
||||
my $Clang;
|
||||
my $DefaultCCompiler;
|
||||
my $DefaultCXXCompiler;
|
||||
|
||||
if (`uname -a` =~ m/Darwin/) {
|
||||
$DefaultCCompiler = 'clang';
|
||||
$DefaultCXXCompiler = 'clang++';
|
||||
} else {
|
||||
$DefaultCCompiler = 'gcc';
|
||||
$DefaultCXXCompiler = 'g++';
|
||||
}
|
||||
|
||||
if ($FindBin::Script =~ /c\+\+-analyzer/) {
|
||||
$Compiler = $ENV{'CCC_CXX'};
|
||||
if (!defined $Compiler) { $Compiler = $DefaultCXXCompiler; }
|
||||
|
||||
$Clang = $ENV{'CLANG_CXX'};
|
||||
if (!defined $Clang) { $Clang = 'clang++'; }
|
||||
}
|
||||
else {
|
||||
$Compiler = $ENV{'CCC_CC'};
|
||||
if (!defined $Compiler) { $Compiler = $DefaultCCompiler; }
|
||||
|
||||
$Clang = $ENV{'CLANG'};
|
||||
if (!defined $Clang) { $Clang = 'clang'; }
|
||||
}
|
||||
|
||||
##===----------------------------------------------------------------------===##
|
||||
# Cleanup.
|
||||
##===----------------------------------------------------------------------===##
|
||||
|
||||
my $ReportFailures = $ENV{'CCC_REPORT_FAILURES'};
|
||||
if (!defined $ReportFailures) { $ReportFailures = 1; }
|
||||
|
||||
my $CleanupFile;
|
||||
my $ResultFile;
|
||||
|
||||
# Remove any stale files at exit.
|
||||
END {
|
||||
if (defined $ResultFile && -z $ResultFile) {
|
||||
`rm -f $ResultFile`;
|
||||
}
|
||||
if (defined $CleanupFile) {
|
||||
`rm -f $CleanupFile`;
|
||||
}
|
||||
}
|
||||
|
||||
##----------------------------------------------------------------------------##
|
||||
# Process Clang Crashes.
|
||||
##----------------------------------------------------------------------------##
|
||||
|
||||
sub GetPPExt {
|
||||
my $Lang = shift;
|
||||
if ($Lang =~ /objective-c\+\+/) { return ".mii" };
|
||||
if ($Lang =~ /objective-c/) { return ".mi"; }
|
||||
if ($Lang =~ /c\+\+/) { return ".ii"; }
|
||||
return ".i";
|
||||
}
|
||||
|
||||
# Set this to 1 if we want to include 'parser rejects' files.
|
||||
my $IncludeParserRejects = 0;
|
||||
my $ParserRejects = "Parser Rejects";
|
||||
my $AttributeIgnored = "Attribute Ignored";
|
||||
my $OtherError = "Other Error";
|
||||
|
||||
sub ProcessClangFailure {
|
||||
my ($Clang, $Lang, $file, $Args, $HtmlDir, $ErrorType, $ofile) = @_;
|
||||
my $Dir = "$HtmlDir/failures";
|
||||
mkpath $Dir;
|
||||
|
||||
my $prefix = "clang_crash";
|
||||
if ($ErrorType eq $ParserRejects) {
|
||||
$prefix = "clang_parser_rejects";
|
||||
}
|
||||
elsif ($ErrorType eq $AttributeIgnored) {
|
||||
$prefix = "clang_attribute_ignored";
|
||||
}
|
||||
elsif ($ErrorType eq $OtherError) {
|
||||
$prefix = "clang_other_error";
|
||||
}
|
||||
|
||||
# Generate the preprocessed file with Clang.
|
||||
my ($PPH, $PPFile) = tempfile( $prefix . "_XXXXXX",
|
||||
SUFFIX => GetPPExt($Lang),
|
||||
DIR => $Dir);
|
||||
system $Clang, @$Args, "-E", "-o", $PPFile;
|
||||
close ($PPH);
|
||||
|
||||
# Create the info file.
|
||||
open (OUT, ">", "$PPFile.info.txt") or die "Cannot open $PPFile.info.txt\n";
|
||||
print OUT abs_path($file), "\n";
|
||||
print OUT "$ErrorType\n";
|
||||
print OUT "@$Args\n";
|
||||
close OUT;
|
||||
`uname -a >> $PPFile.info.txt 2>&1`;
|
||||
`$Compiler -v >> $PPFile.info.txt 2>&1`;
|
||||
system 'mv',$ofile,"$PPFile.stderr.txt";
|
||||
return (basename $PPFile);
|
||||
}
|
||||
|
||||
##----------------------------------------------------------------------------##
|
||||
# Running the analyzer.
|
||||
##----------------------------------------------------------------------------##
|
||||
|
||||
sub GetCCArgs {
|
||||
my $mode = shift;
|
||||
my $Args = shift;
|
||||
|
||||
pipe (FROM_CHILD, TO_PARENT);
|
||||
my $pid = fork();
|
||||
if ($pid == 0) {
|
||||
close FROM_CHILD;
|
||||
open(STDOUT,">&", \*TO_PARENT);
|
||||
open(STDERR,">&", \*TO_PARENT);
|
||||
exec $Clang, "-###", $mode, @$Args;
|
||||
}
|
||||
close(TO_PARENT);
|
||||
my $line;
|
||||
while (<FROM_CHILD>) {
|
||||
next if (!/-cc1/);
|
||||
$line = $_;
|
||||
}
|
||||
|
||||
waitpid($pid,0);
|
||||
close(FROM_CHILD);
|
||||
|
||||
die "could not find clang line\n" if (!defined $line);
|
||||
# Strip the newline and initial whitspace
|
||||
chomp $line;
|
||||
$line =~ s/^\s+//;
|
||||
my @items = quotewords('\s+', 0, $line);
|
||||
my $cmd = shift @items;
|
||||
die "cannot find 'clang' in 'clang' command\n" if (!($cmd =~ /clang/));
|
||||
return \@items;
|
||||
}
|
||||
|
||||
sub Analyze {
|
||||
my ($Clang, $OriginalArgs, $AnalyzeArgs, $Lang, $Output, $Verbose, $HtmlDir,
|
||||
$file) = @_;
|
||||
|
||||
my @Args = @$OriginalArgs;
|
||||
my $Cmd;
|
||||
my @CmdArgs;
|
||||
my @CmdArgsSansAnalyses;
|
||||
|
||||
if ($Lang =~ /header/) {
|
||||
exit 0 if (!defined ($Output));
|
||||
$Cmd = 'cp';
|
||||
push @CmdArgs, $file;
|
||||
# Remove the PCH extension.
|
||||
$Output =~ s/[.]gch$//;
|
||||
push @CmdArgs, $Output;
|
||||
@CmdArgsSansAnalyses = @CmdArgs;
|
||||
}
|
||||
else {
|
||||
$Cmd = $Clang;
|
||||
|
||||
# Create arguments for doing regular parsing.
|
||||
my $SyntaxArgs = GetCCArgs("-fsyntax-only", \@Args);
|
||||
@CmdArgsSansAnalyses = @$SyntaxArgs;
|
||||
|
||||
# Create arguments for doing static analysis.
|
||||
if (defined $ResultFile) {
|
||||
push @Args, '-o', $ResultFile;
|
||||
}
|
||||
elsif (defined $HtmlDir) {
|
||||
push @Args, '-o', $HtmlDir;
|
||||
}
|
||||
if ($Verbose) {
|
||||
push @Args, "-Xclang", "-analyzer-display-progress";
|
||||
}
|
||||
|
||||
foreach my $arg (@$AnalyzeArgs) {
|
||||
push @Args, "-Xclang", $arg;
|
||||
}
|
||||
|
||||
# Display Ubiviz graph?
|
||||
if (defined $ENV{'CCC_UBI'}) {
|
||||
push @Args, "-Xclang", "-analyzer-viz-egraph-ubigraph";
|
||||
}
|
||||
|
||||
my $AnalysisArgs = GetCCArgs("--analyze", \@Args);
|
||||
@CmdArgs = @$AnalysisArgs;
|
||||
}
|
||||
|
||||
my @PrintArgs;
|
||||
my $dir;
|
||||
|
||||
if ($Verbose) {
|
||||
$dir = getcwd();
|
||||
print STDERR "\n[LOCATION]: $dir\n";
|
||||
push @PrintArgs,"'$Cmd'";
|
||||
foreach my $arg (@CmdArgs) {
|
||||
push @PrintArgs,"\'$arg\'";
|
||||
}
|
||||
}
|
||||
if ($Verbose == 1) {
|
||||
# We MUST print to stderr. Some clients use the stdout output of
|
||||
# gcc for various purposes.
|
||||
print STDERR join(' ', @PrintArgs);
|
||||
print STDERR "\n";
|
||||
}
|
||||
elsif ($Verbose == 2) {
|
||||
print STDERR "#SHELL (cd '$dir' && @PrintArgs)\n";
|
||||
}
|
||||
|
||||
# Capture the STDERR of clang and send it to a temporary file.
|
||||
# Capture the STDOUT of clang and reroute it to ccc-analyzer's STDERR.
|
||||
# We save the output file in the 'crashes' directory if clang encounters
|
||||
# any problems with the file.
|
||||
pipe (FROM_CHILD, TO_PARENT);
|
||||
my $pid = fork();
|
||||
if ($pid == 0) {
|
||||
close FROM_CHILD;
|
||||
open(STDOUT,">&", \*TO_PARENT);
|
||||
open(STDERR,">&", \*TO_PARENT);
|
||||
exec $Cmd, @CmdArgs;
|
||||
}
|
||||
|
||||
close TO_PARENT;
|
||||
my ($ofh, $ofile) = tempfile("clang_output_XXXXXX", DIR => $HtmlDir);
|
||||
|
||||
while (<FROM_CHILD>) {
|
||||
print $ofh $_;
|
||||
print STDERR $_;
|
||||
}
|
||||
close $ofh;
|
||||
|
||||
waitpid($pid,0);
|
||||
close(FROM_CHILD);
|
||||
my $Result = $?;
|
||||
|
||||
# Did the command die because of a signal?
|
||||
if ($ReportFailures) {
|
||||
if ($Result & 127 and $Cmd eq $Clang and defined $HtmlDir) {
|
||||
ProcessClangFailure($Clang, $Lang, $file, \@CmdArgsSansAnalyses,
|
||||
$HtmlDir, "Crash", $ofile);
|
||||
}
|
||||
elsif ($Result) {
|
||||
if ($IncludeParserRejects && !($file =~/conftest/)) {
|
||||
ProcessClangFailure($Clang, $Lang, $file, \@CmdArgsSansAnalyses,
|
||||
$HtmlDir, $ParserRejects, $ofile);
|
||||
} else {
|
||||
ProcessClangFailure($Clang, $Lang, $file, \@CmdArgsSansAnalyses,
|
||||
$HtmlDir, $OtherError, $ofile);
|
||||
}
|
||||
}
|
||||
else {
|
||||
# Check if there were any unhandled attributes.
|
||||
if (open(CHILD, $ofile)) {
|
||||
my %attributes_not_handled;
|
||||
|
||||
# Don't flag warnings about the following attributes that we
|
||||
# know are currently not supported by Clang.
|
||||
$attributes_not_handled{"cdecl"} = 1;
|
||||
|
||||
my $ppfile;
|
||||
while (<CHILD>) {
|
||||
next if (! /warning: '([^\']+)' attribute ignored/);
|
||||
|
||||
# Have we already spotted this unhandled attribute?
|
||||
next if (defined $attributes_not_handled{$1});
|
||||
$attributes_not_handled{$1} = 1;
|
||||
|
||||
# Get the name of the attribute file.
|
||||
my $dir = "$HtmlDir/failures";
|
||||
my $afile = "$dir/attribute_ignored_$1.txt";
|
||||
|
||||
# Only create another preprocessed file if the attribute file
|
||||
# doesn't exist yet.
|
||||
next if (-e $afile);
|
||||
|
||||
# Add this file to the list of files that contained this attribute.
|
||||
# Generate a preprocessed file if we haven't already.
|
||||
if (!(defined $ppfile)) {
|
||||
$ppfile = ProcessClangFailure($Clang, $Lang, $file,
|
||||
\@CmdArgsSansAnalyses,
|
||||
$HtmlDir, $AttributeIgnored, $ofile);
|
||||
}
|
||||
|
||||
mkpath $dir;
|
||||
open(AFILE, ">$afile");
|
||||
print AFILE "$ppfile\n";
|
||||
close(AFILE);
|
||||
}
|
||||
close CHILD;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unlink($ofile);
|
||||
}
|
||||
|
||||
##----------------------------------------------------------------------------##
|
||||
# Lookup tables.
|
||||
##----------------------------------------------------------------------------##
|
||||
|
||||
my %CompileOptionMap = (
|
||||
'-nostdinc' => 0,
|
||||
'-fblocks' => 0,
|
||||
'-fno-builtin' => 0,
|
||||
'-fobjc-gc-only' => 0,
|
||||
'-fobjc-gc' => 0,
|
||||
'-ffreestanding' => 0,
|
||||
'-include' => 1,
|
||||
'-idirafter' => 1,
|
||||
'-imacros' => 1,
|
||||
'-iprefix' => 1,
|
||||
'-iquote' => 1,
|
||||
'-isystem' => 1,
|
||||
'-iwithprefix' => 1,
|
||||
'-iwithprefixbefore' => 1
|
||||
);
|
||||
|
||||
my %LinkerOptionMap = (
|
||||
'-framework' => 1,
|
||||
'-fobjc-link-runtime' => 0
|
||||
);
|
||||
|
||||
my %CompilerLinkerOptionMap = (
|
||||
'-fobjc-arc' => 0,
|
||||
'-fno-objc-arc' => 0,
|
||||
'-fobjc-abi-version' => 0, # This is really a 1 argument, but always has '='
|
||||
'-fobjc-legacy-dispatch' => 0,
|
||||
'-mios-simulator-version-min' => 0, # This really has 1 argument, but always has '='
|
||||
'-isysroot' => 1,
|
||||
'-arch' => 1,
|
||||
'-m32' => 0,
|
||||
'-m64' => 0,
|
||||
'-stdlib' => 0, # This is really a 1 argument, but always has '='
|
||||
'-v' => 0,
|
||||
'-fpascal-strings' => 0,
|
||||
'-mmacosx-version-min' => 0, # This is really a 1 argument, but always has '='
|
||||
'-miphoneos-version-min' => 0 # This is really a 1 argument, but always has '='
|
||||
);
|
||||
|
||||
my %IgnoredOptionMap = (
|
||||
'-MT' => 1, # Ignore these preprocessor options.
|
||||
'-MF' => 1,
|
||||
|
||||
'-fsyntax-only' => 0,
|
||||
'-save-temps' => 0,
|
||||
'-install_name' => 1,
|
||||
'-exported_symbols_list' => 1,
|
||||
'-current_version' => 1,
|
||||
'-compatibility_version' => 1,
|
||||
'-init' => 1,
|
||||
'-e' => 1,
|
||||
'-seg1addr' => 1,
|
||||
'-bundle_loader' => 1,
|
||||
'-multiply_defined' => 1,
|
||||
'-sectorder' => 3,
|
||||
'--param' => 1,
|
||||
'-u' => 1,
|
||||
'--serialize-diagnostics' => 1
|
||||
);
|
||||
|
||||
my %LangMap = (
|
||||
'c' => 'c',
|
||||
'cp' => 'c++',
|
||||
'cpp' => 'c++',
|
||||
'cxx' => 'c++',
|
||||
'txx' => 'c++',
|
||||
'cc' => 'c++',
|
||||
'C' => 'c++',
|
||||
'ii' => 'c++',
|
||||
'i' => 'c-cpp-output',
|
||||
'm' => 'objective-c',
|
||||
'mi' => 'objective-c-cpp-output',
|
||||
'mm' => 'objective-c++'
|
||||
);
|
||||
|
||||
my %UniqueOptions = (
|
||||
'-isysroot' => 0
|
||||
);
|
||||
|
||||
##----------------------------------------------------------------------------##
|
||||
# Languages accepted.
|
||||
##----------------------------------------------------------------------------##
|
||||
|
||||
my %LangsAccepted = (
|
||||
"objective-c" => 1,
|
||||
"c" => 1,
|
||||
"c++" => 1,
|
||||
"objective-c++" => 1
|
||||
);
|
||||
|
||||
##----------------------------------------------------------------------------##
|
||||
# Main Logic.
|
||||
##----------------------------------------------------------------------------##
|
||||
|
||||
my $Action = 'link';
|
||||
my @CompileOpts;
|
||||
my @LinkOpts;
|
||||
my @Files;
|
||||
my $Lang;
|
||||
my $Output;
|
||||
my %Uniqued;
|
||||
|
||||
# Forward arguments to gcc.
|
||||
my $Status = system($Compiler,@ARGV);
|
||||
if (defined $ENV{'CCC_ANALYZER_LOG'}) {
|
||||
print "$Compiler @ARGV\n";
|
||||
}
|
||||
if ($Status) { exit($Status >> 8); }
|
||||
|
||||
# Get the analysis options.
|
||||
my $Analyses = $ENV{'CCC_ANALYZER_ANALYSIS'};
|
||||
|
||||
# Get the plugins to load.
|
||||
my $Plugins = $ENV{'CCC_ANALYZER_PLUGINS'};
|
||||
|
||||
# Get the store model.
|
||||
my $StoreModel = $ENV{'CCC_ANALYZER_STORE_MODEL'};
|
||||
|
||||
# Get the constraints engine.
|
||||
my $ConstraintsModel = $ENV{'CCC_ANALYZER_CONSTRAINTS_MODEL'};
|
||||
|
||||
#Get the internal stats setting.
|
||||
my $InternalStats = $ENV{'CCC_ANALYZER_INTERNAL_STATS'};
|
||||
|
||||
# Get the output format.
|
||||
my $OutputFormat = $ENV{'CCC_ANALYZER_OUTPUT_FORMAT'};
|
||||
if (!defined $OutputFormat) { $OutputFormat = "html"; }
|
||||
|
||||
# Determine the level of verbosity.
|
||||
my $Verbose = 0;
|
||||
if (defined $ENV{CCC_ANALYZER_VERBOSE}) { $Verbose = 1; }
|
||||
if (defined $ENV{CCC_ANALYZER_LOG}) { $Verbose = 2; }
|
||||
|
||||
# Get the HTML output directory.
|
||||
my $HtmlDir = $ENV{'CCC_ANALYZER_HTML'};
|
||||
|
||||
my %DisabledArchs = ('ppc' => 1, 'ppc64' => 1);
|
||||
my %ArchsSeen;
|
||||
my $HadArch = 0;
|
||||
|
||||
# Process the arguments.
|
||||
foreach (my $i = 0; $i < scalar(@ARGV); ++$i) {
|
||||
my $Arg = $ARGV[$i];
|
||||
my ($ArgKey) = split /=/,$Arg,2;
|
||||
|
||||
# Modes ccc-analyzer supports
|
||||
if ($Arg =~ /^-(E|MM?)$/) { $Action = 'preprocess'; }
|
||||
elsif ($Arg eq '-c') { $Action = 'compile'; }
|
||||
elsif ($Arg =~ /^-print-prog-name/) { exit 0; }
|
||||
|
||||
# Specially handle duplicate cases of -arch
|
||||
if ($Arg eq "-arch") {
|
||||
my $arch = $ARGV[$i+1];
|
||||
# We don't want to process 'ppc' because of Clang's lack of support
|
||||
# for Altivec (also some #defines won't likely be defined correctly, etc.)
|
||||
if (!(defined $DisabledArchs{$arch})) { $ArchsSeen{$arch} = 1; }
|
||||
$HadArch = 1;
|
||||
++$i;
|
||||
next;
|
||||
}
|
||||
|
||||
# Options with possible arguments that should pass through to compiler.
|
||||
if (defined $CompileOptionMap{$ArgKey}) {
|
||||
my $Cnt = $CompileOptionMap{$ArgKey};
|
||||
push @CompileOpts,$Arg;
|
||||
while ($Cnt > 0) { ++$i; --$Cnt; push @CompileOpts, $ARGV[$i]; }
|
||||
next;
|
||||
}
|
||||
if ($Arg =~ /-m.*/) {
|
||||
push @CompileOpts,$Arg;
|
||||
next;
|
||||
}
|
||||
# Handle the case where there isn't a space after -iquote
|
||||
if ($Arg =~ /-iquote.*/) {
|
||||
push @CompileOpts,$Arg;
|
||||
next;
|
||||
}
|
||||
|
||||
# Options with possible arguments that should pass through to linker.
|
||||
if (defined $LinkerOptionMap{$ArgKey}) {
|
||||
my $Cnt = $LinkerOptionMap{$ArgKey};
|
||||
push @LinkOpts,$Arg;
|
||||
while ($Cnt > 0) { ++$i; --$Cnt; push @LinkOpts, $ARGV[$i]; }
|
||||
next;
|
||||
}
|
||||
|
||||
# Options with possible arguments that should pass through to both compiler
|
||||
# and the linker.
|
||||
if (defined $CompilerLinkerOptionMap{$ArgKey}) {
|
||||
my $Cnt = $CompilerLinkerOptionMap{$ArgKey};
|
||||
|
||||
# Check if this is an option that should have a unique value, and if so
|
||||
# determine if the value was checked before.
|
||||
if ($UniqueOptions{$Arg}) {
|
||||
if (defined $Uniqued{$Arg}) {
|
||||
$i += $Cnt;
|
||||
next;
|
||||
}
|
||||
$Uniqued{$Arg} = 1;
|
||||
}
|
||||
|
||||
push @CompileOpts,$Arg;
|
||||
push @LinkOpts,$Arg;
|
||||
|
||||
while ($Cnt > 0) {
|
||||
++$i; --$Cnt;
|
||||
push @CompileOpts, $ARGV[$i];
|
||||
push @LinkOpts, $ARGV[$i];
|
||||
}
|
||||
next;
|
||||
}
|
||||
|
||||
# Ignored options.
|
||||
if (defined $IgnoredOptionMap{$ArgKey}) {
|
||||
my $Cnt = $IgnoredOptionMap{$ArgKey};
|
||||
while ($Cnt > 0) {
|
||||
++$i; --$Cnt;
|
||||
}
|
||||
next;
|
||||
}
|
||||
|
||||
# Compile mode flags.
|
||||
if ($Arg =~ /^-[D,I,U](.*)$/) {
|
||||
my $Tmp = $Arg;
|
||||
if ($1 eq '') {
|
||||
# FIXME: Check if we are going off the end.
|
||||
++$i;
|
||||
$Tmp = $Arg . $ARGV[$i];
|
||||
}
|
||||
push @CompileOpts,$Tmp;
|
||||
next;
|
||||
}
|
||||
|
||||
# Language.
|
||||
if ($Arg eq '-x') {
|
||||
$Lang = $ARGV[$i+1];
|
||||
++$i; next;
|
||||
}
|
||||
|
||||
# Output file.
|
||||
if ($Arg eq '-o') {
|
||||
++$i;
|
||||
$Output = $ARGV[$i];
|
||||
next;
|
||||
}
|
||||
|
||||
# Get the link mode.
|
||||
if ($Arg =~ /^-[l,L,O]/) {
|
||||
if ($Arg eq '-O') { push @LinkOpts,'-O1'; }
|
||||
elsif ($Arg eq '-Os') { push @LinkOpts,'-O2'; }
|
||||
else { push @LinkOpts,$Arg; }
|
||||
next;
|
||||
}
|
||||
|
||||
if ($Arg =~ /^-std=/) {
|
||||
push @CompileOpts,$Arg;
|
||||
next;
|
||||
}
|
||||
|
||||
# if ($Arg =~ /^-f/) {
|
||||
# # FIXME: Not sure if the remaining -fxxxx options have no arguments.
|
||||
# push @CompileOpts,$Arg;
|
||||
# push @LinkOpts,$Arg; # FIXME: Not sure if these are link opts.
|
||||
# }
|
||||
|
||||
# Get the compiler/link mode.
|
||||
if ($Arg =~ /^-F(.+)$/) {
|
||||
my $Tmp = $Arg;
|
||||
if ($1 eq '') {
|
||||
# FIXME: Check if we are going off the end.
|
||||
++$i;
|
||||
$Tmp = $Arg . $ARGV[$i];
|
||||
}
|
||||
push @CompileOpts,$Tmp;
|
||||
push @LinkOpts,$Tmp;
|
||||
next;
|
||||
}
|
||||
|
||||
# Input files.
|
||||
if ($Arg eq '-filelist') {
|
||||
# FIXME: Make sure we aren't walking off the end.
|
||||
open(IN, $ARGV[$i+1]);
|
||||
while (<IN>) { s/\015?\012//; push @Files,$_; }
|
||||
close(IN);
|
||||
++$i;
|
||||
next;
|
||||
}
|
||||
|
||||
# Handle -Wno-. We don't care about extra warnings, but
|
||||
# we should suppress ones that we don't want to see.
|
||||
if ($Arg =~ /^-Wno-/) {
|
||||
push @CompileOpts, $Arg;
|
||||
next;
|
||||
}
|
||||
|
||||
if (!($Arg =~ /^-/)) {
|
||||
push @Files, $Arg;
|
||||
next;
|
||||
}
|
||||
}
|
||||
|
||||
if ($Action eq 'compile' or $Action eq 'link') {
|
||||
my @Archs = keys %ArchsSeen;
|
||||
# Skip the file if we don't support the architectures specified.
|
||||
exit 0 if ($HadArch && scalar(@Archs) == 0);
|
||||
|
||||
foreach my $file (@Files) {
|
||||
# Determine the language for the file.
|
||||
my $FileLang = $Lang;
|
||||
|
||||
if (!defined($FileLang)) {
|
||||
# Infer the language from the extension.
|
||||
if ($file =~ /[.]([^.]+)$/) {
|
||||
$FileLang = $LangMap{$1};
|
||||
}
|
||||
}
|
||||
|
||||
# FileLang still not defined? Skip the file.
|
||||
next if (!defined $FileLang);
|
||||
|
||||
# Language not accepted?
|
||||
next if (!defined $LangsAccepted{$FileLang});
|
||||
|
||||
my @CmdArgs;
|
||||
my @AnalyzeArgs;
|
||||
|
||||
if ($FileLang ne 'unknown') {
|
||||
push @CmdArgs, '-x', $FileLang;
|
||||
}
|
||||
|
||||
if (defined $StoreModel) {
|
||||
push @AnalyzeArgs, "-analyzer-store=$StoreModel";
|
||||
}
|
||||
|
||||
if (defined $ConstraintsModel) {
|
||||
push @AnalyzeArgs, "-analyzer-constraints=$ConstraintsModel";
|
||||
}
|
||||
|
||||
if (defined $InternalStats) {
|
||||
push @AnalyzeArgs, "-analyzer-stats";
|
||||
}
|
||||
|
||||
if (defined $Analyses) {
|
||||
push @AnalyzeArgs, split '\s+', $Analyses;
|
||||
}
|
||||
|
||||
if (defined $Plugins) {
|
||||
push @AnalyzeArgs, split '\s+', $Plugins;
|
||||
}
|
||||
|
||||
if (defined $OutputFormat) {
|
||||
push @AnalyzeArgs, "-analyzer-output=" . $OutputFormat;
|
||||
if ($OutputFormat =~ /plist/) {
|
||||
# Change "Output" to be a file.
|
||||
my ($h, $f) = tempfile("report-XXXXXX", SUFFIX => ".plist",
|
||||
DIR => $HtmlDir);
|
||||
$ResultFile = $f;
|
||||
# If the HtmlDir is not set, we sould clean up the plist files.
|
||||
if (!defined $HtmlDir || -z $HtmlDir) {
|
||||
$CleanupFile = $f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
push @CmdArgs, @CompileOpts;
|
||||
push @CmdArgs, $file;
|
||||
|
||||
if (scalar @Archs) {
|
||||
foreach my $arch (@Archs) {
|
||||
my @NewArgs;
|
||||
push @NewArgs, '-arch', $arch;
|
||||
push @NewArgs, @CmdArgs;
|
||||
Analyze($Clang, \@NewArgs, \@AnalyzeArgs, $FileLang, $Output,
|
||||
$Verbose, $HtmlDir, $file);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Analyze($Clang, \@CmdArgs, \@AnalyzeArgs, $FileLang, $Output,
|
||||
$Verbose, $HtmlDir, $file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exit($Status >> 8);
|
||||
|
||||
1660
static-analyzer/scan-build
Executable file
1660
static-analyzer/scan-build
Executable file
File diff suppressed because it is too large
Load Diff
131
static-analyzer/scan-view
Executable file
131
static-analyzer/scan-view
Executable file
@@ -0,0 +1,131 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""The clang static analyzer results viewer.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import posixpath
|
||||
import thread
|
||||
import time
|
||||
import urllib
|
||||
import webbrowser
|
||||
|
||||
# How long to wait for server to start.
|
||||
kSleepTimeout = .05
|
||||
kMaxSleeps = int(60 / kSleepTimeout)
|
||||
|
||||
# Default server parameters
|
||||
|
||||
kDefaultHost = '127.0.0.1'
|
||||
kDefaultPort = 8181
|
||||
kMaxPortsToTry = 100
|
||||
|
||||
###
|
||||
|
||||
def url_is_up(url):
|
||||
try:
|
||||
o = urllib.urlopen(url)
|
||||
except IOError:
|
||||
return False
|
||||
o.close()
|
||||
return True
|
||||
|
||||
def start_browser(port, options):
|
||||
import urllib, webbrowser
|
||||
|
||||
url = 'http://%s:%d'%(options.host, port)
|
||||
|
||||
# Wait for server to start...
|
||||
if options.debug:
|
||||
sys.stderr.write('%s: Waiting for server.' % sys.argv[0])
|
||||
sys.stderr.flush()
|
||||
for i in range(kMaxSleeps):
|
||||
if url_is_up(url):
|
||||
break
|
||||
if options.debug:
|
||||
sys.stderr.write('.')
|
||||
sys.stderr.flush()
|
||||
time.sleep(kSleepTimeout)
|
||||
else:
|
||||
print >>sys.stderr,'WARNING: Unable to detect that server started.'
|
||||
|
||||
if options.debug:
|
||||
print >>sys.stderr,'%s: Starting webbrowser...' % sys.argv[0]
|
||||
webbrowser.open(url)
|
||||
|
||||
def run(port, options, root):
|
||||
import ScanView
|
||||
try:
|
||||
print 'Starting scan-view at: http://%s:%d'%(options.host,
|
||||
port)
|
||||
print ' Use Ctrl-C to exit.'
|
||||
httpd = ScanView.create_server((options.host, port),
|
||||
options, root)
|
||||
httpd.serve_forever()
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
|
||||
def port_is_open(port):
|
||||
import SocketServer
|
||||
try:
|
||||
t = SocketServer.TCPServer((kDefaultHost,port),None)
|
||||
except:
|
||||
return False
|
||||
t.server_close()
|
||||
return True
|
||||
|
||||
def main():
|
||||
from optparse import OptionParser
|
||||
parser = OptionParser('usage: %prog [options] <results directory>')
|
||||
parser.set_description(__doc__)
|
||||
parser.add_option(
|
||||
'--host', dest="host", default=kDefaultHost, type="string",
|
||||
help="Host interface to listen on. (default=%s)" % kDefaultHost)
|
||||
parser.add_option(
|
||||
'--port', dest="port", default=None, type="int",
|
||||
help="Port to listen on. (default=%s)" % kDefaultPort)
|
||||
parser.add_option("--debug", dest="debug", default=0,
|
||||
action="count",
|
||||
help="Print additional debugging information.")
|
||||
parser.add_option("--auto-reload", dest="autoReload", default=False,
|
||||
action="store_true",
|
||||
help="Automatically update module for each request.")
|
||||
parser.add_option("--no-browser", dest="startBrowser", default=True,
|
||||
action="store_false",
|
||||
help="Don't open a webbrowser on startup.")
|
||||
parser.add_option("--allow-all-hosts", dest="onlyServeLocal", default=True,
|
||||
action="store_false",
|
||||
help='Allow connections from any host (access restricted to "127.0.0.1" by default)')
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
if len(args) != 1:
|
||||
parser.error('No results directory specified.')
|
||||
root, = args
|
||||
|
||||
# Make sure this directory is in a reasonable state to view.
|
||||
if not posixpath.exists(posixpath.join(root,'index.html')):
|
||||
parser.error('Invalid directory, analysis results not found!')
|
||||
|
||||
# Find an open port. We aren't particularly worried about race
|
||||
# conditions here. Note that if the user specified a port we only
|
||||
# use that one.
|
||||
if options.port is not None:
|
||||
port = options.port
|
||||
else:
|
||||
for i in range(kMaxPortsToTry):
|
||||
if port_is_open(kDefaultPort + i):
|
||||
port = kDefaultPort + i
|
||||
break
|
||||
else:
|
||||
parser.error('Unable to find usable port in [%d,%d)'%(kDefaultPort,
|
||||
kDefaultPort+kMaxPortsToTry))
|
||||
|
||||
# Kick off thread to wait for server and start web browser, if
|
||||
# requested.
|
||||
if options.startBrowser:
|
||||
t = thread.start_new_thread(start_browser, (port,options))
|
||||
|
||||
run(port, options, root)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
62
static-analyzer/scanview.css
Normal file
62
static-analyzer/scanview.css
Normal file
@@ -0,0 +1,62 @@
|
||||
body { color:#000000; background-color:#ffffff }
|
||||
body { font-family: Helvetica, sans-serif; font-size:9pt }
|
||||
h1 { font-size: 14pt; }
|
||||
h2 { font-size: 12pt; }
|
||||
table { font-size:9pt }
|
||||
table { border-spacing: 0px; border: 1px solid black }
|
||||
th, table thead {
|
||||
background-color:#eee; color:#666666;
|
||||
font-weight: bold; cursor: default;
|
||||
text-align:center;
|
||||
font-weight: bold; font-family: Verdana;
|
||||
white-space:nowrap;
|
||||
}
|
||||
.W { font-size:0px }
|
||||
th, td { padding:5px; padding-left:8px; text-align:left }
|
||||
td.SUMM_DESC { padding-left:12px }
|
||||
td.DESC { white-space:pre }
|
||||
td.Q { text-align:right }
|
||||
td { text-align:left }
|
||||
tbody.scrollContent { overflow:auto }
|
||||
|
||||
table.form_group {
|
||||
background-color: #ccc;
|
||||
border: 1px solid #333;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
table.form_inner_group {
|
||||
background-color: #ccc;
|
||||
border: 1px solid #333;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
table.form {
|
||||
background-color: #999;
|
||||
border: 1px solid #333;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
td.form_label {
|
||||
text-align: right;
|
||||
vertical-align: top;
|
||||
}
|
||||
/* For one line entires */
|
||||
td.form_clabel {
|
||||
text-align: right;
|
||||
vertical-align: center;
|
||||
}
|
||||
td.form_value {
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
td.form_submit {
|
||||
text-align: right;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
h1.SubmitFail {
|
||||
color: #f00;
|
||||
}
|
||||
h1.SubmitOk {
|
||||
}
|
||||
115
static-analyzer/set-xcode-analyzer
Executable file
115
static-analyzer/set-xcode-analyzer
Executable file
@@ -0,0 +1,115 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
# [PR 11661] Note that we hardwire to /usr/bin/python because we
|
||||
# want to the use the system version of Python on Mac OS X.
|
||||
# This one has the scripting bridge enabled.
|
||||
|
||||
import sys
|
||||
if sys.version_info < (2, 7):
|
||||
print "set-xcode-analyzer requires Python 2.7 or later"
|
||||
sys.exit(1)
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import re
|
||||
import tempfile
|
||||
import shutil
|
||||
import stat
|
||||
from AppKit import *
|
||||
|
||||
def FindClangSpecs(path):
|
||||
print "(+) Searching for xcspec file in: ", path
|
||||
for root, dirs, files in os.walk(path):
|
||||
for f in files:
|
||||
if f.endswith(".xcspec") and f.startswith("Clang LLVM"):
|
||||
yield os.path.join(root, f)
|
||||
|
||||
def ModifySpec(path, isBuiltinAnalyzer, pathToChecker):
|
||||
t = tempfile.NamedTemporaryFile(delete=False)
|
||||
foundAnalyzer = False
|
||||
with open(path) as f:
|
||||
if isBuiltinAnalyzer:
|
||||
# First search for CLANG_ANALYZER_EXEC. Newer
|
||||
# versions of Xcode set EXEC_PATH to be CLANG_ANALYZER_EXEC.
|
||||
with open(path) as f2:
|
||||
for line in f2:
|
||||
if line.find("CLANG_ANALYZER_EXEC") >= 0:
|
||||
pathToChecker = "$(CLANG_ANALYZER_EXEC)"
|
||||
break
|
||||
# Now create a new file.
|
||||
for line in f:
|
||||
if not foundAnalyzer:
|
||||
if line.find("Static Analyzer") >= 0:
|
||||
foundAnalyzer = True
|
||||
else:
|
||||
m = re.search('^(\s*ExecPath\s*=\s*")', line)
|
||||
if m:
|
||||
line = "".join([m.group(0), pathToChecker, '";\n'])
|
||||
# Do not modify further ExecPath's later in the xcspec.
|
||||
foundAnalyzer = False
|
||||
t.write(line)
|
||||
t.close()
|
||||
print "(+) processing:", path
|
||||
try:
|
||||
shutil.copy(t.name, path)
|
||||
os.chmod(path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH)
|
||||
except IOError, why:
|
||||
print " (-) Cannot update file:", why, "\n"
|
||||
except OSError, why:
|
||||
print " (-) Cannot update file:", why, "\n"
|
||||
os.unlink(t.name)
|
||||
|
||||
def main():
|
||||
from optparse import OptionParser
|
||||
parser = OptionParser('usage: %prog [options]')
|
||||
parser.set_description(__doc__)
|
||||
parser.add_option("--use-checker-build", dest="path",
|
||||
help="Use the Clang located at the provided absolute path, e.g. /Users/foo/checker-1")
|
||||
parser.add_option("--use-xcode-clang", action="store_const",
|
||||
const="$(CLANG)", dest="default",
|
||||
help="Use the Clang bundled with Xcode")
|
||||
(options, args) = parser.parse_args()
|
||||
if options.path is None and options.default is None:
|
||||
parser.error("You must specify a version of Clang to use for static analysis. Specify '-h' for details")
|
||||
|
||||
# determine if Xcode is running
|
||||
for x in NSWorkspace.sharedWorkspace().runningApplications():
|
||||
if x.localizedName().find("Xcode") >= 0:
|
||||
print "(-) You must quit Xcode first before modifying its configuration files."
|
||||
sys.exit(1)
|
||||
|
||||
isBuiltinAnalyzer = False
|
||||
if options.path:
|
||||
# Expand tildes.
|
||||
path = os.path.expanduser(options.path)
|
||||
if not path.endswith("clang"):
|
||||
print "(+) Using Clang bundled with checker build:", path
|
||||
path = os.path.join(path, "bin", "clang");
|
||||
else:
|
||||
print "(+) Using Clang located at:", path
|
||||
else:
|
||||
print "(+) Using the Clang bundled with Xcode"
|
||||
path = options.default
|
||||
isBuiltinAnalyzer = True
|
||||
|
||||
try:
|
||||
xcode_path = subprocess.check_output(["xcode-select", "-print-path"])
|
||||
except AttributeError:
|
||||
# Fall back to the default install location when using Python < 2.7.0
|
||||
xcode_path = "/Developer"
|
||||
if (xcode_path.find(".app/") != -1):
|
||||
# Cut off the 'Developer' dir, as the xcspec lies in another part
|
||||
# of the Xcode.app subtree.
|
||||
xcode_path = os.path.dirname(xcode_path)
|
||||
|
||||
foundSpec = False
|
||||
for x in FindClangSpecs(xcode_path):
|
||||
foundSpec = True
|
||||
ModifySpec(x, isBuiltinAnalyzer, path)
|
||||
|
||||
if foundSpec == False:
|
||||
print "(-) No compiler configuration file was found. Xcode's analyzer has not been updated."
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
493
static-analyzer/sorttable.js
Normal file
493
static-analyzer/sorttable.js
Normal file
@@ -0,0 +1,493 @@
|
||||
/*
|
||||
SortTable
|
||||
version 2
|
||||
7th April 2007
|
||||
Stuart Langridge, http://www.kryogenix.org/code/browser/sorttable/
|
||||
|
||||
Instructions:
|
||||
Download this file
|
||||
Add <script src="sorttable.js"></script> to your HTML
|
||||
Add class="sortable" to any table you'd like to make sortable
|
||||
Click on the headers to sort
|
||||
|
||||
Thanks to many, many people for contributions and suggestions.
|
||||
Licenced as X11: http://www.kryogenix.org/code/browser/licence.html
|
||||
This basically means: do what you want with it.
|
||||
*/
|
||||
|
||||
|
||||
var stIsIE = /*@cc_on!@*/false;
|
||||
|
||||
sorttable = {
|
||||
init: function() {
|
||||
// quit if this function has already been called
|
||||
if (arguments.callee.done) return;
|
||||
// flag this function so we don't do the same thing twice
|
||||
arguments.callee.done = true;
|
||||
// kill the timer
|
||||
if (_timer) clearInterval(_timer);
|
||||
|
||||
if (!document.createElement || !document.getElementsByTagName) return;
|
||||
|
||||
sorttable.DATE_RE = /^(\d\d?)[\/\.-](\d\d?)[\/\.-]((\d\d)?\d\d)$/;
|
||||
|
||||
forEach(document.getElementsByTagName('table'), function(table) {
|
||||
if (table.className.search(/\bsortable\b/) != -1) {
|
||||
sorttable.makeSortable(table);
|
||||
}
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
makeSortable: function(table) {
|
||||
if (table.getElementsByTagName('thead').length == 0) {
|
||||
// table doesn't have a tHead. Since it should have, create one and
|
||||
// put the first table row in it.
|
||||
the = document.createElement('thead');
|
||||
the.appendChild(table.rows[0]);
|
||||
table.insertBefore(the,table.firstChild);
|
||||
}
|
||||
// Safari doesn't support table.tHead, sigh
|
||||
if (table.tHead == null) table.tHead = table.getElementsByTagName('thead')[0];
|
||||
|
||||
if (table.tHead.rows.length != 1) return; // can't cope with two header rows
|
||||
|
||||
// Sorttable v1 put rows with a class of "sortbottom" at the bottom (as
|
||||
// "total" rows, for example). This is B&R, since what you're supposed
|
||||
// to do is put them in a tfoot. So, if there are sortbottom rows,
|
||||
// for backwards compatibility, move them to tfoot (creating it if needed).
|
||||
sortbottomrows = [];
|
||||
for (var i=0; i<table.rows.length; i++) {
|
||||
if (table.rows[i].className.search(/\bsortbottom\b/) != -1) {
|
||||
sortbottomrows[sortbottomrows.length] = table.rows[i];
|
||||
}
|
||||
}
|
||||
if (sortbottomrows) {
|
||||
if (table.tFoot == null) {
|
||||
// table doesn't have a tfoot. Create one.
|
||||
tfo = document.createElement('tfoot');
|
||||
table.appendChild(tfo);
|
||||
}
|
||||
for (var i=0; i<sortbottomrows.length; i++) {
|
||||
tfo.appendChild(sortbottomrows[i]);
|
||||
}
|
||||
delete sortbottomrows;
|
||||
}
|
||||
|
||||
// work through each column and calculate its type
|
||||
headrow = table.tHead.rows[0].cells;
|
||||
for (var i=0; i<headrow.length; i++) {
|
||||
// manually override the type with a sorttable_type attribute
|
||||
if (!headrow[i].className.match(/\bsorttable_nosort\b/)) { // skip this col
|
||||
mtch = headrow[i].className.match(/\bsorttable_([a-z0-9]+)\b/);
|
||||
if (mtch) { override = mtch[1]; }
|
||||
if (mtch && typeof sorttable["sort_"+override] == 'function') {
|
||||
headrow[i].sorttable_sortfunction = sorttable["sort_"+override];
|
||||
} else {
|
||||
headrow[i].sorttable_sortfunction = sorttable.guessType(table,i);
|
||||
}
|
||||
// make it clickable to sort
|
||||
headrow[i].sorttable_columnindex = i;
|
||||
headrow[i].sorttable_tbody = table.tBodies[0];
|
||||
dean_addEvent(headrow[i],"click", function(e) {
|
||||
|
||||
if (this.className.search(/\bsorttable_sorted\b/) != -1) {
|
||||
// if we're already sorted by this column, just
|
||||
// reverse the table, which is quicker
|
||||
sorttable.reverse(this.sorttable_tbody);
|
||||
this.className = this.className.replace('sorttable_sorted',
|
||||
'sorttable_sorted_reverse');
|
||||
this.removeChild(document.getElementById('sorttable_sortfwdind'));
|
||||
sortrevind = document.createElement('span');
|
||||
sortrevind.id = "sorttable_sortrevind";
|
||||
sortrevind.innerHTML = stIsIE ? ' <font face="webdings">5</font>' : ' ▴';
|
||||
this.appendChild(sortrevind);
|
||||
return;
|
||||
}
|
||||
if (this.className.search(/\bsorttable_sorted_reverse\b/) != -1) {
|
||||
// if we're already sorted by this column in reverse, just
|
||||
// re-reverse the table, which is quicker
|
||||
sorttable.reverse(this.sorttable_tbody);
|
||||
this.className = this.className.replace('sorttable_sorted_reverse',
|
||||
'sorttable_sorted');
|
||||
this.removeChild(document.getElementById('sorttable_sortrevind'));
|
||||
sortfwdind = document.createElement('span');
|
||||
sortfwdind.id = "sorttable_sortfwdind";
|
||||
sortfwdind.innerHTML = stIsIE ? ' <font face="webdings">6</font>' : ' ▾';
|
||||
this.appendChild(sortfwdind);
|
||||
return;
|
||||
}
|
||||
|
||||
// remove sorttable_sorted classes
|
||||
theadrow = this.parentNode;
|
||||
forEach(theadrow.childNodes, function(cell) {
|
||||
if (cell.nodeType == 1) { // an element
|
||||
cell.className = cell.className.replace('sorttable_sorted_reverse','');
|
||||
cell.className = cell.className.replace('sorttable_sorted','');
|
||||
}
|
||||
});
|
||||
sortfwdind = document.getElementById('sorttable_sortfwdind');
|
||||
if (sortfwdind) { sortfwdind.parentNode.removeChild(sortfwdind); }
|
||||
sortrevind = document.getElementById('sorttable_sortrevind');
|
||||
if (sortrevind) { sortrevind.parentNode.removeChild(sortrevind); }
|
||||
|
||||
this.className += ' sorttable_sorted';
|
||||
sortfwdind = document.createElement('span');
|
||||
sortfwdind.id = "sorttable_sortfwdind";
|
||||
sortfwdind.innerHTML = stIsIE ? ' <font face="webdings">6</font>' : ' ▾';
|
||||
this.appendChild(sortfwdind);
|
||||
|
||||
// build an array to sort. This is a Schwartzian transform thing,
|
||||
// i.e., we "decorate" each row with the actual sort key,
|
||||
// sort based on the sort keys, and then put the rows back in order
|
||||
// which is a lot faster because you only do getInnerText once per row
|
||||
row_array = [];
|
||||
col = this.sorttable_columnindex;
|
||||
rows = this.sorttable_tbody.rows;
|
||||
for (var j=0; j<rows.length; j++) {
|
||||
row_array[row_array.length] = [sorttable.getInnerText(rows[j].cells[col]), rows[j]];
|
||||
}
|
||||
/* If you want a stable sort, uncomment the following line */
|
||||
sorttable.shaker_sort(row_array, this.sorttable_sortfunction);
|
||||
/* and comment out this one */
|
||||
//row_array.sort(this.sorttable_sortfunction);
|
||||
|
||||
tb = this.sorttable_tbody;
|
||||
for (var j=0; j<row_array.length; j++) {
|
||||
tb.appendChild(row_array[j][1]);
|
||||
}
|
||||
|
||||
delete row_array;
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
guessType: function(table, column) {
|
||||
// guess the type of a column based on its first non-blank row
|
||||
sortfn = sorttable.sort_alpha;
|
||||
for (var i=0; i<table.tBodies[0].rows.length; i++) {
|
||||
text = sorttable.getInnerText(table.tBodies[0].rows[i].cells[column]);
|
||||
if (text != '') {
|
||||
if (text.match(/^-?[<5B>$<24>]?[\d,.]+%?$/)) {
|
||||
return sorttable.sort_numeric;
|
||||
}
|
||||
// check for a date: dd/mm/yyyy or dd/mm/yy
|
||||
// can have / or . or - as separator
|
||||
// can be mm/dd as well
|
||||
possdate = text.match(sorttable.DATE_RE)
|
||||
if (possdate) {
|
||||
// looks like a date
|
||||
first = parseInt(possdate[1]);
|
||||
second = parseInt(possdate[2]);
|
||||
if (first > 12) {
|
||||
// definitely dd/mm
|
||||
return sorttable.sort_ddmm;
|
||||
} else if (second > 12) {
|
||||
return sorttable.sort_mmdd;
|
||||
} else {
|
||||
// looks like a date, but we can't tell which, so assume
|
||||
// that it's dd/mm (English imperialism!) and keep looking
|
||||
sortfn = sorttable.sort_ddmm;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return sortfn;
|
||||
},
|
||||
|
||||
getInnerText: function(node) {
|
||||
// gets the text we want to use for sorting for a cell.
|
||||
// strips leading and trailing whitespace.
|
||||
// this is *not* a generic getInnerText function; it's special to sorttable.
|
||||
// for example, you can override the cell text with a customkey attribute.
|
||||
// it also gets .value for <input> fields.
|
||||
|
||||
hasInputs = (typeof node.getElementsByTagName == 'function') &&
|
||||
node.getElementsByTagName('input').length;
|
||||
|
||||
if (node.getAttribute("sorttable_customkey") != null) {
|
||||
return node.getAttribute("sorttable_customkey");
|
||||
}
|
||||
else if (typeof node.textContent != 'undefined' && !hasInputs) {
|
||||
return node.textContent.replace(/^\s+|\s+$/g, '');
|
||||
}
|
||||
else if (typeof node.innerText != 'undefined' && !hasInputs) {
|
||||
return node.innerText.replace(/^\s+|\s+$/g, '');
|
||||
}
|
||||
else if (typeof node.text != 'undefined' && !hasInputs) {
|
||||
return node.text.replace(/^\s+|\s+$/g, '');
|
||||
}
|
||||
else {
|
||||
switch (node.nodeType) {
|
||||
case 3:
|
||||
if (node.nodeName.toLowerCase() == 'input') {
|
||||
return node.value.replace(/^\s+|\s+$/g, '');
|
||||
}
|
||||
case 4:
|
||||
return node.nodeValue.replace(/^\s+|\s+$/g, '');
|
||||
break;
|
||||
case 1:
|
||||
case 11:
|
||||
var innerText = '';
|
||||
for (var i = 0; i < node.childNodes.length; i++) {
|
||||
innerText += sorttable.getInnerText(node.childNodes[i]);
|
||||
}
|
||||
return innerText.replace(/^\s+|\s+$/g, '');
|
||||
break;
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
reverse: function(tbody) {
|
||||
// reverse the rows in a tbody
|
||||
newrows = [];
|
||||
for (var i=0; i<tbody.rows.length; i++) {
|
||||
newrows[newrows.length] = tbody.rows[i];
|
||||
}
|
||||
for (var i=newrows.length-1; i>=0; i--) {
|
||||
tbody.appendChild(newrows[i]);
|
||||
}
|
||||
delete newrows;
|
||||
},
|
||||
|
||||
/* sort functions
|
||||
each sort function takes two parameters, a and b
|
||||
you are comparing a[0] and b[0] */
|
||||
sort_numeric: function(a,b) {
|
||||
aa = parseFloat(a[0].replace(/[^0-9.-]/g,''));
|
||||
if (isNaN(aa)) aa = 0;
|
||||
bb = parseFloat(b[0].replace(/[^0-9.-]/g,''));
|
||||
if (isNaN(bb)) bb = 0;
|
||||
return aa-bb;
|
||||
},
|
||||
sort_alpha: function(a,b) {
|
||||
if (a[0]==b[0]) return 0;
|
||||
if (a[0]<b[0]) return -1;
|
||||
return 1;
|
||||
},
|
||||
sort_ddmm: function(a,b) {
|
||||
mtch = a[0].match(sorttable.DATE_RE);
|
||||
y = mtch[3]; m = mtch[2]; d = mtch[1];
|
||||
if (m.length == 1) m = '0'+m;
|
||||
if (d.length == 1) d = '0'+d;
|
||||
dt1 = y+m+d;
|
||||
mtch = b[0].match(sorttable.DATE_RE);
|
||||
y = mtch[3]; m = mtch[2]; d = mtch[1];
|
||||
if (m.length == 1) m = '0'+m;
|
||||
if (d.length == 1) d = '0'+d;
|
||||
dt2 = y+m+d;
|
||||
if (dt1==dt2) return 0;
|
||||
if (dt1<dt2) return -1;
|
||||
return 1;
|
||||
},
|
||||
sort_mmdd: function(a,b) {
|
||||
mtch = a[0].match(sorttable.DATE_RE);
|
||||
y = mtch[3]; d = mtch[2]; m = mtch[1];
|
||||
if (m.length == 1) m = '0'+m;
|
||||
if (d.length == 1) d = '0'+d;
|
||||
dt1 = y+m+d;
|
||||
mtch = b[0].match(sorttable.DATE_RE);
|
||||
y = mtch[3]; d = mtch[2]; m = mtch[1];
|
||||
if (m.length == 1) m = '0'+m;
|
||||
if (d.length == 1) d = '0'+d;
|
||||
dt2 = y+m+d;
|
||||
if (dt1==dt2) return 0;
|
||||
if (dt1<dt2) return -1;
|
||||
return 1;
|
||||
},
|
||||
|
||||
shaker_sort: function(list, comp_func) {
|
||||
// A stable sort function to allow multi-level sorting of data
|
||||
// see: http://en.wikipedia.org/wiki/Cocktail_sort
|
||||
// thanks to Joseph Nahmias
|
||||
var b = 0;
|
||||
var t = list.length - 1;
|
||||
var swap = true;
|
||||
|
||||
while(swap) {
|
||||
swap = false;
|
||||
for(var i = b; i < t; ++i) {
|
||||
if ( comp_func(list[i], list[i+1]) > 0 ) {
|
||||
var q = list[i]; list[i] = list[i+1]; list[i+1] = q;
|
||||
swap = true;
|
||||
}
|
||||
} // for
|
||||
t--;
|
||||
|
||||
if (!swap) break;
|
||||
|
||||
for(var i = t; i > b; --i) {
|
||||
if ( comp_func(list[i], list[i-1]) < 0 ) {
|
||||
var q = list[i]; list[i] = list[i-1]; list[i-1] = q;
|
||||
swap = true;
|
||||
}
|
||||
} // for
|
||||
b++;
|
||||
|
||||
} // while(swap)
|
||||
}
|
||||
}
|
||||
|
||||
/* ******************************************************************
|
||||
Supporting functions: bundled here to avoid depending on a library
|
||||
****************************************************************** */
|
||||
|
||||
// Dean Edwards/Matthias Miller/John Resig
|
||||
|
||||
/* for Mozilla/Opera9 */
|
||||
if (document.addEventListener) {
|
||||
document.addEventListener("DOMContentLoaded", sorttable.init, false);
|
||||
}
|
||||
|
||||
/* for Internet Explorer */
|
||||
/*@cc_on @*/
|
||||
/*@if (@_win32)
|
||||
document.write("<script id=__ie_onload defer src=javascript:void(0)><\/script>");
|
||||
var script = document.getElementById("__ie_onload");
|
||||
script.onreadystatechange = function() {
|
||||
if (this.readyState == "complete") {
|
||||
sorttable.init(); // call the onload handler
|
||||
}
|
||||
};
|
||||
/*@end @*/
|
||||
|
||||
/* for Safari */
|
||||
if (/WebKit/i.test(navigator.userAgent)) { // sniff
|
||||
var _timer = setInterval(function() {
|
||||
if (/loaded|complete/.test(document.readyState)) {
|
||||
sorttable.init(); // call the onload handler
|
||||
}
|
||||
}, 10);
|
||||
}
|
||||
|
||||
/* for other browsers */
|
||||
window.onload = sorttable.init;
|
||||
|
||||
// written by Dean Edwards, 2005
|
||||
// with input from Tino Zijdel, Matthias Miller, Diego Perini
|
||||
|
||||
// http://dean.edwards.name/weblog/2005/10/add-event/
|
||||
|
||||
function dean_addEvent(element, type, handler) {
|
||||
if (element.addEventListener) {
|
||||
element.addEventListener(type, handler, false);
|
||||
} else {
|
||||
// assign each event handler a unique ID
|
||||
if (!handler.$$guid) handler.$$guid = dean_addEvent.guid++;
|
||||
// create a hash table of event types for the element
|
||||
if (!element.events) element.events = {};
|
||||
// create a hash table of event handlers for each element/event pair
|
||||
var handlers = element.events[type];
|
||||
if (!handlers) {
|
||||
handlers = element.events[type] = {};
|
||||
// store the existing event handler (if there is one)
|
||||
if (element["on" + type]) {
|
||||
handlers[0] = element["on" + type];
|
||||
}
|
||||
}
|
||||
// store the event handler in the hash table
|
||||
handlers[handler.$$guid] = handler;
|
||||
// assign a global event handler to do all the work
|
||||
element["on" + type] = handleEvent;
|
||||
}
|
||||
};
|
||||
// a counter used to create unique IDs
|
||||
dean_addEvent.guid = 1;
|
||||
|
||||
function removeEvent(element, type, handler) {
|
||||
if (element.removeEventListener) {
|
||||
element.removeEventListener(type, handler, false);
|
||||
} else {
|
||||
// delete the event handler from the hash table
|
||||
if (element.events && element.events[type]) {
|
||||
delete element.events[type][handler.$$guid];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function handleEvent(event) {
|
||||
var returnValue = true;
|
||||
// grab the event object (IE uses a global event object)
|
||||
event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event);
|
||||
// get a reference to the hash table of event handlers
|
||||
var handlers = this.events[event.type];
|
||||
// execute each event handler
|
||||
for (var i in handlers) {
|
||||
this.$$handleEvent = handlers[i];
|
||||
if (this.$$handleEvent(event) === false) {
|
||||
returnValue = false;
|
||||
}
|
||||
}
|
||||
return returnValue;
|
||||
};
|
||||
|
||||
function fixEvent(event) {
|
||||
// add W3C standard event methods
|
||||
event.preventDefault = fixEvent.preventDefault;
|
||||
event.stopPropagation = fixEvent.stopPropagation;
|
||||
return event;
|
||||
};
|
||||
fixEvent.preventDefault = function() {
|
||||
this.returnValue = false;
|
||||
};
|
||||
fixEvent.stopPropagation = function() {
|
||||
this.cancelBubble = true;
|
||||
}
|
||||
|
||||
// Dean's forEach: http://dean.edwards.name/base/forEach.js
|
||||
/*
|
||||
forEach, version 1.0
|
||||
Copyright 2006, Dean Edwards
|
||||
License: http://www.opensource.org/licenses/mit-license.php
|
||||
*/
|
||||
|
||||
// array-like enumeration
|
||||
if (!Array.forEach) { // mozilla already supports this
|
||||
Array.forEach = function(array, block, context) {
|
||||
for (var i = 0; i < array.length; i++) {
|
||||
block.call(context, array[i], i, array);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// generic enumeration
|
||||
Function.prototype.forEach = function(object, block, context) {
|
||||
for (var key in object) {
|
||||
if (typeof this.prototype[key] == "undefined") {
|
||||
block.call(context, object[key], key, object);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// character enumeration
|
||||
String.forEach = function(string, block, context) {
|
||||
Array.forEach(string.split(""), function(chr, index) {
|
||||
block.call(context, chr, index, string);
|
||||
});
|
||||
};
|
||||
|
||||
// globally resolve forEach enumeration
|
||||
var forEach = function(object, block, context) {
|
||||
if (object) {
|
||||
var resolve = Object; // default
|
||||
if (object instanceof Function) {
|
||||
// functions have a "length" property
|
||||
resolve = Function;
|
||||
} else if (object.forEach instanceof Function) {
|
||||
// the object implements a custom forEach method so use that
|
||||
object.forEach(block, context);
|
||||
return;
|
||||
} else if (typeof object == "string") {
|
||||
// the object is a string
|
||||
resolve = String;
|
||||
} else if (typeof object.length == "number") {
|
||||
// the object is array-like
|
||||
resolve = Array;
|
||||
}
|
||||
resolve.forEach(object, block, context);
|
||||
}
|
||||
};
|
||||
|
||||
203
static-analyzer/startfile.py
Normal file
203
static-analyzer/startfile.py
Normal file
@@ -0,0 +1,203 @@
|
||||
"""Utility for opening a file using the default application in a cross-platform
|
||||
manner. Modified from http://code.activestate.com/recipes/511443/.
|
||||
"""
|
||||
|
||||
__version__ = '1.1x'
|
||||
__all__ = ['open']
|
||||
|
||||
import os
|
||||
import sys
|
||||
import webbrowser
|
||||
import subprocess
|
||||
|
||||
_controllers = {}
|
||||
_open = None
|
||||
|
||||
|
||||
class BaseController(object):
|
||||
'''Base class for open program controllers.'''
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def open(self, filename):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class Controller(BaseController):
|
||||
'''Controller for a generic open program.'''
|
||||
|
||||
def __init__(self, *args):
|
||||
super(Controller, self).__init__(os.path.basename(args[0]))
|
||||
self.args = list(args)
|
||||
|
||||
def _invoke(self, cmdline):
|
||||
if sys.platform[:3] == 'win':
|
||||
closefds = False
|
||||
startupinfo = subprocess.STARTUPINFO()
|
||||
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
||||
else:
|
||||
closefds = True
|
||||
startupinfo = None
|
||||
|
||||
if (os.environ.get('DISPLAY') or sys.platform[:3] == 'win' or
|
||||
sys.platform == 'darwin'):
|
||||
inout = file(os.devnull, 'r+')
|
||||
else:
|
||||
# for TTY programs, we need stdin/out
|
||||
inout = None
|
||||
|
||||
# if possible, put the child precess in separate process group,
|
||||
# so keyboard interrupts don't affect child precess as well as
|
||||
# Python
|
||||
setsid = getattr(os, 'setsid', None)
|
||||
if not setsid:
|
||||
setsid = getattr(os, 'setpgrp', None)
|
||||
|
||||
pipe = subprocess.Popen(cmdline, stdin=inout, stdout=inout,
|
||||
stderr=inout, close_fds=closefds,
|
||||
preexec_fn=setsid, startupinfo=startupinfo)
|
||||
|
||||
# It is assumed that this kind of tools (gnome-open, kfmclient,
|
||||
# exo-open, xdg-open and open for OSX) immediately exit after lauching
|
||||
# the specific application
|
||||
returncode = pipe.wait()
|
||||
if hasattr(self, 'fixreturncode'):
|
||||
returncode = self.fixreturncode(returncode)
|
||||
return not returncode
|
||||
|
||||
def open(self, filename):
|
||||
if isinstance(filename, basestring):
|
||||
cmdline = self.args + [filename]
|
||||
else:
|
||||
# assume it is a sequence
|
||||
cmdline = self.args + filename
|
||||
try:
|
||||
return self._invoke(cmdline)
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
|
||||
# Platform support for Windows
|
||||
if sys.platform[:3] == 'win':
|
||||
|
||||
class Start(BaseController):
|
||||
'''Controller for the win32 start progam through os.startfile.'''
|
||||
|
||||
def open(self, filename):
|
||||
try:
|
||||
os.startfile(filename)
|
||||
except WindowsError:
|
||||
# [Error 22] No application is associated with the specified
|
||||
# file for this operation: '<URL>'
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
_controllers['windows-default'] = Start('start')
|
||||
_open = _controllers['windows-default'].open
|
||||
|
||||
|
||||
# Platform support for MacOS
|
||||
elif sys.platform == 'darwin':
|
||||
_controllers['open']= Controller('open')
|
||||
_open = _controllers['open'].open
|
||||
|
||||
|
||||
# Platform support for Unix
|
||||
else:
|
||||
|
||||
import commands
|
||||
|
||||
# @WARNING: use the private API of the webbrowser module
|
||||
from webbrowser import _iscommand
|
||||
|
||||
class KfmClient(Controller):
|
||||
'''Controller for the KDE kfmclient program.'''
|
||||
|
||||
def __init__(self, kfmclient='kfmclient'):
|
||||
super(KfmClient, self).__init__(kfmclient, 'exec')
|
||||
self.kde_version = self.detect_kde_version()
|
||||
|
||||
def detect_kde_version(self):
|
||||
kde_version = None
|
||||
try:
|
||||
info = commands.getoutput('kde-config --version')
|
||||
|
||||
for line in info.splitlines():
|
||||
if line.startswith('KDE'):
|
||||
kde_version = line.split(':')[-1].strip()
|
||||
break
|
||||
except (OSError, RuntimeError):
|
||||
pass
|
||||
|
||||
return kde_version
|
||||
|
||||
def fixreturncode(self, returncode):
|
||||
if returncode is not None and self.kde_version > '3.5.4':
|
||||
return returncode
|
||||
else:
|
||||
return os.EX_OK
|
||||
|
||||
def detect_desktop_environment():
|
||||
'''Checks for known desktop environments
|
||||
|
||||
Return the desktop environments name, lowercase (kde, gnome, xfce)
|
||||
or "generic"
|
||||
|
||||
'''
|
||||
|
||||
desktop_environment = 'generic'
|
||||
|
||||
if os.environ.get('KDE_FULL_SESSION') == 'true':
|
||||
desktop_environment = 'kde'
|
||||
elif os.environ.get('GNOME_DESKTOP_SESSION_ID'):
|
||||
desktop_environment = 'gnome'
|
||||
else:
|
||||
try:
|
||||
info = commands.getoutput('xprop -root _DT_SAVE_MODE')
|
||||
if ' = "xfce4"' in info:
|
||||
desktop_environment = 'xfce'
|
||||
except (OSError, RuntimeError):
|
||||
pass
|
||||
|
||||
return desktop_environment
|
||||
|
||||
|
||||
def register_X_controllers():
|
||||
if _iscommand('kfmclient'):
|
||||
_controllers['kde-open'] = KfmClient()
|
||||
|
||||
for command in ('gnome-open', 'exo-open', 'xdg-open'):
|
||||
if _iscommand(command):
|
||||
_controllers[command] = Controller(command)
|
||||
|
||||
def get():
|
||||
controllers_map = {
|
||||
'gnome': 'gnome-open',
|
||||
'kde': 'kde-open',
|
||||
'xfce': 'exo-open',
|
||||
}
|
||||
|
||||
desktop_environment = detect_desktop_environment()
|
||||
|
||||
try:
|
||||
controller_name = controllers_map[desktop_environment]
|
||||
return _controllers[controller_name].open
|
||||
|
||||
except KeyError:
|
||||
if _controllers.has_key('xdg-open'):
|
||||
return _controllers['xdg-open'].open
|
||||
else:
|
||||
return webbrowser.open
|
||||
|
||||
|
||||
if os.environ.get("DISPLAY"):
|
||||
register_X_controllers()
|
||||
_open = get()
|
||||
|
||||
|
||||
def open(filename):
|
||||
'''Open a file or an URL in the registered default application.'''
|
||||
|
||||
return _open(filename)
|
||||
Reference in New Issue
Block a user