blob: 07248c9b1d30d88e2deb8e436f153b9e4a9e89e5 [file] [log] [blame]
#!/usr/bin/env python
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from skypy.paths import Paths
from skypy.skyserver import SkyServer
import argparse
import json
import logging
import os
import requests
import signal
import skypy.configuration as configuration
import subprocess
import urlparse
SUPPORTED_MIME_TYPES = [
'text/html',
'text/sky',
'text/plain',
]
HTTP_PORT = 9999
PID_FILE_PATH = "/tmp/skydb.pids"
class SkyDebugger(object):
def __init__(self):
self.paths = None
self.pids = {}
# FIXME: This is not android aware nor aware of the port
# skyserver is listening on.
self.base_url = 'http://localhost:7777'
def _server_root_and_url_from_path_arg(self, url_or_path):
# This is already a valid url we don't need a local server.
if urlparse.urlparse(url_or_path).scheme:
return None, url_or_path
path = os.path.abspath(url_or_path)
if os.path.commonprefix([path, self.paths.src_root]) == self.paths.src_root:
server_root = self.paths.src_root
else:
server_root = os.path.dirname(path)
logging.warn(
'%s is outside of mojo root, using %s as server root' %
(path, server_root))
local_url = SkyServer.url_for_path(HTTP_PORT, server_root, path)
return server_root, local_url
def _in_chromoting(self):
return os.environ.get('CHROME_REMOTE_DESKTOP_SESSION', False)
def _build_mojo_shell_command(self, args):
sky_server = None
self.paths = Paths(os.path.join('out', args.configuration))
content_handlers = ['%s,%s' % (mime_type, 'mojo:sky_viewer')
for mime_type in SUPPORTED_MIME_TYPES]
shell_command = [
self.paths.mojo_shell_path,
'--v=1',
'--content-handlers=%s' % ','.join(content_handlers),
'--url-mappings=mojo:window_manager=mojo:sky_debugger',
'mojo:window_manager',
]
if args.use_osmesa:
shell_command.append('--args-for=mojo:native_viewport_service --use-osmesa')
if args.url_or_path:
# Check if we need a local server for the url/path arg:
server_root, url = \
self._server_root_and_url_from_path_arg(args.url_or_path)
sky_server = SkyServer(self.paths, HTTP_PORT, args.configuration,
server_root)
prompt_args = '--args-for=mojo:sky_debugger_prompt %s' % url
shell_command.append(prompt_args)
if args.gdb:
shell_command = ['gdb', '--args'] + shell_command
return shell_command, sky_server
def start_command(self, args):
shell_command, sky_server = self._build_mojo_shell_command(args)
if args.show_command:
print " ".join(shell_command)
return
self.stop_command([]) # Quit any existing process.
if sky_server:
self.pids['sky_server_pid'] = sky_server.start()
# self.pids['sky_server_port'] = sky_server.port
# self.pids['sky_server_root'] = sky_server.root
self.pids['mojo_shell_pid'] = subprocess.Popen(shell_command).pid
def _kill_if_exists(self, key, name):
pid = self.pids.pop(key, None)
if not pid:
logging.info('No pid for %s, nothing to do.' % name)
return
logging.info('Killing %s (%s).' % (name, pid))
try:
os.kill(pid, signal.SIGTERM)
except OSError:
logging.info('%s (%s) already gone.' % (name, pid))
def stop_command(self, args):
# FIXME: Send /quit to sky prompt instead of killing.
# self._send_command_to_sky('/quit')
self._kill_if_exists('mojo_shell_pid', 'mojo_shell')
self._kill_if_exists('sky_server_pid', 'sky_server')
def load_command(self, args):
# Should resolve paths to relative urls like start does.
# self.pids['sky_server_root'] and port should help.
self._send_command_to_sky('/load', args.url_or_path)
def _send_command_to_sky(self, command_path, payload=None):
url = self.base_url + command_path
if payload:
response = requests.post(url, payload)
else:
response = requests.get(url)
print response.text
# FIXME: These could be made into a context object with __enter__/__exit__.
def _load_pid_file(self, path):
try:
with open(path, 'r') as pid_file:
return json.load(pid_file)
except:
if os.path.exists(path):
logging.warn('Failed to read pid file: %s' % path)
return {}
def _write_pid_file(self, path, pids):
try:
with open(path, 'w') as pid_file:
json.dump(pids, pid_file)
except:
logging.warn('Failed to write pid file: %s' % path)
def _add_basic_command(self, subparsers, name, url_path, help_text):
parser = subparsers.add_parser(name, help=help_text)
command = lambda args: self._send_command_to_sky(url_path)
parser.set_defaults(func=command)
def main(self):
logging.basicConfig(level=logging.INFO)
self.pids = self._load_pid_file(PID_FILE_PATH)
parser = argparse.ArgumentParser(description='Sky launcher/debugger')
subparsers = parser.add_subparsers(help='sub-command help')
start_parser = subparsers.add_parser('start',
help='launch a new mojo_shell with sky')
configuration.add_arguments(start_parser)
start_parser.add_argument('--gdb', action='store_true')
start_parser.add_argument('--use-osmesa', action='store_true',
default=self._in_chromoting())
start_parser.add_argument('url_or_path', nargs='?', type=str)
start_parser.add_argument('--show-command', action='store_true',
help='Display the shell command and exit')
start_parser.set_defaults(func=self.start_command)
stop_parser = subparsers.add_parser('stop',
help=('stop sky (as listed in %s)' % PID_FILE_PATH))
stop_parser.set_defaults(func=self.stop_command)
self._add_basic_command(subparsers, 'trace', '/trace',
'toggle tracing')
self._add_basic_command(subparsers, 'reload', '/reload',
'reload the current page')
self._add_basic_command(subparsers, 'inspect', '/inspect',
'stop the running sky instance')
load_parser = subparsers.add_parser('load',
help='load a new page in the currently running sky')
load_parser.add_argument('url_or_path', type=str)
load_parser.set_defaults(func=self.load_command)
args = parser.parse_args()
args.func(args)
self._write_pid_file(PID_FILE_PATH, self.pids)
if __name__ == '__main__':
SkyDebugger().main()