Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
matrix:
platform:
- { os: ubuntu-latest, arch: x64 }
- { os: macos-13, arch: x64 }
- { os: macos-14, arch: x64 }
- { os: macos-14, arch: arm }
- { os: windows-latest, arch: x64 }
ghc-version:
Expand Down
61 changes: 30 additions & 31 deletions System/Process.hs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{-# LANGUAGE CPP, ForeignFunctionInterface #-}
{-# LANGUAGE LambdaCase #-}
#if __GLASGOW_HASKELL__ >= 709
{-# LANGUAGE Safe #-}
#else
Expand Down Expand Up @@ -40,6 +41,7 @@ module System.Process (
ProcessHandle,

-- ** Simpler functions for common tasks
callCreateProcess,
callProcess,
callCommand,
spawnProcess,
Expand Down Expand Up @@ -337,42 +339,46 @@ spawnCommand cmd = do
-- ----------------------------------------------------------------------------
-- callProcess/callCommand

-- | Creates a new process to run the specified command with the given
-- arguments, and wait for it to finish. If the command returns a non-zero
-- exit code, an exception is raised.
-- | Creates a new process from the provided `CreateProcess`, and wait for it
-- to finish. If the process returns a non-zero exit code, an exception is
-- raised.
--
-- If an asynchronous exception is thrown to the thread executing
-- @callProcess@, the forked process will be terminated and
-- @callProcess@ will wait (block) until the process has been
-- @callCreateProcess@, the forked process will be terminated and
-- @callCreateProcess@ will wait (block) until the process has been
-- terminated.
--
-- @since TODO
callCreateProcess :: CreateProcess -> IO ()
callCreateProcess = callCreateProcess_ "callCreateProcess"

-- | \"@callProcess cmd args@\" is a shorthand for
-- \"@'callCreateProcess' ('proc' cmd args)@\".
--
-- @since 1.2.0.0
callProcess :: FilePath -> [String] -> IO ()
callProcess cmd args = do
exit_code <- withCreateProcess_ "callProcess"
(proc cmd args) { delegate_ctlc = True } $ \_ _ _ p ->
waitForProcess p
case exit_code of
ExitSuccess -> return ()
ExitFailure r -> processFailedException "callProcess" cmd args r
callProcess cmd = callCreateProcess_ "callProcess" . proc cmd
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically I would be in favor of:

  1. Implementing this in terms of callCreateProcess
  2. get rid of callCreateProcess_
  3. state in the documentation that this is an alias for callCreateProcess (proc cmd args)

However, technically that is a breaking change, as it will change error messages. Not sure how big of an issue that is, but when readCreateProcess was introduced back in 2015 error messages did change, so there is some precedence at least.

From a users perspective, you don't really want a whole bunch of different functions that are all slightly different and are implemented in terms of internal primitives. What you want is:

  1. General functions that are flexible
  2. Specialized functions that are implemented in terms of those more general functions

Sadly, process is very far from this ideal.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And presumably for callCommand too?

I don't really have an objection to this. It makes a lot of sense and I personally don't feel strongly about just the function name changing in the error message.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we need spawnCreateProcess too!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the latter see #360


-- | Creates a new process to run the specified shell command. If the
-- command returns a non-zero exit code, an exception is raised.
--
-- If an asynchronous exception is thrown to the thread executing
-- @callCommand@, the forked process will be terminated and
-- @callCommand@ will wait (block) until the process has been
-- terminated.
-- | \"@callCommand cmd@\" is a shorthand for \"@'callCreateProcess'
-- ('shell' cmd)@\".
--
-- @since 1.2.0.0
callCommand :: String -> IO ()
callCommand cmd = do
exit_code <- withCreateProcess_ "callCommand"
(shell cmd) { delegate_ctlc = True } $ \_ _ _ p ->
callCommand = callCreateProcess_ "callCommand" . shell

callCreateProcess_ :: String -> CreateProcess -> IO ()
callCreateProcess_ fun command = do
exit_code <- withCreateProcess_ fun
command { delegate_ctlc = True } $ \_ _ _ p ->
waitForProcess p
case exit_code of
ExitSuccess -> return ()
ExitFailure r -> processFailedException "callCommand" cmd [] r
ExitFailure r -> processFailed fun (cmdspec command) r

processFailed :: String -> CmdSpec -> Int -> IO a
processFailed fun = \ case
ShellCommand cmd -> processFailedException fun cmd []
RawCommand cmd args -> processFailedException fun cmd args

processFailedException :: String -> String -> [String] -> Int -> IO a
processFailedException fun cmd args exit_code =
Expand Down Expand Up @@ -574,14 +580,7 @@ readCreateProcess cp input = do

case ex of
ExitSuccess -> return output
ExitFailure r -> processFailedException "readCreateProcess" cmd args r
where
cmd = case cp of
CreateProcess { cmdspec = ShellCommand sc } -> sc
CreateProcess { cmdspec = RawCommand fp _ } -> fp
args = case cp of
CreateProcess { cmdspec = ShellCommand _ } -> []
CreateProcess { cmdspec = RawCommand _ args' } -> args'
ExitFailure r -> processFailed "readCreateProcess" (cmdspec cp) r


-- | @readProcessWithExitCode@ is like 'readProcess' but with two differences:
Expand Down
3 changes: 3 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog for [`process` package](http://hackage.haskell.org/package/process)

## next
* Add `callCreateProcess`

## 1.6.27.0 *March 2026*

* Support being configured without fork
Expand Down
Loading