7ab10ef0a4
Add an experimental data-assets flag to enable data assets in hooks. Change-Id: I0df53c225638414db625a0433498ef2c10d31d96 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/450781 Commit-Queue: Moritz Sümmermann <mosum@google.com> Reviewed-by: Johnni Winther <johnniwinther@google.com>
297 lines
11 KiB
Dart
297 lines
11 KiB
Dart
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
// Do not call exit() directly. Use VmInteropHandler.exit() instead.
|
|
import 'dart:async';
|
|
import 'dart:io' as io hide exit;
|
|
import 'dart:isolate';
|
|
|
|
import 'package:args/args.dart';
|
|
import 'package:args/command_runner.dart';
|
|
import 'package:cli_util/cli_logging.dart';
|
|
import 'package:dart_style/src/cli/format_command.dart';
|
|
import 'package:meta/meta.dart';
|
|
import 'package:pub/pub.dart';
|
|
import 'package:unified_analytics/unified_analytics.dart';
|
|
|
|
import 'src/commands/analyze.dart';
|
|
import 'src/commands/build.dart';
|
|
import 'src/commands/compilation_server.dart';
|
|
import 'src/commands/compile.dart';
|
|
import 'src/commands/create.dart';
|
|
import 'src/commands/dart_mcp_server.dart';
|
|
import 'src/commands/debug_adapter.dart';
|
|
import 'src/commands/development_service.dart';
|
|
import 'src/commands/devtools.dart';
|
|
import 'src/commands/doc.dart';
|
|
import 'src/commands/fix.dart';
|
|
import 'src/commands/info.dart';
|
|
import 'src/commands/install.dart';
|
|
import 'src/commands/installed.dart';
|
|
import 'src/commands/language_server.dart';
|
|
import 'src/commands/run.dart';
|
|
import 'src/commands/test.dart';
|
|
import 'src/commands/tooling_daemon.dart';
|
|
import 'src/commands/uninstall.dart';
|
|
import 'src/core.dart';
|
|
import 'src/experiments.dart';
|
|
import 'src/unified_analytics.dart';
|
|
import 'src/utils.dart';
|
|
import 'src/vm_interop_handler.dart';
|
|
|
|
/// This is typically called from bin/, but given the length of the method and
|
|
/// analytics logic, it has been moved here.
|
|
Future<void> runDartdev(List<String> args, SendPort? port) async {
|
|
int? exitCode = 1;
|
|
try {
|
|
VmInteropHandler.initialize(port);
|
|
// Call the runner to execute the command; see DartdevRunner.
|
|
final runner = DartdevRunner(args, vmArgs: io.Platform.executableArguments);
|
|
exitCode = await runner.run(args);
|
|
} on UsageException catch (e) {
|
|
// TODO(sigurdm): It is unclear when a UsageException gets to here, and
|
|
// when it is in DartdevRunner.runCommand.
|
|
io.stderr.writeln('$e');
|
|
exitCode = 64;
|
|
} catch (e, st) {
|
|
// Unexpected error encountered.
|
|
io.stderr.writeln('An unexpected error was encountered by the Dart CLI.');
|
|
io.stderr.writeln('Please file an issue at '
|
|
'https://github.com/dart-lang/sdk/issues/new with the following '
|
|
'details:\n');
|
|
io.stderr.writeln("Invocation: 'dart ${args.join(' ')}'");
|
|
io.stderr.writeln("Exception: '$e'");
|
|
io.stderr.writeln('Stack Trace:');
|
|
io.stderr.writeln(st.toString());
|
|
exitCode = 255;
|
|
} finally {
|
|
VmInteropHandler.exit(exitCode);
|
|
}
|
|
}
|
|
|
|
class DartdevRunner extends CommandRunner<int> {
|
|
static const String dartdevDescription =
|
|
'A command-line utility for Dart development';
|
|
|
|
@override
|
|
final ArgParser argParser;
|
|
|
|
final bool verbose;
|
|
|
|
final List<String> vmEnabledExperiments;
|
|
|
|
Analytics? _unifiedAnalytics;
|
|
final bool _isAnalyticsTest;
|
|
|
|
DartdevRunner(
|
|
List<String> args, {
|
|
Analytics? analyticsOverride,
|
|
bool isAnalyticsTest = false,
|
|
List<String> vmArgs = const [],
|
|
}) : verbose = args.contains('-v') || args.contains('--verbose'),
|
|
argParser = globalDartdevOptionsParser(
|
|
verbose: args.contains('-v') || args.contains('--verbose')),
|
|
vmEnabledExperiments = parseVmEnabledExperiments(vmArgs),
|
|
_unifiedAnalytics = analyticsOverride,
|
|
_isAnalyticsTest = isAnalyticsTest,
|
|
super('dart', '$dartdevDescription.') {
|
|
// The list of commands should be kept in sync with
|
|
// `DartDevIsolate::ShouldParseCommand` in `runtime/bin/dartdev_isolate.cc`.
|
|
addCommand(AnalyzeCommand(verbose: verbose));
|
|
addCommand(CompilationServerCommand(verbose: verbose));
|
|
final nativeAssetsExperimentEnabled =
|
|
nativeAssetsEnabled(vmEnabledExperiments);
|
|
final dataAssetsExperimentEnabled = dataAssetsEnabled(vmEnabledExperiments);
|
|
if (nativeAssetsExperimentEnabled) {
|
|
final recordUseExperimentEnabled = recordUseEnabled(vmEnabledExperiments);
|
|
addCommand(BuildCommand(
|
|
verbose: verbose,
|
|
recordUseEnabled: recordUseExperimentEnabled,
|
|
dataAssetsExperimentEnabled: dataAssetsExperimentEnabled));
|
|
}
|
|
addCommand(CompileCommand(
|
|
verbose: verbose,
|
|
nativeAssetsExperimentEnabled: nativeAssetsExperimentEnabled,
|
|
));
|
|
addCommand(CreateCommand(verbose: verbose));
|
|
addCommand(DebugAdapterCommand(verbose: verbose));
|
|
addCommand(DevelopmentServiceCommand(verbose: verbose));
|
|
addCommand(DevToolsCommand(verbose: verbose));
|
|
addCommand(DocCommand(verbose: verbose));
|
|
addCommand(FixCommand(verbose: verbose));
|
|
addCommand(FormatCommand(
|
|
verbose: verbose,
|
|
category: CommandCategory.sourceCode.name,
|
|
));
|
|
addCommand(InfoCommand(verbose: verbose));
|
|
addCommand(LanguageServerCommand(verbose: verbose));
|
|
addCommand(DartMCPServerCommand(verbose: verbose));
|
|
addCommand(pubCommand(
|
|
isVerbose: () => verbose,
|
|
category: CommandCategory.project.name,
|
|
));
|
|
addCommand(RunCommand(
|
|
verbose: verbose,
|
|
nativeAssetsExperimentEnabled: nativeAssetsExperimentEnabled,
|
|
dataAssetsExperimentEnabled: dataAssetsExperimentEnabled,
|
|
));
|
|
addCommand(TestCommand(
|
|
nativeAssetsExperimentEnabled: nativeAssetsExperimentEnabled,
|
|
dataAssetsExperimentEnabled: dataAssetsExperimentEnabled));
|
|
addCommand(ToolingDaemonCommand(verbose: verbose));
|
|
if (nativeAssetsExperimentEnabled) {
|
|
addCommand(InstallCommand(verbose: verbose));
|
|
addCommand(InstalledCommand(verbose: verbose));
|
|
addCommand(UninstallCommand(verbose: verbose));
|
|
}
|
|
}
|
|
|
|
@visibleForTesting
|
|
Analytics get unifiedAnalytics => _unifiedAnalytics!;
|
|
|
|
@override
|
|
String get invocation =>
|
|
'dart ${verbose ? '[vm-options] ' : ''}<command|dart-file> [arguments]';
|
|
|
|
@override
|
|
String get usageFooter =>
|
|
'See https://dart.dev/tools/dart-tool for detailed documentation.';
|
|
|
|
@override
|
|
Future<int> runCommand(ArgResults topLevelResults) async {
|
|
final stopwatch = Stopwatch()..start();
|
|
|
|
// We don't want to run analytics when we're running in a CI environment
|
|
// unless we're explicitly testing analytics for dartdev.
|
|
final implicitlySuppressAnalytics = isBot() && !_isAnalyticsTest;
|
|
bool suppressAnalytics = !topLevelResults.flag('analytics') ||
|
|
topLevelResults.flag('suppress-analytics') ||
|
|
implicitlySuppressAnalytics;
|
|
|
|
if (topLevelResults.wasParsed('analytics')) {
|
|
io.stderr.writeln(
|
|
'`--[no-]analytics` is deprecated. Use `--suppress-analytics` '
|
|
'to disable analytics for one run instead.');
|
|
}
|
|
final enableAnalytics = topLevelResults.flag('enable-analytics');
|
|
final disableAnalytics = topLevelResults.flag('disable-analytics');
|
|
|
|
if (!implicitlySuppressAnalytics &&
|
|
suppressAnalytics &&
|
|
(enableAnalytics || disableAnalytics)) {
|
|
// This isn't an error if we're implicitly disabling analytics because
|
|
// we're running in a CI environment.
|
|
io.stderr.writeln('`--suppress-analytics` cannot be used with either'
|
|
' `--enable-analytics` or `--disable-analytics`.');
|
|
return 254;
|
|
}
|
|
// The Analytics instance used to report information back to Google Analytics;
|
|
// see lib/src/unified_analytics.dart.
|
|
_unifiedAnalytics ??= createUnifiedAnalytics(
|
|
disableAnalytics: suppressAnalytics,
|
|
);
|
|
|
|
// If we have not printed the analytics notification to stdout, the user is
|
|
// on a terminal, and the machine is not a bot, then print the disclosure.
|
|
bool analyticsMessagePrinted = false;
|
|
if (unifiedAnalytics.shouldShowMessage &&
|
|
io.stdout.hasTerminal &&
|
|
!isBot()) {
|
|
print(unifiedAnalytics.getConsentMessage);
|
|
unifiedAnalytics.clientShowedMessage();
|
|
analyticsMessagePrinted = true;
|
|
}
|
|
|
|
// When `--disable-analytics` or `--enable-analytics` are called we perform
|
|
// the respective intention and print any notices to standard out and exit.
|
|
if (disableAnalytics) {
|
|
// Disable sending data via the unified analytics package.
|
|
await unifiedAnalytics.setTelemetry(false);
|
|
await unifiedAnalytics.close();
|
|
|
|
// Alert the user that analytics has been disabled.
|
|
print(analyticsDisabledNoticeMessage);
|
|
return 0;
|
|
} else if (enableAnalytics) {
|
|
// Enable sending data via the unified analytics package.
|
|
await unifiedAnalytics.setTelemetry(true);
|
|
await unifiedAnalytics.close();
|
|
|
|
// Alert the user again that data will be collected.
|
|
if (!analyticsMessagePrinted) {
|
|
print(unifiedAnalytics.getConsentMessage);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (topLevelResults.command == null &&
|
|
topLevelResults.arguments.isNotEmpty) {
|
|
final firstArg = topLevelResults.arguments.first;
|
|
// If we make it this far, it means the VM couldn't find the file on disk.
|
|
if (firstArg.endsWith('.dart')) {
|
|
io.stderr.writeln(
|
|
"Error when reading '$firstArg': No such file or directory.");
|
|
// This is the exit code used by the frontend.
|
|
return 254;
|
|
}
|
|
}
|
|
|
|
if (topLevelResults.flag('diagnostics')) {
|
|
log = Logger.verbose(ansi: ansi);
|
|
}
|
|
|
|
late final List<String> experimentErrors =
|
|
validateExperiments(vmEnabledExperiments);
|
|
if (experimentErrors.isNotEmpty) {
|
|
experimentErrors.forEach(io.stderr.writeln);
|
|
return 254;
|
|
}
|
|
|
|
var command = topLevelResults.command;
|
|
final commandNames = [];
|
|
while (command != null) {
|
|
commandNames.add(command.name);
|
|
if (command.command == null) break;
|
|
command = command.command;
|
|
}
|
|
|
|
// The exit code for the dartdev process; null indicates that it has not been
|
|
// set yet. The value is set in the catch and finally blocks below.
|
|
int? exitCode;
|
|
|
|
// Any caught non-UsageExceptions when running the sub command.
|
|
try {
|
|
exitCode = await super.runCommand(topLevelResults);
|
|
if (unifiedAnalytics.telemetryEnabled) {
|
|
// Send the event to analytics
|
|
final path = commandNames.join('/');
|
|
final experiments = topLevelResults.enabledExperiments
|
|
..sort((a, b) => a.compareTo(b));
|
|
unifiedAnalytics.send(
|
|
Event.dartCliCommandExecuted(
|
|
name: path,
|
|
enabledExperiments: experiments.join(','),
|
|
),
|
|
);
|
|
}
|
|
} on UsageException catch (e) {
|
|
io.stderr.writeln('$e');
|
|
exitCode = 64;
|
|
} catch (e, st) {
|
|
// Set the exception and stack trace only for non-UsageException cases:
|
|
io.stderr.writeln('$e');
|
|
io.stderr.writeln('$st');
|
|
exitCode = 1;
|
|
} finally {
|
|
stopwatch.stop();
|
|
|
|
// Set the exitCode, if it wasn't set in the catch block above.
|
|
exitCode ??= 0;
|
|
await unifiedAnalytics.close();
|
|
}
|
|
|
|
return exitCode;
|
|
}
|
|
}
|