Files
sdk/runtime/bin/dartdev_options.cc
T
asiva 5399dbf6f6 [VM/dartdev] Switch dartdev to use an AOT runtime.
- split the Dart CLI tool out of the VM into it's own embedder which
  runs in AOT mode. The pure Dart VM executable is called 'dartvm' and
  has no Dart CLI functionality in it
- the Dart CLI executable parses the CLI commands and invokes the rest
  of the AOT tools in the same process, for the 'run' and 'test'
  commands it execs a process which runs 'dartvm' to run
- 'dart hello.dart' execs the 'dartvm' process and runs 'hello.dart'
- the Dart CLI is not generated for ia32 as we are not shipping a
  Dart SDK for ia32 anymore (support to execute the 'dartvm' for ia32
  architecture is retained)
- the Dart CLI tool is not built in the internal Dart SDK builds

TEST=ci

Some performance improvement numbers
'dart format pkg/dartdev' goes from 1.17 secs to 0.22 secs
'dart doc pkg/dartdev' goes from 100.2 secs to 66.6 secs
'dart fix pkg/dartdev' goes from 19.3 secs to 14.5 secs

Change-Id: I66984a26cb2ab014b34dc1873f1f3d2884e13518
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/364202
Commit-Queue: Ben Konyi <bkonyi@google.com>
Reviewed-by: Ben Konyi <bkonyi@google.com>
2025-07-04 13:22:13 -07:00

379 lines
15 KiB
C++

