Skip to content

Commit ce642d0

Browse files
bkonyiCommit Queue
authored and
Commit Queue
committed
[ 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 #54281 TEST=compile_test.dart Change-Id: I1d94ab1b992753a7dd69da722c051c9464d6d1cf Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/353820 Reviewed-by: Siva Annamalai <[email protected]> Commit-Queue: Ben Konyi <[email protected]>
1 parent f6e1f63 commit ce642d0

File tree

5 files changed

+183
-30
lines changed

5 files changed

+183
-30
lines changed

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,20 @@
8181
[#53218]: https://github.com/dart-lang/sdk/issues/53218
8282
[#53785]: https://github.com/dart-lang/sdk/issues/53785
8383

84+
### Dart Runtime
85+
- Dart VM flags and options can now be provided to any executable
86+
generated using `dart compile exe` via the `DART_VM_OPTIONS` environment
87+
variable. `DART_VM_OPTIONS` should be set to a list of comma-separated flags
88+
and options with no whitespace. Options that allow for multiple values to be
89+
provided as comma-separated values are not supported
90+
(e.g., `--timeline-streams=Dart,GC,Compiler`).
91+
92+
Example of a valid `DART_VM_OPTIONS` environment variable:
93+
94+
```bash
95+
DART_VM_OPTIONS=--random_seed=42,--verbose_gc
96+
```
97+
8498
## 3.3.0
8599

86100
### Language

pkg/dartdev/test/commands/compile_test.dart

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -738,6 +738,42 @@ void main() {}
738738
expect(result.stdout, contains('sound'));
739739
}, skip: isRunningOnIA32);
740740

741+
test('Compile and run exe with DART_VM_OPTIONS', () async {
742+
final p = project(mainSrc: '''void main() {
743+
// Empty
744+
}''');
745+
final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));
746+
final outFile = path.canonicalize(path.join(p.dirPath, 'myexe'));
747+
748+
var result = await p.run(
749+
[
750+
'compile',
751+
'exe',
752+
'-o',
753+
outFile,
754+
inFile,
755+
],
756+
);
757+
758+
expect(result.stdout, isNot(contains(soundNullSafetyMessage)));
759+
expect(result.stderr, isEmpty);
760+
expect(result.exitCode, 0);
761+
expect(File(outFile).existsSync(), true,
762+
reason: 'File not found: $outFile');
763+
764+
result = Process.runSync(
765+
outFile,
766+
[],
767+
environment: <String, String>{
768+
'DART_VM_OPTIONS': '--help,--verbose',
769+
},
770+
);
771+
772+
expect(result.stderr, isEmpty);
773+
expect(result.stdout, contains('vm_name'));
774+
expect(result.exitCode, 255);
775+
}, skip: isRunningOnIA32);
776+
741777
test('Compile exe without info', () async {
742778
final p = project(mainSrc: '''void main() {}''');
743779
final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));

runtime/bin/main_impl.cc

Lines changed: 45 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1192,6 +1192,39 @@ void main(int argc, char** argv) {
11921192
}
11931193
vm_options.AddArgument("--new_gen_growth_factor=4");
11941194

