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