1#!/usr/bin/env python
2
3"""The clang static analyzer results viewer.
4"""
5
6import sys
7import posixpath
8import thread
9import time
10import urllib
11import webbrowser
12
13# How long to wait for server to start.
14kSleepTimeout = .05
15kMaxSleeps = int(60 / kSleepTimeout)
16
17# Default server parameters
18
19kDefaultHost = '127.0.0.1'
20kDefaultPort = 8181
21kMaxPortsToTry = 100
22
23###
24
25def url_is_up(url):
26    try:
27        o = urllib.urlopen(url)
28    except IOError:
29        return False
30    o.close()
31    return True
32
33def start_browser(port, options):
34    import urllib, webbrowser
35
36    url = 'http://%s:%d'%(options.host, port)
37    
38    # Wait for server to start...
39    if options.debug:
40        sys.stderr.write('%s: Waiting for server.' % sys.argv[0])
41        sys.stderr.flush()
42    for i in range(kMaxSleeps):
43        if url_is_up(url):
44            break
45        if options.debug:
46            sys.stderr.write('.')
47            sys.stderr.flush()
48        time.sleep(kSleepTimeout)
49    else:
50        print >>sys.stderr,'WARNING: Unable to detect that server started.'
51
52    if options.debug:
53        print >>sys.stderr,'%s: Starting webbrowser...' % sys.argv[0]
54    webbrowser.open(url)
55
56def run(port, options, root):
57    import ScanView
58    try:
59        print 'Starting scan-view at: http://%s:%d'%(options.host,
60                                                     port)
61        print '  Use Ctrl-C to exit.'
62        httpd = ScanView.create_server((options.host, port),
63                                       options, root)
64        httpd.serve_forever()
65    except KeyboardInterrupt:
66        pass
67
68def port_is_open(port):
69    import SocketServer
70    try:
71        t = SocketServer.TCPServer((kDefaultHost,port),None)
72    except:
73        return False
74    t.server_close()
75    return True
76
77def main():    
78    from optparse import OptionParser
79    parser = OptionParser('usage: %prog [options] <results directory>')
80    parser.set_description(__doc__)
81    parser.add_option(
82        '--host', dest="host", default=kDefaultHost, type="string",
83        help="Host interface to listen on. (default=%s)" % kDefaultHost)
84    parser.add_option(
85        '--port', dest="port", default=None, type="int",
86        help="Port to listen on. (default=%s)" % kDefaultPort)
87    parser.add_option("--debug", dest="debug", default=0, 
88                      action="count",
89                      help="Print additional debugging information.")
90    parser.add_option("--auto-reload", dest="autoReload", default=False, 
91                      action="store_true",
92                      help="Automatically update module for each request.")
93    parser.add_option("--no-browser", dest="startBrowser", default=True, 
94                      action="store_false",
95                      help="Don't open a webbrowser on startup.")
96    parser.add_option("--allow-all-hosts", dest="onlyServeLocal", default=True, 
97                      action="store_false",
98                      help='Allow connections from any host (access restricted to "127.0.0.1" by default)')
99    (options, args) = parser.parse_args()
100
101    if len(args) != 1:
102        parser.error('No results directory specified.')
103    root, = args
104
105    # Make sure this directory is in a reasonable state to view.
106    if not posixpath.exists(posixpath.join(root,'index.html')):
107        parser.error('Invalid directory, analysis results not found!')
108
109    # Find an open port. We aren't particularly worried about race
110    # conditions here. Note that if the user specified a port we only
111    # use that one.
112    if options.port is not None:
113        port = options.port
114    else:    
115        for i in range(kMaxPortsToTry):
116            if port_is_open(kDefaultPort + i):
117                port = kDefaultPort + i
118                break
119        else:
120            parser.error('Unable to find usable port in [%d,%d)'%(kDefaultPort,
121                                                                  kDefaultPort+kMaxPortsToTry))
122
123    # Kick off thread to wait for server and start web browser, if
124    # requested.
125    if options.startBrowser:
126        t = thread.start_new_thread(start_browser, (port,options))
127
128    run(port, options, root)
129
130if __name__ == '__main__':
131    main()
132