aboutsummaryrefslogtreecommitdiff
path: root/tools/dev_server
diff options
context:
space:
mode:
Diffstat (limited to 'tools/dev_server')
-rwxr-xr-xtools/dev_server173
1 files changed, 173 insertions, 0 deletions
diff --git a/tools/dev_server b/tools/dev_server
new file mode 100755
index 000000000..331720eda
--- /dev/null
+++ b/tools/dev_server
@@ -0,0 +1,173 @@
+#!/usr/bin/env python
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import print_function
+import sys
+import os
+import time
+import argparse
+import socket
+import subprocess
+import threading
+
+try:
+ import socketserver
+ from http.server import SimpleHTTPRequestHandler
+except ImportError:
+ import SocketServer as socketserver
+ import SimpleHTTPServer
+ SimpleHTTPRequestHandler = SimpleHTTPServer.SimpleHTTPRequestHandler
+
+
+class TCPServer(socketserver.TCPServer):
+
+ def server_bind(self):
+ self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ self.socket.bind(self.server_address)
+
+
+class Server(object):
+
+ def __init__(self, port, directory, rebuilder):
+ self.port = port
+ self.directory = directory
+ self.rebuilder = rebuilder
+ self.lock = threading.Lock()
+
+ def serve(self):
+ this = self
+
+ class Handler(SimpleHTTPRequestHandler):
+
+ def __init__(self, *args, **kwargs):
+ SimpleHTTPRequestHandler.__init__(self, *args, **kwargs)
+ self.extensions_map['.wasm'] = 'application/wasm'
+
+ def translate_path(self, path):
+ path = SimpleHTTPRequestHandler.translate_path(self, path)
+ relpath = os.path.relpath(path, os.getcwd())
+ fullpath = os.path.join(this.directory, relpath)
+ return fullpath
+
+ def do_GET(self):
+ try:
+ with this.lock:
+ this.rebuilder.rebuild_if_needed()
+ except RebuildFailed as e:
+ self.send_response(200)
+ self.send_header("Content-type", "text/html")
+ self.end_headers()
+ self.wfile.write("<pre>")
+ self.wfile.write(e.stdout_and_stderr)
+ return
+ return SimpleHTTPRequestHandler.do_GET(self)
+
+ print('Starting server at http://localhost:{}'.format(self.port))
+ httpd = TCPServer(('', self.port), Handler)
+ try:
+ httpd.serve_forever()
+ except KeyboardInterrupt:
+ httpd.shutdown()
+ httpd.server_close()
+
+
+class RebuildFailed(Exception):
+
+ def __init__(self, stdout_and_stderr):
+ self.stdout_and_stderr = stdout_and_stderr
+
+
+class Rebuilder(object):
+
+ def __init__(self, command, ignored_paths):
+ self.command = command
+ self.ignored_paths = ignored_paths
+ self.last_disk_state = None
+ self.abs_ignored_paths = [os.path.abspath(p) for p in self.ignored_paths]
+
+ def rebuild_if_needed(self):
+ if self.last_disk_state == self.last_modified_time():
+ return
+ stdout_and_stderr, success = self.rebuild()
+ if not success:
+ message = b"Failed to build! Command output:\n\n" + stdout_and_stderr
+ raise RebuildFailed(message)
+ self.last_disk_state = self.last_modified_time()
+
+ def last_modified_time(self):
+ start_time = time.time()
+ max_mtime = 0
+ for dirpath, dirnames, filenames in os.walk(
+ os.path.abspath('.'), topdown=True):
+ dirnames[:] = [
+ n for n in dirnames
+ if not self.should_ignore_path(os.path.join(dirpath, n))
+ ]
+ for filename in filenames:
+ path = os.path.join(dirpath, filename)
+ if self.should_ignore_path(path):
+ continue
+ mtime = os.stat(path).st_mtime
+ max_mtime = max(max_mtime, mtime)
+ print(' scanned in {:.03f}s'.format(time.time() - start_time).rjust(
+ 80, '='))
+ return max_mtime
+
+ def should_ignore_path(self, path):
+ return path in self.abs_ignored_paths
+
+ def rebuild(self):
+ print('Running command: {}'.format(self.command))
+ print(' begin build'.rjust(80, '='))
+ start_time = time.time()
+ status = 0
+ try:
+ stdout_and_stderr = subprocess.check_output(
+ self.command, shell=True, stderr=subprocess.STDOUT)
+ except subprocess.CalledProcessError as e:
+ status = e.returncode
+ stdout_and_stderr = e.output
+ print(stdout_and_stderr.decode('utf8'))
+ print(' built in {:.03f}s'.format(time.time() - start_time).rjust(80, '='))
+ return stdout_and_stderr, status == 0
+
+
+def main(argv):
+ parser = argparse.ArgumentParser(description='HTTP server for UI development')
+ parser.add_argument(
+ '-p',
+ '--port',
+ help='port number (default: 3000)',
+ type=int,
+ default=3000)
+ parser.add_argument(
+ '-i', '--ignore', default=[], action='append', help='Ignore this path')
+ parser.add_argument(
+ '-s',
+ '--serve',
+ default=os.getcwd(),
+ help='Serve this directory (default: current directory)')
+ parser.add_argument('command', default='', nargs='?', help='Command to run')
+
+ args = parser.parse_args(argv)
+
+ rebuilder = Rebuilder(args.command, args.ignore)
+ server = Server(args.port, args.serve, rebuilder)
+ server.serve()
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))