1195+
auto parse_arguments = [&](int argc, char** argv,
1196+
CommandLineOptions* vm_options,
1197+
CommandLineOptions* dart_options) {
1198+
bool success = Options::ParseArguments(
1199+
argc, argv, vm_run_app_snapshot, vm_options, &script_name, dart_options,
1200+
&print_flags_seen, &verbose_debug_seen);
1201+
if (!success) {
1202+
if (Options::help_option()) {
1203+
Options::PrintUsage();
1204+
Platform::Exit(0);
1205+
} else if (Options::version_option()) {
1206+
Options::PrintVersion();
1207+
Platform::Exit(0);
1208+
} else if (print_flags_seen) {
1209+
// Will set the VM flags, print them out and then we exit as no
1210+
// script was specified on the command line.
1211+
char* error =
1212+
Dart_SetVMFlags(vm_options->count(), vm_options->arguments());
1213+
if (error != nullptr) {
1214+
Syslog::PrintErr("Setting VM flags failed: %s\n", error);
1215+
free(error);
1216+
Platform::Exit(kErrorExitCode);
1217+
}
1218+
Platform::Exit(0);
1219+
} else {
1220+
// This usage error case will only be invoked when
1221+
// Options::disable_dart_dev() is false.
1222+
Options::PrintUsage();
1223+
Platform::Exit(kErrorExitCode);
1224+
}
1225+
}
1226+
};
1227+
11951228
AppSnapshot* app_snapshot = nullptr;
11961229
#if defined(DART_PRECOMPILED_RUNTIME)
11971230
// If the executable binary contains the runtime together with an appended
@@ -1213,40 +1246,23 @@ void main(int argc, char** argv) {
12131246
for (int i = 1; i < argc; i++) {
12141247
dart_options.AddArgument(argv[i]);
12151248
}
1249+
1250+
// Parse DART_VM_OPTIONS options.
1251+
int env_argc = 0;
1252+
char** env_argv = Options::GetEnvArguments(&env_argc);
1253+
if (env_argv != nullptr) {
1254+
// Any Dart options that are generated based on parsing DART_VM_OPTIONS
1255+
// are useless, so we'll throw them away rather than passing them along.
1256+
CommandLineOptions tmp_options(env_argc + EXTRA_VM_ARGUMENTS);
1257+
parse_arguments(env_argc, env_argv, &vm_options, &tmp_options);
1258+
}
12161259
}
12171260
}
12181261
#endif
12191262

12201263
// Parse command line arguments.
12211264
if (app_snapshot == nullptr) {
1222-
bool success = Options::ParseArguments(
1223-
argc, argv, vm_run_app_snapshot, &vm_options, &script_name,
1224-
&dart_options, &print_flags_seen, &verbose_debug_seen);
1225-
if (!success) {
1226-
if (Options::help_option()) {
1227-
Options::PrintUsage();
1228-
Platform::Exit(0);
1229-
} else if (Options::version_option()) {
1230-
Options::PrintVersion();
1231-
Platform::Exit(0);
1232-
} else if (print_flags_seen) {
1233-
// Will set the VM flags, print them out and then we exit as no
1234-
// script was specified on the command line.
1235-
char* error =
1236-
Dart_SetVMFlags(vm_options.count(), vm_options.arguments());
1237-
if (error != nullptr) {
1238-
Syslog::PrintErr("Setting VM flags failed: %s\n", error);
1239-
free(error);
1240-
Platform::Exit(kErrorExitCode);
1241-
}
1242-
Platform::Exit(0);
1243-
} else {
1244-
// This usage error case will only be invoked when
1245-
// Options::disable_dart_dev() is false.
1246-
Options::PrintUsage();
1247-
Platform::Exit(kErrorExitCode);
1248-
}
1249-
}
1265+
parse_arguments(argc, argv, &vm_options, &dart_options);
12501266
}
12511267

12521268
DartUtils::SetEnvironment(Options::environment());
@@ -1452,7 +1468,7 @@ void main(int argc, char** argv) {
14521468
}
14531469

14541470
// Free environment if any.
1455-
Options::DestroyEnvironment();
1471+
Options::Cleanup();
14561472

14571473
Platform::Exit(global_exit_code);
14581474
}

runtime/bin/main_options.cc

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,13 @@ bool Options::ProcessEnvironmentOption(const char* arg,
261261
&Options::environment_);
262262
}
263263

