Commit 0cc758d2 authored by Ian Fischer's avatar Ian Fischer

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.
parent 20d9d6a9
...@@ -11,6 +11,7 @@ import 'package:sky_tools/src/common.dart'; ...@@ -11,6 +11,7 @@ import 'package:sky_tools/src/common.dart';
import 'package:sky_tools/src/init.dart'; import 'package:sky_tools/src/init.dart';
import 'package:sky_tools/src/install.dart'; import 'package:sky_tools/src/install.dart';
import 'package:sky_tools/src/run_mojo.dart'; import 'package:sky_tools/src/run_mojo.dart';
import 'package:sky_tools/src/application_package.dart';
void main(List<String> args) { void main(List<String> args) {
Logger.root.level = Level.WARNING; Logger.root.level = Level.WARNING;
...@@ -27,7 +28,7 @@ void main(List<String> args) { ...@@ -27,7 +28,7 @@ void main(List<String> args) {
Map<String, CommandHandler> handlers = {}; Map<String, CommandHandler> handlers = {};
ArgParser parser = new ArgParser(); ArgParser parser = new ArgParser();
parser.addSeparator('options:'); parser.addSeparator('basic options:');
parser.addFlag('help', parser.addFlag('help',
abbr: 'h', negatable: false, help: 'Display this help message.'); abbr: 'h', negatable: false, help: 'Display this help message.');
parser.addFlag('verbose', parser.addFlag('verbose',
...@@ -38,6 +39,55 @@ void main(List<String> args) { ...@@ -38,6 +39,55 @@ void main(List<String> args) {
negatable: false, negatable: false,
help: 'Very noisy logging, including the output of all ' help: 'Very noisy logging, including the output of all '
'shell commands executed.'); '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:'); parser.addSeparator('commands:');
for (CommandHandler handler in [ for (CommandHandler handler in [
...@@ -67,6 +117,8 @@ void main(List<String> args) { ...@@ -67,6 +117,8 @@ void main(List<String> args) {
Logger.root.level = Level.FINE; Logger.root.level = Level.FINE;
} }
_setupPaths(results);
if (results['help']) { if (results['help']) {
_printUsage(parser, handlers); _printUsage(parser, handlers);
} else if (results.command != null) { } else if (results.command != null) {
...@@ -84,6 +136,40 @@ void main(List<String> args) { ...@@ -84,6 +136,40 @@ void main(List<String> args) {
} }
} }
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, void _printUsage(ArgParser parser, Map<String, CommandHandler> handlers,
[String message]) { [String message]) {
if (message != null) { if (message != null) {
......
// 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);
}
}
...@@ -10,6 +10,7 @@ import 'package:logging/logging.dart'; ...@@ -10,6 +10,7 @@ import 'package:logging/logging.dart';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
import 'process_wrapper.dart'; import 'process_wrapper.dart';
import 'application_package.dart';
final Logger _logging = new Logger('sky_tools.device'); final Logger _logging = new Logger('sky_tools.device');
...@@ -40,13 +41,13 @@ abstract class _Device { ...@@ -40,13 +41,13 @@ abstract class _Device {
_Device._(this.id); _Device._(this.id);
/// Install an app package on the current device /// 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 /// Check if the device is currently connected
bool isConnected(); bool isConnected();
/// Check if the current version of the given app is already installed /// 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 { class AndroidDevice extends _Device {
...@@ -175,68 +176,58 @@ class AndroidDevice extends _Device { ...@@ -175,68 +176,58 @@ class AndroidDevice extends _Device {
return false; return false;
} }
String _getDeviceSha1Path(String appPackageID, String appFileName) { String _getDeviceSha1Path(ApplicationPackage app) {
return '/sdcard/$appPackageID/$appFileName.sha1'; return '/sdcard/${app.appPackageID}/${app.appFileName}.sha1';
} }
String _getDeviceApkSha1(String appPackageID, String appFileName) { String _getDeviceApkSha1(ApplicationPackage app) {
return runCheckedSync([ return runCheckedSync([adbPath, 'shell', 'cat', _getDeviceSha1Path(app)]);
adbPath,
'shell',
'cat',
_getDeviceSha1Path(appPackageID, appFileName)
]);
} }
String _getSourceSha1(String apkPath) { String _getSourceSha1(ApplicationPackage app) {
String sha1 = String sha1 =
runCheckedSync(['shasum', '-a', '1', '-p', apkPath]).split(' ')[0]; runCheckedSync(['shasum', '-a', '1', '-p', app.appPath]).split(' ')[0];
return sha1; return sha1;
} }
@override @override
bool isAppInstalled(String appPath, String appPackageID, String appFileName) { bool isAppInstalled(ApplicationPackage app) {
if (!isConnected()) { if (!isConnected()) {
return false; return false;
} }
if (runCheckedSync([adbPath, 'shell', 'pm', 'path', appPackageID]) == '') { if (runCheckedSync([adbPath, 'shell', 'pm', 'path', app.appPackageID]) ==
'') {
_logging.info( _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; return false;
} }
if (_getDeviceApkSha1(appPackageID, appFileName) != if (_getDeviceApkSha1(app) != _getSourceSha1(app)) {
_getSourceSha1(appPath)) {
_logging.info( _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 false;
} }
return true; return true;
} }
@override @override
bool installApp(String appPath, String appPackageID, String appFileName) { bool installApp(ApplicationPackage app) {
if (!isConnected()) { if (!isConnected()) {
_logging.info('Android device not connected. Not installing.'); _logging.info('Android device not connected. Not installing.');
return false; return false;
} }
if (!FileSystemEntity.isFileSync(appPath)) { if (!FileSystemEntity.isFileSync(app.appPath)) {
_logging.severe('"$appPath" does not exist.'); _logging.severe('"${app.appPath}" does not exist.');
return false; return false;
} }
runCheckedSync([adbPath, 'install', '-r', appPath]); runCheckedSync([adbPath, 'install', '-r', app.appPath]);
Directory tempDir = Directory.systemTemp; Directory tempDir = Directory.systemTemp;
String sha1Path = path.join( String sha1Path = path.join(
tempDir.path, appPath.replaceAll(path.separator, '_'), '.sha1'); tempDir.path, (app.appPath + '.sha1').replaceAll(path.separator, '_'));
File sha1TempFile = new File(sha1Path); File sha1TempFile = new File(sha1Path);
sha1TempFile.writeAsStringSync(_getSourceSha1(appPath), flush: true); sha1TempFile.writeAsStringSync(_getSourceSha1(app), flush: true);
runCheckedSync([ runCheckedSync([adbPath, 'push', sha1Path, _getDeviceSha1Path(app)]);
adbPath,
'push',
sha1Path,
_getDeviceSha1Path(appPackageID, appFileName)
]);
sha1TempFile.deleteSync(); sha1TempFile.deleteSync();
return true; return true;
} }
......
...@@ -8,6 +8,7 @@ import 'dart:async'; ...@@ -8,6 +8,7 @@ import 'dart:async';
import 'package:args/args.dart'; import 'package:args/args.dart';
import 'application_package.dart';
import 'common.dart'; import 'common.dart';
import 'device.dart'; import 'device.dart';
...@@ -33,11 +34,14 @@ class InstallCommandHandler extends CommandHandler { ...@@ -33,11 +34,14 @@ class InstallCommandHandler extends CommandHandler {
bool installedSomewhere = false; bool installedSomewhere = false;
Map<BuildPlatform, ApplicationPackage> packages =
ApplicationPackageFactory.getAvailableApplicationPackages();
if (android == null) { if (android == null) {
android = new AndroidDevice(); android = new AndroidDevice();
} }
if (android.isConnected()) { ApplicationPackage androidApp = packages[BuildPlatform.android];
installedSomewhere = installedSomewhere || android.installApp('', '', ''); if (androidApp != null && android.isConnected()) {
installedSomewhere = installedSomewhere || android.installApp(androidApp);
} }
if (installedSomewhere) { if (installedSomewhere) {
......
...@@ -6,6 +6,7 @@ library install_test; ...@@ -6,6 +6,7 @@ library install_test;
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
import 'package:sky_tools/src/install.dart'; import 'package:sky_tools/src/install.dart';
import 'package:sky_tools/src/application_package.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'src/common.dart'; import 'src/common.dart';
...@@ -15,9 +16,13 @@ main() => defineTests(); ...@@ -15,9 +16,13 @@ main() => defineTests();
defineTests() { defineTests() {
group('install', () { group('install', () {
test('returns 0 when Android is connected and ready for an 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(); MockAndroidDevice android = new MockAndroidDevice();
when(android.isConnected()).thenReturn(true); when(android.isConnected()).thenReturn(true);
when(android.installApp(any, any, any)).thenReturn(true); when(android.installApp(any)).thenReturn(true);
InstallCommandHandler handler = new InstallCommandHandler(android); InstallCommandHandler handler = new InstallCommandHandler(android);
MockArgResults results = new MockArgResults(); MockArgResults results = new MockArgResults();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment