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