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