Commit 3638f938 authored by Ian Fischer's avatar Ian Fischer

Merge pull request #46 from iansf/android_start

Refactor all the commands to be Commands from the Args package.  Also use CommandRunner for the top-level command.
parents 206104b8 cae053c3
This diff is collapsed.
...@@ -8,11 +8,10 @@ import 'dart:async'; ...@@ -8,11 +8,10 @@ import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:archive/archive.dart'; import 'package:archive/archive.dart';
import 'package:args/args.dart'; import 'package:args/command_runner.dart';
import 'package:yaml/yaml.dart'; import 'package:yaml/yaml.dart';
import 'artifacts.dart'; import 'artifacts.dart';
import 'common.dart';
const String _kSnapshotKey = 'snapshot_blob.bin'; const String _kSnapshotKey = 'snapshot_blob.bin';
const List<String> _kDensities = const ['drawable-xxhdpi']; const List<String> _kDensities = const ['drawable-xxhdpi'];
...@@ -127,41 +126,33 @@ Future<ArchiveFile> _createSnapshotFile(String snapshotPath) async { ...@@ -127,41 +126,33 @@ Future<ArchiveFile> _createSnapshotFile(String snapshotPath) async {
return new ArchiveFile(_kSnapshotKey, content.length, content); return new ArchiveFile(_kSnapshotKey, content.length, content);
} }
class BuildCommandHandler extends CommandHandler { class BuildCommand extends Command {
BuildCommandHandler() : super('build', 'Create a Flutter app.'); final name = 'build';
final description = 'Create a Flutter app.';
ArgParser get parser { BuildCommand() {
ArgParser parser = new ArgParser(); argParser.addOption('asset-base', defaultsTo: 'packages/material_design_icons/icons');
parser.addFlag('help', abbr: 'h', negatable: false); argParser.addOption('compiler');
parser.addOption('asset-base', defaultsTo: 'packages/material_design_icons/icons'); argParser.addOption('main', defaultsTo: 'lib/main.dart');
parser.addOption('compiler'); argParser.addOption('manifest');
parser.addOption('main', defaultsTo: 'lib/main.dart'); argParser.addOption('output-file', abbr: 'o', defaultsTo: 'app.flx');
parser.addOption('manifest'); argParser.addOption('package-root', defaultsTo: 'packages');
parser.addOption('output-file', abbr: 'o', defaultsTo: 'app.flx'); argParser.addOption('snapshot', defaultsTo: 'snapshot_blob.bin');
parser.addOption('package-root', defaultsTo: 'packages');
parser.addOption('snapshot', defaultsTo: 'snapshot_blob.bin');
return parser;
} }
@override @override
Future<int> processArgResults(ArgResults results) async { Future<int> run() async {
if (results['help']) { String manifestPath = argResults['manifest'];
print(parser.usage);
return 0;
}
String manifestPath = results['manifest'];
Map manifestDescriptor = await _loadManifest(manifestPath); Map manifestDescriptor = await _loadManifest(manifestPath);
Iterable<_Asset> assets = _parseAssets(manifestDescriptor, manifestPath); Iterable<_Asset> assets = _parseAssets(manifestDescriptor, manifestPath);
Iterable<_MaterialAsset> materialAssets = _parseMaterialAssets(manifestDescriptor); Iterable<_MaterialAsset> materialAssets = _parseMaterialAssets(manifestDescriptor);
Archive archive = new Archive(); Archive archive = new Archive();
String snapshotPath = results['snapshot']; String snapshotPath = argResults['snapshot'];
await _compileSnapshot( await _compileSnapshot(
compilerPath: results['compiler'], compilerPath: argResults['compiler'],
mainPath: results['main'], mainPath: argResults['main'],
packageRoot: results['package-root'], packageRoot: argResults['package-root'],
snapshotPath: snapshotPath); snapshotPath: snapshotPath);
archive.addFile(await _createSnapshotFile(snapshotPath)); archive.addFile(await _createSnapshotFile(snapshotPath));
...@@ -169,12 +160,12 @@ class BuildCommandHandler extends CommandHandler { ...@@ -169,12 +160,12 @@ class BuildCommandHandler extends CommandHandler {
archive.addFile(await _createFile(asset.key, asset.base)); archive.addFile(await _createFile(asset.key, asset.base));
for (_MaterialAsset asset in materialAssets) { for (_MaterialAsset asset in materialAssets) {
ArchiveFile file = await _createFile(asset.key, results['asset-base']); ArchiveFile file = await _createFile(asset.key, argResults['asset-base']);
if (file != null) if (file != null)
archive.addFile(file); archive.addFile(file);
} }
File outputFile = new File(results['output-file']); File outputFile = new File(argResults['output-file']);
await outputFile.writeAsString('#!mojo mojo:sky_viewer\n'); await outputFile.writeAsString('#!mojo mojo:sky_viewer\n');
await outputFile.writeAsBytes(new ZipEncoder().encode(archive), mode: FileMode.APPEND); await outputFile.writeAsBytes(new ZipEncoder().encode(archive), mode: FileMode.APPEND);
return 0; return 0;
......
...@@ -6,63 +6,48 @@ library sky_tools.cache; ...@@ -6,63 +6,48 @@ library sky_tools.cache;
import 'dart:async'; import 'dart:async';
import 'package:args/args.dart'; import 'package:args/command_runner.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'artifacts.dart'; import 'artifacts.dart';
import 'common.dart';
final Logger _logging = new Logger('sky_tools.cache'); final Logger _logging = new Logger('sky_tools.cache');
class CacheCommandHandler extends CommandHandler { class CacheCommand extends Command {
CacheCommandHandler() : super('cache', 'Manages sky_tools\' cache of binary artifacts.'); final name = 'cache';
final description = 'Manages sky_tools\' cache of binary artifacts.';
ArgParser get parser { CacheCommand() {
ArgParser parser = new ArgParser(); addSubcommand(new _ClearCommand());
parser.addFlag('help', abbr: 'h', negatable: false); addSubcommand(new _PopulateCommand());
parser.addOption('package-root', defaultsTo: 'packages');
ArgParser clearParser = parser.addCommand('clear');
clearParser.addFlag('help', abbr: 'h', negatable: false);
ArgParser populateParser = parser.addCommand('populate');
populateParser.addFlag('help', abbr: 'h', negatable: false);
return parser;
} }
}
Future<int> _clear(String packageRoot, ArgResults results) async { class _ClearCommand extends Command {
if (results['help']) { final name = 'clear';
print('Clears all artifacts from the cache.'); final description = 'Clears all artifacts from the cache.';
print(parser.usage); _ClearCommand() {
return 0; argParser.addOption('package-root', defaultsTo: 'packages');
}
ArtifactStore artifacts = new ArtifactStore(packageRoot);
await artifacts.clear();
return 0;
} }
Future<int> _populate(String packageRoot, ArgResults results) async { @override
if (results['help']) { Future<int> run() async {
print('Populates the cache with all known artifacts.'); ArtifactStore artifacts = new ArtifactStore(argResults['package-root']);
print(parser.usage);
return 0;
}
ArtifactStore artifacts = new ArtifactStore(packageRoot);
await artifacts.populate(); await artifacts.populate();
return 0; return 0;
} }
}
class _PopulateCommand extends Command {
final name = 'populate';
final description = 'Populates the cache with all known artifacts.';
_PopulateCommand() {
argParser.addOption('package-root', defaultsTo: 'packages');
}
@override @override
Future<int> processArgResults(ArgResults results) async { Future<int> run() async {
if (results['help'] || results.command == null) { ArtifactStore artifacts = new ArtifactStore(argResults['package-root']);
print(parser.usage); await artifacts.populate();
return 0; return 0;
}
if (results.command.name == 'clear') {
return _clear(results['package-root'], results.command);
} else if (results.command.name == 'populate') {
return _populate(results['package-root'], results.command);
} else {
_logging.severe('Unknown cache command \"${results.command.name}\"');
return 2;
}
} }
} }
// 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 'dart:async';
import 'package:args/args.dart';
abstract class CommandHandler {
final String name;
final String description;
CommandHandler(this.name, this.description);
ArgParser get parser;
/// Returns 0 for no errors or warnings executing command, 1 for warnings, 2
/// for errors.
Future<int> processArgResults(ArgResults results);
void printUsage([String message]) {
if (message != null) {
print('${message}\n');
}
print('usage: sky_tools ${name} [arguments]');
print('');
print(parser.usage);
}
String toString() => name;
}
...@@ -9,8 +9,8 @@ import 'dart:io'; ...@@ -9,8 +9,8 @@ import 'dart:io';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
import 'process_wrapper.dart';
import 'application_package.dart'; import 'application_package.dart';
import 'process_wrapper.dart';
final Logger _logging = new Logger('sky_tools.device'); final Logger _logging = new Logger('sky_tools.device');
......
...@@ -7,41 +7,30 @@ library sky_tools.init; ...@@ -7,41 +7,30 @@ library sky_tools.init;
import 'dart:async'; import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:args/args.dart'; import 'package:args/command_runner.dart';
import 'package:mustache4dart/mustache4dart.dart' as mustache; import 'package:mustache4dart/mustache4dart.dart' as mustache;
import 'package:path/path.dart' as p; import 'package:path/path.dart' as p;
import 'common.dart'; class InitCommand extends Command {
final name = 'init';
class InitCommandHandler extends CommandHandler { final description = 'Create a new sky project.';
InitCommandHandler() : super('init', 'Create a new sky project.'); InitCommand() {
argParser.addOption('out', abbr: 'o', help: 'The output directory.');
ArgParser get parser { argParser.addFlag('pub',
// TODO: Add a --template option for template selection when we have more than one.
ArgParser parser = new ArgParser();
parser.addFlag('help',
abbr: 'h', negatable: false, help: 'Display this help message.');
parser.addOption('out', abbr: 'o', help: 'The output directory.');
parser.addFlag('pub',
defaultsTo: true, defaultsTo: true,
help: 'Whether to run pub after the project has been created.'); help: 'Whether to run pub after the project has been created.');
return parser;
} }
@override @override
Future<int> processArgResults(ArgResults results) async { Future<int> run() async {
if (results['help']) { if (!argResults.wasParsed('out')) {
printUsage(); print('No option specified for the output directory.');
return 0; print(argParser.getUsage());
}
if (!results.wasParsed('out')) {
printUsage('No option specified for the output directory.');
return 2; return 2;
} }
// TODO: Confirm overwrite of an existing directory with the user. // TODO: Confirm overwrite of an existing directory with the user.
Directory out = new Directory(results['out']); Directory out = new Directory(argResults['out']);
new SkySimpleTemplate().generateInto(out); new SkySimpleTemplate().generateInto(out);
...@@ -58,7 +47,7 @@ Or if the Sky APK is not already on your device, run: ...@@ -58,7 +47,7 @@ Or if the Sky APK is not already on your device, run:
'''; ''';
if (results['pub']) { if (argResults['pub']) {
print("Running pub get..."); print("Running pub get...");
Process process = Process process =
await Process.start('pub', ['get'], workingDirectory: out.path); await Process.start('pub', ['get'], workingDirectory: out.path);
......
...@@ -6,32 +6,19 @@ library sky_tools.install; ...@@ -6,32 +6,19 @@ library sky_tools.install;
import 'dart:async'; import 'dart:async';
import 'package:args/args.dart'; import 'package:args/command_runner.dart';
import 'application_package.dart'; import 'application_package.dart';
import 'common.dart';
import 'device.dart'; import 'device.dart';
class InstallCommandHandler extends CommandHandler { class InstallCommand extends Command {
final name = 'install';
final description = 'Install your Flutter app on attached devices.';
AndroidDevice android = null; AndroidDevice android = null;
InstallCommandHandler([this.android]) InstallCommand([this.android]);
: super('install', 'Install your Sky app on attached devices.');
@override @override
ArgParser get parser { Future<int> run() async {
ArgParser parser = new ArgParser();
parser.addFlag('help',
abbr: 'h', negatable: false, help: 'Display this help message.');
return parser;
}
@override
Future<int> processArgResults(ArgResults results) async {
if (results['help']) {
printUsage();
return 0;
}
bool installedSomewhere = false; bool installedSomewhere = false;
Map<BuildPlatform, ApplicationPackage> packages = Map<BuildPlatform, ApplicationPackage> packages =
......
...@@ -8,26 +8,23 @@ import 'dart:async'; ...@@ -8,26 +8,23 @@ import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:args/args.dart'; import 'package:args/args.dart';
import 'package:args/command_runner.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
import 'artifacts.dart'; import 'artifacts.dart';
import 'common.dart';
import 'process.dart'; import 'process.dart';
final Logger _logging = new Logger('sky_tools.run_mojo'); final Logger _logging = new Logger('sky_tools.run_mojo');
class RunMojoCommandHandler extends CommandHandler { class RunMojoCommand extends Command {
RunMojoCommandHandler() : super('run_mojo', 'Run a Flutter app in mojo.'); final name = 'run_mojo';
final description = 'Run a Flutter app in mojo.';
ArgParser get parser { RunMojoCommand() {
ArgParser parser = new ArgParser(); argParser.addFlag('android', negatable: false, help: 'Run on an Android device');
parser.addFlag('android', negatable: false, help: 'Run on an Android device'); argParser.addOption('app', defaultsTo: 'app.flx');
parser.addFlag('help', abbr: 'h', negatable: false); argParser.addOption('mojo-path', help: 'Path to directory containing mojo_shell and services');
parser.addOption('app', defaultsTo: 'app.flx'); argParser.addOption('package-root', defaultsTo: 'packages');
parser.addOption('mojo-path', help: 'Path to directory containing mojo_shell and services');
parser.addOption('package-root', defaultsTo: 'packages');
return parser;
} }
Future<String> _makePathAbsolute(String relativePath) async { Future<String> _makePathAbsolute(String relativePath) async {
...@@ -73,22 +70,18 @@ class RunMojoCommandHandler extends CommandHandler { ...@@ -73,22 +70,18 @@ class RunMojoCommandHandler extends CommandHandler {
} }
@override @override
Future<int> processArgResults(ArgResults results) async { Future<int> run() async {
if (results['help']) { if (argResults['mojo-path'] == null) {
print(parser.usage);
return 0;
}
if (results['mojo-path'] == null) {
_logging.severe('Must specify --mojo-path to mojo_run'); _logging.severe('Must specify --mojo-path to mojo_run');
return 1; return 1;
} }
String packageRoot = results['package-root']; String packageRoot = argResults['package-root'];
ArtifactStore artifacts = new ArtifactStore(packageRoot); ArtifactStore artifacts = new ArtifactStore(packageRoot);
String appPath = await _makePathAbsolute(results['app']); String appPath = await _makePathAbsolute(argResults['app']);
if (results['android']) { if (argResults['android']) {
return _runAndroid(results, appPath, artifacts); return _runAndroid(argResults, appPath, artifacts);
} else { } else {
return _runLinux(results, appPath, artifacts); return _runLinux(argResults, appPath, artifacts);
} }
} }
} }
...@@ -4,13 +4,11 @@ ...@@ -4,13 +4,11 @@
import 'dart:io'; import 'dart:io';
import 'package:mockito/mockito.dart'; import 'package:args/command_runner.dart';
import 'package:path/path.dart' as p; import 'package:path/path.dart' as p;
import 'package:sky_tools/src/init.dart'; import 'package:sky_tools/src/init.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'src/common.dart';
main() => defineTests(); main() => defineTests();
defineTests() { defineTests() {
...@@ -27,14 +25,13 @@ defineTests() { ...@@ -27,14 +25,13 @@ defineTests() {
// Verify that we create a project that is well-formed. // Verify that we create a project that is well-formed.
test('init sky-simple', () async { test('init sky-simple', () async {
InitCommandHandler handler = new InitCommandHandler(); InitCommand command = new InitCommand();
MockArgResults results = new MockArgResults(); CommandRunner runner = new CommandRunner('test_flutter', '')
when(results['help']).thenReturn(false); ..addCommand(command);
when(results['pub']).thenReturn(true); await runner.run(['init', '--out', temp.path])
when(results.wasParsed('out')).thenReturn(true); .then((int code) => expect(code, equals(0)));
when(results['out']).thenReturn(temp.path);
await handler.processArgResults(results); String path = p.join(temp.path, 'lib', 'main.dart');
String path = p.join(temp.path, 'lib/main.dart');
expect(new File(path).existsSync(), true); expect(new File(path).existsSync(), true);
ProcessResult exec = Process.runSync( ProcessResult exec = Process.runSync(
'dartanalyzer', ['--fatal-warnings', path], 'dartanalyzer', ['--fatal-warnings', path],
......
...@@ -4,9 +4,10 @@ ...@@ -4,9 +4,10 @@
library install_test; library install_test;
import 'package:args/command_runner.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
import 'package:sky_tools/src/install.dart';
import 'package:sky_tools/src/application_package.dart'; import 'package:sky_tools/src/application_package.dart';
import 'package:sky_tools/src/install.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'src/common.dart'; import 'src/common.dart';
...@@ -23,12 +24,11 @@ defineTests() { ...@@ -23,12 +24,11 @@ defineTests() {
MockAndroidDevice android = new MockAndroidDevice(); MockAndroidDevice android = new MockAndroidDevice();
when(android.isConnected()).thenReturn(true); when(android.isConnected()).thenReturn(true);
when(android.installApp(any)).thenReturn(true); when(android.installApp(any)).thenReturn(true);
InstallCommandHandler handler = new InstallCommandHandler(android); InstallCommand command = new InstallCommand(android);
MockArgResults results = new MockArgResults(); CommandRunner runner = new CommandRunner('test_flutter', '')
when(results['help']).thenReturn(false); ..addCommand(command);
handler runner.run(['install'])
.processArgResults(results)
.then((int code) => expect(code, equals(0))); .then((int code) => expect(code, equals(0)));
}); });
}); });
......
...@@ -2,15 +2,9 @@ ...@@ -2,15 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:args/args.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
import 'package:sky_tools/src/device.dart'; import 'package:sky_tools/src/device.dart';
class MockArgResults extends Mock implements ArgResults {
@override
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
class MockAndroidDevice extends Mock implements AndroidDevice { class MockAndroidDevice extends Mock implements AndroidDevice {
@override @override
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
......
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