Add sky_tools start command and associated android support.

This commit is contained in:
Ian Fischer 2015-09-25 16:29:47 -07:00
parent 6bfd60f217
commit 8cac55a4e2
5 changed files with 184 additions and 10 deletions

View file

@ -14,11 +14,12 @@ import 'package:sky_tools/src/cache.dart';
import 'package:sky_tools/src/init.dart';
import 'package:sky_tools/src/install.dart';
import 'package:sky_tools/src/run_mojo.dart';
import 'package:sky_tools/src/start.dart';
import 'package:sky_tools/src/stop.dart';
class FlutterCommandRunner extends CommandRunner {
FlutterCommandRunner()
: super('flutter', 'Manage your flutter app development.') {
: super('flutter', 'Manage your Flutter app development.') {
argParser.addFlag('verbose',
abbr: 'v',
negatable: false,
@ -77,8 +78,7 @@ class FlutterCommandRunner extends CommandRunner {
'This path is relative to sky-src-path. Not normally required.',
defaultsTo: 'out/ios_sim_Release/');
argParser.addOption('package-root',
help: 'Path to your packages directory.',
defaultsTo: 'packages');
help: 'Path to your packages directory.', defaultsTo: 'packages');
}
Future<int> runCommand(ArgResults topLevelResults) async {
@ -144,11 +144,12 @@ void main(List<String> args) {
});
new FlutterCommandRunner()
..addCommand(new StopCommand())
..addCommand(new BuildCommand())
..addCommand(new CacheCommand())
..addCommand(new InitCommand())
..addCommand(new InstallCommand())
..addCommand(new RunMojoCommand())
..addCommand(new StartCommand())
..addCommand(new StopCommand())
..run(args);
}

View file

@ -4,7 +4,9 @@
library sky_tools.device;
import 'dart:async';
import 'dart:io';
import 'dart:math';
import 'package:logging/logging.dart';
import 'package:path/path.dart' as path;
@ -52,6 +54,7 @@ abstract class _Device {
class AndroidDevice extends _Device {
static const String _ADB_PATH = 'adb';
static const String _observatoryPort = '8181';
static const String _serverPort = '9888';
static const String className = 'AndroidDevice';
@ -233,6 +236,66 @@ class AndroidDevice extends _Device {
return true;
}
Future<bool> startServer(
String target, bool poke, bool checked, AndroidApk apk) async {
String serverRoot = '';
String mainDart = '';
String missingMessage = '';
if (await FileSystemEntity.isDirectory(target)) {
serverRoot = target;
mainDart = path.join(serverRoot, 'lib', 'main.dart');
missingMessage = 'Missing lib/main.dart in project: $serverRoot';
} else {
serverRoot = Directory.current.path;
mainDart = target;
missingMessage = '$mainDart does not exist.';
}
if (!await FileSystemEntity.isFile(mainDart)) {
_logging.severe(missingMessage);
return false;
}
// Set up port forwarding for observatory.
String observatoryPortString = 'tcp:$_observatoryPort';
runCheckedSync(
[adbPath, 'forward', observatoryPortString, observatoryPortString]);
// Actually start the server.
await Process.start('pub', ['run', 'sky_tools:sky_server', _serverPort],
workingDirectory: serverRoot, mode: ProcessStartMode.DETACHED);
// Set up reverse port-forwarding so that the Android app can reach the
// server running on localhost.
String serverPortString = 'tcp:$_serverPort';
runCheckedSync([adbPath, 'reverse', serverPortString, serverPortString]);
String relativeDartMain = path.relative(mainDart, from: serverRoot);
String url = 'http://localhost:$_serverPort/$relativeDartMain';
if (poke) {
url += '?rand=${new Random().nextDouble()}';
}
// Actually launch the app on Android.
List<String> cmd = [
adbPath,
'shell',
'am',
'start',
'-a',
'android.intent.action.VIEW',
'-d',
url,
];
if (checked) {
cmd.addAll(['--ez', 'enable-checked-mode', 'true']);
}
cmd.add(apk.component);
runCheckedSync(cmd);
return true;
}
bool stop(AndroidApk apk) {
// Turn off reverse port forwarding

View file

@ -19,6 +19,14 @@ class InstallCommand extends Command {
@override
Future<int> run() async {
if (install()) {
return 0;
} else {
return 2;
}
}
bool install() {
bool installedSomewhere = false;
Map<BuildPlatform, ApplicationPackage> packages =
@ -28,13 +36,9 @@ class InstallCommand extends Command {
}
ApplicationPackage androidApp = packages[BuildPlatform.android];
if (androidApp != null && android.isConnected()) {
installedSomewhere = installedSomewhere || android.installApp(androidApp);
installedSomewhere = android.installApp(androidApp) || installedSomewhere;
}
if (installedSomewhere) {
return 0;
} else {
return 2;
}
return installedSomewhere;
}
}

View file

@ -0,0 +1,71 @@
// 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.
library sky_tools.start;
import 'dart:async';
import 'package:args/command_runner.dart';
import 'package:logging/logging.dart';
import 'package:path/path.dart' as path;
import 'package:sky_tools/src/application_package.dart';
import 'package:sky_tools/src/device.dart';
import 'package:sky_tools/src/install.dart';
import 'package:sky_tools/src/stop.dart';
final Logger _logging = new Logger('sky_tools.start');
class StartCommand extends Command {
final name = 'start';
final description = 'Start your Flutter app on attached devices.';
AndroidDevice android = null;
StartCommand([this.android]) {
argParser.addFlag('poke',
negatable: false,
help: 'Restart the connection to the server (Android only).');
argParser.addFlag('checked',
negatable: true,
defaultsTo: true,
help: 'Toggle Dart\'s checked mode.');
argParser.addOption('target',
defaultsTo: '.',
abbr: 't',
help: 'Target app path or filename to start.');
if (android == null) {
android = new AndroidDevice();
}
}
@override
Future<int> run() async {
bool startedSomewhere = false;
bool poke = argResults['poke'];
if (!poke) {
StopCommand stopper = new StopCommand(android);
stopper.stop();
// Only install if the user did not specify a poke
InstallCommand installer = new InstallCommand(android);
startedSomewhere = installer.install();
}
bool startedOnAndroid = false;
if (android.isConnected()) {
Map<BuildPlatform, ApplicationPackage> packages =
ApplicationPackageFactory.getAvailableApplicationPackages();
ApplicationPackage androidApp = packages[BuildPlatform.android];
String target = path.absolute(argResults['target']);
startedOnAndroid = await android.startServer(
target, poke, argResults['checked'], androidApp);
}
if (startedSomewhere || startedOnAndroid) {
return 0;
} else {
return 2;
}
}
}

View file

@ -0,0 +1,35 @@
// 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.
library start_test;
import 'package:args/command_runner.dart';
import 'package:mockito/mockito.dart';
import 'package:sky_tools/src/application_package.dart';
import 'package:sky_tools/src/start.dart';
import 'package:test/test.dart';
import 'src/common.dart';
main() => defineTests();
defineTests() {
group('start', () {
test('returns 0 when Android is connected and ready to be started', () {
ApplicationPackageFactory.srcPath = './';
ApplicationPackageFactory.setBuildPath(
BuildType.prebuilt, BuildPlatform.android, './');
MockAndroidDevice android = new MockAndroidDevice();
when(android.isConnected()).thenReturn(true);
when(android.installApp(any)).thenReturn(true);
when(android.stop(any)).thenReturn(true);
StartCommand command = new StartCommand(android);
CommandRunner runner = new CommandRunner('test_flutter', '')
..addCommand(command);
runner.run(['start']).then((int code) => expect(code, equals(0)));
});
});
}