bc7554107b
Changes: ``` > git log --format="%C(auto) %h %s" dcb6aba..8f5ab7b https://dart.googlesource.com/pub.git/+/8f5ab7b1 Avoid raceconditions in `global activate`, `run` and `global run` (#3285) https://dart.googlesource.com/pub.git/+/6e03ec02 Support multiple packages in 'dart pub add' (#3283) https://dart.googlesource.com/pub.git/+/1a298f4d Fix --verbose doc, and accept verboseness setting from embedder (#3279) https://dart.googlesource.com/pub.git/+/5bda798c Avoid race condition for hosted source on package extraction (#3277) https://dart.googlesource.com/pub.git/+/17c5addb Error on acquireDependencies when the root pubspec has an unknown sdk in the environment. (#3271) https://dart.googlesource.com/pub.git/+/0a32ae4a folder/* should not ignore the folder itself, only the contents (#3267) https://dart.googlesource.com/pub.git/+/44b785aa Normalize file names for tar.gz (#3268) https://dart.googlesource.com/pub.git/+/2fd6b5f0 Fix repository specification: isRetracted -> retracted (#3249) https://dart.googlesource.com/pub.git/+/0fc71db4 Write log trace to $PUB_CACHE/log/pub_log.txt when crashing (#3240) ``` Diff: https://dart.googlesource.com/pub.git/+/dcb6abac2d7d43258c03b348be42bf4aab9529b1~..8f5ab7b1aba3b9f66b56246d77e167990339d317/ Change-Id: I54f1fde0f50ca19ca036bb1e6d9b166455031183 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/229542 Reviewed-by: Jonas Jensen <jonasfj@google.com> Commit-Queue: Sigurd Meldgaard <sigurdm@google.com>
305 lines
10 KiB
Dart
305 lines
10 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:usage/usage.dart';
|
|
|
|
import 'src/analytics.dart';
|
|
import 'src/commands/analyze.dart';
|
|
import 'src/commands/compile.dart';
|
|
import 'src/commands/create.dart';
|
|
import 'src/commands/debug_adapter.dart';
|
|
import 'src/commands/devtools.dart';
|
|
import 'src/commands/doc.dart';
|
|
import 'src/commands/fix.dart';
|
|
import 'src/commands/language_server.dart';
|
|
import 'src/commands/migrate.dart';
|
|
import 'src/commands/run.dart';
|
|
import 'src/commands/test.dart';
|
|
import 'src/core.dart';
|
|
import 'src/events.dart';
|
|
import 'src/experiments.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 {
|
|
VmInteropHandler.initialize(port);
|
|
|
|
// TODO(sigurdm): Remove when top-level pub is removed.
|
|
if (args[0] == '__deprecated_pub') {
|
|
// This is the entry-point supporting the top-level `pub` script.
|
|
// ignore: deprecated_member_use
|
|
VmInteropHandler.exit(await deprecatedpubCommand().run(args.skip(1)));
|
|
return;
|
|
}
|
|
|
|
if (args.contains('run')) {
|
|
// These flags have a format that can't be handled by package:args, so while
|
|
// they are valid flags we'll assume the VM has verified them by this point.
|
|
args = args
|
|
.where(
|
|
(element) => !(element.contains('--observe') ||
|
|
element.contains('--enable-vm-service') ||
|
|
element.contains('--devtools')),
|
|
)
|
|
.toList();
|
|
}
|
|
|
|
// Finally, call the runner to execute the command; see DartdevRunner.
|
|
|
|
final runner = DartdevRunner(args);
|
|
int? exitCode = 1;
|
|
try {
|
|
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;
|
|
} finally {
|
|
VmInteropHandler.exit(exitCode);
|
|
}
|
|
}
|
|
|
|
class DartdevRunner extends CommandRunner<int> {
|
|
static const String dartdevDescription =
|
|
'A command-line utility for Dart development';
|
|
|
|
@override
|
|
final ArgParser argParser = ArgParser(
|
|
usageLineLength: dartdevUsageLineLength,
|
|
allowTrailingOptions: false,
|
|
);
|
|
|
|
final bool verbose;
|
|
|
|
late Analytics _analytics;
|
|
|
|
DartdevRunner(List<String> args)
|
|
: verbose = args.contains('-v') || args.contains('--verbose'),
|
|
super('dart', '$dartdevDescription.') {
|
|
argParser.addFlag('verbose',
|
|
abbr: 'v', negatable: false, help: 'Show additional command output.');
|
|
argParser.addFlag('version',
|
|
negatable: false, help: 'Print the Dart SDK version.');
|
|
argParser.addFlag('enable-analytics',
|
|
negatable: false, help: 'Enable analytics.');
|
|
argParser.addFlag('disable-analytics',
|
|
negatable: false, help: 'Disable analytics.');
|
|
|
|
argParser.addFlag('diagnostics',
|
|
negatable: false, help: 'Show tool diagnostic output.', hide: !verbose);
|
|
|
|
argParser.addFlag(
|
|
'analytics',
|
|
negatable: true,
|
|
help: 'Disable analytics for this `dart *` run',
|
|
hide: true,
|
|
);
|
|
|
|
addCommand(AnalyzeCommand(verbose: verbose));
|
|
addCommand(CreateCommand(verbose: verbose));
|
|
addCommand(DebugAdapterCommand(verbose: verbose));
|
|
addCommand(CompileCommand(verbose: verbose));
|
|
addCommand(DocCommand(verbose: verbose));
|
|
addCommand(DevToolsCommand(
|
|
verbose: verbose,
|
|
));
|
|
addCommand(FixCommand(verbose: verbose));
|
|
addCommand(FormatCommand(verbose: verbose));
|
|
addCommand(LanguageServerCommand(verbose: verbose));
|
|
addCommand(MigrateCommand(verbose: verbose));
|
|
addCommand(
|
|
pubCommand(
|
|
analytics: PubAnalytics(
|
|
() => analytics,
|
|
dependencyKindCustomDimensionName: dependencyKindCustomDimensionName,
|
|
),
|
|
isVerbose: () => verbose,
|
|
),
|
|
);
|
|
addCommand(RunCommand(verbose: verbose));
|
|
addCommand(TestCommand());
|
|
}
|
|
|
|
@visibleForTesting
|
|
Analytics get analytics => _analytics;
|
|
|
|
@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();
|
|
// The Analytics instance used to report information back to Google Analytics;
|
|
// see lib/src/analytics.dart.
|
|
_analytics = createAnalyticsInstance(
|
|
topLevelResults.wasParsed('analytics')
|
|
? !topLevelResults['analytics']
|
|
: false,
|
|
);
|
|
|
|
// If we have not printed the analyticsNoticeOnFirstRunMessage to stdout,
|
|
// the user is on a terminal, and the machine is not a bot, then print the
|
|
// disclosure and set analytics.disclosureShownOnTerminal to true.
|
|
if (analytics is DartdevAnalytics &&
|
|
!(analytics as DartdevAnalytics).disclosureShownOnTerminal &&
|
|
io.stdout.hasTerminal &&
|
|
!isBot()) {
|
|
print(analyticsNoticeOnFirstRunMessage);
|
|
(analytics as DartdevAnalytics).disclosureShownOnTerminal = true;
|
|
}
|
|
|
|
// When `--disable-analytics` or `--enable-analytics` are called we perform
|
|
// the respective intention and print any notices to standard out and exit.
|
|
if (topLevelResults['disable-analytics']) {
|
|
// This block also potentially catches the case of (disableAnalytics &&
|
|
// enableAnalytics), in which we favor the disabling of analytics.
|
|
analytics.enabled = false;
|
|
|
|
// Alert the user that analytics has been disabled.
|
|
print(analyticsDisabledNoticeMessage);
|
|
return 0;
|
|
} else if (topLevelResults['enable-analytics']) {
|
|
analytics.enabled = true;
|
|
|
|
// Alert the user again that data will be collected.
|
|
print(analyticsNoticeOnFirstRunMessage);
|
|
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;
|
|
}
|
|
}
|
|
|
|
final Ansi ansi = Ansi(Ansi.terminalSupportsAnsi);
|
|
log = topLevelResults['diagnostics']
|
|
? Logger.verbose(ansi: ansi)
|
|
: Logger.standard(ansi: ansi);
|
|
|
|
var command = topLevelResults.command;
|
|
final commandNames = [];
|
|
while (command != null) {
|
|
commandNames.add(command.name);
|
|
if (command.command == null) break;
|
|
command = command.command;
|
|
}
|
|
|
|
final path = commandNames.join('/');
|
|
// Send the screen view to analytics
|
|
unawaited(
|
|
analytics.sendScreenView(path, parameters:
|
|
// Starts a new analytics session.
|
|
// https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters#sc
|
|
{'sc': 'start'}),
|
|
);
|
|
|
|
// 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.
|
|
Object? exception;
|
|
StackTrace? stackTrace;
|
|
try {
|
|
exitCode = await super.runCommand(topLevelResults);
|
|
|
|
if (analytics.enabled) {
|
|
// Send the event to analytics
|
|
unawaited(
|
|
sendUsageEvent(
|
|
analytics,
|
|
path,
|
|
exitCode: exitCode,
|
|
commandFlags:
|
|
// This finds the options that where explicitly given to the command
|
|
// (and not for an eventual subcommand) without including the actual
|
|
// value.
|
|
//
|
|
// Note that this will also conflate short-options and long-options.
|
|
command?.options.where(command.wasParsed).toList() ??
|
|
const <String>[],
|
|
specifiedExperiments: topLevelResults.enabledExperiments,
|
|
),
|
|
);
|
|
}
|
|
} on UsageException catch (e) {
|
|
io.stderr.writeln('$e');
|
|
exitCode = 64;
|
|
} catch (e, st) {
|
|
// Set the exception and stack trace only for non-UsageException cases:
|
|
exception = e;
|
|
stackTrace = st;
|
|
io.stderr.writeln('$e');
|
|
io.stderr.writeln('$st');
|
|
exitCode = 1;
|
|
} finally {
|
|
stopwatch.stop();
|
|
|
|
if (analytics.enabled) {
|
|
unawaited(
|
|
analytics.sendTiming(
|
|
path,
|
|
stopwatch.elapsedMilliseconds,
|
|
category: 'commands',
|
|
),
|
|
);
|
|
}
|
|
|
|
// Set the exitCode, if it wasn't set in the catch block above.
|
|
exitCode ??= 0;
|
|
|
|
// Send analytics before exiting
|
|
if (analytics.enabled) {
|
|
// And now send the exceptions and events to Google Analytics:
|
|
if (exception != null) {
|
|
unawaited(
|
|
analytics.sendException(
|
|
'${exception.runtimeType}\n${sanitizeStacktrace(stackTrace)}',
|
|
fatal: true),
|
|
);
|
|
}
|
|
|
|
await analytics.waitForLastPing(
|
|
timeout: const Duration(milliseconds: 200));
|
|
}
|
|
|
|
// Set the enabled flag in the analytics object to true. Note: this will not
|
|
// enable the analytics unless the disclosure was shown (terminal detected),
|
|
// and the machine is not detected to be a bot.
|
|
if (analytics.firstRun) {
|
|
analytics.enabled = true;
|
|
}
|
|
analytics.close();
|
|
}
|
|
|
|
return exitCode;
|
|
}
|
|
}
|