[ CLI ] Add support for modifying the environment from package:dartdev
This change makes it possible to set environment variables for the current process from package:dartdev. As a proof of concept, package:dartdev now sets `DART_ROOT` to the path of the Dart SDK in the environment. Related to https://github.com/dart-lang/sdk/issues/63210 and https://github.com/dart-lang/sdk/issues/62876 TEST=pkg/dartdev/test/environment_test.dart Change-Id: If3a90279e99dadaba435ae3e43a752dcfda69227 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/499300 Reviewed-by: Phil Quitslund <pquitslund@google.com> Reviewed-by: Alexander Aprelev <aam@google.com>
This commit is contained in:
@@ -36,6 +36,7 @@ import 'src/commands/tooling_daemon.dart';
|
||||
import 'src/commands/uninstall.dart';
|
||||
import 'src/core.dart';
|
||||
import 'src/experiments.dart';
|
||||
import 'src/sdk.dart';
|
||||
import 'src/unified_analytics.dart';
|
||||
import 'src/utils.dart';
|
||||
import 'src/vm_interop_handler.dart';
|
||||
@@ -46,6 +47,8 @@ Future<void> runDartdev(List<String> args, SendPort? port) async {
|
||||
int? exitCode = 1;
|
||||
try {
|
||||
VmInteropHandler.initialize(port);
|
||||
// Set the DART_ROOT environment variable to the SDK path.
|
||||
VmInteropHandler.setEnvironmentVariable('DART_ROOT', sdk.sdkPath);
|
||||
// Call the runner to execute the command; see DartdevRunner.
|
||||
final runner = DartdevRunner(args, vmArgs: io.Platform.executableArguments);
|
||||
exitCode = await runner.run(args);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
|
||||
// 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.
|
||||
|
||||
@@ -72,6 +72,16 @@ abstract class VmInteropHandler {
|
||||
port.send(message);
|
||||
}
|
||||
|
||||
/// Sets the environment variable [name] to [value] for the current process.
|
||||
///
|
||||
/// If [value] is null, the environment variable is removed.
|
||||
static void setEnvironmentVariable(String name, String? value) {
|
||||
final port = _port;
|
||||
if (port == null) return;
|
||||
final message = <dynamic>[_kResultSetEnvironmentVariable, name, value];
|
||||
port.send(message);
|
||||
}
|
||||
|
||||
/// This code is identical to the one in process_patch.dart, please ensure
|
||||
/// changes made here are also done in process_patch.dart.
|
||||
/// TODO : figure out if this functionality can be abstracted out to a
|
||||
@@ -127,10 +137,11 @@ abstract class VmInteropHandler {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Note: keep in sync with runtime/bin/dartdev_isolate.h
|
||||
// Note: keep in sync with runtime/bin/dartdev.cc
|
||||
static const int _kResultRun = 1;
|
||||
static const int _kResultRunExec = 2;
|
||||
static const int _kResultExit = 3;
|
||||
static const int _kResultSetEnvironmentVariable = 4;
|
||||
|
||||
static SendPort? _port;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
// Copyright (c) 2026, 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.
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dartdev/src/sdk.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'utils.dart';
|
||||
|
||||
void main() {
|
||||
ensureRunFromSdkBinDart();
|
||||
|
||||
group('Environment modification', () {
|
||||
test('run command sets DART_ROOT', () async {
|
||||
final p = project(
|
||||
mainSrc: '''
|
||||
import 'dart:io';
|
||||
void main() {
|
||||
print('DART_ROOT: \${Platform.environment['DART_ROOT']}');
|
||||
}
|
||||
''',
|
||||
);
|
||||
|
||||
final result = await p.run(['run', p.relativeFilePath]);
|
||||
expect(result.exitCode, 0);
|
||||
// The environment variable was set in the parent isolate (dartdev)
|
||||
// via VmInteropHandler.setEnvironmentVariable before the run command
|
||||
// spawned the new isolate.
|
||||
expect(result.stdout, contains('DART_ROOT: ${sdk.sdkPath}'));
|
||||
});
|
||||
|
||||
test('run command does not overwrite existing DART_ROOT', () async {
|
||||
final p = project(
|
||||
mainSrc: '''
|
||||
import 'dart:io';
|
||||
void main() {
|
||||
print('DART_ROOT: \${Platform.environment['DART_ROOT']}');
|
||||
}
|
||||
''',
|
||||
);
|
||||
|
||||
final result = await Process.run(
|
||||
Platform.resolvedExecutable,
|
||||
['run', p.relativeFilePath],
|
||||
workingDirectory: p.dir.path,
|
||||
environment: {
|
||||
'PUB_CACHE': p.pubCachePath,
|
||||
'DART_ROOT': 'original_value',
|
||||
},
|
||||
);
|
||||
expect(result.exitCode, 0);
|
||||
// The environment variable was already set to 'original_value',
|
||||
// so dartdev should not overwrite it.
|
||||
expect(result.stdout, contains('DART_ROOT: original_value'));
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -198,7 +198,9 @@ class TestProject {
|
||||
Platform.resolvedExecutable,
|
||||
[...arguments],
|
||||
workingDirectory: workingDir ?? dir.path,
|
||||
environment: {'PUB_CACHE': pubCachePath},
|
||||
environment: {
|
||||
'PUB_CACHE': pubCachePath,
|
||||
},
|
||||
)..then((p) => _process = p);
|
||||
}
|
||||
|
||||
|
||||
@@ -429,6 +429,7 @@ class DartDev {
|
||||
DartDev_Result_Run = 1,
|
||||
DartDev_Result_RunExec = 2,
|
||||
DartDev_Result_Exit = 3,
|
||||
DartDev_Result_SetEnvironmentVariable = 4,
|
||||
} DartDev_Result;
|
||||
|
||||
static CStringUniquePtr ResolvedDartVmPath() {
|
||||
@@ -734,6 +735,16 @@ class DartDev {
|
||||
}
|
||||
}
|
||||
|
||||
static void SetEnvironmentVariableCallback(Dart_CObject* message) {
|
||||
ASSERT(GetArrayItem(message, 1)->type == Dart_CObject_kString);
|
||||
const char* name = GetArrayItem(message, 1)->value.as_string;
|
||||
const char* value = nullptr;
|
||||
if (GetArrayItem(message, 2)->type == Dart_CObject_kString) {
|
||||
value = GetArrayItem(message, 2)->value.as_string;
|
||||
}
|
||||
Platform::SetEnvironmentVariable(name, value);
|
||||
}
|
||||
|
||||
// Callback that processes the result from execution of dartdev
|
||||
//
|
||||
static void ResultCallback(Dart_Port dest_port_id, Dart_CObject* message) {
|
||||
@@ -754,6 +765,10 @@ class DartDev {
|
||||
ExitResultCallback(message);
|
||||
break;
|
||||
}
|
||||
case DartDev_Result_SetEnvironmentVariable: {
|
||||
SetEnvironmentVariableCallback(message);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
@@ -110,6 +110,8 @@ class Platform {
|
||||
|
||||
static void SetCoreDumpResourceLimit(int value);
|
||||
|
||||
static bool SetEnvironmentVariable(const char* name, const char* value);
|
||||
|
||||
private:
|
||||
// The path to the executable.
|
||||
static const char* executable_name_;
|
||||
|
||||
@@ -215,6 +215,13 @@ void Platform::SetCoreDumpResourceLimit(int value) {
|
||||
setrlimit(RLIMIT_CORE, &limit);
|
||||
}
|
||||
|
||||
bool Platform::SetEnvironmentVariable(const char* name, const char* value) {
|
||||
if (value == nullptr) {
|
||||
return unsetenv(name) == 0;
|
||||
}
|
||||
return setenv(name, value, 0) == 0;
|
||||
}
|
||||
|
||||
} // namespace bin
|
||||
} // namespace dart
|
||||
|
||||
|
||||
@@ -408,6 +408,13 @@ void Platform::SetCoreDumpResourceLimit(int value) {
|
||||
setrlimit(RLIMIT_CORE, &limit);
|
||||
}
|
||||
|
||||
bool Platform::SetEnvironmentVariable(const char* name, const char* value) {
|
||||
if (value == nullptr) {
|
||||
return unsetenv(name) == 0;
|
||||
}
|
||||
return setenv(name, value, 0) == 0;
|
||||
}
|
||||
|
||||
} // namespace bin
|
||||
} // namespace dart
|
||||
|
||||
|
||||
@@ -430,6 +430,19 @@ void Platform::SetCoreDumpResourceLimit(int value) {
|
||||
// Not supported.
|
||||
}
|
||||
|
||||
bool Platform::SetEnvironmentVariable(const char* name, const char* value) {
|
||||
std::unique_ptr<wchar_t[]> name_w = Utf8ToWideChar(name);
|
||||
if (value == nullptr) {
|
||||
return ::SetEnvironmentVariableW(name_w.get(), nullptr) != 0;
|
||||
}
|
||||
DWORD ret = ::GetEnvironmentVariableW(name_w.get(), nullptr, 0);
|
||||
if (ret == 0 && ::GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
|
||||
std::unique_ptr<wchar_t[]> value_w = Utf8ToWideChar(value);
|
||||
return ::SetEnvironmentVariableW(name_w.get(), value_w.get()) != 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace bin
|
||||
} // namespace dart
|
||||
|
||||
|
||||
Reference in New Issue
Block a user