Skip to content

Commit 77bcc7b

Browse files
committed
Fix Windows Command search path bug
1 parent c51b9b6 commit 77bcc7b

File tree

2 files changed

+75
-1
lines changed

2 files changed

+75
-1
lines changed

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

+4-1
Original file line numberDiff line numberDiff line change
@@ -260,9 +260,12 @@ 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 let Some(env) = maybe_env.as_ref()
267+
&& env_saw_path
268+
{
266269
env.get(&EnvKey::new("PATH")).map(|s| s.as_os_str())
267270
} else {
268271
None
+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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:
25+
fn setup() {
26+
let exe = env::current_exe().unwrap();
27+
28+
fs::create_dir_all("app").unwrap();
29+
fs::copy(&exe, "app/myapp.exe").unwrap();
30+
fs::create_dir_all("parent").unwrap();
31+
fs::copy(&exe, "parent/myapp.exe").unwrap();
32+
fs::create_dir_all("child").unwrap();
33+
fs::copy(&exe, "child/myapp.exe").unwrap();
34+
35+
let parent_path = path::absolute("parent").unwrap();
36+
let status =
37+
Command::new("./app/myapp.exe").env("PATH", parent_path).arg("--parent").status().unwrap();
38+
// print the status in case of abnormal exit
39+
dbg!(status);
40+
assert!(status.success());
41+
}
42+
43+
// The child simply prints the name of its parent directory.
44+
fn child() {
45+
let exe = env::current_exe().unwrap();
46+
let parent = exe.parent().unwrap().file_name().unwrap();
47+
println!("{}", parent.display());
48+
}
49+
50+
fn parent() {
51+
let exe = env::current_exe().unwrap();
52+
let name = exe.file_name().unwrap();
53+
54+
// By default, the application dir will be search first for the exe
55+
let output = Command::new(&name).arg("--child").output().unwrap();
56+
assert_eq!(output.stdout, b"app\n");
57+
58+
// Setting an environment variable should not change the above.
59+
let output = Command::new(&name).arg("--child").env("a", "b").output().unwrap();
60+
assert_eq!(output.stdout, b"app\n");
61+
62+
// Setting a child path means that path will be searched first.
63+
let child_path = path::absolute("child").unwrap();
64+
let output = Command::new(&name).arg("--child").env("PATH", child_path).output().unwrap();
65+
assert_eq!(output.stdout, b"child\n");
66+
67+
// Setting a child path that does not contain the exe (currently) means
68+
// we fallback to searching the app dir.
69+
let output = Command::new(&name).arg("--child").env("PATH", "").output().unwrap();
70+
assert_eq!(output.stdout, b"app\n");
71+
}

0 commit comments

Comments
 (0)