// 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 'dart:io'; import 'package:args/command_runner.dart'; import '../application_package.dart'; import '../build_configuration.dart'; import '../device.dart'; import '../globals.dart'; import '../toolchain.dart'; import 'flutter_command_runner.dart'; typedef bool Validator(); abstract class FlutterCommand extends Command { FlutterCommandRunner get runner => super.runner; /// Whether this command needs to be run from the root of a project. bool get requiresProjectRoot => true; /// Whether this command requires a (single) Flutter target device to be connected. bool get requiresDevice => false; /// Whether this command only applies to Android devices. bool get androidOnly => false; List<BuildConfiguration> get buildConfigurations => runner.buildConfigurations; Future downloadToolchain() async { toolchain ??= await Toolchain.forConfigs(buildConfigurations); } Future downloadApplicationPackagesAndConnectToDevices() async { await downloadApplicationPackages(); _connectToDevices(); } Future downloadApplicationPackages() async { applicationPackages ??= await ApplicationPackageStore.forConfigs(buildConfigurations); } void _connectToDevices() { devices ??= new DeviceStore.forConfigs(buildConfigurations); } Future<int> run() { Stopwatch stopwatch = new Stopwatch()..start(); return _run().then((int exitCode) { printTrace("'flutter $name' exiting with code $exitCode; " "elasped time ${stopwatch.elapsedMilliseconds}ms."); return exitCode; }); } Future<int> _run() async { if (requiresProjectRoot && !projectRootValidator()) return 1; // Ensure at least one toolchain is installed. if (requiresDevice && !doctor.canLaunchAnything) { printError("Unable to locate a development device; please run 'flutter doctor' " "for information about installing additional components."); return 1; } // Validate devices. if (requiresDevice) { List<Device> devices = await deviceManager.getDevices(); if (devices.isEmpty && deviceManager.hasSpecifiedDeviceId) { printError("No device found with id '${deviceManager.specifiedDeviceId}'."); return 1; } else if (devices.isEmpty) { printStatus('No connected devices.'); return 1; } devices = devices.where((Device device) => device.isSupported()).toList(); if (androidOnly) devices = devices.where((Device device) => device.platform == TargetPlatform.android).toList(); // TODO(devoncarew): Switch this to just supporting one connected device? if (devices.isEmpty) { printStatus('No supported devices connected.'); return 1; } _devicesForCommand = await _getDevicesForCommand(); } return await runInProject(); } // This is a field so that you can modify the value for testing. Validator projectRootValidator = () { if (!FileSystemEntity.isFileSync('pubspec.yaml')) { printError('Error: No pubspec.yaml file found.\n' 'This command should be run from the root of your Flutter project.\n' 'Do not run this command from the root of your git clone of Flutter.'); return false; } return true; }; Future<int> runInProject(); List<Device> get devicesForCommand => _devicesForCommand; Device get deviceForCommand { // TODO(devoncarew): Switch this to just supporting one connected device? return devicesForCommand.isNotEmpty ? devicesForCommand.first : null; } // This is caculated in run() if the command has [requiresDevice] specified. List<Device> _devicesForCommand; ApplicationPackageStore applicationPackages; Toolchain toolchain; DeviceStore devices; Future<List<Device>> _getDevicesForCommand() async { List<Device> devices = await deviceManager.getDevices(); if (devices.isEmpty) return null; if (deviceManager.hasSpecifiedDeviceId) { Device device = await deviceManager.getDeviceById(deviceManager.specifiedDeviceId); return device == null ? <Device>[] : <Device>[device]; } devices = devices.where((Device device) => device.isSupported()).toList(); if (androidOnly) devices = devices.where((Device device) => device.platform == TargetPlatform.android).toList(); return devices; } }