OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright 2014 The Chromium Authors. All rights reserved. | 2 # Copyright 2014 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 from skypy.paths import Paths | 6 from skypy.paths import Paths |
7 from skypy.skyserver import SkyServer | 7 from skypy.skyserver import SkyServer |
8 import argparse | 8 import argparse |
9 import json | 9 import json |
10 import logging | 10 import logging |
11 import os | 11 import os |
12 import requests | 12 import requests |
13 import signal | 13 import signal |
14 import skypy.configuration as configuration | 14 import skypy.configuration as configuration |
15 import subprocess | 15 import subprocess |
16 import urlparse | 16 import urlparse |
| 17 import time |
17 | 18 |
18 | 19 |
19 SUPPORTED_MIME_TYPES = [ | 20 SUPPORTED_MIME_TYPES = [ |
20 'text/html', | 21 'text/html', |
21 'text/sky', | 22 'text/sky', |
22 'text/plain', | 23 'text/plain', |
23 ] | 24 ] |
24 | 25 |
25 HTTP_PORT = 9999 | 26 DEFAULT_SKY_COMMAND_PORT = 7777 |
| 27 SKY_SERVER_PORT = 9999 |
26 PID_FILE_PATH = "/tmp/skydb.pids" | 28 PID_FILE_PATH = "/tmp/skydb.pids" |
| 29 DEFAULT_URL = "https://raw.githubusercontent.com/domokit/mojo/master/sky/example
s/home.sky" |
27 | 30 |
28 | 31 |
29 class SkyDebugger(object): | 32 class SkyDebugger(object): |
30 def __init__(self): | 33 def __init__(self): |
31 self.paths = None | 34 self.paths = None |
32 self.pids = {} | 35 self.pids = {} |
33 # FIXME: This is not android aware nor aware of the port | |
34 # skyserver is listening on. | |
35 self.base_url = 'http://localhost:7777' | |
36 | 36 |
37 def _server_root_and_url_from_path_arg(self, url_or_path): | 37 def _server_root_for_url(self, url_or_path): |
38 # This is already a valid url we don't need a local server. | |
39 if urlparse.urlparse(url_or_path).scheme: | |
40 return None, url_or_path | |
41 | |
42 path = os.path.abspath(url_or_path) | 38 path = os.path.abspath(url_or_path) |
43 if os.path.commonprefix([path, self.paths.src_root]) == self.paths.src_r
oot: | 39 if os.path.commonprefix([path, self.paths.src_root]) == self.paths.src_r
oot: |
44 server_root = self.paths.src_root | 40 server_root = self.paths.src_root |
45 else: | 41 else: |
46 server_root = os.path.dirname(path) | 42 server_root = os.path.dirname(path) |
47 logging.warn( | 43 logging.warn( |
48 '%s is outside of mojo root, using %s as server root' % | 44 '%s is outside of mojo root, using %s as server root' % |
49 (path, server_root)) | 45 (path, server_root)) |
50 local_url = SkyServer.url_for_path(HTTP_PORT, server_root, path) | 46 return server_root |
51 return server_root, local_url | |
52 | 47 |
53 def _in_chromoting(self): | 48 def _in_chromoting(self): |
54 return os.environ.get('CHROME_REMOTE_DESKTOP_SESSION', False) | 49 return os.environ.get('CHROME_REMOTE_DESKTOP_SESSION', False) |
55 | 50 |
56 def _build_mojo_shell_command(self, args): | 51 def _build_mojo_shell_command(self, args): |
57 sky_server = None | |
58 self.paths = Paths(os.path.join('out', args.configuration)) | 52 self.paths = Paths(os.path.join('out', args.configuration)) |
59 | 53 |
60 content_handlers = ['%s,%s' % (mime_type, 'mojo:sky_viewer') | 54 content_handlers = ['%s,%s' % (mime_type, 'mojo:sky_viewer') |
61 for mime_type in SUPPORTED_MIME_TYPES] | 55 for mime_type in SUPPORTED_MIME_TYPES] |
62 shell_command = [ | 56 shell_command = [ |
63 self.paths.mojo_shell_path, | 57 self.paths.mojo_shell_path, |
64 '--v=1', | 58 '--v=1', |
65 '--content-handlers=%s' % ','.join(content_handlers), | 59 '--content-handlers=%s' % ','.join(content_handlers), |
66 '--url-mappings=mojo:window_manager=mojo:sky_debugger', | 60 '--url-mappings=mojo:window_manager=mojo:sky_debugger', |
| 61 '--args-for=mojo:sky_debugger_prompt %d' % args.command_port, |
67 'mojo:window_manager', | 62 'mojo:window_manager', |
68 ] | 63 ] |
69 if args.use_osmesa: | 64 if args.use_osmesa: |
70 shell_command.append('--args-for=mojo:native_viewport_service --use-
osmesa') | 65 shell_command.append('--args-for=mojo:native_viewport_service --use-
osmesa') |
71 | 66 |
72 if args.url_or_path: | |
73 # Check if we need a local server for the url/path arg: | |
74 server_root, url = \ | |
75 self._server_root_and_url_from_path_arg(args.url_or_path) | |
76 sky_server = SkyServer(self.paths, HTTP_PORT, args.configuration, | |
77 server_root) | |
78 | |
79 prompt_args = '--args-for=mojo:sky_debugger_prompt %s' % url | |
80 shell_command.append(prompt_args) | |
81 | |
82 if args.gdb: | 67 if args.gdb: |
83 shell_command = ['gdb', '--args'] + shell_command | 68 shell_command = ['gdb', '--args'] + shell_command |
84 | 69 |
85 return shell_command, sky_server | 70 return shell_command |
86 | 71 |
87 def start_command(self, args): | 72 def start_command(self, args): |
88 shell_command, sky_server = self._build_mojo_shell_command(args) | 73 shell_command = self._build_mojo_shell_command(args) |
89 if args.show_command: | 74 if args.show_command: |
90 print " ".join(shell_command) | 75 print " ".join(shell_command) |
91 return | 76 return |
92 | 77 |
93 self.stop_command([]) # Quit any existing process. | 78 self.stop_command(None) # Quit any existing process. |
94 | 79 |
95 if sky_server: | 80 print args.url_or_path |
| 81 # We only start a server for paths: |
| 82 if not urlparse.urlparse(args.url_or_path).scheme: |
| 83 server_root = self._server_root_for_url(args.url_or_path) |
| 84 sky_server = SkyServer(self.paths, SKY_SERVER_PORT, |
| 85 args.configuration, server_root) |
96 self.pids['sky_server_pid'] = sky_server.start() | 86 self.pids['sky_server_pid'] = sky_server.start() |
97 # self.pids['sky_server_port'] = sky_server.port | 87 self.pids['sky_server_port'] = sky_server.port |
98 # self.pids['sky_server_root'] = sky_server.root | 88 self.pids['sky_server_root'] = sky_server.root |
| 89 |
99 self.pids['mojo_shell_pid'] = subprocess.Popen(shell_command).pid | 90 self.pids['mojo_shell_pid'] = subprocess.Popen(shell_command).pid |
| 91 self.pids['sky_command_port'] = args.command_port |
| 92 |
| 93 if not self._wait_for_sky_command_port(): |
| 94 logging.error('Failed to start sky') |
| 95 self.stop_command(None) |
| 96 else: |
| 97 self.load_command(args) |
100 | 98 |
101 def _kill_if_exists(self, key, name): | 99 def _kill_if_exists(self, key, name): |
102 pid = self.pids.pop(key, None) | 100 pid = self.pids.pop(key, None) |
103 if not pid: | 101 if not pid: |
104 logging.info('No pid for %s, nothing to do.' % name) | 102 logging.info('No pid for %s, nothing to do.' % name) |
105 return | 103 return |
106 logging.info('Killing %s (%s).' % (name, pid)) | 104 logging.info('Killing %s (%s).' % (name, pid)) |
107 try: | 105 try: |
108 os.kill(pid, signal.SIGTERM) | 106 os.kill(pid, signal.SIGTERM) |
109 except OSError: | 107 except OSError: |
110 logging.info('%s (%s) already gone.' % (name, pid)) | 108 logging.info('%s (%s) already gone.' % (name, pid)) |
111 | 109 |
112 def stop_command(self, args): | 110 def stop_command(self, args): |
113 # FIXME: Send /quit to sky prompt instead of killing. | 111 # FIXME: Send /quit to sky prompt instead of killing. |
114 # self._send_command_to_sky('/quit') | 112 # self._send_command_to_sky('/quit') |
115 self._kill_if_exists('mojo_shell_pid', 'mojo_shell') | 113 self._kill_if_exists('mojo_shell_pid', 'mojo_shell') |
116 self._kill_if_exists('sky_server_pid', 'sky_server') | 114 self._kill_if_exists('sky_server_pid', 'sky_server') |
117 | 115 |
118 def load_command(self, args): | 116 def load_command(self, args): |
119 # Should resolve paths to relative urls like start does. | 117 if not urlparse.urlparse(args.url_or_path).scheme: |
120 # self.pids['sky_server_root'] and port should help. | 118 url = SkyServer.url_for_path(self.pids['sky_server_port'], |
121 self._send_command_to_sky('/load', args.url_or_path) | 119 self.pids['sky_server_root'], args.url_or_path) |
| 120 else: |
| 121 url = args.url_or_path |
| 122 self._send_command_to_sky('/load', url) |
| 123 |
| 124 def _command_base_url(self): |
| 125 return 'http://localhost:%s' % self.pids['sky_command_port'] |
122 | 126 |
123 def _send_command_to_sky(self, command_path, payload=None): | 127 def _send_command_to_sky(self, command_path, payload=None): |
124 url = self.base_url + command_path | 128 url = 'http://localhost:%s%s' % ( |
| 129 self.pids['sky_command_port'], command_path) |
125 if payload: | 130 if payload: |
126 response = requests.post(url, payload) | 131 response = requests.post(url, payload) |
127 else: | 132 else: |
128 response = requests.get(url) | 133 response = requests.get(url) |
129 print response.text | 134 print response.text |
130 | 135 |
131 # FIXME: These could be made into a context object with __enter__/__exit__. | 136 # FIXME: These could be made into a context object with __enter__/__exit__. |
132 def _load_pid_file(self, path): | 137 def _load_pid_file(self, path): |
133 try: | 138 try: |
134 with open(path, 'r') as pid_file: | 139 with open(path, 'r') as pid_file: |
135 return json.load(pid_file) | 140 return json.load(pid_file) |
136 except: | 141 except: |
137 if os.path.exists(path): | 142 if os.path.exists(path): |
138 logging.warn('Failed to read pid file: %s' % path) | 143 logging.warn('Failed to read pid file: %s' % path) |
139 return {} | 144 return {} |
140 | 145 |
141 def _write_pid_file(self, path, pids): | 146 def _write_pid_file(self, path, pids): |
142 try: | 147 try: |
143 with open(path, 'w') as pid_file: | 148 with open(path, 'w') as pid_file: |
144 json.dump(pids, pid_file) | 149 json.dump(pids, pid_file) |
145 except: | 150 except: |
146 logging.warn('Failed to write pid file: %s' % path) | 151 logging.warn('Failed to write pid file: %s' % path) |
147 | 152 |
148 def _add_basic_command(self, subparsers, name, url_path, help_text): | 153 def _add_basic_command(self, subparsers, name, url_path, help_text): |
149 parser = subparsers.add_parser(name, help=help_text) | 154 parser = subparsers.add_parser(name, help=help_text) |
150 command = lambda args: self._send_command_to_sky(url_path) | 155 command = lambda args: self._send_command_to_sky(url_path) |
151 parser.set_defaults(func=command) | 156 parser.set_defaults(func=command) |
152 | 157 |
| 158 def _wait_for_sky_command_port(self): |
| 159 tries = 0 |
| 160 while True: |
| 161 try: |
| 162 self._send_command_to_sky('/') |
| 163 return True |
| 164 except: |
| 165 tries += 1 |
| 166 if tries == 3: |
| 167 logging.warn('Still waiting for sky on port %s' % |
| 168 self.pids['sky_command_port']) |
| 169 if tries > 10: |
| 170 return False |
| 171 time.sleep(1) |
| 172 |
153 def main(self): | 173 def main(self): |
154 logging.basicConfig(level=logging.INFO) | 174 logging.basicConfig(level=logging.INFO) |
155 | 175 |
156 self.pids = self._load_pid_file(PID_FILE_PATH) | 176 self.pids = self._load_pid_file(PID_FILE_PATH) |
157 | 177 |
158 parser = argparse.ArgumentParser(description='Sky launcher/debugger') | 178 parser = argparse.ArgumentParser(description='Sky launcher/debugger') |
159 subparsers = parser.add_subparsers(help='sub-command help') | 179 subparsers = parser.add_subparsers(help='sub-command help') |
160 | 180 |
161 start_parser = subparsers.add_parser('start', | 181 start_parser = subparsers.add_parser('start', |
162 help='launch a new mojo_shell with sky') | 182 help='launch a new mojo_shell with sky') |
163 configuration.add_arguments(start_parser) | 183 configuration.add_arguments(start_parser) |
164 start_parser.add_argument('--gdb', action='store_true') | 184 start_parser.add_argument('--gdb', action='store_true') |
| 185 start_parser.add_argument('--command-port', type=int, |
| 186 default=DEFAULT_SKY_COMMAND_PORT) |
165 start_parser.add_argument('--use-osmesa', action='store_true', | 187 start_parser.add_argument('--use-osmesa', action='store_true', |
166 default=self._in_chromoting()) | 188 default=self._in_chromoting()) |
167 start_parser.add_argument('url_or_path', nargs='?', type=str) | 189 start_parser.add_argument('url_or_path', nargs='?', type=str, |
| 190 default=DEFAULT_URL) |
168 start_parser.add_argument('--show-command', action='store_true', | 191 start_parser.add_argument('--show-command', action='store_true', |
169 help='Display the shell command and exit') | 192 help='Display the shell command and exit') |
170 start_parser.set_defaults(func=self.start_command) | 193 start_parser.set_defaults(func=self.start_command) |
171 | 194 |
172 stop_parser = subparsers.add_parser('stop', | 195 stop_parser = subparsers.add_parser('stop', |
173 help=('stop sky (as listed in %s)' % PID_FILE_PATH)) | 196 help=('stop sky (as listed in %s)' % PID_FILE_PATH)) |
174 stop_parser.set_defaults(func=self.stop_command) | 197 stop_parser.set_defaults(func=self.stop_command) |
175 | 198 |
176 self._add_basic_command(subparsers, 'trace', '/trace', | 199 self._add_basic_command(subparsers, 'trace', '/trace', |
177 'toggle tracing') | 200 'toggle tracing') |
178 self._add_basic_command(subparsers, 'reload', '/reload', | 201 self._add_basic_command(subparsers, 'reload', '/reload', |
179 'reload the current page') | 202 'reload the current page') |
180 self._add_basic_command(subparsers, 'inspect', '/inspect', | 203 self._add_basic_command(subparsers, 'inspect', '/inspect', |
181 'stop the running sky instance') | 204 'stop the running sky instance') |
182 | 205 |
183 load_parser = subparsers.add_parser('load', | 206 load_parser = subparsers.add_parser('load', |
184 help='load a new page in the currently running sky') | 207 help='load a new page in the currently running sky') |
185 load_parser.add_argument('url_or_path', type=str) | 208 load_parser.add_argument('url_or_path', type=str) |
186 load_parser.set_defaults(func=self.load_command) | 209 load_parser.set_defaults(func=self.load_command) |
187 | 210 |
188 args = parser.parse_args() | 211 args = parser.parse_args() |
189 args.func(args) | 212 args.func(args) |
190 | 213 |
191 self._write_pid_file(PID_FILE_PATH, self.pids) | 214 self._write_pid_file(PID_FILE_PATH, self.pids) |
192 | 215 |
193 | 216 |
194 if __name__ == '__main__': | 217 if __name__ == '__main__': |
195 SkyDebugger().main() | 218 SkyDebugger().main() |
OLD | NEW |