From 52d0434bec6a95e7648fa50941964234bdfc19c4 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Tue, 10 May 2016 15:04:29 -0700 Subject: [PATCH] create a flutter setup command (#3837) --- packages/flutter_tools/lib/executable.dart | 2 + .../flutter_tools/lib/src/commands/setup.dart | 159 ++++++++++++++++++ packages/flutter_tools/lib/src/doctor.dart | 28 ++- 3 files changed, 183 insertions(+), 6 deletions(-) create mode 100644 packages/flutter_tools/lib/src/commands/setup.dart diff --git a/packages/flutter_tools/lib/executable.dart b/packages/flutter_tools/lib/executable.dart index 0d9cddfbe6..e075a41c09 100644 --- a/packages/flutter_tools/lib/executable.dart +++ b/packages/flutter_tools/lib/executable.dart @@ -23,6 +23,7 @@ import 'src/commands/drive.dart'; import 'src/commands/install.dart'; import 'src/commands/listen.dart'; import 'src/commands/logs.dart'; +import 'src/commands/setup.dart'; import 'src/commands/precache.dart'; import 'src/commands/refresh.dart'; import 'src/commands/run.dart'; @@ -70,6 +71,7 @@ Future main(List args) async { ..addCommand(new RunCommand()) ..addCommand(new RunMojoCommand(hidden: !verboseHelp)) ..addCommand(new ScreenshotCommand()) + ..addCommand(new SetupCommand(hidden: !verboseHelp)) ..addCommand(new SkiaCommand()) ..addCommand(new StopCommand()) ..addCommand(new TestCommand()) diff --git a/packages/flutter_tools/lib/src/commands/setup.dart b/packages/flutter_tools/lib/src/commands/setup.dart new file mode 100644 index 0000000000..3a428ab33c --- /dev/null +++ b/packages/flutter_tools/lib/src/commands/setup.dart @@ -0,0 +1,159 @@ +// Copyright 2016 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 'dart:io'; + +import 'package:path/path.dart' as path; + +import '../base/os.dart'; +import '../base/process.dart'; +import '../doctor.dart'; +import '../artifacts.dart'; +import '../globals.dart'; +import '../runner/flutter_command.dart'; + +/// This setup command will install dependencies necessary for Flutter development. +/// +/// This is a hidden command, and is currently designed to work in a custom kiosk +/// environment, but may be generalized in the future. +class SetupCommand extends FlutterCommand { + SetupCommand({ this.hidden: false }); + + @override + final String name = 'setup'; + + @override + final String description = 'Setup a machine to support Flutter development.'; + + @override + final bool hidden; + + @override + bool get requiresProjectRoot => false; + + @override + Future runInProject() async { + printStatus('Running Flutter setup...'); + + // setup brew on mac + if (os.isMacOS) { + printStatus('\nChecking brew:'); + + if (os.which('brew') == null) { + printError('homebrew is not installed; please install at http://brew.sh/.'); + } else { + printStatus('brew is installed.'); + + await runCommandAndStreamOutput(['brew', 'install', 'ideviceinstaller']); + await runCommandAndStreamOutput(['brew', 'install', 'ios-deploy']); + } + } + + // setup atom + printStatus('\nChecking Atom:'); + String apmPath = os.which('apm')?.path; + if (apmPath == null && FileSystemEntity.isFileSync('/usr/local/bin/apm')) + apmPath = '/usr/local/bin/apm'; + if (apmPath == null && FileSystemEntity.isFileSync('/usr/bin/apm')) + apmPath = '/usr/bin/apm'; + if (apmPath == null) { + final String expectedLocation = '/Applications/Atom.app/Contents/Resources/app/apm/bin/apm'; + if (FileSystemEntity.isFileSync(expectedLocation)) + apmPath = expectedLocation; + } + + if (apmPath == null) { + printError('Unable to locate the Atom installation.'); + } else { + printStatus('apm command available at $apmPath'); + + AtomValidator atomValidator = new AtomValidator(); + + if (!atomValidator.hasPackage('dartlang')) + await runCommandAndStreamOutput([apmPath, 'install', 'dartlang']); + else + printStatus('dartlang plugin installed'); + + if (!atomValidator.hasPackage('flutter')) + await runCommandAndStreamOutput([apmPath, 'install', 'flutter']); + else + printStatus('flutter plugin installed'); + + // Set up the ~/.atom/config.cson file - make sure the path the the + // flutter and dart sdks are correct. + _updateAtomConfigFile(); + } + + // run doctor + printStatus('\nFlutter doctor:'); + bool goodInstall = doctor.diagnose(); + + // Validate that flutter is available on the path. + if (os.which('flutter') == null) { + printError( + '\nThe flutter command is not available on the path.\n' + 'Please set up your PATH environment variable to point to the flutter/bin directory.' + ); + } else { + printStatus('\nThe flutter command is available on the path.'); + } + + if (goodInstall) + printStatus('\nFlutter setup complete!'); + + return goodInstall ? 0 : 1; + } + + // Quick-and-dirty manipulation of the cson file. + void _updateAtomConfigFile() { + // flutter: + // flutterRoot: "..." + // dartlang: + // sdkLocation: "..." + + String flutterRoot = path.normalize(path.absolute(ArtifactStore.flutterRoot)); + String sdkLocation = path.join(flutterRoot, 'bin/cache/dart-sdk'); + + File file = AtomValidator.getConfigFile(); + + if (file.existsSync()) { + String cson = file.readAsStringSync(); + cson = cson.trimRight() + '\n'; + + List lines = cson.split('\n').map((String line) => line.trim()).toList(); + + if (!lines.contains('flutter:')) { + cson += ''' + flutter: + flutterRoot: "$flutterRoot" +'''; + } + + if (!lines.contains('dartlang:')) { + cson += ''' + dartlang: + sdkLocation: "$sdkLocation" +'''; + } + + if (cson.trim() != file.readAsStringSync().trim()) { + printStatus('Updating ${file.path}'); + file.writeAsStringSync(cson); + } + } else { + // Create a new config file. + printStatus('Creating ${file.path}'); + + String cson = ''' +"*": + flutter: + flutterRoot: "$flutterRoot" + dartlang: + sdkLocation: "$sdkLocation" +'''; + file.writeAsStringSync(cson); + } + } +} diff --git a/packages/flutter_tools/lib/src/doctor.dart b/packages/flutter_tools/lib/src/doctor.dart index 86ac941162..0505f1c662 100644 --- a/packages/flutter_tools/lib/src/doctor.dart +++ b/packages/flutter_tools/lib/src/doctor.dart @@ -37,7 +37,7 @@ class Doctor { if (_iosWorkflow.appliesToHostPlatform) _validators.add(_iosWorkflow); - _validators.add(new _AtomValidator()); + _validators.add(new AtomValidator()); } static void initGlobal() { @@ -94,8 +94,9 @@ class Doctor { } /// Print verbose information about the state of installed tooling. - void diagnose() { + bool diagnose() { bool firstLine = true; + bool doctorResult = true; for (DoctorValidator validator in _validators) { if (!firstLine) @@ -104,6 +105,9 @@ class Doctor { ValidationResult result = validator.validate(); + if (result.type == ValidationType.missing) + doctorResult = false; + if (result.statusInfo != null) printStatus('${result.leadingBox} ${validator.title} (${result.statusInfo})'); else @@ -119,6 +123,8 @@ class Doctor { } } } + + return doctorResult; } bool get canListAnything => workflows.any((Workflow workflow) => workflow.canListDevices); @@ -214,8 +220,13 @@ class _FlutterValidator extends DoctorValidator { } } -class _AtomValidator extends DoctorValidator { - _AtomValidator() : super('Atom - a lightweight development environment for Flutter'); +class AtomValidator extends DoctorValidator { + AtomValidator() : super('Atom - a lightweight development environment for Flutter'); + + static File getConfigFile() { + // ~/.atom/config.cson + return new File(path.join(_getAtomHomePath(), 'config.cson')); + } static String _getAtomHomePath() { final Map env = Platform.environment; @@ -241,8 +252,7 @@ class _AtomValidator extends DoctorValidator { installCount++; } - String flutterPluginPath = path.join(_getAtomHomePath(), 'packages', 'flutter'); - if (!FileSystemEntity.isDirectorySync(flutterPluginPath)) { + if (!hasPackage('flutter')) { messages.add(new ValidationMessage.error( 'Flutter plugin not installed; this adds Flutter specific functionality to Atom.\n' 'Install the \'flutter\' plugin in Atom or run \'apm install flutter\'.' @@ -251,6 +261,7 @@ class _AtomValidator extends DoctorValidator { installCount++; try { + String flutterPluginPath = path.join(_getAtomHomePath(), 'packages', 'flutter'); File packageFile = new File(path.join(flutterPluginPath, 'package.json')); dynamic packageInfo = JSON.decode(packageFile.readAsStringSync()); String version = packageInfo['version']; @@ -267,4 +278,9 @@ class _AtomValidator extends DoctorValidator { messages ); } + + bool hasPackage(String packageName) { + String packagePath = path.join(_getAtomHomePath(), 'packages', packageName); + return FileSystemEntity.isDirectorySync(packagePath); + } }