Set up plumbing for getting relevant paths to the right places without too much global state.
`dart bin/sky_tools.dart --debug --sky-src-path=/path/to/sky/src/ install` now works.
diff --git a/packages/flutter_tools/bin/sky_tools.dart b/packages/flutter_tools/bin/sky_tools.dart
index c4275ef..6e0576c 100644
--- a/packages/flutter_tools/bin/sky_tools.dart
+++ b/packages/flutter_tools/bin/sky_tools.dart
@@ -11,6 +11,7 @@
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/application_package.dart';
void main(List<String> args) {
Logger.root.level = Level.WARNING;
@@ -27,7 +28,7 @@
Map<String, CommandHandler> handlers = {};
ArgParser parser = new ArgParser();
- parser.addSeparator('options:');
+ parser.addSeparator('basic options:');
parser.addFlag('help',
abbr: 'h', negatable: false, help: 'Display this help message.');
parser.addFlag('verbose',
@@ -38,6 +39,55 @@
negatable: false,
help: 'Very noisy logging, including the output of all '
'shell commands executed.');
+
+ parser.addSeparator('build selection options:');
+ parser.addFlag('debug',
+ negatable: false,
+ help:
+ 'Set this if you are building Sky locally and want to use the debug build products. '
+ 'When set, attempts to automaticaly determine sky-src-path if sky-src-path is '
+ 'not set. Not normally required.');
+ parser.addFlag('release',
+ negatable: false,
+ help:
+ 'Set this if you are building Sky locally and want to use the release build products. '
+ 'When set, attempts to automaticaly determine sky-src-path if sky-src-path is '
+ 'not set. Note that release is not compatible with the listen command '
+ 'on iOS devices and simulators. Not normally required.');
+ parser.addOption('sky-src-path',
+ help: 'Path to your Sky src directory, if you are building Sky locally. '
+ 'Ignored if neither debug nor release is set. Not normally required.');
+ parser.addOption('android-debug-build-path',
+ help:
+ 'Path to your Android Debug out directory, if you are building Sky locally. '
+ 'This path is relative to sky-src-path. Not normally required.',
+ defaultsTo: 'out/android_Debug/');
+ parser.addOption('android-release-build-path',
+ help:
+ 'Path to your Android Release out directory, if you are building Sky locally. '
+ 'This path is relative to sky-src-path. Not normally required.',
+ defaultsTo: 'out/android_Release/');
+ parser.addOption('ios-debug-build-path',
+ help:
+ 'Path to your iOS Debug out directory, if you are building Sky locally. '
+ 'This path is relative to sky-src-path. Not normally required.',
+ defaultsTo: 'out/ios_Debug/');
+ parser.addOption('ios-release-build-path',
+ help:
+ 'Path to your iOS Release out directory, if you are building Sky locally. '
+ 'This path is relative to sky-src-path. Not normally required.',
+ defaultsTo: 'out/ios_Release/');
+ parser.addOption('ios-sim-debug-build-path',
+ help:
+ 'Path to your iOS Simulator Debug out directory, if you are building Sky locally. '
+ 'This path is relative to sky-src-path. Not normally required.',
+ defaultsTo: 'out/ios_sim_Debug/');
+ parser.addOption('ios-sim-release-build-path',
+ help:
+ 'Path to your iOS Simulator Release out directory, if you are building Sky locally. '
+ 'This path is relative to sky-src-path. Not normally required.',
+ defaultsTo: 'out/ios_sim_Release/');
+
parser.addSeparator('commands:');
for (CommandHandler handler in [
@@ -67,6 +117,8 @@
Logger.root.level = Level.FINE;
}
+ _setupPaths(results);
+
if (results['help']) {
_printUsage(parser, handlers);
} else if (results.command != null) {
@@ -84,6 +136,40 @@
}
}
+void _setupPaths(ArgResults results) {
+ if (results['debug'] || results['release']) {
+ if (results['sky-src-path'] == null) {
+ // TODO(iansf): Figure out how to get the default src path
+ assert(false);
+ }
+ ApplicationPackageFactory.srcPath = results['sky-src-path'];
+ } else {
+ assert(false);
+ // TODO(iansf): set paths up for commands using PREBUILT binaries
+ // ApplicationPackageFactory.setBuildPath(BuildType.PREBUILT,
+ // BuildPlatform.android, results['android-debug-build-path']);
+ }
+
+ if (results['debug']) {
+ ApplicationPackageFactory.defaultBuildType = BuildType.debug;
+ ApplicationPackageFactory.setBuildPath(BuildType.debug,
+ BuildPlatform.android, results['android-debug-build-path']);
+ ApplicationPackageFactory.setBuildPath(
+ BuildType.debug, BuildPlatform.iOS, results['ios-debug-build-path']);
+ ApplicationPackageFactory.setBuildPath(BuildType.debug,
+ BuildPlatform.iOSSimulator, results['ios-sim-debug-build-path']);
+ }
+ if (results['release']) {
+ ApplicationPackageFactory.defaultBuildType = BuildType.release;
+ ApplicationPackageFactory.setBuildPath(BuildType.release,
+ BuildPlatform.android, results['android-release-build-path']);
+ ApplicationPackageFactory.setBuildPath(BuildType.release, BuildPlatform.iOS,
+ results['ios-release-build-path']);
+ ApplicationPackageFactory.setBuildPath(BuildType.release,
+ BuildPlatform.iOSSimulator, results['ios-sim-release-build-path']);
+ }
+}
+
void _printUsage(ArgParser parser, Map<String, CommandHandler> handlers,
[String message]) {
if (message != null) {
diff --git a/packages/flutter_tools/lib/src/application_package.dart b/packages/flutter_tools/lib/src/application_package.dart
new file mode 100644
index 0000000..37b7e5e
--- /dev/null
+++ b/packages/flutter_tools/lib/src/application_package.dart
@@ -0,0 +1,113 @@
+// 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.application_package;
+
+import 'dart:io';
+
+import 'package:logging/logging.dart';
+import 'package:path/path.dart' as path;
+
+final Logger _logging = new Logger('sky_tools.application_package');
+
+abstract class ApplicationPackage {
+ /// Path to the directory the apk or bundle lives in.
+ String appDir;
+
+ /// Path to the actual apk or bundle.
+ String get appPath => path.join(appDir, appFileName);
+
+ /// Package ID from the Android Manifest or equivalent.
+ String appPackageID;
+
+ /// File name of the apk or bundle.
+ String appFileName;
+
+ ApplicationPackage(this.appDir, this.appPackageID, this.appFileName);
+}
+
+class AndroidApk extends ApplicationPackage {
+ static const String _apkName = 'SkyShell.apk';
+ static const String _androidPackage = 'org.domokit.sky.shell';
+
+ AndroidApk(String appDir,
+ [String appPackageID = _androidPackage, String appFileName = _apkName])
+ : super(path.join(appDir, 'apks'), appPackageID, appFileName);
+}
+
+enum BuildType { prebuilt, release, debug, }
+
+enum BuildPlatform { android, iOS, iOSSimulator, mac, linux, }
+
+class ApplicationPackageFactory {
+ static final Map<BuildPlatform, Map<BuildType, String>> _buildPaths =
+ _initBuildPaths();
+
+ /// Path to your Sky src directory, if you are building Sky locally.
+ /// Required if you are requesting release or debug BuildTypes.
+ static String _srcPath = null;
+ static String get srcPath => _srcPath;
+ static void set srcPath(String newPath) {
+ _srcPath = path.normalize(newPath);
+ }
+
+ /// Default BuildType chosen if no BuildType is specified.
+ static BuildType defaultBuildType = BuildType.prebuilt;
+
+ /// Default BuildPlatforms chosen if no BuildPlatforms are specified.
+ static List<BuildPlatform> defaultBuildPlatforms = [BuildPlatform.android];
+
+ static Map<BuildPlatform, ApplicationPackage> getAvailableApplicationPackages(
+ {BuildType requestedType, List<BuildPlatform> requestedPlatforms}) {
+ if (requestedType == null) {
+ requestedType = defaultBuildType;
+ }
+ if (requestedPlatforms == null) {
+ requestedPlatforms = defaultBuildPlatforms;
+ }
+
+ Map<BuildPlatform, ApplicationPackage> packages = {};
+ for (BuildPlatform platform in requestedPlatforms) {
+ String buildPath = _getBuildPath(requestedType, platform);
+ switch (platform) {
+ case BuildPlatform.android:
+ packages[platform] = new AndroidApk(buildPath);
+ break;
+ default:
+ // TODO(iansf): Add other platforms
+ assert(false);
+ }
+ }
+ return packages;
+ }
+
+ static Map<BuildPlatform, Map<BuildType, String>> _initBuildPaths() {
+ Map<BuildPlatform, Map<BuildType, String>> buildPaths = {};
+ for (BuildPlatform platform in BuildPlatform.values) {
+ buildPaths[platform] = {};
+ }
+ return buildPaths;
+ }
+
+ static String _getBuildPath(BuildType type, BuildPlatform platform) {
+ String path = _buildPaths[platform][type];
+ // You must set paths before getting them
+ assert(path != null);
+ return path;
+ }
+
+ static void setBuildPath(
+ BuildType type, BuildPlatform platform, String buildPath) {
+ // You must set srcPath before attempting to set a BuildPath for
+ // non prebuilt ApplicationPackages.
+ assert(type != BuildType.prebuilt || srcPath != null);
+ if (type != BuildType.prebuilt) {
+ buildPath = path.join(srcPath, buildPath);
+ }
+ if (!FileSystemEntity.isDirectorySync(buildPath)) {
+ _logging.warning('$buildPath is not a valid directory');
+ }
+ _buildPaths[platform][type] = path.normalize(buildPath);
+ }
+}
diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart
index 5547555..ffe3c56 100644
--- a/packages/flutter_tools/lib/src/device.dart
+++ b/packages/flutter_tools/lib/src/device.dart
@@ -10,6 +10,7 @@
import 'package:path/path.dart' as path;
import 'process_wrapper.dart';
+import 'application_package.dart';
final Logger _logging = new Logger('sky_tools.device');
@@ -40,13 +41,13 @@
_Device._(this.id);
/// Install an app package on the current device
- bool installApp(String appPath, String appPackageID, String appFileName);
+ bool installApp(ApplicationPackage app);
/// Check if the device is currently connected
bool isConnected();
/// Check if the current version of the given app is already installed
- bool isAppInstalled(String appPath, String appPackageID, String appFileName);
+ bool isAppInstalled(ApplicationPackage app);
}
class AndroidDevice extends _Device {
@@ -175,68 +176,58 @@
return false;
}
- String _getDeviceSha1Path(String appPackageID, String appFileName) {
- return '/sdcard/$appPackageID/$appFileName.sha1';
+ String _getDeviceSha1Path(ApplicationPackage app) {
+ return '/sdcard/${app.appPackageID}/${app.appFileName}.sha1';
}
- String _getDeviceApkSha1(String appPackageID, String appFileName) {
- return runCheckedSync([
- adbPath,
- 'shell',
- 'cat',
- _getDeviceSha1Path(appPackageID, appFileName)
- ]);
+ String _getDeviceApkSha1(ApplicationPackage app) {
+ return runCheckedSync([adbPath, 'shell', 'cat', _getDeviceSha1Path(app)]);
}
- String _getSourceSha1(String apkPath) {
+ String _getSourceSha1(ApplicationPackage app) {
String sha1 =
- runCheckedSync(['shasum', '-a', '1', '-p', apkPath]).split(' ')[0];
+ runCheckedSync(['shasum', '-a', '1', '-p', app.appPath]).split(' ')[0];
return sha1;
}
@override
- bool isAppInstalled(String appPath, String appPackageID, String appFileName) {
+ bool isAppInstalled(ApplicationPackage app) {
if (!isConnected()) {
return false;
}
- if (runCheckedSync([adbPath, 'shell', 'pm', 'path', appPackageID]) == '') {
+ if (runCheckedSync([adbPath, 'shell', 'pm', 'path', app.appPackageID]) ==
+ '') {
_logging.info(
- 'TODO(iansf): move this log to the caller. $appFileName is not on the device. Installing now...');
+ 'TODO(iansf): move this log to the caller. ${app.appFileName} is not on the device. Installing now...');
return false;
}
- if (_getDeviceApkSha1(appPackageID, appFileName) !=
- _getSourceSha1(appPath)) {
+ if (_getDeviceApkSha1(app) != _getSourceSha1(app)) {
_logging.info(
- 'TODO(iansf): move this log to the caller. $appFileName is out of date. Installing now...');
+ 'TODO(iansf): move this log to the caller. ${app.appFileName} is out of date. Installing now...');
return false;
}
return true;
}
@override
- bool installApp(String appPath, String appPackageID, String appFileName) {
+ bool installApp(ApplicationPackage app) {
if (!isConnected()) {
_logging.info('Android device not connected. Not installing.');
return false;
}
- if (!FileSystemEntity.isFileSync(appPath)) {
- _logging.severe('"$appPath" does not exist.');
+ if (!FileSystemEntity.isFileSync(app.appPath)) {
+ _logging.severe('"${app.appPath}" does not exist.');
return false;
}
- runCheckedSync([adbPath, 'install', '-r', appPath]);
+ runCheckedSync([adbPath, 'install', '-r', app.appPath]);
Directory tempDir = Directory.systemTemp;
String sha1Path = path.join(
- tempDir.path, appPath.replaceAll(path.separator, '_'), '.sha1');
+ tempDir.path, (app.appPath + '.sha1').replaceAll(path.separator, '_'));
File sha1TempFile = new File(sha1Path);
- sha1TempFile.writeAsStringSync(_getSourceSha1(appPath), flush: true);
- runCheckedSync([
- adbPath,
- 'push',
- sha1Path,
- _getDeviceSha1Path(appPackageID, appFileName)
- ]);
+ sha1TempFile.writeAsStringSync(_getSourceSha1(app), flush: true);
+ runCheckedSync([adbPath, 'push', sha1Path, _getDeviceSha1Path(app)]);
sha1TempFile.deleteSync();
return true;
}
diff --git a/packages/flutter_tools/lib/src/install.dart b/packages/flutter_tools/lib/src/install.dart
index d0b30d6..56583da 100644
--- a/packages/flutter_tools/lib/src/install.dart
+++ b/packages/flutter_tools/lib/src/install.dart
@@ -8,6 +8,7 @@
import 'package:args/args.dart';
+import 'application_package.dart';
import 'common.dart';
import 'device.dart';
@@ -33,11 +34,14 @@
bool installedSomewhere = false;
+ Map<BuildPlatform, ApplicationPackage> packages =
+ ApplicationPackageFactory.getAvailableApplicationPackages();
if (android == null) {
android = new AndroidDevice();
}
- if (android.isConnected()) {
- installedSomewhere = installedSomewhere || android.installApp('', '', '');
+ ApplicationPackage androidApp = packages[BuildPlatform.android];
+ if (androidApp != null && android.isConnected()) {
+ installedSomewhere = installedSomewhere || android.installApp(androidApp);
}
if (installedSomewhere) {
diff --git a/packages/flutter_tools/test/install_test.dart b/packages/flutter_tools/test/install_test.dart
index 4415191..00e9180 100644
--- a/packages/flutter_tools/test/install_test.dart
+++ b/packages/flutter_tools/test/install_test.dart
@@ -6,6 +6,7 @@
import 'package:mockito/mockito.dart';
import 'package:sky_tools/src/install.dart';
+import 'package:sky_tools/src/application_package.dart';
import 'package:test/test.dart';
import 'src/common.dart';
@@ -15,9 +16,13 @@
defineTests() {
group('install', () {
test('returns 0 when Android is connected and ready for an install', () {
+ ApplicationPackageFactory.srcPath = './';
+ ApplicationPackageFactory.setBuildPath(
+ BuildType.prebuilt, BuildPlatform.android, './');
+
MockAndroidDevice android = new MockAndroidDevice();
when(android.isConnected()).thenReturn(true);
- when(android.installApp(any, any, any)).thenReturn(true);
+ when(android.installApp(any)).thenReturn(true);
InstallCommandHandler handler = new InstallCommandHandler(android);
MockArgResults results = new MockArgResults();