fix(windows): force UTF-8 encoding for all spawned child processes

- Set UTF-8 environment variables (PYTHONUTF8, PYTHONIOENCODING, LANG,
  LC_ALL) on all child processes via process.rs helpers
- Configure PTY terminals per shell flavor: chcp 65001 for cmd.exe,
  [Console]::OutputEncoding for PowerShell, LANG=C.UTF-8 for Git Bash
- Use /K and -NoExit for interactive shells to avoid nested processes

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
xintaofei
2026-04-12 18:47:31 +08:00
parent 843cf8df19
commit 25def31a23
2 changed files with 56 additions and 3 deletions

View File

@@ -13,6 +13,7 @@ pub fn configure_std_command(command: &mut Command) -> &mut Command {
{
use std::os::windows::process::CommandExt;
command.creation_flags(CREATE_NO_WINDOW);
set_utf8_env(command);
}
command
@@ -33,11 +34,47 @@ pub fn configure_tokio_command(
#[cfg(windows)]
{
command.creation_flags(CREATE_NO_WINDOW);
set_utf8_env(command);
}
command
}
/// Hint child processes to produce UTF-8 output on Windows.
///
/// Sets environment variables recognised by common runtimes (Python, MSYS2/Git
/// Bash, .NET console apps). Not all programs honour these, but they cover the
/// most frequent sources of mojibake in practice.
#[cfg(windows)]
fn set_utf8_env<C: SetEnv>(command: &mut C) {
// Python
command.env("PYTHONUTF8", "1");
command.env("PYTHONIOENCODING", "utf-8");
// MSYS2 / Git-for-Windows / POSIX-layer tools
command.env("LANG", "C.UTF-8");
command.env("LC_ALL", "C.UTF-8");
}
/// Abstraction over the `.env()` method shared by std and tokio Command types.
#[cfg(windows)]
trait SetEnv {
fn env(&mut self, key: &str, val: &str) -> &mut Self;
}
#[cfg(windows)]
impl SetEnv for Command {
fn env(&mut self, key: &str, val: &str) -> &mut Self {
Command::env(self, key, val)
}
}
#[cfg(windows)]
impl SetEnv for tokio::process::Command {
fn env(&mut self, key: &str, val: &str) -> &mut Self {
tokio::process::Command::env(self, key, val)
}
}
#[cfg(windows)]
fn maybe_windows_cmd_shim(program: &OsStr) -> Option<OsString> {
let path = Path::new(program);