1// Copyright 2016 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5import 'dart:async'; 6import 'dart:convert'; 7import 'dart:io'; 8 9import 'package:args/args.dart'; 10import 'package:path/path.dart' as path; 11 12import 'package:flutter_devicelab/framework/manifest.dart'; 13import 'package:flutter_devicelab/framework/runner.dart'; 14import 'package:flutter_devicelab/framework/utils.dart'; 15 16List<String> _taskNames = <String>[]; 17 18/// Runs tasks. 19/// 20/// The tasks are chosen depending on the command-line options 21/// (see [_argParser]). 22Future<void> main(List<String> rawArgs) async { 23 ArgResults args; 24 try { 25 args = _argParser.parse(rawArgs); 26 } on FormatException catch (error) { 27 stderr.writeln('${error.message}\n'); 28 stderr.writeln('Usage:\n'); 29 stderr.writeln(_argParser.usage); 30 exitCode = 1; 31 return; 32 } 33 34 if (!args.wasParsed('task')) { 35 if (args.wasParsed('stage')) { 36 final String stageName = args['stage']; 37 final List<ManifestTask> tasks = loadTaskManifest().tasks; 38 for (ManifestTask task in tasks) { 39 if (task.stage == stageName) 40 _taskNames.add(task.name); 41 } 42 } else if (args.wasParsed('all')) { 43 final List<ManifestTask> tasks = loadTaskManifest().tasks; 44 for (ManifestTask task in tasks) { 45 _taskNames.add(task.name); 46 } 47 } 48 } 49 50 if (_taskNames.isEmpty) { 51 stderr.writeln('Failed to find tasks to run based on supplied options.'); 52 exitCode = 1; 53 return; 54 } 55 56 final bool silent = args['silent']; 57 final String localEngine = args['local-engine']; 58 final String localEngineSrcPath = args['local-engine-src-path']; 59 60 for (String taskName in _taskNames) { 61 section('Running task "$taskName"'); 62 final Map<String, dynamic> result = await runTask( 63 taskName, 64 silent: silent, 65 localEngine: localEngine, 66 localEngineSrcPath: localEngineSrcPath, 67 ); 68 69 if (!result['success']) 70 exitCode = 1; 71 72 print('Task result:'); 73 print(const JsonEncoder.withIndent(' ').convert(result)); 74 section('Finished task "$taskName"'); 75 } 76} 77 78/// Command-line options for the `run.dart` command. 79final ArgParser _argParser = ArgParser() 80 ..addMultiOption( 81 'task', 82 abbr: 't', 83 splitCommas: true, 84 help: 'Either:\n' 85 ' - the name of a task defined in manifest.yaml. Example: complex_layout__start_up.\n' 86 ' - the path to a Dart file corresponding to a task, which resides in bin/tasks. Example: bin/tasks/complex_layout__start_up.dart.\n' 87 '\n' 88 'This option may be repeated to specify multiple tasks.', 89 callback: (List<String> value) { 90 for (String nameOrPath in value) { 91 final List<String> fragments = path.split(nameOrPath); 92 final bool isDartFile = fragments.last.endsWith('.dart'); 93 94 if (fragments.length == 1 && !isDartFile) { 95 // Not a path 96 _taskNames.add(nameOrPath); 97 } else if (!isDartFile || fragments.length != 3 || !_listsEqual(<String>['bin', 'tasks'], fragments.take(2).toList())) { 98 // Unsupported executable location 99 throw FormatException('Invalid value for option -t (--task): $nameOrPath'); 100 } else { 101 _taskNames.add(path.withoutExtension(fragments.last)); 102 } 103 } 104 }, 105 ) 106 ..addOption( 107 'stage', 108 abbr: 's', 109 help: 'Name of the stage. Runs all tasks for that stage. ' 110 'The tasks and their stages are read from manifest.yaml.', 111 ) 112 ..addFlag( 113 'all', 114 abbr: 'a', 115 help: 'Runs all tasks defined in manifest.yaml.', 116 ) 117 ..addMultiOption( 118 'test', 119 hide: true, 120 splitCommas: true, 121 callback: (List<String> value) { 122 if (value.isNotEmpty) { 123 throw const FormatException( 124 'Invalid option --test. Did you mean --task (-t)?', 125 ); 126 } 127 }, 128 ) 129 ..addFlag( 130 'silent', 131 negatable: true, 132 defaultsTo: false, 133 ) 134 ..addOption( 135 'local-engine', 136 help: 'Name of a build output within the engine out directory, if you are ' 137 'building Flutter locally. Use this to select a specific version of ' 138 'the engine if you have built multiple engine targets. This path is ' 139 'relative to --local-engine-src-path/out.', 140 ) 141 ..addOption( 142 'local-engine-src-path', 143 help: 'Path to your engine src directory, if you are building Flutter ' 144 'locally. Defaults to \$FLUTTER_ENGINE if set, or tries to guess at ' 145 'the location based on the value of the --flutter-root option.', 146 ); 147 148bool _listsEqual(List<dynamic> a, List<dynamic> b) { 149 if (a.length != b.length) 150 return false; 151 152 for (int i = 0; i < a.length; i++) { 153 if (a[i] != b[i]) 154 return false; 155 } 156 157 return true; 158} 159