Re-organize flutter_tools code to better support proper layering. (#12957)
* executable.dart#main() depends on runner.dart#run()
* Refactor code such that non-commands don't depend on commands.
No code was actually changed in this PR - code was merely moved from
point A to point B.
diff --git a/packages/flutter_tools/lib/executable.dart b/packages/flutter_tools/lib/executable.dart
index 5dca420..5e89040 100644
--- a/packages/flutter_tools/lib/executable.dart
+++ b/packages/flutter_tools/lib/executable.dart
@@ -4,23 +4,7 @@
import 'dart:async';
-import 'package:args/command_runner.dart';
-import 'package:intl/intl_standalone.dart' as intl;
-import 'package:meta/meta.dart';
-import 'package:process/process.dart';
-
-import 'src/artifacts.dart';
-import 'src/base/common.dart';
-import 'src/base/config.dart';
-import 'src/base/context.dart';
-import 'src/base/file_system.dart';
-import 'src/base/io.dart';
-import 'src/base/logger.dart';
-import 'src/base/platform.dart';
-import 'src/base/process.dart';
-import 'src/base/terminal.dart';
-import 'src/base/utils.dart';
-import 'src/cache.dart';
+import 'runner.dart' as runner;
import 'src/commands/analyze.dart';
import 'src/commands/build.dart';
import 'src/commands/channel.dart';
@@ -44,17 +28,7 @@
import 'src/commands/trace.dart';
import 'src/commands/update_packages.dart';
import 'src/commands/upgrade.dart';
-import 'src/crash_reporting.dart';
-import 'src/devfs.dart';
-import 'src/device.dart';
-import 'src/doctor.dart';
-import 'src/globals.dart';
-import 'src/ios/simulators.dart';
-import 'src/run_hot.dart';
import 'src/runner/flutter_command.dart';
-import 'src/runner/flutter_command_runner.dart';
-import 'src/usage.dart';
-import 'src/version.dart';
/// Main entry point for commands.
///
@@ -65,7 +39,7 @@
(args.isNotEmpty && args.first == 'help') || (args.length == 1 && verbose);
final bool verboseHelp = help && verbose;
- await run(args, <FlutterCommand>[
+ await runner.run(args, <FlutterCommand>[
new AnalyzeCommand(verboseHelp: verboseHelp),
new BuildCommand(verboseHelp: verboseHelp),
new ChannelCommand(),
@@ -91,240 +65,3 @@
new UpgradeCommand(),
], verbose: verbose, verboseHelp: verboseHelp);
}
-
-Future<int> run(List<String> args, List<FlutterCommand> subCommands, {
- bool verbose: false,
- bool verboseHelp: false,
- bool reportCrashes,
- String flutterVersion,
-}) async {
- reportCrashes ??= !isRunningOnBot;
-
- if (verboseHelp) {
- // Remove the verbose option; for help, users don't need to see verbose logs.
- args = new List<String>.from(args);
- args.removeWhere((String option) => option == '-v' || option == '--verbose');
- }
-
- final FlutterCommandRunner runner = new FlutterCommandRunner(verboseHelp: verboseHelp);
- subCommands.forEach(runner.addCommand);
-
- // Construct a context.
- final AppContext _executableContext = new AppContext();
-
- // Make the context current.
- return await _executableContext.runInZone(() async {
- // Initialize the context with some defaults.
- // NOTE: Similar lists also exist in `bin/fuchsia_builder.dart` and
- // `test/src/context.dart`. If you update this list of defaults, look
- // in those locations as well to see if you need a similar update there.
-
- // Seed these context entries first since others depend on them
- context.putIfAbsent(Stdio, () => const Stdio());
- context.putIfAbsent(Platform, () => const LocalPlatform());
- context.putIfAbsent(FileSystem, () => const LocalFileSystem());
- context.putIfAbsent(ProcessManager, () => const LocalProcessManager());
- context.putIfAbsent(AnsiTerminal, () => new AnsiTerminal());
- context.putIfAbsent(Logger, () => platform.isWindows ? new WindowsStdoutLogger() : new StdoutLogger());
- context.putIfAbsent(Config, () => new Config());
-
- // Order-independent context entries
- context.putIfAbsent(DeviceManager, () => new DeviceManager());
- context.putIfAbsent(DevFSConfig, () => new DevFSConfig());
- context.putIfAbsent(Doctor, () => new Doctor());
- context.putIfAbsent(HotRunnerConfig, () => new HotRunnerConfig());
- context.putIfAbsent(Cache, () => new Cache());
- context.putIfAbsent(Artifacts, () => new CachedArtifacts());
- context.putIfAbsent(IOSSimulatorUtils, () => new IOSSimulatorUtils());
- context.putIfAbsent(SimControl, () => new SimControl());
-
- // Initialize the system locale.
- await intl.findSystemLocale();
-
- try {
- await runner.run(args);
- await _exit(0);
- } catch (error, stackTrace) {
- String getVersion() => flutterVersion ?? FlutterVersion.getVersionString();
- return await _handleToolError(error, stackTrace, verbose, args, reportCrashes, getVersion);
- }
- return 0;
- });
-}
-
-/// Writes the [string] to one of the standard output streams.
-@visibleForTesting
-typedef void WriteCallback([String string]);
-
-/// Writes a line to STDERR.
-///
-/// Overwrite this in tests to avoid spurious test output.
-@visibleForTesting
-WriteCallback writelnStderr = stderr.writeln;
-
-Future<int> _handleToolError(
- dynamic error,
- StackTrace stackTrace,
- bool verbose,
- List<String> args,
- bool reportCrashes,
- String getFlutterVersion(),
-) async {
- if (error is UsageException) {
- writelnStderr(error.message);
- writelnStderr();
- writelnStderr(
- "Run 'flutter -h' (or 'flutter <command> -h') for available "
- 'flutter commands and options.'
- );
- // Argument error exit code.
- return _exit(64);
- } else if (error is ToolExit) {
- if (error.message != null)
- writelnStderr(error.message);
- if (verbose) {
- writelnStderr();
- writelnStderr(stackTrace.toString());
- writelnStderr();
- }
- return _exit(error.exitCode ?? 1);
- } else if (error is ProcessExit) {
- // We've caught an exit code.
- if (error.immediate) {
- exit(error.exitCode);
- return error.exitCode;
- } else {
- return _exit(error.exitCode);
- }
- } else {
- // We've crashed; emit a log report.
- writelnStderr();
-
- if (!reportCrashes) {
- // Print the stack trace on the bots - don't write a crash report.
- writelnStderr('$error');
- writelnStderr(stackTrace.toString());
- return _exit(1);
- } else {
- flutterUsage.sendException(error, stackTrace);
-
- if (error is String)
- writelnStderr('Oops; flutter has exited unexpectedly: "$error".');
- else
- writelnStderr('Oops; flutter has exited unexpectedly.');
-
- await CrashReportSender.instance.sendReport(
- error: error,
- stackTrace: stackTrace,
- getFlutterVersion: getFlutterVersion,
- );
- try {
- final File file = await _createLocalCrashReport(args, error, stackTrace);
- writelnStderr(
- 'Crash report written to ${file.path};\n'
- 'please let us know at https://github.com/flutter/flutter/issues.',
- );
- return _exit(1);
- } catch (error) {
- writelnStderr(
- 'Unable to generate crash report due to secondary error: $error\n'
- 'please let us know at https://github.com/flutter/flutter/issues.',
- );
- // Any exception throw here (including one thrown by `_exit()`) will
- // get caught by our zone's `onError` handler. In order to avoid an
- // infinite error loop, we throw an error that is recognized above
- // and will trigger an immediate exit.
- throw new ProcessExit(1, immediate: true);
- }
- }
- }
-}
-
-/// File system used by the crash reporting logic.
-///
-/// We do not want to use the file system stored in the context because it may
-/// be recording. Additionally, in the case of a crash we do not trust the
-/// integrity of the [AppContext].
-@visibleForTesting
-FileSystem crashFileSystem = const LocalFileSystem();
-
-/// Saves the crash report to a local file.
-Future<File> _createLocalCrashReport(List<String> args, dynamic error, StackTrace stackTrace) async {
- File crashFile = getUniqueFile(crashFileSystem.currentDirectory, 'flutter', 'log');
-
- final StringBuffer buffer = new StringBuffer();
-
- buffer.writeln('Flutter crash report; please file at https://github.com/flutter/flutter/issues.\n');
-
- buffer.writeln('## command\n');
- buffer.writeln('flutter ${args.join(' ')}\n');
-
- buffer.writeln('## exception\n');
- buffer.writeln('${error.runtimeType}: $error\n');
- buffer.writeln('```\n$stackTrace```\n');
-
- buffer.writeln('## flutter doctor\n');
- buffer.writeln('```\n${await _doctorText()}```');
-
- try {
- await crashFile.writeAsString(buffer.toString());
- } on FileSystemException catch (_) {
- // Fallback to the system temporary directory.
- crashFile = getUniqueFile(crashFileSystem.systemTempDirectory, 'flutter', 'log');
- try {
- await crashFile.writeAsString(buffer.toString());
- } on FileSystemException catch (e) {
- printError('Could not write crash report to disk: $e');
- printError(buffer.toString());
- }
- }
-
- return crashFile;
-}
-
-Future<String> _doctorText() async {
- try {
- final BufferLogger logger = new BufferLogger();
- final AppContext appContext = new AppContext();
-
- appContext.setVariable(Logger, logger);
-
- await appContext.runInZone(() => doctor.diagnose());
-
- return logger.statusText;
- } catch (error, trace) {
- return 'encountered exception: $error\n\n${trace.toString().trim()}\n';
- }
-}
-
-Future<int> _exit(int code) async {
- if (flutterUsage.isFirstRun)
- flutterUsage.printWelcome();
-
- // Send any last analytics calls that are in progress without overly delaying
- // the tool's exit (we wait a maximum of 250ms).
- if (flutterUsage.enabled) {
- final Stopwatch stopwatch = new Stopwatch()..start();
- await flutterUsage.ensureAnalyticsSent();
- printTrace('ensureAnalyticsSent: ${stopwatch.elapsedMilliseconds}ms');
- }
-
- // Run shutdown hooks before flushing logs
- await runShutdownHooks();
-
- final Completer<Null> completer = new Completer<Null>();
-
- // Give the task / timer queue one cycle through before we hard exit.
- Timer.run(() {
- try {
- printTrace('exiting with code $code');
- exit(code);
- completer.complete();
- } catch (error, stackTrace) {
- completer.completeError(error, stackTrace);
- }
- });
-
- await completer.future;
- return code;
-}
diff --git a/packages/flutter_tools/lib/runner.dart b/packages/flutter_tools/lib/runner.dart
new file mode 100644
index 0000000..9479bea
--- /dev/null
+++ b/packages/flutter_tools/lib/runner.dart
@@ -0,0 +1,274 @@
+// 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 'package:args/command_runner.dart';
+import 'package:intl/intl_standalone.dart' as intl;
+import 'package:meta/meta.dart';
+import 'package:process/process.dart';
+
+import 'src/artifacts.dart';
+import 'src/base/common.dart';
+import 'src/base/config.dart';
+import 'src/base/context.dart';
+import 'src/base/file_system.dart';
+import 'src/base/io.dart';
+import 'src/base/logger.dart';
+import 'src/base/platform.dart';
+import 'src/base/process.dart';
+import 'src/base/terminal.dart';
+import 'src/base/utils.dart';
+import 'src/cache.dart';
+import 'src/crash_reporting.dart';
+import 'src/devfs.dart';
+import 'src/device.dart';
+import 'src/doctor.dart';
+import 'src/globals.dart';
+import 'src/ios/simulators.dart';
+import 'src/run_hot.dart';
+import 'src/runner/flutter_command.dart';
+import 'src/runner/flutter_command_runner.dart';
+import 'src/usage.dart';
+import 'src/version.dart';
+
+/// Runs the Flutter tool with support for the specified list of [commands].
+Future<int> run(
+ List<String> args,
+ List<FlutterCommand> commands, {
+ bool verbose: false,
+ bool verboseHelp: false,
+ bool reportCrashes,
+ String flutterVersion,
+}) async {
+ reportCrashes ??= !isRunningOnBot;
+
+ if (verboseHelp) {
+ // Remove the verbose option; for help, users don't need to see verbose logs.
+ args = new List<String>.from(args);
+ args.removeWhere((String option) => option == '-v' || option == '--verbose');
+ }
+
+ final FlutterCommandRunner runner = new FlutterCommandRunner(verboseHelp: verboseHelp);
+ commands.forEach(runner.addCommand);
+
+ // Construct a context.
+ final AppContext _executableContext = new AppContext();
+
+ // Make the context current.
+ return await _executableContext.runInZone(() async {
+ // Initialize the context with some defaults.
+ // NOTE: Similar lists also exist in `bin/fuchsia_builder.dart` and
+ // `test/src/context.dart`. If you update this list of defaults, look
+ // in those locations as well to see if you need a similar update there.
+
+ // Seed these context entries first since others depend on them
+ context.putIfAbsent(Stdio, () => const Stdio());
+ context.putIfAbsent(Platform, () => const LocalPlatform());
+ context.putIfAbsent(FileSystem, () => const LocalFileSystem());
+ context.putIfAbsent(ProcessManager, () => const LocalProcessManager());
+ context.putIfAbsent(AnsiTerminal, () => new AnsiTerminal());
+ context.putIfAbsent(Logger, () => platform.isWindows ? new WindowsStdoutLogger() : new StdoutLogger());
+ context.putIfAbsent(Config, () => new Config());
+
+ // Order-independent context entries
+ context.putIfAbsent(DeviceManager, () => new DeviceManager());
+ context.putIfAbsent(DevFSConfig, () => new DevFSConfig());
+ context.putIfAbsent(Doctor, () => new Doctor());
+ context.putIfAbsent(HotRunnerConfig, () => new HotRunnerConfig());
+ context.putIfAbsent(Cache, () => new Cache());
+ context.putIfAbsent(Artifacts, () => new CachedArtifacts());
+ context.putIfAbsent(IOSSimulatorUtils, () => new IOSSimulatorUtils());
+ context.putIfAbsent(SimControl, () => new SimControl());
+
+ // Initialize the system locale.
+ await intl.findSystemLocale();
+
+ try {
+ await runner.run(args);
+ await _exit(0);
+ } catch (error, stackTrace) {
+ String getVersion() => flutterVersion ?? FlutterVersion.getVersionString();
+ return await _handleToolError(error, stackTrace, verbose, args, reportCrashes, getVersion);
+ }
+ return 0;
+ });
+}
+
+/// Writes the [string] to one of the standard output streams.
+@visibleForTesting
+typedef void WriteCallback([String string]);
+
+/// Writes a line to STDERR.
+///
+/// Overwrite this in tests to avoid spurious test output.
+@visibleForTesting
+WriteCallback writelnStderr = stderr.writeln;
+
+Future<int> _handleToolError(
+ dynamic error,
+ StackTrace stackTrace,
+ bool verbose,
+ List<String> args,
+ bool reportCrashes,
+ String getFlutterVersion(),
+ ) async {
+ if (error is UsageException) {
+ writelnStderr(error.message);
+ writelnStderr();
+ writelnStderr(
+ "Run 'flutter -h' (or 'flutter <command> -h') for available "
+ 'flutter commands and options.'
+ );
+ // Argument error exit code.
+ return _exit(64);
+ } else if (error is ToolExit) {
+ if (error.message != null)
+ writelnStderr(error.message);
+ if (verbose) {
+ writelnStderr();
+ writelnStderr(stackTrace.toString());
+ writelnStderr();
+ }
+ return _exit(error.exitCode ?? 1);
+ } else if (error is ProcessExit) {
+ // We've caught an exit code.
+ if (error.immediate) {
+ exit(error.exitCode);
+ return error.exitCode;
+ } else {
+ return _exit(error.exitCode);
+ }
+ } else {
+ // We've crashed; emit a log report.
+ writelnStderr();
+
+ if (!reportCrashes) {
+ // Print the stack trace on the bots - don't write a crash report.
+ writelnStderr('$error');
+ writelnStderr(stackTrace.toString());
+ return _exit(1);
+ } else {
+ flutterUsage.sendException(error, stackTrace);
+
+ if (error is String)
+ writelnStderr('Oops; flutter has exited unexpectedly: "$error".');
+ else
+ writelnStderr('Oops; flutter has exited unexpectedly.');
+
+ await CrashReportSender.instance.sendReport(
+ error: error,
+ stackTrace: stackTrace,
+ getFlutterVersion: getFlutterVersion,
+ );
+ try {
+ final File file = await _createLocalCrashReport(args, error, stackTrace);
+ writelnStderr(
+ 'Crash report written to ${file.path};\n'
+ 'please let us know at https://github.com/flutter/flutter/issues.',
+ );
+ return _exit(1);
+ } catch (error) {
+ writelnStderr(
+ 'Unable to generate crash report due to secondary error: $error\n'
+ 'please let us know at https://github.com/flutter/flutter/issues.',
+ );
+ // Any exception throw here (including one thrown by `_exit()`) will
+ // get caught by our zone's `onError` handler. In order to avoid an
+ // infinite error loop, we throw an error that is recognized above
+ // and will trigger an immediate exit.
+ throw new ProcessExit(1, immediate: true);
+ }
+ }
+ }
+}
+
+/// File system used by the crash reporting logic.
+///
+/// We do not want to use the file system stored in the context because it may
+/// be recording. Additionally, in the case of a crash we do not trust the
+/// integrity of the [AppContext].
+@visibleForTesting
+FileSystem crashFileSystem = const LocalFileSystem();
+
+/// Saves the crash report to a local file.
+Future<File> _createLocalCrashReport(List<String> args, dynamic error, StackTrace stackTrace) async {
+ File crashFile = getUniqueFile(crashFileSystem.currentDirectory, 'flutter', 'log');
+
+ final StringBuffer buffer = new StringBuffer();
+
+ buffer.writeln('Flutter crash report; please file at https://github.com/flutter/flutter/issues.\n');
+
+ buffer.writeln('## command\n');
+ buffer.writeln('flutter ${args.join(' ')}\n');
+
+ buffer.writeln('## exception\n');
+ buffer.writeln('${error.runtimeType}: $error\n');
+ buffer.writeln('```\n$stackTrace```\n');
+
+ buffer.writeln('## flutter doctor\n');
+ buffer.writeln('```\n${await _doctorText()}```');
+
+ try {
+ await crashFile.writeAsString(buffer.toString());
+ } on FileSystemException catch (_) {
+ // Fallback to the system temporary directory.
+ crashFile = getUniqueFile(crashFileSystem.systemTempDirectory, 'flutter', 'log');
+ try {
+ await crashFile.writeAsString(buffer.toString());
+ } on FileSystemException catch (e) {
+ printError('Could not write crash report to disk: $e');
+ printError(buffer.toString());
+ }
+ }
+
+ return crashFile;
+}
+
+Future<String> _doctorText() async {
+ try {
+ final BufferLogger logger = new BufferLogger();
+ final AppContext appContext = new AppContext();
+
+ appContext.setVariable(Logger, logger);
+
+ await appContext.runInZone(() => doctor.diagnose());
+
+ return logger.statusText;
+ } catch (error, trace) {
+ return 'encountered exception: $error\n\n${trace.toString().trim()}\n';
+ }
+}
+
+Future<int> _exit(int code) async {
+ if (flutterUsage.isFirstRun)
+ flutterUsage.printWelcome();
+
+ // Send any last analytics calls that are in progress without overly delaying
+ // the tool's exit (we wait a maximum of 250ms).
+ if (flutterUsage.enabled) {
+ final Stopwatch stopwatch = new Stopwatch()..start();
+ await flutterUsage.ensureAnalyticsSent();
+ printTrace('ensureAnalyticsSent: ${stopwatch.elapsedMilliseconds}ms');
+ }
+
+ // Run shutdown hooks before flushing logs
+ await runShutdownHooks();
+
+ final Completer<Null> completer = new Completer<Null>();
+
+ // Give the task / timer queue one cycle through before we hard exit.
+ Timer.run(() {
+ try {
+ printTrace('exiting with code $code');
+ exit(code);
+ completer.complete();
+ } catch (error, stackTrace) {
+ completer.completeError(error, stackTrace);
+ }
+ });
+
+ await completer.future;
+ return code;
+}
diff --git a/packages/flutter_tools/lib/src/android/android_device.dart b/packages/flutter_tools/lib/src/android/android_device.dart
index fde165e..49872b3 100644
--- a/packages/flutter_tools/lib/src/android/android_device.dart
+++ b/packages/flutter_tools/lib/src/android/android_device.dart
@@ -7,6 +7,7 @@
import '../android/android_sdk.dart';
import '../android/android_workflow.dart';
+import '../android/apk.dart';
import '../application_package.dart';
import '../base/common.dart' show throwToolExit;
import '../base/file_system.dart';
@@ -17,7 +18,6 @@
import '../base/process_manager.dart';
import '../base/utils.dart';
import '../build_info.dart';
-import '../commands/build_apk.dart';
import '../device.dart';
import '../globals.dart';
import '../protocol_discovery.dart';
diff --git a/packages/flutter_tools/lib/src/android/apk.dart b/packages/flutter_tools/lib/src/android/apk.dart
new file mode 100644
index 0000000..1c34cac
--- /dev/null
+++ b/packages/flutter_tools/lib/src/android/apk.dart
@@ -0,0 +1,37 @@
+// 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 '../base/common.dart';
+import '../build_info.dart';
+import '../globals.dart';
+import 'android_sdk.dart';
+import 'gradle.dart';
+
+Future<Null> buildApk({
+ String target,
+ BuildInfo buildInfo: BuildInfo.debug
+}) async {
+ if (!isProjectUsingGradle()) {
+ throwToolExit(
+ 'The build process for Android has changed, and the current project configuration\n'
+ 'is no longer valid. Please consult\n\n'
+ ' https://github.com/flutter/flutter/wiki/Upgrading-Flutter-projects-to-build-with-gradle\n\n'
+ 'for details on how to upgrade the project.'
+ );
+ }
+
+ // Validate that we can find an android sdk.
+ if (androidSdk == null)
+ throwToolExit('No Android SDK found. Try setting the ANDROID_HOME environment variable.');
+
+ final List<String> validationResult = androidSdk.validateSdkWellFormed();
+ if (validationResult.isNotEmpty) {
+ validationResult.forEach(printError);
+ throwToolExit('Try re-installing or updating your Android SDK.');
+ }
+
+ return buildGradleProject(buildInfo, target);
+}
diff --git a/packages/flutter_tools/lib/src/commands/build_apk.dart b/packages/flutter_tools/lib/src/commands/build_apk.dart
index 2041908..fc9d97f 100644
--- a/packages/flutter_tools/lib/src/commands/build_apk.dart
+++ b/packages/flutter_tools/lib/src/commands/build_apk.dart
@@ -4,31 +4,9 @@
import 'dart:async';
-import 'package:meta/meta.dart' show required;
-
-import '../android/android_sdk.dart';
-import '../android/gradle.dart';
-import '../base/common.dart';
-import '../build_info.dart';
-import '../globals.dart';
+import '../android/apk.dart';
import 'build.dart';
-export '../android/android_device.dart' show AndroidDevice;
-
-class ApkKeystoreInfo {
- ApkKeystoreInfo({
- @required this.keystore,
- this.password,
- this.keyAlias,
- @required this.keyPassword,
- }) : assert(keystore != null);
-
- final String keystore;
- final String password;
- final String keyAlias;
- final String keyPassword;
-}
-
class BuildApkCommand extends BuildSubCommand {
BuildApkCommand() {
usesTargetOption();
@@ -53,29 +31,3 @@
await buildApk(buildInfo: getBuildInfo(), target: targetFile);
}
}
-
-Future<Null> buildApk({
- String target,
- BuildInfo buildInfo: BuildInfo.debug
-}) async {
- if (!isProjectUsingGradle()) {
- throwToolExit(
- 'The build process for Android has changed, and the current project configuration\n'
- 'is no longer valid. Please consult\n\n'
- ' https://github.com/flutter/flutter/wiki/Upgrading-Flutter-projects-to-build-with-gradle\n\n'
- 'for details on how to upgrade the project.'
- );
- }
-
- // Validate that we can find an android sdk.
- if (androidSdk == null)
- throwToolExit('No Android SDK found. Try setting the ANDROID_HOME environment variable.');
-
- final List<String> validationResult = androidSdk.validateSdkWellFormed();
- if (validationResult.isNotEmpty) {
- validationResult.forEach(printError);
- throwToolExit('Try re-installing or updating your Android SDK.');
- }
-
- return buildGradleProject(buildInfo, target);
-}
diff --git a/packages/flutter_tools/lib/src/commands/trace.dart b/packages/flutter_tools/lib/src/commands/trace.dart
index 33e88ed..045200d 100644
--- a/packages/flutter_tools/lib/src/commands/trace.dart
+++ b/packages/flutter_tools/lib/src/commands/trace.dart
@@ -8,16 +8,10 @@
import '../base/common.dart';
import '../base/file_system.dart';
import '../base/utils.dart';
-import '../build_info.dart';
import '../cache.dart';
import '../globals.dart';
import '../runner/flutter_command.dart';
-import '../vmservice.dart';
-
-// Names of some of the Timeline events we care about.
-const String kFlutterEngineMainEnterEventName = 'FlutterEngineMainEnter';
-const String kFrameworkInitEventName = 'Framework initialization';
-const String kFirstUsefulFrameEventName = 'Widgets completed first useful frame';
+import '../tracing.dart';
class TraceCommand extends FlutterCommand {
TraceCommand() {
@@ -93,119 +87,3 @@
printStatus('Trace file saved to ${localFile.path}');
}
}
-
-class Tracing {
- Tracing(this.vmService);
-
- static Future<Tracing> connect(Uri uri) async {
- final VMService observatory = await VMService.connect(uri);
- return new Tracing(observatory);
- }
-
- final VMService vmService;
-
- Future<Null> startTracing() async {
- await vmService.vm.setVMTimelineFlags(<String>['Compiler', 'Dart', 'Embedder', 'GC']);
- await vmService.vm.clearVMTimeline();
- }
-
- /// Stops tracing; optionally wait for first frame.
- Future<Map<String, dynamic>> stopTracingAndDownloadTimeline({
- bool waitForFirstFrame: false
- }) async {
- Map<String, dynamic> timeline;
-
- if (!waitForFirstFrame) {
- // Stop tracing immediately and get the timeline
- await vmService.vm.setVMTimelineFlags(<String>[]);
- timeline = await vmService.vm.getVMTimeline();
- } else {
- final Completer<Null> whenFirstFrameRendered = new Completer<Null>();
-
- vmService.onTimelineEvent.listen((ServiceEvent timelineEvent) {
- final List<Map<String, dynamic>> events = timelineEvent.timelineEvents;
- for (Map<String, dynamic> event in events) {
- if (event['name'] == kFirstUsefulFrameEventName)
- whenFirstFrameRendered.complete();
- }
- });
-
- await whenFirstFrameRendered.future.timeout(
- const Duration(seconds: 10),
- onTimeout: () {
- printError(
- 'Timed out waiting for the first frame event. Either the '
- 'application failed to start, or the event was missed because '
- '"flutter run" took too long to subscribe to timeline events.'
- );
- return null;
- }
- );
-
- timeline = await vmService.vm.getVMTimeline();
-
- await vmService.vm.setVMTimelineFlags(<String>[]);
- }
-
- return timeline;
- }
-}
-
-/// Download the startup trace information from the given observatory client and
-/// store it to build/start_up_info.json.
-Future<Null> downloadStartupTrace(VMService observatory) async {
- final String traceInfoFilePath = fs.path.join(getBuildDirectory(), 'start_up_info.json');
- final File traceInfoFile = fs.file(traceInfoFilePath);
-
- // Delete old startup data, if any.
- if (await traceInfoFile.exists())
- await traceInfoFile.delete();
-
- // Create "build" directory, if missing.
- if (!(await traceInfoFile.parent.exists()))
- await traceInfoFile.parent.create();
-
- final Tracing tracing = new Tracing(observatory);
-
- final Map<String, dynamic> timeline = await tracing.stopTracingAndDownloadTimeline(
- waitForFirstFrame: true
- );
-
- int extractInstantEventTimestamp(String eventName) {
- final List<Map<String, dynamic>> events = timeline['traceEvents'];
- final Map<String, dynamic> event = events.firstWhere(
- (Map<String, dynamic> event) => event['name'] == eventName, orElse: () => null
- );
- return event == null ? null : event['ts'];
- }
-
- final int engineEnterTimestampMicros = extractInstantEventTimestamp(kFlutterEngineMainEnterEventName);
- final int frameworkInitTimestampMicros = extractInstantEventTimestamp(kFrameworkInitEventName);
- final int firstFrameTimestampMicros = extractInstantEventTimestamp(kFirstUsefulFrameEventName);
-
- if (engineEnterTimestampMicros == null) {
- printTrace('Engine start event is missing in the timeline: $timeline');
- throw 'Engine start event is missing in the timeline. Cannot compute startup time.';
- }
-
- if (firstFrameTimestampMicros == null) {
- printTrace('First frame event is missing in the timeline: $timeline');
- throw 'First frame event is missing in the timeline. Cannot compute startup time.';
- }
-
- final int timeToFirstFrameMicros = firstFrameTimestampMicros - engineEnterTimestampMicros;
- final Map<String, dynamic> traceInfo = <String, dynamic>{
- 'engineEnterTimestampMicros': engineEnterTimestampMicros,
- 'timeToFirstFrameMicros': timeToFirstFrameMicros,
- };
-
- if (frameworkInitTimestampMicros != null) {
- traceInfo['timeToFrameworkInitMicros'] = frameworkInitTimestampMicros - engineEnterTimestampMicros;
- traceInfo['timeAfterFrameworkInitMicros'] = firstFrameTimestampMicros - frameworkInitTimestampMicros;
- }
-
- traceInfoFile.writeAsStringSync(toPrettyJson(traceInfo));
-
- printStatus('Time to first frame: ${timeToFirstFrameMicros ~/ 1000}ms.');
- printStatus('Saved startup trace info in ${traceInfoFile.path}.');
-}
diff --git a/packages/flutter_tools/lib/src/run_cold.dart b/packages/flutter_tools/lib/src/run_cold.dart
index 76d8bd6..713d61c 100644
--- a/packages/flutter_tools/lib/src/run_cold.dart
+++ b/packages/flutter_tools/lib/src/run_cold.dart
@@ -7,10 +7,10 @@
import 'package:meta/meta.dart';
import 'base/file_system.dart';
-import 'commands/trace.dart';
import 'device.dart';
import 'globals.dart';
import 'resident_runner.dart';
+import 'tracing.dart';
class ColdRunner extends ResidentRunner {
ColdRunner(
diff --git a/packages/flutter_tools/lib/src/tracing.dart b/packages/flutter_tools/lib/src/tracing.dart
new file mode 100644
index 0000000..e2e258e
--- /dev/null
+++ b/packages/flutter_tools/lib/src/tracing.dart
@@ -0,0 +1,132 @@
+// 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 'base/file_system.dart';
+import 'base/utils.dart';
+import 'build_info.dart';
+import 'globals.dart';
+import 'vmservice.dart';
+
+// Names of some of the Timeline events we care about.
+const String _kFlutterEngineMainEnterEventName = 'FlutterEngineMainEnter';
+const String _kFrameworkInitEventName = 'Framework initialization';
+const String _kFirstUsefulFrameEventName = 'Widgets completed first useful frame';
+
+class Tracing {
+ Tracing(this.vmService);
+
+ static Future<Tracing> connect(Uri uri) async {
+ final VMService observatory = await VMService.connect(uri);
+ return new Tracing(observatory);
+ }
+
+ final VMService vmService;
+
+ Future<Null> startTracing() async {
+ await vmService.vm.setVMTimelineFlags(<String>['Compiler', 'Dart', 'Embedder', 'GC']);
+ await vmService.vm.clearVMTimeline();
+ }
+
+ /// Stops tracing; optionally wait for first frame.
+ Future<Map<String, dynamic>> stopTracingAndDownloadTimeline({
+ bool waitForFirstFrame: false
+ }) async {
+ Map<String, dynamic> timeline;
+
+ if (!waitForFirstFrame) {
+ // Stop tracing immediately and get the timeline
+ await vmService.vm.setVMTimelineFlags(<String>[]);
+ timeline = await vmService.vm.getVMTimeline();
+ } else {
+ final Completer<Null> whenFirstFrameRendered = new Completer<Null>();
+
+ vmService.onTimelineEvent.listen((ServiceEvent timelineEvent) {
+ final List<Map<String, dynamic>> events = timelineEvent.timelineEvents;
+ for (Map<String, dynamic> event in events) {
+ if (event['name'] == _kFirstUsefulFrameEventName)
+ whenFirstFrameRendered.complete();
+ }
+ });
+
+ await whenFirstFrameRendered.future.timeout(
+ const Duration(seconds: 10),
+ onTimeout: () {
+ printError(
+ 'Timed out waiting for the first frame event. Either the '
+ 'application failed to start, or the event was missed because '
+ '"flutter run" took too long to subscribe to timeline events.'
+ );
+ return null;
+ }
+ );
+
+ timeline = await vmService.vm.getVMTimeline();
+
+ await vmService.vm.setVMTimelineFlags(<String>[]);
+ }
+
+ return timeline;
+ }
+}
+
+/// Download the startup trace information from the given observatory client and
+/// store it to build/start_up_info.json.
+Future<Null> downloadStartupTrace(VMService observatory) async {
+ final String traceInfoFilePath = fs.path.join(getBuildDirectory(), 'start_up_info.json');
+ final File traceInfoFile = fs.file(traceInfoFilePath);
+
+ // Delete old startup data, if any.
+ if (await traceInfoFile.exists())
+ await traceInfoFile.delete();
+
+ // Create "build" directory, if missing.
+ if (!(await traceInfoFile.parent.exists()))
+ await traceInfoFile.parent.create();
+
+ final Tracing tracing = new Tracing(observatory);
+
+ final Map<String, dynamic> timeline = await tracing.stopTracingAndDownloadTimeline(
+ waitForFirstFrame: true
+ );
+
+ int extractInstantEventTimestamp(String eventName) {
+ final List<Map<String, dynamic>> events = timeline['traceEvents'];
+ final Map<String, dynamic> event = events.firstWhere(
+ (Map<String, dynamic> event) => event['name'] == eventName, orElse: () => null
+ );
+ return event == null ? null : event['ts'];
+ }
+
+ final int engineEnterTimestampMicros = extractInstantEventTimestamp(_kFlutterEngineMainEnterEventName);
+ final int frameworkInitTimestampMicros = extractInstantEventTimestamp(_kFrameworkInitEventName);
+ final int firstFrameTimestampMicros = extractInstantEventTimestamp(_kFirstUsefulFrameEventName);
+
+ if (engineEnterTimestampMicros == null) {
+ printTrace('Engine start event is missing in the timeline: $timeline');
+ throw 'Engine start event is missing in the timeline. Cannot compute startup time.';
+ }
+
+ if (firstFrameTimestampMicros == null) {
+ printTrace('First frame event is missing in the timeline: $timeline');
+ throw 'First frame event is missing in the timeline. Cannot compute startup time.';
+ }
+
+ final int timeToFirstFrameMicros = firstFrameTimestampMicros - engineEnterTimestampMicros;
+ final Map<String, dynamic> traceInfo = <String, dynamic>{
+ 'engineEnterTimestampMicros': engineEnterTimestampMicros,
+ 'timeToFirstFrameMicros': timeToFirstFrameMicros,
+ };
+
+ if (frameworkInitTimestampMicros != null) {
+ traceInfo['timeToFrameworkInitMicros'] = frameworkInitTimestampMicros - engineEnterTimestampMicros;
+ traceInfo['timeAfterFrameworkInitMicros'] = firstFrameTimestampMicros - frameworkInitTimestampMicros;
+ }
+
+ traceInfoFile.writeAsStringSync(toPrettyJson(traceInfo));
+
+ printStatus('Time to first frame: ${timeToFirstFrameMicros ~/ 1000}ms.');
+ printStatus('Saved startup trace info in ${traceInfoFile.path}.');
+}
diff --git a/packages/flutter_tools/test/crash_reporting_test.dart b/packages/flutter_tools/test/crash_reporting_test.dart
index c7aa531..2416d25 100644
--- a/packages/flutter_tools/test/crash_reporting_test.dart
+++ b/packages/flutter_tools/test/crash_reporting_test.dart
@@ -13,7 +13,7 @@
import 'package:http/testing.dart';
import 'package:test/test.dart';
-import 'package:flutter_tools/executable.dart' as tools;
+import 'package:flutter_tools/runner.dart' as tools;
import 'package:flutter_tools/src/base/context.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart';
diff --git a/packages/flutter_tools/test/replay/common.dart b/packages/flutter_tools/test/replay/common.dart
index b9159e3..2467652 100644
--- a/packages/flutter_tools/test/replay/common.dart
+++ b/packages/flutter_tools/test/replay/common.dart
@@ -5,7 +5,7 @@
import 'dart:async';
import 'package:file/local.dart';
-import 'package:flutter_tools/executable.dart' as tools;
+import 'package:flutter_tools/runner.dart' as tools;
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/base/context.dart';
import 'package:flutter_tools/src/base/io.dart' as io;