| 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 |