foo
diff --git a/sky/SDK.md b/sky/SDK.md index f70a03c..76e8cd9 100644 --- a/sky/SDK.md +++ b/sky/SDK.md
@@ -6,34 +6,41 @@ both likely to change in incompatible ways several times, but if you're interested in trying out the system, this document can help you get started. +Set up your computer +-------------------- + +1. Install the Dart SDK: + - https://www.dartlang.org/tools/download.html + +2. Install the ``adb`` tool from the Android SDK: + - https://developer.android.com/sdk/installing/index.html + +3. Install the Sky SDK: + - ``git clone https://github.com/domokit/sky_sdk.git`` + +4. Ensure sure $DART_SDK is set to the path of your Dart SDK and 'adb' + (inside 'platform-tools' in the android sdk) is in your $PATH. + + Set up your device ------------------ Currently Sky requires an Android device running the Lollipop (or newer) version of the Android operating system. -1. Install ``Sky`` on your device by via the Play Store. The Sky app on your - device is capable of displaying applications written using Sky. - -2. Enable developer mode on your device by visiting ``Settings > About phone`` +1. Enable developer mode on your device by visiting ``Settings > About phone`` and tapping the ``Build number`` field five times. -3. Enable ``USB debugging`` in ``Settings > Developer options``. - -Set up your computer --------------------- - -1. Install the ``adb`` tool from the Android SDK. - - Mac: ``brew install android-platform-tools`` - - Linux: ``sudo apt-get install android-tools-adb`` - -2. Download the Sky framework: - - ``git clone https://github.com/domokit/sky-sdk.git`` +2. Enable ``USB debugging`` in ``Settings > Developer options``. 3. Using a USB cable, plug your phone into your computer. If prompted on your device, authorize your computer to access your device. + Running a Sky application ------------------------- -1. ``/path/to/sky-sdk/shelldb myapp.sky && adb logcat`` +1. ``sky_sdk/bin/sky --install sky_sdk/examples/index.sky`` + The --install flag is only necessary the first time to install SkyDemo.apk. + +2. Use ``adb logcat`` to view any errors or Dart print() output from the app. \ No newline at end of file
diff --git a/sky/engine/core/script/dart_loader.cc b/sky/engine/core/script/dart_loader.cc index 62a72ea..19a878e 100644 --- a/sky/engine/core/script/dart_loader.cc +++ b/sky/engine/core/script/dart_loader.cc
@@ -26,11 +26,12 @@ Dart_Handle library, Dart_Handle url) { String string = StringFromDart(url); + String original = string; if (string.startsWith("dart:") || string.startsWith("mojo:")) return url; // TODO(dart): Figure out how 'package:' should work in sky. if (string.startsWith("package:")) { - string.replace("package:", "/gen/"); + string.replace("package:", "/packages/"); } String library_url_string = StringFromDart(Dart_LibraryUrl(library)); KURL library_url = KURL(ParsedURLString, library_url_string);
diff --git a/sky/framework/components/icon.dart b/sky/framework/components/icon.dart index 3b0f9d0..b37bd4c 100644 --- a/sky/framework/components/icon.dart +++ b/sky/framework/components/icon.dart
@@ -4,7 +4,8 @@ import '../fn.dart'; -const String kAssetBase = '/sky/assets/material-design-icons'; +// TODO(eseidel): This should use package:. +const String kAssetBase = '/packages/sky/assets/material-design-icons'; class Icon extends Component { List<Style> styles;
diff --git a/sky/sdk/README.md b/sky/sdk/README.md new file mode 100644 index 0000000..32bbe5f --- /dev/null +++ b/sky/sdk/README.md
@@ -0,0 +1,7 @@ +Sky SDK + +This repository is autogenerated by: +https://github.com/domokit/mojo/tree/master/sky/tools/deploy_sdk.py + +To add files, add them to: +https://github.com/domokit/mojo/tree/master/sky/sdk
diff --git a/sky/sdk/packages/mojo/pubspec.yaml b/sky/sdk/packages/mojo/pubspec.yaml new file mode 100644 index 0000000..289a1d3 --- /dev/null +++ b/sky/sdk/packages/mojo/pubspec.yaml
@@ -0,0 +1 @@ +name: mojo
diff --git a/sky/sdk/packages/sky/pubspec.yaml b/sky/sdk/packages/sky/pubspec.yaml new file mode 100644 index 0000000..b3456c9 --- /dev/null +++ b/sky/sdk/packages/sky/pubspec.yaml
@@ -0,0 +1,4 @@ +name: sky +dependencies: + mojo: + path: ../mojo
diff --git a/sky/sdk/tools/download_material_design_icons b/sky/sdk/tools/download_material_design_icons new file mode 100755 index 0000000..fd08a1d --- /dev/null +++ b/sky/sdk/tools/download_material_design_icons
@@ -0,0 +1,35 @@ +#!/usr/bin/env python +# Copyright (c) 2015 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. + +import os +import subprocess + +from webkitpy.common.system import filesystem +from webkitpy.common.webkit_finder import WebKitFinder + +finder = WebKitFinder(filesystem.FileSystem()) + +assets_dir = finder.path_from_chromium_base('sky', 'assets') +sha1_path = os.path.join(assets_dir, 'material-design-icons.sha1') + +with open(sha1_path) as f: + sha1 = f.read() + +tgz_path = os.path.join(assets_dir, 'material-design-icons.tgz') +subprocess.call([ + 'download_from_google_storage', + '--no_resume', + '--no_auth', + '--bucket', 'mojo', + '--output', tgz_path, + 'material-design-icons/%s' % sha1, +]) + +output_path = os.path.join(assets_dir, tgz_path) +subprocess.call([ + 'tar', '-xzf', output_path, '-C', assets_dir +]) + +os.unlink(tgz_path)
diff --git a/sky/sdk/tools/sky b/sky/sdk/tools/sky new file mode 100755 index 0000000..6890876 --- /dev/null +++ b/sky/sdk/tools/sky
@@ -0,0 +1,254 @@ +#!/usr/bin/env python +# Copyright 2015 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. + +import argparse +import json +import logging +import os +import subprocess +import sys +import urlparse +import socket + +SDK_TOOLS_DIR = os.path.dirname(os.path.abspath(__file__)) +SDK_ROOT = os.path.dirname(SDK_TOOLS_DIR) + +SKY_SERVER_PORT = 9888 +DEFAULT_URL = os.path.join(SDK_ROOT, "examples/index.sky") +APK_NAME = 'SkyDemo.apk' +ANDROID_PACKAGE = "org.domokit.sky.demo" +# FIXME: This assumes adb is in $PATH, we could look for ANDROID_HOME, etc? +ADB_PATH = 'adb' + +PID_FILE_PATH = "/tmp/skydemo.pids" +PID_FILE_KEYS = frozenset([ + 'remote_sky_server_port', + 'sky_server_pid', + 'sky_server_port', + 'sky_server_root', + 'build_dir', +]) + + +def _port_in_use(port): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + return sock.connect_ex(('localhost', port)) == 0 + + +# We need something to serve sky files, python's httpserver is sufficient. +def _start_http_server(port, root): + server_command = [ + 'python', '-m', 'SimpleHTTPServer', str(port), + ] + return subprocess.Popen(server_command, cwd=root).pid + + +# This 'strict dictionary' approach is useful for catching typos. +class Pids(object): + def __init__(self, known_keys, contents=None): + self._known_keys = known_keys + self._dict = contents if contents is not None else {} + + def __len__(self): + return len(self._dict) + + def get(self, key, default=None): + assert key in self._known_keys, '%s not in known_keys' % key + return self._dict.get(key, default) + + def __getitem__(self, key): + assert key in self._known_keys, '%s not in known_keys' % key + return self._dict[key] + + def __setitem__(self, key, value): + assert key in self._known_keys, '%s not in known_keys' % key + self._dict[key] = value + + def __delitem__(self, key): + assert key in self._known_keys, '%s not in known_keys' % key + del self._dict[key] + + def __iter__(self): + return iter(self._dict) + + def __contains__(self, key): + assert key in self._known_keys, '%s not in allowed_keys' % key + return key in self._dict + + def clear(self): + self._dict = {} + + def pop(self, key, default=None): + assert key in self._known_keys, '%s not in known_keys' % key + return self._dict.pop(key, default) + + @classmethod + def read_from(cls, path, known_keys): + contents = {} + try: + with open(path, 'r') as pid_file: + contents = json.load(pid_file) + except: + if os.path.exists(path): + logging.warn('Failed to read pid file: %s' % path) + return cls(known_keys, contents) + + def write_to(self, path): + try: + with open(path, 'w') as pid_file: + json.dump(self._dict, pid_file, indent=2, sort_keys=True) + except: + logging.warn('Failed to write pid file: %s' % path) + + +def _url_for_path(port, root, path): + relative_path = os.path.relpath(path, root) + return 'sky://localhost:%s/%s' % (port, relative_path) + + +class StartSky(object): + def add_subparser(self, subparsers): + start_parser = subparsers.add_parser('start', + help='launch SKyShell.apk on the device') + start_parser.add_argument('--install', action='store_true') + start_parser.add_argument('project_or_path', nargs='?', type=str, + default='main.sky') + start_parser.set_defaults(func=self.run) + + def run(self, args, pids): + if args.install: + apk_path = os.path.join(SDK_ROOT, 'apks', APK_NAME) + if not os.path.exists(apk_path): + print "'%s' does not exist?" % apk_path + return 2 + + subprocess.check_call([ADB_PATH, 'install', '-r', apk_path]) + + project_or_path = os.path.abspath(args.project_or_path) + + if os.path.isdir(project_or_path): + sky_server_root = project_or_path + main_sky = os.path.join(project_or_path, 'main.sky') + missing_msg = "Missing main.sky in project: %s" % sky_server_root + else: + sky_server_root = os.path.dirname(project_or_path) + main_sky = project_or_path + missing_msg = "%s does not exist." % main_sky + + if not os.path.isfile(main_sky): + print missing_msg + return 2 + + sky_server_port = SKY_SERVER_PORT + pids['sky_server_port'] = sky_server_port + if _port_in_use(sky_server_port): + logging.warn(('Port %s already in use. ' + ' Not starting server for %s') % (sky_server_port, sky_server_root)) + else: + sky_server_pid = _start_http_server(sky_server_port, sky_server_root) + pids['sky_server_pid'] = sky_server_pid + pids['sky_server_root'] = sky_server_root + + port_string = 'tcp:%s' % sky_server_port + subprocess.check_call([ + ADB_PATH, 'reverse', port_string, port_string + ]) + pids['remote_sky_server_port'] = sky_server_port + + # The load happens on the remote device, use the remote port. + sky_url = _url_for_path(pids['remote_sky_server_port'], sky_server_root, + main_sky) + + subprocess.check_call([ADB_PATH, 'shell', + 'am', 'start', + '-a', 'android.intent.action.VIEW', + '-d', sky_url]) + + +class StopSky(object): + def add_subparser(self, subparsers): + stop_parser = subparsers.add_parser('stop', + help=('kill all running SkyShell.apk processes')) + stop_parser.set_defaults(func=self.run) + + def _kill_if_exists(self, pids, key, name): + pid = pids.pop(key, None) + if not pid: + logging.info('No pid for %s, nothing to do.' % name) + return + logging.info('Killing %s (%d).' % (name, pid)) + try: + os.kill(pid, signal.SIGTERM) + except OSError: + logging.info('%s (%d) already gone.' % (name, pid)) + + def run(self, args, pids): + self._kill_if_exists(pids, 'sky_server_pid', 'sky_server') + + if 'remote_sky_server_port' in pids: + port_string = 'tcp:%s' % pids['remote_sky_server_port'] + subprocess.call([ADB_PATH, 'reverse', '--remove', port_string]) + + subprocess.call([ + ADB_PATH, 'shell', 'am', 'force-stop', ANDROID_PACKAGE]) + + pids.clear() + + +# TODO(eseidel): I'm not quite sure what to do about the bindings paths yet. +class Analyze(object): + def add_subparser(self, subparsers): + analyze_parser = subparsers.add_parser('analyze', + help=('run the dart analyzer with sky url mappings')) + analyze_parser.add_argument('app_path', type=str) + analyze_parser.set_defaults(func=self.run) + + def run(self, args, pids): + DART_SDK_PATH = os.env.get('DART_SDK') + if DART_SDK_PATH is None or not os.path.exists(DART_SDK_PATH): + print ("Can't find dart-sdk at %s. " + "Did you set DART_SDK in the environment?") % DART_SDK_PATH + return 2 + + ANALYZER_PATH = os.path.join(DART_SDK_PATH, 'bin/dartanalyzer') + + build_dir = os.path.abspath(pids['build_dir']) + bindings_path = os.path.join(build_dir, 'gen/sky/bindings') + sky_builtin_path = \ + os.path.join(SRC_ROOT, 'sky/engine/bindings/builtin.dart') + dart_sky_path = os.path.join(bindings_path, 'dart_sky.dart') + mojo_bindings_path = \ + os.path.join(SRC_ROOT, 'mojo/public/dart/bindings.dart') + mojo_core_path = os.path.join(SRC_ROOT, 'mojo/public/dart/core.dart') + analyzer_args = [ANALYZER_PATH, + "--url-mapping=dart:sky,%s" % dart_sky_path, + "--url-mapping=dart:sky_builtin,%s" % sky_builtin_path, + "--url-mapping=mojo:bindings,%s" % mojo_bindings_path, + "--url-mapping=mojo:core,%s" % mojo_core_path, + args.app_path + ] + subprocess.call(analyzer_args) + + +class SkyShellRunner(object): + def main(self): + logging.basicConfig(level=logging.WARNING) + + parser = argparse.ArgumentParser(description='Sky Demo Runner') + subparsers = parser.add_subparsers(help='sub-command help') + + for command in [StartSky(), StopSky()]: + command.add_subparser(subparsers) + + args = parser.parse_args() + pids = Pids.read_from(PID_FILE_PATH, PID_FILE_KEYS) + exit_code = args.func(args, pids) + # We could do this with an at-exit handler instead? + pids.write_to(PID_FILE_PATH) + sys.exit(exit_code) + + +if __name__ == '__main__': + SkyShellRunner().main()
diff --git a/sky/tools/deploy_sdk.py b/sky/tools/deploy_sdk.py new file mode 100755 index 0000000..f499bac --- /dev/null +++ b/sky/tools/deploy_sdk.py
@@ -0,0 +1,156 @@ +#!/usr/bin/env python +# Copyright 2015 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. + +import argparse +import logging +import os +import shutil +import subprocess +import sys + +SKY_TOOLS_DIR = os.path.dirname(os.path.abspath(__file__)) +SKY_DIR = os.path.dirname(SKY_TOOLS_DIR) +SRC_ROOT = os.path.dirname(SKY_DIR) + + +def git_revision(): + return subprocess.check_output(['git', 'rev-parse', 'HEAD']).strip() + + +def mojo_filter(path): + if not os.path.isfile(path): + return False + _, ext = os.path.splitext(path) + if ext != '.mojo': + return False + return 'apptests' not in os.path.basename(path) + + +def gen_filter(path): + if os.path.isdir(path): + return True + _, ext = os.path.splitext(path) + # Don't include all .dart, just .mojom.dart. + return path.endswith('.mojom.dart') + +def dart_filter(path): + if os.path.isdir(path): + return True + _, ext = os.path.splitext(path) + # .dart includes '.mojom.dart' + return ext == '.dart' + + +def sky_or_dart_filter(path): + if os.path.isdir(path): + return True + _, ext = os.path.splitext(path) + # .dart includes '.mojom.dart' + return ext == '.sky' or ext == '.dart' + + +def ensure_dir_exists(path): + if not os.path.exists(path): + os.makedirs(path) + + +def copy(from_root, to_root, filter_func=None): + ensure_dir_exists(to_root) + + for root, dirs, files in os.walk(from_root): + # filter_func expects paths not names, so wrap it to make them absolute. + wrapped_filter = None + if filter_func: + wrapped_filter = lambda name: filter_func(os.path.join(root, name)) + + for name in filter(wrapped_filter, files): + from_path = os.path.join(root, name) + root_rel_path = os.path.relpath(from_path, from_root) + to_path = os.path.join(to_root, root_rel_path) + to_dir = os.path.dirname(to_path) + if not os.path.exists(to_dir): + os.makedirs(to_dir) + shutil.copy(from_path, to_path) + + dirs[:] = filter(wrapped_filter, dirs) + + +def confirm(prompt): + response = raw_input('%s [N]|y: ' % prompt) + return response and response.lower() == 'y' + + +def delete_all_non_hidden_files_in_directory(root): + to_delete = [os.path.join(root, p) + for p in os.listdir(root) if not p.startswith('.')] + if not confirm('This will delete everything in %s:\n%s\nAre you sure?' % ( + root, '\n'.join(to_delete))): + print 'User aborted.' + sys.exit(2) + + for path in to_delete: + if os.path.isdir(path): + shutil.rmtree(path) + else: + os.remove(path) + + +def main(): + logging.basicConfig(level=logging.WARN) + parser = argparse.ArgumentParser(description='Deploy a new sky_sdk.') + parser.add_argument('sdk_root', type=str) + args = parser.parse_args() + + # Always use android release? + rel_build_dir = os.path.join('out', 'android_Release') + build_dir = os.path.join(SRC_ROOT, rel_build_dir) + sdk_root = os.path.abspath(args.sdk_root) + + def sdk_path(rel_path): + return os.path.join(sdk_root, rel_path) + + def src_path(rel_path): + return os.path.join(SRC_ROOT, rel_path) + + delete_all_non_hidden_files_in_directory(sdk_root) + + # Manually clear sdk_root above to avoid deleting dot-files. + copy(src_path('sky/sdk'), sdk_root) + + copy(src_path('sky/examples'), sdk_path('examples'), + sky_or_dart_filter) + + # Sky package + copy(src_path('sky/framework'), sdk_path('packages/sky/lib/framework'), + sky_or_dart_filter) + copy(src_path('sky/assets'), sdk_path('packages/sky/lib/assets')) + copy(os.path.join(build_dir, 'gen/sky'), sdk_path('packages/sky/lib'), + gen_filter) + + # Mojo package + copy(src_path('mojo'), sdk_path('packages/mojo'), dart_filter) + copy(os.path.join(build_dir, 'gen/mojo'), sdk_path('packages/mojo/lib'), + gen_filter) + + + ensure_dir_exists(sdk_path('apks')) + shutil.copy(os.path.join(build_dir, 'apks', 'SkyShell.apk'), + sdk_path('apks')) + + + with open(sdk_path('LICENSES.sky'), 'w') as license_file: + subprocess.check_call([src_path('tools/licenses.py'), 'credits'], + stdout=license_file) + + + subprocess.check_call(['git', 'add', '.'], cwd=sdk_root) + subprocess.check_call([ + 'git', 'commit', + '-m', '%s from %s' % (rel_build_dir, git_revision()) + ], cwd=sdk_root) + + +if __name__ == '__main__': + main()