* Support CMake 4
CMake 4 has removed compatibility with CMake < 3.5
Bumping minimum required version to 3.5 enables
CMake 4 to build this code.
* Move header into subdir
* Improve CMake setup
This commit configures and installs CMake metadata files. This also
provides the namespaced ALIAS target `cpp-subprocess::subprocess`.
* Update README with CMake instructions
* Update include paths in tests
Add missing error check for fcntl(fd, F_GETFD, 0) in set_clo_on_exec.
Raise OSError on failure to align with existing FD_SETFD behavior.
This improves robustness in subprocess setup and error visibility.
* refactor: Remove unused `util::is_ready()` function
Commit ffd4c731a2c7c09f34e7b81a16e04bdf91fa973d introduced the
`util::is_ready()` function, which has never been used internally and is
not part of the public API.
This change removes the function.
* refactor: Guard `util::quote_argument()` with `#ifdef __USING_WINDOWS__`
The `util::quote_argument()` function is specific to Windows and is used
in code already guarded by `#ifdef __USING_WINDOWS__`.
* Do not escape double quotes for command line arguments on Windows
This change fixes the handling of double quotes and aligns the behavior
with Python's `Popen` class. For example:
```
>py -3
>>> import subprocess
>>> p = subprocess.Popen("cmd.exe /c dir \"C:\\Program Files\"", stdout=subprocess.PIPE, text=True)
>>> print(f"Captured stdout:\n{stdout}")
```
Currently, the same command line processed by the `quote_argument()`
function looks like `cmd.exe /c dir "\"C:\Program" "Files\""`, which is
broken.
With this change, it looks correct: `cmd.exe /c dir "C:\Program Files"`.
* Add `test_double_quotes` test
The new test ensures that no regressions are introduced in the future.
This suppresses the following warning caused by clang-20.
```
error: definition of implicit copy constructor for 'Streams' is deprecated because it has a user-declared copy assignment operator [-Werror,-Wdeprecated-copy]
```
Copy constructor or move constructor is called when std::vector re-allocates
memory. In this case, move constructor should be called, because copying
Streams instances breaks file-descriptor management.
Communication class is modified as well, since it's instance is a member of
Streams class.
The commit a32c0f3df4b6bcd1d2e93f19e8f380bb890cd507 introduced code to
silence MSVC's "warning C4996: The POSIX name for this item is
deprecated."
However, it exhibits several issues:
1. The aliases may leak into code outside the `subprocess.hpp` header.
2. They are unnecessarily applied when using the MinGW-w64 toolchain.
3. The fix is incomplete: downstream projects still see C4996 warnings.
4. The implementation lacks documentation.
This change addresses all of the above shortcomings.
Co-authored-by: Luke Dashjr <luke-jr+git@utopios.org>
When passing in a rvalue reference, compiler
considers it ambiguous between std::string and
std::string&&. Making one of them take a lvalue
reference makes compilers correctly pick the right
one depending on whether the passed in value binds
to a rvalue or lvalue reference.
This requires making cwd arg be either narrow or
wide char depending on platform and plumbing the
argument to CreateProcessW. Since `env_char_t` and
`env_str_t` is now being used beyond just env,
I've renamed those types to `platform_char_t` and
`platform_str_t`. `env_char_t` and `env_str_t` are
provided as alias to the new name for backwards
compatibility.
* Fix exception when `CreateProcessW` fails
This change makes the behavior on Windows consistent with the behavior
on Linux.
* test: Add `test_exception`
* Added 'call' overload
* Use CREATE_NO_WINDOW flag for process creation in Win32
* Change way of quoting arguments so explorer /select can be called
(should find a better way to do this)
---------
Co-authored-by: Santiago <san@san.san>
Expose through CMake directives, the name of the project so that package managers such as
CPM (https://github.com/cpm-cmake/CPM.cmake) could consume this library cleanly
It's useful to be able to know the exit code of the process when it
fails with a non-zero exit code.
To get this to work, I had to fix a bug in the implementation of
check_output. Previously, check_output would call both `p.communicate()`
and `p.poll()`. The former has the effect of waiting for EOF on the
input and then waiting for the child to exit, reaping it with
`waitpid(2)`. Unfortunately, `p.poll()` was hoping to be able to also
use `waitpid(2)` to retrieve the exit code of the process. But since the
child had already been reaped, the given pid no longer existed, and thus
`waitpid(2)` would return `ECHILD`.
Luckily the call to `p.poll()` is unnecessary, as the process already
provides `p.retcode()` for retrieving the exit code.
While this is header-only, and users are expected
to download the header and commit it to their project, it's better to
have unique namespace for the header by default.
This has two reasons:
1. I wish to enable installing it with conan.io, and want to reduce
the possibility for namespace clashes. The term "subprocess" is
pretty overloaded.
2. Currently the README and the CMakeLists differ in their include
path, it's better to be consistent, even though users can put it
wherever they please in their project.
Since most users are using it as header only anyway, I don't think it'd
make much diference.
Co-authored-by: Elazar Leibovich <elazar.leibovich@nextsilicon.com>
* Fixed: forked subprocess not exited if execve failed
In lengthy processes (daemons, etc) this bug keeps open this subprocess. The subprocess continues to work (parallel to parent process) from after failed command start
* Fixed: deadlock on using multithreaded application
For example if application uses spdlog logger library and set spdlog::flush_every(std::chrono::seconds(1)); then it starts it's own thread which flushes logs every 1 second. In this case if execve failed to execute given command, then parent and subprocess deadlocked.