OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright 2015 The Chromium Authors. All rights reserved. | 2 # Copyright 2015 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.skyserver import SkyServer | 6 from skypy.skyserver import SkyServer |
7 import argparse | 7 import argparse |
8 import json | 8 import json |
9 import logging | 9 import logging |
10 import os | 10 import os |
11 import subprocess | 11 import subprocess |
12 import sys | 12 import sys |
13 import urllib | |
14 import urlparse | 13 import urlparse |
15 | 14 |
16 SKY_TOOLS_DIR = os.path.dirname(__file__) | 15 SKY_TOOLS_DIR = os.path.dirname(os.path.abspath(__file__)) |
17 SKY_ROOT = os.path.dirname(SKY_TOOLS_DIR) | 16 SKY_ROOT = os.path.dirname(SKY_TOOLS_DIR) |
18 SRC_ROOT = os.path.dirname(SKY_ROOT) | 17 SRC_ROOT = os.path.dirname(SKY_ROOT) |
19 | 18 |
20 SKY_SERVER_PORT = 9888 | 19 SKY_SERVER_PORT = 9888 |
21 DEFAULT_URL = "sky://domokit.github.io/home" | 20 DEFAULT_URL = "sky://domokit.github.io/home" |
22 APK_NAME = 'SkyDemo.apk' | 21 APK_NAME = 'SkyDemo.apk' |
23 ADB_PATH = os.path.join(SRC_ROOT, | 22 ADB_PATH = os.path.join(SRC_ROOT, |
24 'third_party/android_tools/sdk/platform-tools/adb') | 23 'third_party/android_tools/sdk/platform-tools/adb') |
25 | 24 |
26 | 25 |
27 PID_FILE_PATH = "/tmp/skydemo.pids" | 26 PID_FILE_PATH = "/tmp/skydemo.pids" |
28 PID_FILE_KEYS = frozenset([ | 27 PID_FILE_KEYS = frozenset([ |
29 'remote_sky_server_port', | 28 'remote_sky_server_port', |
30 'sky_server_pid', | 29 'sky_server_pid', |
31 'sky_server_port', | 30 'sky_server_port', |
32 'sky_server_root', | 31 'sky_server_root', |
33 'build_dir', | 32 'build_dir', |
34 ]) | 33 ]) |
35 | 34 |
36 # This 'strict dictionary' approach is useful for catching typos. | 35 # This 'strict dictionary' approach is useful for catching typos. |
37 class Pids(object): | 36 class Pids(object): |
38 def __init__(self, known_keys, contents=None): | 37 def __init__(self, known_keys, contents=None): |
39 self._known_keys = known_keys | 38 self._known_keys = known_keys |
40 self._dict = contents if contents is not None else {} | 39 self._dict = contents if contents is not None else {} |
41 | 40 |
42 def __len__(self): | 41 def __len__(self): |
43 return len(self._dict) | 42 return len(self._dict) |
44 | 43 |
| 44 def get(self, key, default=None): |
| 45 assert key in self._known_keys, '%s not in known_keys' % key |
| 46 return self._dict.get(key, default) |
| 47 |
45 def __getitem__(self, key): | 48 def __getitem__(self, key): |
46 assert key in self._known_keys, '%s not in known_keys' % key | 49 assert key in self._known_keys, '%s not in known_keys' % key |
47 return self._dict[key] | 50 return self._dict[key] |
48 | 51 |
49 def __setitem__(self, key, value): | 52 def __setitem__(self, key, value): |
50 assert key in self._known_keys, '%s not in known_keys' % key | 53 assert key in self._known_keys, '%s not in known_keys' % key |
51 self._dict[key] = value | 54 self._dict[key] = value |
52 | 55 |
53 def __delitem__(self, key): | 56 def __delitem__(self, key): |
54 assert key in self._known_keys, '%s not in known_keys' % key | 57 assert key in self._known_keys, '%s not in known_keys' % key |
55 del self._dict[key] | 58 del self._dict[key] |
56 | 59 |
57 def __iter__(self): | 60 def __iter__(self): |
58 return iter(self._dict) | 61 return iter(self._dict) |
59 | 62 |
60 def __contains__(self, key): | 63 def __contains__(self, key): |
61 assert key in self._known_keys, '%s not in allowed_keys' % key | 64 assert key in self._known_keys, '%s not in allowed_keys' % key |
62 return key in self._dict | 65 return key in self._dict |
63 | 66 |
64 def clear(): | 67 def clear(self): |
65 self._dict = {} | 68 self._dict = {} |
66 | 69 |
67 @classmethod | 70 @classmethod |
68 def read_from(cls, path, known_keys): | 71 def read_from(cls, path, known_keys): |
69 contents = {} | 72 contents = {} |
70 try: | 73 try: |
71 with open(path, 'r') as pid_file: | 74 with open(path, 'r') as pid_file: |
72 contents = json.load(pid_file) | 75 contents = json.load(pid_file) |
73 except: | 76 except: |
74 if os.path.exists(path): | 77 if os.path.exists(path): |
75 logging.warn('Failed to read pid file: %s' % path) | 78 logging.warn('Failed to read pid file: %s' % path) |
76 return cls(known_keys, contents) | 79 return cls(known_keys, contents) |
77 | 80 |
78 def write_to(self, path): | 81 def write_to(self, path): |
79 try: | 82 try: |
80 with open(path, 'w') as pid_file: | 83 with open(path, 'w') as pid_file: |
81 json.dump(self._dict, pid_file, indent=2, sort_keys=True) | 84 json.dump(self._dict, pid_file, indent=2, sort_keys=True) |
82 except: | 85 except: |
83 logging.warn('Failed to write pid file: %s' % path) | 86 logging.warn('Failed to write pid file: %s' % path) |
84 | 87 |
85 | 88 |
86 def _convert_to_sky_url(url): | 89 def _convert_to_sky_url(url): |
87 result = urllib.parse.urlsplit(url) | 90 parts = urlparse.urlsplit(url) |
88 result.scheme = 'sky' | 91 parts = parts._replace(scheme='sky') |
89 return urllib.parse.urlunsplit(result) | 92 return parts.geturl() |
90 | 93 |
91 | 94 |
92 # A free function for possible future sharing with a 'load' command. | 95 # A free function for possible future sharing with a 'load' command. |
93 def _url_from_args(args): | 96 def _url_from_args(args, pids): |
94 if urlparse.urlparse(args.url_or_path).scheme: | 97 if urlparse.urlparse(args.url_or_path).scheme: |
95 return args.url_or_path | 98 return args.url_or_path |
96 # The load happens on the remote device, use the remote port. | 99 # The load happens on the remote device, use the remote port. |
97 remote_sky_server_port = self.pids.get('remote_sky_server_port', | 100 remote_sky_server_port = pids.get('remote_sky_server_port', |
98 self.pids['sky_server_port']) | 101 pids['sky_server_port']) |
99 url = SkyServer.url_for_path(remote_sky_server_port, | 102 url = SkyServer.url_for_path(remote_sky_server_port, |
100 self.pids['sky_server_root'], args.url_or_path) | 103 pids['sky_server_root'], args.url_or_path) |
101 return _convert_to_sky_url(url) | 104 return _convert_to_sky_url(url) |
102 | 105 |
103 | 106 |
104 class StartSky(object): | 107 class StartSky(object): |
105 def add_subparser(self, subparsers): | 108 def add_subparser(self, subparsers): |
106 start_parser = subparsers.add_parser('start', | 109 start_parser = subparsers.add_parser('start', |
107 help='launch SKyShell.apk on the device') | 110 help='launch SKyShell.apk on the device') |
108 start_parser.add_argument('build_dir', type=str) | 111 start_parser.add_argument('build_dir', type=str) |
109 start_parser.add_argument('url_or_path', nargs='?', type=str, | 112 start_parser.add_argument('url_or_path', nargs='?', type=str, |
110 default=DEFAULT_URL) | 113 default=DEFAULT_URL) |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
146 | 149 |
147 port_string = 'tcp:%s' % sky_server.port | 150 port_string = 'tcp:%s' % sky_server.port |
148 subprocess.check_call([ | 151 subprocess.check_call([ |
149 ADB_PATH, 'reverse', port_string, port_string | 152 ADB_PATH, 'reverse', port_string, port_string |
150 ]) | 153 ]) |
151 pids['remote_sky_server_port'] = sky_server.port | 154 pids['remote_sky_server_port'] = sky_server.port |
152 | 155 |
153 subprocess.check_call([ADB_PATH, 'shell', | 156 subprocess.check_call([ADB_PATH, 'shell', |
154 'am', 'start', | 157 'am', 'start', |
155 '-a', 'android.intent.action.VIEW', | 158 '-a', 'android.intent.action.VIEW', |
156 '-d', _url_from_args(args)]) | 159 '-d', _url_from_args(args, pids)]) |
157 | 160 |
158 | 161 |
159 class StopSky(object): | 162 class StopSky(object): |
160 def add_subparser(self, subparsers): | 163 def add_subparser(self, subparsers): |
161 stop_parser = subparsers.add_parser('stop', | 164 stop_parser = subparsers.add_parser('stop', |
162 help=('kill all running SkyShell.apk processes')) | 165 help=('kill all running SkyShell.apk processes')) |
163 stop_parser.set_defaults(func=self.run) | 166 stop_parser.set_defaults(func=self.run) |
164 | 167 |
165 def _kill_if_exists(self, pids, key, name): | 168 def _kill_if_exists(self, pids, key, name): |
166 pid = pids.pop(key, None) | 169 pid = pids.pop(key, None) |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
199 args = parser.parse_args() | 202 args = parser.parse_args() |
200 pids = Pids.read_from(PID_FILE_PATH, PID_FILE_KEYS) | 203 pids = Pids.read_from(PID_FILE_PATH, PID_FILE_KEYS) |
201 exit_code = args.func(args, pids) | 204 exit_code = args.func(args, pids) |
202 # We could do this with an at-exit handler instead? | 205 # We could do this with an at-exit handler instead? |
203 pids.write_to(PID_FILE_PATH) | 206 pids.write_to(PID_FILE_PATH) |
204 sys.exit(exit_code) | 207 sys.exit(exit_code) |
205 | 208 |
206 | 209 |
207 if __name__ == '__main__': | 210 if __name__ == '__main__': |
208 SkyShellRunner().main() | 211 SkyShellRunner().main() |
OLD | NEW |