mirror of
https://github.com/dart-lang/sdk
synced 2024-10-01 19:19:16 +00:00
[ CLI ] Add support for passing VM options to Dart runtimes via DART_VM_OPTIONS
The DART_VM_OPTIONS environment variable allows for users to specify a set of VM options to be processed by the Dart runtime in a self-contained executable created by `dart compile exe`. DART_VM_OPTIONS should be a comma separated list of options and flags with no whitespace. Options that accept multiple values as a list of comma separated values are not supported and will result in argument parsing failing. Fixes https://github.com/dart-lang/sdk/issues/54281 TEST=compile_test.dart Change-Id: I1d94ab1b992753a7dd69da722c051c9464d6d1cf Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/353820 Reviewed-by: Siva Annamalai <asiva@google.com> Commit-Queue: Ben Konyi <bkonyi@google.com>
This commit is contained in:
parent
f6e1f6306d
commit
ce642d040f
14
CHANGELOG.md
14
CHANGELOG.md
|
@ -81,6 +81,20 @@
|
||||||
[#53218]: https://github.com/dart-lang/sdk/issues/53218
|
[#53218]: https://github.com/dart-lang/sdk/issues/53218
|
||||||
[#53785]: https://github.com/dart-lang/sdk/issues/53785
|
[#53785]: https://github.com/dart-lang/sdk/issues/53785
|
||||||
|
|
||||||
|
### Dart Runtime
|
||||||
|
- Dart VM flags and options can now be provided to any executable
|
||||||
|
generated using `dart compile exe` via the `DART_VM_OPTIONS` environment
|
||||||
|
variable. `DART_VM_OPTIONS` should be set to a list of comma-separated flags
|
||||||
|
and options with no whitespace. Options that allow for multiple values to be
|
||||||
|
provided as comma-separated values are not supported
|
||||||
|
(e.g., `--timeline-streams=Dart,GC,Compiler`).
|
||||||
|
|
||||||
|
Example of a valid `DART_VM_OPTIONS` environment variable:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
DART_VM_OPTIONS=--random_seed=42,--verbose_gc
|
||||||
|
```
|
||||||
|
|
||||||
## 3.3.0
|
## 3.3.0
|
||||||
|
|
||||||
### Language
|
### Language
|
||||||
|
|
|
@ -738,6 +738,42 @@ void main() {}
|
||||||
expect(result.stdout, contains('sound'));
|
expect(result.stdout, contains('sound'));
|
||||||
}, skip: isRunningOnIA32);
|
}, skip: isRunningOnIA32);
|
||||||
|
|
||||||
|
test('Compile and run exe with DART_VM_OPTIONS', () async {
|
||||||
|
final p = project(mainSrc: '''void main() {
|
||||||
|
// Empty
|
||||||
|
}''');
|
||||||
|
final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));
|
||||||
|
final outFile = path.canonicalize(path.join(p.dirPath, 'myexe'));
|
||||||
|
|
||||||
|
var result = await p.run(
|
||||||
|
[
|
||||||
|
'compile',
|
||||||
|
'exe',
|
||||||
|
'-o',
|
||||||
|
outFile,
|
||||||
|
inFile,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.stdout, isNot(contains(soundNullSafetyMessage)));
|
||||||
|
expect(result.stderr, isEmpty);
|
||||||
|
expect(result.exitCode, 0);
|
||||||
|
expect(File(outFile).existsSync(), true,
|
||||||
|
reason: 'File not found: $outFile');
|
||||||
|
|
||||||
|
result = Process.runSync(
|
||||||
|
outFile,
|
||||||
|
[],
|
||||||
|
environment: <String, String>{
|
||||||
|
'DART_VM_OPTIONS': '--help,--verbose',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.stderr, isEmpty);
|
||||||
|
expect(result.stdout, contains('vm_name'));
|
||||||
|
expect(result.exitCode, 255);
|
||||||
|
}, skip: isRunningOnIA32);
|
||||||
|
|
||||||
test('Compile exe without info', () async {
|
test('Compile exe without info', () async {
|
||||||
final p = project(mainSrc: '''void main() {}''');
|
final p = project(mainSrc: '''void main() {}''');
|
||||||
final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));
|
final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));
|
||||||
|
|
|
@ -1192,6 +1192,39 @@ void main(int argc, char** argv) {
|
||||||
}
|
}
|
||||||
vm_options.AddArgument("--new_gen_growth_factor=4");
|
vm_options.AddArgument("--new_gen_growth_factor=4");
|
||||||
|
|
||||||
|
auto parse_arguments = [&](int argc, char** argv,
|
||||||
|
CommandLineOptions* vm_options,
|
||||||
|
CommandLineOptions* dart_options) {
|
||||||
|
bool success = Options::ParseArguments(
|
||||||
|
argc, argv, vm_run_app_snapshot, vm_options, &script_name, dart_options,
|
||||||
|
&print_flags_seen, &verbose_debug_seen);
|
||||||
|
if (!success) {
|
||||||
|
if (Options::help_option()) {
|
||||||
|
Options::PrintUsage();
|
||||||
|
Platform::Exit(0);
|
||||||
|
} else if (Options::version_option()) {
|
||||||
|
Options::PrintVersion();
|
||||||
|
Platform::Exit(0);
|
||||||
|
} else if (print_flags_seen) {
|
||||||
|
// Will set the VM flags, print them out and then we exit as no
|
||||||
|
// script was specified on the command line.
|
||||||
|
char* error =
|
||||||
|
Dart_SetVMFlags(vm_options->count(), vm_options->arguments());
|
||||||
|
if (error != nullptr) {
|
||||||
|
Syslog::PrintErr("Setting VM flags failed: %s\n", error);
|
||||||
|
free(error);
|
||||||
|
Platform::Exit(kErrorExitCode);
|
||||||
|
}
|
||||||
|
Platform::Exit(0);
|
||||||
|
} else {
|
||||||
|
// This usage error case will only be invoked when
|
||||||
|
// Options::disable_dart_dev() is false.
|
||||||
|
Options::PrintUsage();
|
||||||
|
Platform::Exit(kErrorExitCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
AppSnapshot* app_snapshot = nullptr;
|
AppSnapshot* app_snapshot = nullptr;
|
||||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||||
// If the executable binary contains the runtime together with an appended
|
// If the executable binary contains the runtime together with an appended
|
||||||
|
@ -1213,40 +1246,23 @@ void main(int argc, char** argv) {
|
||||||
for (int i = 1; i < argc; i++) {
|
for (int i = 1; i < argc; i++) {
|
||||||
dart_options.AddArgument(argv[i]);
|
dart_options.AddArgument(argv[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse DART_VM_OPTIONS options.
|
||||||
|
int env_argc = 0;
|
||||||
|
char** env_argv = Options::GetEnvArguments(&env_argc);
|
||||||
|
if (env_argv != nullptr) {
|
||||||
|
// Any Dart options that are generated based on parsing DART_VM_OPTIONS
|
||||||
|
// are useless, so we'll throw them away rather than passing them along.
|
||||||
|
CommandLineOptions tmp_options(env_argc + EXTRA_VM_ARGUMENTS);
|
||||||
|
parse_arguments(env_argc, env_argv, &vm_options, &tmp_options);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Parse command line arguments.
|
// Parse command line arguments.
|
||||||
if (app_snapshot == nullptr) {
|
if (app_snapshot == nullptr) {
|
||||||
bool success = Options::ParseArguments(
|
parse_arguments(argc, argv, &vm_options, &dart_options);
|
||||||
argc, argv, vm_run_app_snapshot, &vm_options, &script_name,
|
|
||||||
&dart_options, &print_flags_seen, &verbose_debug_seen);
|
|
||||||
if (!success) {
|
|
||||||
if (Options::help_option()) {
|
|
||||||
Options::PrintUsage();
|
|
||||||
Platform::Exit(0);
|
|
||||||
} else if (Options::version_option()) {
|
|
||||||
Options::PrintVersion();
|
|
||||||
Platform::Exit(0);
|
|
||||||
} else if (print_flags_seen) {
|
|
||||||
// Will set the VM flags, print them out and then we exit as no
|
|
||||||
// script was specified on the command line.
|
|
||||||
char* error =
|
|
||||||
Dart_SetVMFlags(vm_options.count(), vm_options.arguments());
|
|
||||||
if (error != nullptr) {
|
|
||||||
Syslog::PrintErr("Setting VM flags failed: %s\n", error);
|
|
||||||
free(error);
|
|
||||||
Platform::Exit(kErrorExitCode);
|
|
||||||
}
|
|
||||||
Platform::Exit(0);
|
|
||||||
} else {
|
|
||||||
// This usage error case will only be invoked when
|
|
||||||
// Options::disable_dart_dev() is false.
|
|
||||||
Options::PrintUsage();
|
|
||||||
Platform::Exit(kErrorExitCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DartUtils::SetEnvironment(Options::environment());
|
DartUtils::SetEnvironment(Options::environment());
|
||||||
|
@ -1452,7 +1468,7 @@ void main(int argc, char** argv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free environment if any.
|
// Free environment if any.
|
||||||
Options::DestroyEnvironment();
|
Options::Cleanup();
|
||||||
|
|
||||||
Platform::Exit(global_exit_code);
|
Platform::Exit(global_exit_code);
|
||||||
}
|
}
|
||||||
|
|
|
@ -261,6 +261,13 @@ bool Options::ProcessEnvironmentOption(const char* arg,
|
||||||
&Options::environment_);
|
&Options::environment_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Options::Cleanup() {
|
||||||
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||||
|
DestroyEnvArgv();
|
||||||
|
#endif
|
||||||
|
DestroyEnvironment();
|
||||||
|
}
|
||||||
|
|
||||||
void Options::DestroyEnvironment() {
|
void Options::DestroyEnvironment() {
|
||||||
if (environment_ != nullptr) {
|
if (environment_ != nullptr) {
|
||||||
for (SimpleHashMap::Entry* p = environment_->Start(); p != nullptr;
|
for (SimpleHashMap::Entry* p = environment_->Start(); p != nullptr;
|
||||||
|
@ -273,6 +280,71 @@ void Options::DestroyEnvironment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||||
|
// Retrieves the set of arguments stored in the DART_VM_OPTIONS environment
|
||||||
|
// variable.
|
||||||
|
//
|
||||||
|
// DART_VM_OPTIONS should contain a list of comma-separated options and flags
|
||||||
|
// with no spaces. Options that support providing multiple values as
|
||||||
|
// comma-separated lists (e.g., --timeline-streams=Dart,GC,Compiler) are not
|
||||||
|
// supported and will cause argument parsing to fail.
|
||||||
|
char** Options::GetEnvArguments(int* argc) {
|
||||||
|
ASSERT(argc != nullptr);
|
||||||
|
const char* env_args_str = std::getenv("DART_VM_OPTIONS");
|
||||||
|
if (env_args_str == nullptr) {
|
||||||
|
*argc = 0;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
intptr_t n = strlen(env_args_str);
|
||||||
|
if (n == 0) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the number of arguments based on the number of ','s.
|
||||||
|
//
|
||||||
|
// WARNING: this won't work for arguments that support CSVs. There's less
|
||||||
|
// than a handful of options that support multiple values. If we want to
|
||||||
|
// support this case, we need to determine a way to specify groupings of CSVs
|
||||||
|
// in environment variables.
|
||||||
|
int arg_count = 1;
|
||||||
|
for (int i = 0; i < n; ++i) {
|
||||||
|
// Ignore the last comma if it's the last character in the string.
|
||||||
|
if (env_args_str[i] == ',' && i + 1 != n) {
|
||||||
|
arg_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
env_argv_ = new char*[arg_count];
|
||||||
|
env_argc_ = arg_count;
|
||||||
|
*argc = arg_count;
|
||||||
|
|
||||||
|
int current_arg = 0;
|
||||||
|
char* token;
|
||||||
|
char* rest = const_cast<char*>(env_args_str);
|
||||||
|
|
||||||
|
// Split out the individual arguments.
|
||||||
|
while ((token = strtok_r(rest, ",", &rest)) != nullptr) {
|
||||||
|
// TODO(bkonyi): consider stripping leading/trailing whitespace from
|
||||||
|
// arguments.
|
||||||
|
env_argv_[current_arg++] = Utils::StrNDup(token, rest - token);
|
||||||
|
}
|
||||||
|
|
||||||
|
return env_argv_;
|
||||||
|
}
|
||||||
|
|
||||||
|
char** Options::env_argv_ = nullptr;
|
||||||
|
int Options::env_argc_ = 0;
|
||||||
|
|
||||||
|
void Options::DestroyEnvArgv() {
|
||||||
|
for (int i = 0; i < env_argc_; ++i) {
|
||||||
|
free(env_argv_[i]);
|
||||||
|
}
|
||||||
|
delete[] env_argv_;
|
||||||
|
env_argv_ = nullptr;
|
||||||
|
}
|
||||||
|
#endif // defined(DART_PRECOMPILED_RUNTIME)
|
||||||
|
|
||||||
bool Options::ExtractPortAndAddress(const char* option_value,
|
bool Options::ExtractPortAndAddress(const char* option_value,
|
||||||
int* out_port,
|
int* out_port,
|
||||||
const char** out_ip,
|
const char** out_ip,
|
||||||
|
|
|
@ -157,9 +157,19 @@ class Options {
|
||||||
static void PrintUsage();
|
static void PrintUsage();
|
||||||
static void PrintVersion();
|
static void PrintVersion();
|
||||||
|
|
||||||
static void DestroyEnvironment();
|
static void Cleanup();
|
||||||
|
|
||||||
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||||
|
// Get the list of options in DART_VM_OPTIONS.
|
||||||
|
static char** GetEnvArguments(int* argc);
|
||||||
|
#endif // defined(DART_PRECOMPILED_RUNTIME)
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static void DestroyEnvironment();
|
||||||
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||||
|
static void DestroyEnvArgv();
|
||||||
|
#endif // defined(DART_PRECOMPILED_RUNTIME)
|
||||||
|
|
||||||
#define STRING_OPTION_DECL(flag, variable) static const char* variable##_;
|
#define STRING_OPTION_DECL(flag, variable) static const char* variable##_;
|
||||||
STRING_OPTIONS_LIST(STRING_OPTION_DECL)
|
STRING_OPTIONS_LIST(STRING_OPTION_DECL)
|
||||||
#undef STRING_OPTION_DECL
|
#undef STRING_OPTION_DECL
|
||||||
|
@ -182,6 +192,11 @@ class Options {
|
||||||
|
|
||||||
static dart::SimpleHashMap* environment_;
|
static dart::SimpleHashMap* environment_;
|
||||||
|
|
||||||
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||||
|
static char** env_argv_;
|
||||||
|
static int env_argc_;
|
||||||
|
#endif // defined(DART_PRECOMPILED_RUNTIME)
|
||||||
|
|
||||||
// Frontend argument processing.
|
// Frontend argument processing.
|
||||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||||
static DFE* dfe_;
|
static DFE* dfe_;
|
||||||
|
|
Loading…
Reference in a new issue