Skip to content

Commit 9b42b5f

Browse files
authored
[flutter_migrate] base boilerplate files (#2694)
1 parent 2561a90 commit 9b42b5f

24 files changed

+5942
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Specify analysis options.
2+
3+
include: ../../analysis_options.yaml
4+
5+
linter:
6+
rules:
7+
public_member_api_docs: false # Standalone executable, no public API
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Copyright 2013 The Flutter 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+
5+
import 'package:args/command_runner.dart';
6+
7+
enum ExitStatus {
8+
success,
9+
warning,
10+
fail,
11+
killed,
12+
}
13+
14+
class CommandResult {
15+
const CommandResult(this.exitStatus);
16+
17+
/// A command that succeeded. It is used to log the result of a command invocation.
18+
factory CommandResult.success() {
19+
return const CommandResult(ExitStatus.success);
20+
}
21+
22+
/// A command that exited with a warning. It is used to log the result of a command invocation.
23+
factory CommandResult.warning() {
24+
return const CommandResult(ExitStatus.warning);
25+
}
26+
27+
/// A command that failed. It is used to log the result of a command invocation.
28+
factory CommandResult.fail() {
29+
return const CommandResult(ExitStatus.fail);
30+
}
31+
32+
final ExitStatus exitStatus;
33+
34+
@override
35+
String toString() {
36+
switch (exitStatus) {
37+
case ExitStatus.success:
38+
return 'success';
39+
case ExitStatus.warning:
40+
return 'warning';
41+
case ExitStatus.fail:
42+
return 'fail';
43+
case ExitStatus.killed:
44+
return 'killed';
45+
}
46+
}
47+
}
48+
49+
abstract class MigrateCommand extends Command<void> {
50+
@override
51+
Future<void> run() async {
52+
await runCommand();
53+
}
54+
55+
Future<CommandResult> runCommand();
56+
57+
/// Gets the parsed command-line option named [name] as a `bool?`.
58+
bool? boolArg(String name) {
59+
if (!argParser.options.containsKey(name)) {
60+
return null;
61+
}
62+
return argResults == null ? null : argResults![name] as bool;
63+
}
64+
65+
String? stringArg(String name) {
66+
if (!argParser.options.containsKey(name)) {
67+
return null;
68+
}
69+
return argResults == null ? null : argResults![name] as String?;
70+
}
71+
72+
/// Gets the parsed command-line option named [name] as an `int`.
73+
int? intArg(String name) => argResults?[name] as int?;
74+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
// Copyright 2013 The Flutter 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+
5+
import 'dart:async';
6+
import 'dart:io';
7+
8+
import 'file_system.dart';
9+
10+
/// Throw a specialized exception for expected situations
11+
/// where the tool should exit with a clear message to the user
12+
/// and no stack trace unless the --verbose option is specified.
13+
/// For example: network errors.
14+
Never throwToolExit(String? message, {int? exitCode}) {
15+
throw ToolExit(message, exitCode: exitCode);
16+
}
17+
18+
/// Specialized exception for expected situations
19+
/// where the tool should exit with a clear message to the user
20+
/// and no stack trace unless the --verbose option is specified.
21+
/// For example: network errors.
22+
class ToolExit implements Exception {
23+
ToolExit(this.message, {this.exitCode});
24+
25+
final String? message;
26+
final int? exitCode;
27+
28+
@override
29+
String toString() => 'Error: $message';
30+
}
31+
32+
/// Return the name of an enum item.
33+
String getEnumName(dynamic enumItem) {
34+
final String name = '$enumItem';
35+
final int index = name.indexOf('.');
36+
return index == -1 ? name : name.substring(index + 1);
37+
}
38+
39+
/// Runs [fn] with special handling of asynchronous errors.
40+
///
41+
/// If the execution of [fn] does not throw a synchronous exception, and if the
42+
/// [Future] returned by [fn] is completed with a value, then the [Future]
43+
/// returned by [asyncGuard] is completed with that value if it has not already
44+
/// been completed with an error.
45+
///
46+
/// If the execution of [fn] throws a synchronous exception, and no [onError]
47+
/// callback is provided, then the [Future] returned by [asyncGuard] is
48+
/// completed with an error whose object and stack trace are given by the
49+
/// synchronous exception. If an [onError] callback is provided, then the
50+
/// [Future] returned by [asyncGuard] is completed with its result when passed
51+
/// the error object and stack trace.
52+
///
53+
/// If the execution of [fn] results in an asynchronous exception that would
54+
/// otherwise be unhandled, and no [onError] callback is provided, then the
55+
/// [Future] returned by [asyncGuard] is completed with an error whose object
56+
/// and stack trace are given by the asynchronous exception. If an [onError]
57+
/// callback is provided, then the [Future] returned by [asyncGuard] is
58+
/// completed with its result when passed the error object and stack trace.
59+
///
60+
/// After the returned [Future] is completed, whether it be with a value or an
61+
/// error, all further errors resulting from the execution of [fn] are ignored.
62+
///
63+
/// Rationale:
64+
///
65+
/// Consider the following snippet:
66+
/// ```
67+
/// try {
68+
/// await foo();
69+
/// ...
70+
/// } catch (e) {
71+
/// ...
72+
/// }
73+
/// ```
74+
/// If the [Future] returned by `foo` is completed with an error, that error is
75+
/// handled by the catch block. However, if `foo` spawns an asynchronous
76+
/// operation whose errors are unhandled, those errors will not be caught by
77+
/// the catch block, and will instead propagate to the containing [Zone]. This
78+
/// behavior is non-intuitive to programmers expecting the `catch` to catch all
79+
/// the errors resulting from the code under the `try`.
80+
///
81+
/// As such, it would be convenient if the `try {} catch {}` here could handle
82+
/// not only errors completing the awaited [Future]s it contains, but also
83+
/// any otherwise unhandled asynchronous errors occurring as a result of awaited
84+
/// expressions. This is how `await` is often assumed to work, which leads to
85+
/// unexpected unhandled exceptions.
86+
///
87+
/// [asyncGuard] is intended to wrap awaited expressions occurring in a `try`
88+
/// block. The behavior described above gives the behavior that users
89+
/// intuitively expect from `await`. Consider the snippet:
90+
/// ```
91+
/// try {
92+
/// await asyncGuard(() async {
93+
/// var c = Completer();
94+
/// c.completeError('Error');
95+
/// });
96+
/// } catch (e) {
97+
/// // e is 'Error';
98+
/// }
99+
/// ```
100+
/// Without the [asyncGuard] the error 'Error' would be propagated to the
101+
/// error handler of the containing [Zone]. With the [asyncGuard], the error
102+
/// 'Error' is instead caught by the `catch`.
103+
///
104+
/// [asyncGuard] also accepts an [onError] callback for situations in which
105+
/// completing the returned [Future] with an error is not appropriate.
106+
/// For example, it is not always possible to immediately await the returned
107+
/// [Future]. In these cases, an [onError] callback is needed to prevent an
108+
/// error from propagating to the containing [Zone].
109+
///
110+
/// [onError] must have type `FutureOr<T> Function(Object error)` or
111+
/// `FutureOr<T> Function(Object error, StackTrace stackTrace)` otherwise an
112+
/// [ArgumentError] will be thrown synchronously.
113+
Future<T> asyncGuard<T>(
114+
Future<T> Function() fn, {
115+
Function? onError,
116+
}) {
117+
if (onError != null &&
118+
onError is! _UnaryOnError<T> &&
119+
onError is! _BinaryOnError<T>) {
120+
throw ArgumentError('onError must be a unary function accepting an Object, '
121+
'or a binary function accepting an Object and '
122+
'StackTrace. onError must return a T');
123+
}
124+
final Completer<T> completer = Completer<T>();
125+
126+
void handleError(Object e, StackTrace s) {
127+
if (completer.isCompleted) {
128+
return;
129+
}
130+
if (onError == null) {
131+
completer.completeError(e, s);
132+
return;
133+
}
134+
if (onError is _BinaryOnError<T>) {
135+
completer.complete(onError(e, s));
136+
} else if (onError is _UnaryOnError<T>) {
137+
completer.complete(onError(e));
138+
}
139+
}
140+
141+
runZoned<void>(() async {
142+
try {
143+
final T result = await fn();
144+
if (!completer.isCompleted) {
145+
completer.complete(result);
146+
}
147+
// This catches all exceptions so that they can be propagated to the
148+
// caller-supplied error handling or the completer.
149+
} catch (e, s) {
150+
// ignore: avoid_catches_without_on_clauses, forwards to Future
151+
handleError(e, s);
152+
}
153+
// ignore: deprecated_member_use
154+
}, onError: (Object e, StackTrace s) {
155+
handleError(e, s);
156+
});
157+
158+
return completer.future;
159+
}
160+
161+
typedef _UnaryOnError<T> = FutureOr<T> Function(Object error);
162+
typedef _BinaryOnError<T> = FutureOr<T> Function(
163+
Object error, StackTrace stackTrace);
164+
165+
/// Whether the test is running in a web browser compiled to JavaScript.
166+
///
167+
/// See also:
168+
///
169+
/// * [kIsWeb], the equivalent constant in the `foundation` library.
170+
const bool isBrowser = identical(0, 0.0);
171+
172+
/// Whether the test is running on the Windows operating system.
173+
///
174+
/// This does not include tests compiled to JavaScript running in a browser on
175+
/// the Windows operating system.
176+
///
177+
/// See also:
178+
///
179+
/// * [isBrowser], which reports true for tests running in browsers.
180+
bool get isWindows {
181+
if (isBrowser) {
182+
return false;
183+
}
184+
return Platform.isWindows;
185+
}
186+
187+
/// Whether the test is running on the macOS operating system.
188+
///
189+
/// This does not include tests compiled to JavaScript running in a browser on
190+
/// the macOS operating system.
191+
///
192+
/// See also:
193+
///
194+
/// * [isBrowser], which reports true for tests running in browsers.
195+
bool get isMacOS {
196+
if (isBrowser) {
197+
return false;
198+
}
199+
return Platform.isMacOS;
200+
}
201+
202+
/// Whether the test is running on the Linux operating system.
203+
///
204+
/// This does not include tests compiled to JavaScript running in a browser on
205+
/// the Linux operating system.
206+
///
207+
/// See also:
208+
///
209+
/// * [isBrowser], which reports true for tests running in browsers.
210+
bool get isLinux {
211+
if (isBrowser) {
212+
return false;
213+
}
214+
return Platform.isLinux;
215+
}
216+
217+
String? flutterRoot;
218+
219+
/// Determine the absolute and normalized path for the root of the current
220+
/// Flutter checkout.
221+
///
222+
/// This method has a series of fallbacks for determining the repo location. The
223+
/// first success will immediately return the root without further checks.
224+
///
225+
/// The order of these tests is:
226+
/// 1. FLUTTER_ROOT environment variable contains the path.
227+
/// 2. Platform script is a data URI scheme, returning `../..` to support
228+
/// tests run from `packages/flutter_tools`.
229+
/// 3. Platform script is package URI scheme, returning the grandparent directory
230+
/// of the package config file location from `packages/flutter_tools/.packages`.
231+
/// 4. Platform script file path is the snapshot path generated by `bin/flutter`,
232+
/// returning the grandparent directory from `bin/cache`.
233+
/// 5. Platform script file name is the entrypoint in `packages/flutter_tools/bin/flutter_tools.dart`,
234+
/// returning the 4th parent directory.
235+
/// 6. The current directory
236+
///
237+
/// If an exception is thrown during any of these checks, an error message is
238+
/// printed and `.` is returned by default (6).
239+
String defaultFlutterRoot({
240+
required FileSystem fileSystem,
241+
}) {
242+
const String kFlutterRootEnvironmentVariableName =
243+
'FLUTTER_ROOT'; // should point to //flutter/ (root of flutter/flutter repo)
244+
const String kSnapshotFileName =
245+
'flutter_tools.snapshot'; // in //flutter/bin/cache/
246+
const String kFlutterToolsScriptFileName =
247+
'flutter_tools.dart'; // in //flutter/packages/flutter_tools/bin/
248+
String normalize(String path) {
249+
return fileSystem.path.normalize(fileSystem.path.absolute(path));
250+
}
251+
252+
if (Platform.environment.containsKey(kFlutterRootEnvironmentVariableName)) {
253+
return normalize(
254+
Platform.environment[kFlutterRootEnvironmentVariableName]!);
255+
}
256+
try {
257+
if (Platform.script.scheme == 'data') {
258+
return normalize('../..'); // The tool is running as a test.
259+
}
260+
final String Function(String) dirname = fileSystem.path.dirname;
261+
262+
if (Platform.script.scheme == 'package') {
263+
final String packageConfigPath =
264+
Uri.parse(Platform.packageConfig!).toFilePath(
265+
windows: isWindows,
266+
);
267+
return normalize(dirname(dirname(dirname(packageConfigPath))));
268+
}
269+
270+
if (Platform.script.scheme == 'file') {
271+
final String script = Platform.script.toFilePath(
272+
windows: isWindows,
273+
);
274+
if (fileSystem.path.basename(script) == kSnapshotFileName) {
275+
return normalize(dirname(dirname(fileSystem.path.dirname(script))));
276+
}
277+
if (fileSystem.path.basename(script) == kFlutterToolsScriptFileName) {
278+
return normalize(dirname(dirname(dirname(dirname(script)))));
279+
}
280+
}
281+
} on Exception catch (error) {
282+
// There is currently no logger attached since this is computed at startup.
283+
// ignore: avoid_print
284+
print('$error');
285+
}
286+
return normalize('.');
287+
}

0 commit comments

Comments
 (0)