add a sky_tools init command to create a new project
diff --git a/packages/flutter_tools/bin/sky_tools.dart b/packages/flutter_tools/bin/sky_tools.dart
new file mode 100644
index 0000000..c077124
--- /dev/null
+++ b/packages/flutter_tools/bin/sky_tools.dart
@@ -0,0 +1,47 @@
+// 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:io';
+
+import 'package:args/args.dart';
+import 'package:sky_tools/src/common.dart';
+import 'package:sky_tools/src/init.dart';
+
+void main(List<String> args) {
+ Map<String, CommandHandler> handlers = {};
+
+ ArgParser parser = new ArgParser();
+ parser.addSeparator('options:');
+ parser.addFlag('help',
+ abbr: 'h', negatable: false, help: 'Display this help message.');
+ parser.addSeparator('commands:');
+
+ CommandHandler handler = new InitCommandHandler();
+ parser.addCommand(handler.name, handler.parser);
+ handlers[handler.name] = handler;
+
+ ArgResults results = parser.parse(args);
+
+ if (results['help']) {
+ _printUsage(parser, handlers);
+ } else if (results.command != null) {
+ handlers[results.command.name].processArgResults(results.command);
+ } else {
+ _printUsage(parser, handlers, 'No command specified.');
+ exit(1);
+ }
+}
+
+void _printUsage(ArgParser parser, Map<String, CommandHandler> handlers,
+ [String message]) {
+ if (message != null) {
+ print('${message}\n');
+ }
+ print('usage: sky_tools <command> [arguments]');
+ print('');
+ print(parser.usage);
+ handlers.forEach((String command, CommandHandler handler) {
+ print(' ${command.padRight(10)} ${handler.description}');
+ });
+}
diff --git a/packages/flutter_tools/lib/src/common.dart b/packages/flutter_tools/lib/src/common.dart
new file mode 100644
index 0000000..ef5d5fc
--- /dev/null
+++ b/packages/flutter_tools/lib/src/common.dart
@@ -0,0 +1,27 @@
+// 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 'package:args/args.dart';
+
+abstract class CommandHandler {
+ final String name;
+ final String description;
+
+ CommandHandler(this.name, this.description);
+
+ ArgParser get parser;
+
+ void 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;
+}
diff --git a/packages/flutter_tools/lib/src/init.dart b/packages/flutter_tools/lib/src/init.dart
new file mode 100644
index 0000000..f331bce
--- /dev/null
+++ b/packages/flutter_tools/lib/src/init.dart
@@ -0,0 +1,162 @@
+// 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.init;
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:args/args.dart';
+import 'package:path/path.dart' as p;
+
+import 'common.dart';
+
+class InitCommandHandler extends CommandHandler {
+ InitCommandHandler() : super('init', 'Create a new sky project.');
+
+ ArgParser get parser {
+ ArgParser parser = new ArgParser();
+ parser.addFlag('help',
+ abbr: 'h', negatable: false, help: 'Display this help message.');
+ // parser.addOption('template',
+ // abbr: 't',
+ // help: "The template to generate; either sky-simple or sky-full.");
+ parser.addOption('out', abbr: 'o', help: 'The output directory.');
+ parser.addFlag('pub', defaultsTo: true,
+ help: 'Whether to run pub after the project has been created.');
+ return parser;
+ }
+
+ Future processArgResults(ArgResults results) async {
+ if (results['help']) {
+ printUsage();
+ return new Future.value();
+ }
+
+ if (!results.wasParsed('out')) {
+ printUsage('No option specified for the output directory.');
+ return new Future.value();
+ }
+
+ Directory out = new Directory(results['out']);
+
+ // TODO: confirm overwrite with user
+ //if (out.existsSync())
+
+ new SkySimpleTemplate().generateInto(out);
+
+ print('');
+
+ String message = 'All done! To run your application:\n'
+ 'cd ${out.path}\n'
+ './packages/sky/sky_tool start';
+
+ if (results['pub']) {
+ print("Running pub get...");
+ Process process = await Process.start('pub', ['get'], workingDirectory: out.path);
+ stdout.addStream(process.stdout);
+ stderr.addStream(process.stderr);
+ int code = await process.exitCode;
+ if (code == 0) {
+ print('\n${message}');
+ }
+ } else {
+ print(message);
+ }
+ }
+}
+
+abstract class Template {
+ final String name;
+ final String description;
+
+ Map<String, String> files = {};
+
+ Template(this.name, this.description);
+
+ void generateInto(Directory dir) {
+ String projectName = _normalizeProjectName(p.basename(dir.path));
+ print('Creating ${p.basename(projectName)}...');
+ dir.createSync(recursive: true);
+
+ files.forEach((String path, String contents) {
+ contents = contents
+ .replaceAll('{{projectName}}', projectName)
+ .replaceAll('{{description}}', description);
+ File file = new File(p.join(dir.path, path));
+ file.parent.createSync();
+ file.writeAsStringSync(contents);
+ print(file.path);
+ });
+ }
+
+ String toString() => name;
+}
+
+class SkySimpleTemplate extends Template {
+ SkySimpleTemplate() : super('sky-simple', 'A minimal Sky project.') {
+ files['.gitignore']= _gitignore;
+ files['pubspec.yaml']= _pubspec;
+ files['README.md']= _readme;
+ files['lib/main.dart']= _libMain;
+ }
+}
+
+String _normalizeProjectName(String name) {
+ name = name.replaceAll('-', '_').replaceAll(' ', '_');
+ // Strip any extension (like .dart).
+ if (name.contains('.')) {
+ name = name.substring(0, name.indexOf('.'));
+ }
+ return name;
+}
+
+const _gitignore = r'''
+.DS_Store
+.idea
+.packages
+.pub/
+build/
+packages
+pubspec.lock
+''';
+
+const _readme = r'''
+# {{projectName}}
+
+{{description}}
+
+## Getting Started
+
+For help getting started, view our online
+[readme](https://github.com/domokit/sky_engine/blob/master/sky/packages/sky/README.md).
+''';
+
+const _pubspec = r'''
+name: {{projectName}}
+description: {{description}}
+dependencies:
+ sky: any
+ sky_tools: any
+''';
+
+const _libMain = r'''
+import 'package:sky/widgets.dart';
+
+class HelloWorldApp extends App {
+ Widget build() {
+ return new Scaffold(
+ toolbar: new ToolBar(center: new Text("Demo")),
+ body: new Material(child: new Center(child: new Text("Hello world!"))),
+ floatingActionButton: new FloatingActionButton(
+ child: new Icon(type: 'content/add', size: 24)
+ )
+ );
+ }
+}
+
+void main() {
+ runApp(new HelloWorldApp());
+}
+''';
diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml
index 2d84548..8915f9e 100644
--- a/packages/flutter_tools/pubspec.yaml
+++ b/packages/flutter_tools/pubspec.yaml
@@ -9,5 +9,12 @@
dependencies:
args: ^0.13.0
+ path: ^1.3.0
shelf: ^0.6.2
shelf_static: ^0.2.3
+
+dev_dependencies:
+ test: ^0.12.0
+
+executable:
+ sky_tools:
diff --git a/packages/flutter_tools/test/init_test.dart b/packages/flutter_tools/test/init_test.dart
new file mode 100644
index 0000000..66340eb
--- /dev/null
+++ b/packages/flutter_tools/test/init_test.dart
@@ -0,0 +1,53 @@
+// 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:io';
+
+import 'package:args/args.dart';
+import 'package:path/path.dart' as p;
+import 'package:sky_tools/src/init.dart';
+import 'package:test/test.dart';
+
+main() => defineTests();
+
+defineTests() {
+ group('', () {
+ Directory temp;
+
+ setUp(() {
+ temp = Directory.systemTemp.createTempSync('sky_tools');
+ });
+
+ tearDown(() {
+ temp.deleteSync(recursive: true);
+ });
+
+ // Verify that we create a project that os well-formed.
+ test('init sky-simple', () async {
+ InitCommandHandler handler = new InitCommandHandler();
+ _MockArgResults results = new _MockArgResults();
+ results.values['help'] = false;
+ results.values['pub'] = true;
+ results.values['out'] = temp.path;
+ await handler.processArgResults(results);
+ String path = p.join(temp.path, 'lib/main.dart');
+ print(path);
+ expect(new File(path).existsSync(), true);
+ ProcessResult exec = Process.runSync('dartanalyzer', [path],
+ workingDirectory: temp.path);
+ expect(exec.exitCode, 0);
+ });
+ });
+}
+
+class _MockArgResults implements ArgResults {
+ Map values = {};
+ operator [](String name) => values[name];
+ List<String> get arguments => null;
+ ArgResults get command => null;
+ String get name => null;
+ Iterable<String> get options => values.keys;
+ List<String> get rest => null;
+ bool wasParsed(String name) => values.containsKey(name);
+}
diff --git a/packages/flutter_tools/tool/travis.sh b/packages/flutter_tools/tool/travis.sh
index f7b4013..c435e02 100755
--- a/packages/flutter_tools/tool/travis.sh
+++ b/packages/flutter_tools/tool/travis.sh
@@ -10,4 +10,5 @@
# Verify that the libraries are error free.
dartanalyzer --fatal-warnings \
bin/build_sky_apk.dart \
- bin/sky_server.dart
+ bin/sky_server.dart \
+ bin/sky_tools.dart