// Copyright (c) 2025, 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.
#include "bin/dartdev_options.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "bin/common_options.h"
#include "bin/error_exit.h"
#include "bin/file_system_watcher.h"
#include "bin/platform.h"
#include "bin/socket.h"
#include "bin/utils.h"
#include "include/dart_api.h"
#include "platform/assert.h"
#include "platform/globals.h"
#include "platform/hashmap.h"
#include "platform/syslog.h"
namespace dart {
namespace bin {
#if defined(DART_PRECOMPILED_RUNTIME)
static bool PotentialDartdevCommand(const char* script_uri) {
// If script_uri is a known DartDev command, we should not try to run it.
//
// Otherwise if script_uri is not a file path or of a known URI scheme, we
// assume this is a mistyped DartDev command.
//
// This should be kept in sync with the commands in
// `pkg/dartdev/lib/dartdev.dart`.
return (
(strcmp(script_uri, "analyze") == 0) ||
(strcmp(script_uri, "compilation-server") == 0) ||
(strcmp(script_uri, "build") == 0) ||
(strcmp(script_uri, "compile") == 0) ||
(strcmp(script_uri, "create") == 0) ||
(strcmp(script_uri, "development-service") == 0) ||
(strcmp(script_uri, "devtools") == 0) ||
(strcmp(script_uri, "doc") == 0) || (strcmp(script_uri, "fix") == 0) ||
(strcmp(script_uri, "format") == 0) ||
(strcmp(script_uri, "info") == 0) ||
(strcmp(script_uri, "mcp-server") == 0) ||
(strcmp(script_uri, "pub") == 0) || (strcmp(script_uri, "run") == 0) ||
(strcmp(script_uri, "test") == 0) || (strcmp(script_uri, "info") == 0) ||
(strcmp(script_uri, "language-server") == 0) ||
(strcmp(script_uri, "tooling-daemon") == 0) ||
(!File::ExistsUri(nullptr, script_uri) &&
(strncmp(script_uri, "http://", 7) != 0) &&
(strncmp(script_uri, "https://", 8) != 0) &&
(strncmp(script_uri, "file://", 7) != 0) &&
(strncmp(script_uri, "package:", 8) != 0) &&
(strncmp(script_uri, "google3://", 10) != 0)));
}
#define OPTION_FIELD(variable) Options::variable##_
#define STRING_OPTION_DEFINITION(name, variable) \
const char* OPTION_FIELD(variable) = nullptr; \
DEFINE_STRING_OPTION(name, OPTION_FIELD(variable))
STRING_OPTIONS_LIST(STRING_OPTION_DEFINITION)
#undef STRING_OPTION_DEFINITION
#define BOOL_OPTION_DEFINITION(name, variable) \
bool OPTION_FIELD(variable) = false; \
DEFINE_BOOL_OPTION(name, OPTION_FIELD(variable))
BOOL_OPTIONS_LIST(BOOL_OPTION_DEFINITION)
#undef BOOL_OPTION_DEFINITION
#define SHORT_BOOL_OPTION_DEFINITION(short_name, long_name, variable) \
bool OPTION_FIELD(variable) = false; \
DEFINE_BOOL_OPTION_SHORT(short_name, long_name, OPTION_FIELD(variable))
SHORT_BOOL_OPTIONS_LIST(SHORT_BOOL_OPTION_DEFINITION)
#undef SHORT_BOOL_OPTION_DEFINITION
#define CB_OPTION_DEFINITION(callback) \
static bool callback##Helper(const char* arg, CommandLineOptions* o) { \
return Options::callback(arg, o); \
} \
DEFINE_CB_OPTION(callback##Helper)
CB_OPTIONS_LIST(CB_OPTION_DEFINITION)
#undef CB_OPTION_DEFINITION
// Explicitly handle VM flags that can be parsed by DartDev's run command.
bool Options::ProcessVMOptions(const char* arg,
CommandLineOptions* vm_options) {
#define IS_VM_OPTION(name, arg) \
if (OptionProcessor::ProcessOption(arg, name) != nullptr) { \
vm_options->AddArgument(arg); \
return true; \
}
// This is an exhaustive set of VM flags that are accepted by 'dart run' and
// 'dart test' commands, these options need to be collected and passed to
// the dart VM process that is going to run the command.
//
// NOTE: When updating this list of VM flags, be sure to make the corresponding
// changes in pkg/dartdev/lib/src/commands/run.dart.
#define HANDLE_DARTDEV_VM_OPTIONS(V, arg) \
V("--enable-asserts", arg) \
V("--pause-isolates-on-exit", arg) \
V("--no-pause-isolates-on-exit", arg) \
V("--pause-isolates-on-start", arg) \
V("--no-pause-isolates-on-start", arg) \
V("--pause-isolates-on-unhandled-exception", arg) \
V("--no-pause-isolates-on-unhandled-exception", arg) \
V("--warn-on-pause-with-no-debugger", arg) \
V("--no-warn-on-pause-with-no-debugger", arg) \
V("--timeline-streams", arg) \
V("--timeline-recorder", arg) \
V("--dds", arg) \
V("--no-dds", arg) \
V("--profiler", arg) \
V("--disable-service-auth-codes", arg) \
V("--write-service-info", arg) \
V("--enable-service-port-fallback", arg) \
V("--disable-service-auth-codes", arg) \
V("--serve-observatory", arg) \
V("--print-dtd", arg) \
V("--packages", arg) \
V("--resident", arg) \
V("--resident-server-info-file", arg) \
V("--resident-compiler-info-file", arg) \
V("--observe", arg) \
V("--enable-vm-service", arg) \
V("--serve-devtools", arg) \
V("--no-serve-devtools", arg) \
V("--serve-observatory", arg) \
V("--no-serve-observatory", arg) \
V("--profile-microtasks", arg) \
V("--enable-experiment", arg)
HANDLE_DARTDEV_VM_OPTIONS(IS_VM_OPTION, arg);
#undef IS_VM_OPTION
#undef HANDLE_DARTDEV_VM_OPTIONS
return false;
}
bool Options::ParseDartDevArguments(int argc,
char** argv,
CommandLineOptions* vm_options,
CommandLineOptions* dart_vm_options,
CommandLineOptions* dart_options,
bool* skip_dartdev) {
// First figure out if a dartdev command has been explicitly specified.
*skip_dartdev = false;
int tmp_i = 1;
while (tmp_i < argc) {
// Check if this flag is a potentially valid VM flag, we skip over all
// VM flags until we see a command or a script file.
if (!OptionProcessor::IsValidFlag(argv[tmp_i]) &&
!OptionProcessor::IsValidShortFlag(argv[tmp_i])) {
break;
}
tmp_i++;
}
if (tmp_i < argc) {
// Check if we have a dartdev command.
if (!PotentialDartdevCommand(argv[tmp_i])) {
// We don't have a dartdev command so skip dartdev and execute the
// script directly.
*skip_dartdev = true;
return true;
}
}
bool enable_dartdev_analytics = false;
bool disable_dartdev_analytics = false;
char* packages_argument = nullptr;
// First parse out the vm options into dart_vm_options so that it can be
// passed down to the 'run' and 'test' commands.
// Start processing arguments after argv[0] which would be the executable.
int i = 1;
while (i < argc) {
bool skipVmOption = false;
if (!OptionProcessor::TryProcess(argv[i], dart_vm_options)) {
// Check if this flag is a potentially valid VM flag.
if (!OptionProcessor::IsValidFlag(argv[i])) {
break;
}
// The following flags are processed as DartDev flags and are not to
// be treated as if they are VM flags.
if (IsOption(argv[i], "enable-analytics")) {
enable_dartdev_analytics = true;
skipVmOption = true;
} else if (IsOption(argv[i], "disable-analytics")) {
disable_dartdev_analytics = true;
skipVmOption = true;
} else if (IsOption(argv[i], "disable-telemetry")) {
disable_dartdev_analytics = true;
skipVmOption = true;
} else if (IsOption(argv[i], "suppress-analytics")) {
dart_options->AddArgument("--suppress-analytics");
skipVmOption = true;
} else if (IsOption(argv[i], "no-analytics")) {
// Just add this option even if we don't go to dartdev.
// It is irrelevant for the vm.
dart_options->AddArgument("--no-analytics");
skipVmOption = true;
} else if (IsOption(argv[i], "serve-observatory")) {
// This flag is currently set by default in vmservice_io.dart, so we
// ignore it. --no-serve-observatory is a VM flag so we don't need to
// handle that case here.
skipVmOption = true;
} else if (IsOption(argv[i], "print-dtd-uri")) {
skipVmOption = true;
} else if (IsOption(argv[i], "executable-name")) {
skipVmOption = true;
} else if (IsOption(argv[i], "enable-experiment")) {
dart_options->AddArgument(argv[i]);
}
}
if (!skipVmOption) {
dart_vm_options->AddArgument(argv[i]);
}
if (IsOption(argv[i], "packages")) {
packages_argument = argv[i];
}
i++;
}
// The arguments to the VM are at positions 1 through i-1 in argv.
Platform::SetExecutableArguments(i, argv);
// If we have exhausted all the arguments and haven't see a dartdev
// command then we set up some scenarios where it still makes sense
// to start up dartdev and have it process the options.
if (i >= argc) {
// Handles following invocation arguments:
// - dart help
// - dart --help
// - dart
if (((Options::help_option() && !Options::verbose_option()) ||
(argc == 1))) {
// Let DartDev handle the default help message.
dart_options->AddArgument("help");
return true;
}
// Handles cases where only analytics flags are provided. We need to launch
// DartDev for this.
else if (enable_dartdev_analytics || disable_dartdev_analytics) { // NOLINT
// The analytics flags are a special case as we don't have a target script
// or DartDev command but we still want to launch DartDev.
dart_options->AddArgument(enable_dartdev_analytics
? "--enable-analytics"
: "--disable-analytics");
return true;
}
// If it is not '--version' and '--help' we will launch DartDev
// to print its help message and set an error exit code.
else if (!Options::help_option() && !Options::version_option()) { // NOLINT
// Pass in an invalid option so that dartdev prints the help message
// and exits with an error exit code.
dart_options->Reset();
dart_options->AddArgument(argv[argc - 1]);
dart_options->AddArgument("help");
return true;
}
return false;
}
USE(enable_dartdev_analytics);
USE(disable_dartdev_analytics);
USE(packages_argument);
// Record the dartdev command.
dart_options->AddArgument(argv[i++]);
// Bring any --packages option into the dartdev command
if (packages_argument != nullptr) {
dart_options->AddArgument(packages_argument);
dart_vm_options->AddArgument(packages_argument);
}
// Scan remaining arguments and separate them into
// dart_vm_options (vm options to be passed to the dart process executing
// the dartdev command) or dart_options (options to be passed to the
// executing dart script).
bool script_seen = false;
while (i < argc) {
if (!IsOption(argv[i], "disable-dart-dev")) {
if (!script_seen) {
// We scan for VM options that are passed to the 'run' and 'test'
// command. These options are accepted by both the VM and dartdev
// commands and need to be carried over to the VM running the app for
// these commands.
if (Options::ProcessVMOptions(argv[i], dart_vm_options)) {
// dartdev isn't able to parse these options properly. Since it
// doesn't need to use the values from these options, just strip them
// from the argument list passed to dartdev.
if (!IsOption(argv[i], "observe") &&
!IsOption(argv[i], "enable-vm-service")) {
dart_options->AddArgument(argv[i]);
}
} else {
if (!OptionProcessor::IsValidFlag(argv[i]) &&
!OptionProcessor::IsValidShortFlag(argv[i])) {
script_seen = true;
}
dart_options->AddArgument(argv[i]);
}
} else {
dart_options->AddArgument(argv[i]);
}
} else {
Syslog::PrintErr(
"Attempted to use --disable-dart-dev with a Dart CLI command.\n");
return false;
}
i++;
}
// Store the executable name.
Platform::SetExecutableName(argv[0]);
// Verify consistency of arguments.
if ((packages_file_ != nullptr) && (strlen(packages_file_) == 0)) {
Syslog::PrintErr("Empty package file name specified.\n");
return false;
}
return true;
}
void Options::PrintVersion() {
_PrintVersion();
}
// clang-format off
void Options::PrintUsage() {
_PrintUsage();
if (!Options::verbose_option()) {
_PrintNonVerboseUsage();
} else {
_PrintVerboseUsage();
}
}
// clang-format on
dart::SimpleHashMap* Options::environment_ = nullptr;
bool Options::ProcessEnvironmentOption(const char* arg,
CommandLineOptions* vm_options) {
return OptionProcessor::ProcessEnvironmentOption(arg, vm_options,
&Options::environment_);
}
void Options::Cleanup() {
DestroyEnvironment();
}
void Options::DestroyEnvironment() {
if (environment_ != nullptr) {
for (SimpleHashMap::Entry* p = environment_->Start(); p != nullptr;
p = environment_->Next(p)) {
free(p->key);
free(p->value);
}
delete environment_;
environment_ = nullptr;
}
}
char** Options::GetEnvArguments(int* argc) {
return nullptr;
}
void Options::DestroyEnvArgv() {}
#endif // defined(DART_PRECOMPILED_RUNTIME)
} // namespace bin
} // namespace dart