Skip to content

Commit 6af92dc

Browse files
committed
fix
1 parent ede2738 commit 6af92dc

File tree

2 files changed

+63
-0
lines changed

2 files changed

+63
-0
lines changed

waspc/src/Wasp/Util/System.hs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
module Wasp.Util.System
2+
( resolveExecNameIO,
3+
isSystemWindows,
4+
isSystemMacOS,
5+
ExecName,
6+
)
7+
where
8+
9+
import Control.Exception (throwIO)
10+
import Control.Monad.Extra (firstJustM)
11+
import StrongPath (Abs, File', Path', parseAbsFile)
12+
import System.Directory (findExecutable)
13+
import qualified System.Info
14+
15+
-- | Executable name as expected by Haskell's "System.Process" and its 'System.Process.RawCommand',
16+
-- therefore suited for passing to their functions for creating/executing processes.
17+
-- It can be just "node", or "node.exe", or relative or full path, ... .
18+
type ExecName = FilePath
19+
20+
-- | Resolve given executable name (e.g. "node") to the full executable path.
21+
--
22+
-- Note that filename of the resolved path might not be exactly equal to the provided executable
23+
-- name, but may have additional extension (e.g. "node" might resolve to "/some/path/node.cmd").
24+
--
25+
-- The reason why we return resolved absolute executable path and not just the resolved filename
26+
-- (e.g. "node.cmd" for "node") is that, as per Haskell docs, when passing just the filename to
27+
-- 'System.Process.createProcess', we can get unexpected resolution if 'cwd' option is set.
28+
-- For example it can resolve to "npm.cmd" in the local ".node_modules" instead of a global one.
29+
-- So it is better to stick with absolute paths.
30+
--
31+
-- Motivation for this function was mainly driven by how exec names are resolved when executing a
32+
-- process on Windows.
33+
-- On Linux/MacOS situation is simple, the system will normally do the name resolution for us, so
34+
-- e.g. if we pass "npm" to 'System.Process.proc', that will work out of the box.
35+
-- But on Windows, that will normally fail, since there is no "npm" really but instead "npm.cmd" or
36+
-- "npm.exe". In that case, we want to figure out what exactly is the right exec name to use.
37+
-- Note that we don't have to bother with this when using 'System.Process.shell' instead of
38+
-- 'System.Process.proc', becuase then the shell will do system resolution for us,
39+
-- but at the price of abandoning any argument escaping.
40+
--
41+
-- Throws IOError if it failed to resolve the name.
42+
--
43+
-- Example: resolveExecNameIO "npm" -> "C:\...\npm.cmd"
44+
resolveExecNameIO :: ExecName -> IO (Path' Abs File')
45+
resolveExecNameIO execName = do
46+
firstJustM findExecutable execNamesToLookForByPriorityDesc >>= \case
47+
Just execPath -> parseAbsFile execPath
48+
Nothing ->
49+
(throwIO . userError . unlines)
50+
[ "Could not find '" <> execName <> "' executable on your system.",
51+
"Please ensure " <> execName <> " is installed and available in your PATH."
52+
]
53+
where
54+
execNamesToLookForByPriorityDesc
55+
| isSystemWindows = (execName <>) <$> ["", ".cmd", ".exe", ".ps1"]
56+
| otherwise = [execName]
57+
58+
isSystemWindows :: Bool
59+
isSystemWindows = System.Info.os == "mingw32"
60+
61+
isSystemMacOS :: Bool
62+
isSystemMacOS = System.Info.os == "darwin"

waspc/waspc.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,7 @@ library
450450
Wasp.Util.Network.HTTP
451451
Wasp.Util.Network.Socket
452452
Wasp.Util.StrongPath
453+
Wasp.Util.System
453454
Wasp.Util.Terminal
454455
Wasp.Util.TH
455456
Wasp.Util.WebRouterPath

0 commit comments

Comments
 (0)