264+
void Options::Cleanup() {
265+
#if defined(DART_PRECOMPILED_RUNTIME)
266+
DestroyEnvArgv();
267+
#endif
268+
DestroyEnvironment();
269+
}
270+
264271
void Options::DestroyEnvironment() {
265272
if (environment_ != nullptr) {
266273
for (SimpleHashMap::Entry* p = environment_->Start(); p != nullptr;
@@ -273,6 +280,71 @@ void Options::DestroyEnvironment() {
273280
}
274281
}
275282

283+
#if defined(DART_PRECOMPILED_RUNTIME)
284+
// Retrieves the set of arguments stored in the DART_VM_OPTIONS environment
285+
// variable.
286+
//
287+
// DART_VM_OPTIONS should contain a list of comma-separated options and flags
288+
// with no spaces. Options that support providing multiple values as
289+
// comma-separated lists (e.g., --timeline-streams=Dart,GC,Compiler) are not
290+
// supported and will cause argument parsing to fail.
291+
char** Options::GetEnvArguments(int* argc) {
292+
ASSERT(argc != nullptr);
293+
const char* env_args_str = std::getenv("DART_VM_OPTIONS");
294+
if (env_args_str == nullptr) {
295+
*argc = 0;
296+
return nullptr;
297+
}
298+
299+
intptr_t n = strlen(env_args_str);
300+
if (n == 0) {
301+
return nullptr;
302+
}
303+
304+
// Find the number of arguments based on the number of ','s.
305+
//
306+
// WARNING: this won't work for arguments that support CSVs. There's less
307+
// than a handful of options that support multiple values. If we want to
308+
// support this case, we need to determine a way to specify groupings of CSVs
309+
// in environment variables.
310+
int arg_count = 1;
311+
for (int i = 0; i < n; ++i) {
312+
// Ignore the last comma if it's the last character in the string.
313+
if (env_args_str[i] == ',' && i + 1 != n) {
314+
arg_count++;
315+
}
316+
}
317+
318+
env_argv_ = new char*[arg_count];
319+
env_argc_ = arg_count;
320+
*argc = arg_count;
321+
322+
int current_arg = 0;
323+
char* token;
324+
char* rest = const_cast<char*>(env_args_str);
325+
326+
// Split out the individual arguments.
327+
while ((token = strtok_r(rest, ",", &rest)) != nullptr) {
328+
// TODO(bkonyi): consider stripping leading/trailing whitespace from
329+
// arguments.
330+
env_argv_[current_arg++] = Utils::StrNDup(token, rest - token);
331+
}
332+
333+
return env_argv_;
334+
}
335+
336+
char** Options::env_argv_ = nullptr;
337+
int Options::env_argc_ = 0;
338+
339+
void Options::DestroyEnvArgv() {
340+
for (int i = 0; i < env_argc_; ++i) {
341+
free(env_argv_[i]);
342+
}
343+
delete[] env_argv_;
344+
env_argv_ = nullptr;
345+
}
346+
#endif // defined(DART_PRECOMPILED_RUNTIME)
347+
276348
bool Options::ExtractPortAndAddress(const char* option_value,
277349
int* out_port,
278350
const char** out_ip,

runtime/bin/main_options.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,9 +157,19 @@ class Options {
157157
static void PrintUsage();
158158
static void PrintVersion();
159159

160-
static void DestroyEnvironment();
160+
static void Cleanup();
161+
162+
#if defined(DART_PRECOMPILED_RUNTIME)
163+
// Get the list of options in DART_VM_OPTIONS.
164+
static char** GetEnvArguments(int* argc);
165+
#endif // defined(DART_PRECOMPILED_RUNTIME)
161166

162167
private:
168+
static void DestroyEnvironment();
169+
#if defined(DART_PRECOMPILED_RUNTIME)
170+
static void DestroyEnvArgv();
171+
#endif // defined(DART_PRECOMPILED_RUNTIME)
172+
163173
#define STRING_OPTION_DECL(flag, variable) static const char* variable##_;
164174
STRING_OPTIONS_LIST(STRING_OPTION_DECL)
165175
#undef STRING_OPTION_DECL
@@ -182,6 +192,11 @@ class Options {
182192

183193
static dart::SimpleHashMap* environment_;
184194

195+
#if defined(DART_PRECOMPILED_RUNTIME)
196+
static char** env_argv_;
197+
static int env_argc_;
198+
#endif // defined(DART_PRECOMPILED_RUNTIME)
199+
185200
// Frontend argument processing.
186201
#if !defined(DART_PRECOMPILED_RUNTIME)
187202
static DFE* dfe_;

0 commit comments

Comments
 (0)