babashka / process

Clojure library for shelling out / spawning sub-processes
Eclipse Public License 1.0
220 stars 29 forks source link

Windows: executable with `.cmd` extension not found on PATH when specifying `:dir` opt #163

Closed lread closed 3 months ago

lread commented 3 months ago

Symptom

On Windows, using clj-msi, from a CMD shell, from an empty dir, launch a REPL:

> clj -Sdeps "{:deps {babashka/process {:mvn/version \"0.5.22\"}}}"
Clojure 1.11.3
user=> (require '[babashka.process :as p] '[babashka.fs :as fs])
nil

Unexpected behaviour:

Test with npx (part of nodejs and how borkdude noticed this behaviour) First, double-check that npx has a .cmd ext:

user=> (fs/which "npx")
#object[sun.nio.fs.WindowsPath 0x8cc8cdb "C:\\Users\\lee\\scoop\\apps\\nodejs\\current\\npx.cmd"]

Run npx with :dir and we get an error:

user=> (p/shell :dir "." "npx -v")
Execution error (IOException) at java.lang.ProcessImpl/create (ProcessImpl.java:-2).
CreateProcess error=2, The system cannot find the file specified

Expected behaviour:

The npx should run successfully. Like when we do not specify a :dir:

user=> (p/shell "npx -v")
10.8.1
{:proc #object[java.lang.ProcessImpl 0x7a4d582c "Process[pid=2272, exitValue=0]"], :exit 0, :in #object[java.lang.ProcessBuilder$NullOutputStream 0x5626d18c "java.lang.ProcessBuilder$NullOutputStream@5626d18c"], :out #object[java.lang.ProcessBuilder$NullInputStream 0x45e9b12d "java.lang.ProcessBuilder$NullInputStream@45e9b12d"], :err #object[java.lang.ProcessBuilder$NullInputStream 0x45e9b12d "java.lang.ProcessBuilder$NullInputStream@45e9b12d"], :prev nil, :cmd ["C:\\Users\\lee\\scoop\\apps\\nodejs\\current\\npx.cmd" "-v"]}

Or like for java when we do specify a :dir (you'll notice java has an .exe extension):

user=> (fs/which "java")
#object[sun.nio.fs.WindowsPath 0x3bc4ef12 "C:\\Users\\lee\\scoop\\apps\\temurin11-jdk\\current\\bin\\java.exe"]
user=> (p/shell {:dir "."} "java --version")
openjdk 11.0.23 2024-04-16
OpenJDK Runtime Environment Temurin-11.0.23+9 (build 11.0.23+9)
OpenJDK 64-Bit Server VM Temurin-11.0.23+9 (build 11.0.23+9, mixed mode)
{:proc #object[java.lang.ProcessImpl 0x633fd91 "Process[pid=7804, exitValue=0]"], :exit 0, :in #object[java.lang.ProcessBuilder$NullOutputStream 0x5626d18c "java.lang.ProcessBuilder$NullOutputStream@5626d18c"], :out #object[java.lang.ProcessBuilder$NullInputStream 0x45e9b12d "java.lang.ProcessBuilder$NullInputStream@45e9b12d"], :err #object[java.lang.ProcessBuilder$NullInputStream 0x45e9b12d "java.lang.ProcessBuilder$NullInputStream@45e9b12d"], :prev nil, :cmd ["java" "--version"]}

Diagnosis

On a Slack chat with borkdude we explored behaviour of ProcessBulder from Windows REPL:

java succeeds

user=> (-> (java.lang.ProcessBuilder. ^java.util.List ["java" "--version"]) (.start) (.waitFor))
0

java.exe succeeds

user=> (-> (java.lang.ProcessBuilder. ^java.util.List ["java.exe" "--version"]) (.start) (.waitFor))
0

npx fails

user=> (-> (java.lang.ProcessBuilder. ^java.util.List ["npx" "-v"]) (.start) (.waitFor))
Execution error (IOException) at java.lang.ProcessImpl/create (ProcessImpl.java:-2).
CreateProcess error=2, The system cannot find the file specified

npx.cmd succeeds:

user=> (-> (java.lang.ProcessBuilder. ^java.util.List ["npx.cmd" "-v"]) (.start) (.waitFor))
0

While we are at it, let's check .com and .bat (tree, I think is included with Windows, but nodevars might not be on your system for me it was installed with scoop nodejs):

tree.com succeeds:

user=> (-> (java.lang.ProcessBuilder. ^java.util.List ["tree.com"]) (.start) (.waitFor))
0

tree fails:

user=> (-> (java.lang.ProcessBuilder. ^java.util.List ["tree"]) (.start) (.waitFor))
Execution error (IOException) at java.lang.ProcessImpl/create (ProcessImpl.java:-2).
CreateProcess error=2, The system cannot find the file specified

nodevars.bat succeeds:

user=> (-> (java.lang.ProcessBuilder. ^java.util.List ["nodevars.bat"]) (.start) (.waitFor))
0

nodevars fails:

user=> (-> (java.lang.ProcessBuilder. ^java.util.List ["nodevars"]) (.start) (.waitFor))
Execution error (IOException) at java.lang.ProcessImpl/create (ProcessImpl.java:-2).
CreateProcess error=2, The system cannot find the file specified

To Address...

From borkdude on our chat:

I think this only matches the case when the executable is in the :dir : https://github.com/babashka/process/blob/ae1656bbcacc9e5c025d5cc6d54276312bbc4f0e/src/babashka/process.cljc#L185 but when it can't be found there, we should still search the path with fs/which to make a repro, we could make a foo.cmd which is on the PATH

Next steps

I'll follow up with a PR with and verify test coverage for .exe, .cmd,.com and .bat executables on and not-on the PATH with and without :dir specified.