Skip to content

Commit 4fcebee

Browse files
committed
Fix Windows Command search path bug
1 parent c51b9b6 commit 4fcebee

File tree

2 files changed

+74
-1
lines changed

2 files changed

+74
-1
lines changed

library/std/src/sys/pal/windows/process.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -260,9 +260,10 @@ impl Command {
260260
needs_stdin: bool,
261261
proc_thread_attribute_list: Option<&ProcThreadAttributeList<'_>>,
262262
) -> io::Result<(Process, StdioPipes)> {
263+
let env_saw_path = self.env.have_changed_path();
263264
let maybe_env = self.env.capture_if_changed();
264265

265-
let child_paths = if let Some(env) = maybe_env.as_ref() {
266+
let child_paths = if env_saw_path && let Some(env) = maybe_env.as_ref() {
266267
env.get(&EnvKey::new("PATH")).map(|s| s.as_os_str())
267268
} else {
268269
None
+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
//@ run-pass
2+
//@ only-windows
3+
//@ needs-subprocess
4+
//@ no-prefer-dynamic
5+
6+
// Test Windows std::process::Command search path semantics when setting PATH on the child process.
7+
// NOTE: The exact semantics here are (possibly) subject to change.
8+
9+
use std::process::Command;
10+
use std::{env, fs, path};
11+
12+
fn main() {
13+
if env::args().skip(1).any(|s| s == "--child") {
14+
child();
15+
} else if env::args().skip(1).any(|s| s == "--parent") {
16+
parent();
17+
} else {
18+
setup();
19+
}
20+
}
21+
22+
// Set up the directories so that there are three app dirs:
23+
// app: Where the parent app is run from
24+
// parent: In the parent's PATH env var
25+
// child: In the child's PATH env var
26+
fn setup() {
27+
let exe = env::current_exe().unwrap();
28+
29+
fs::create_dir_all("app").unwrap();
30+
fs::copy(&exe, "app/myapp.exe").unwrap();
31+
fs::create_dir_all("parent").unwrap();
32+
fs::copy(&exe, "parent/myapp.exe").unwrap();
33+
fs::create_dir_all("child").unwrap();
34+
fs::copy(&exe, "child/myapp.exe").unwrap();
35+
36+
let parent_path = path::absolute("parent").unwrap();
37+
let status =
38+
Command::new("./app/myapp.exe").env("PATH", parent_path).arg("--parent").status().unwrap();
39+
// print the status in case of abnormal exit
40+
dbg!(status);
41+
assert!(status.success());
42+
}
43+
44+
// The child simply prints the name of its parent directory.
45+
fn child() {
46+
let exe = env::current_exe().unwrap();
47+
let parent = exe.parent().unwrap().file_name().unwrap();
48+
println!("{}", parent.display());
49+
}
50+
51+
fn parent() {
52+
let exe = env::current_exe().unwrap();
53+
let name = exe.file_name().unwrap();
54+
55+
// By default, the application dir will be search first for the exe
56+
let output = Command::new(&name).arg("--child").output().unwrap();
57+
assert_eq!(output.stdout, b"app\n");
58+
59+
// Setting an environment variable should not change the above.
60+
let output = Command::new(&name).arg("--child").env("a", "b").output().unwrap();
61+
assert_eq!(output.stdout, b"app\n");
62+
63+
// Setting a child path means that path will be searched first.
64+
let child_path = path::absolute("child").unwrap();
65+
let output = Command::new(&name).arg("--child").env("PATH", child_path).output().unwrap();
66+
assert_eq!(output.stdout, b"child\n");
67+
68+
// Setting a child path that does not contain the exe (currently) means
69+
// we fallback to searching the app dir.
70+
let output = Command::new(&name).arg("--child").env("PATH", "").output().unwrap();
71+
assert_eq!(output.stdout, b"app\n");
72+
}

0 commit comments

Comments
 (0)