Compare commits

...

57 Commits

Author SHA1 Message Date
ImJotaM
ad47657ddd
Fix(windows): Resolve header conflicts and update tests (#123)
* Refactor: Isolate WinAPI declarations.

This commit replaces the direct inclusion of `<windows.h>` with a minimal set of manual forward declarations.

This is a deliberate architectural change to:
- **Improve Compilation Speed:** Avoids parsing the notoriously large Windows header.
- **Eliminate Naming Pollution:** Prevents name clashes with common names like `min`/`max` which conflict with the C++ standard library.
- **Enhance Encapsulation:** Makes the library more self-contained by not exposing the entire Windows API.

* fix(windows): Resolve header conflicts and update tests

Previously, including both `subprocess.hpp` and `<Windows.h>` in the same file would cause compilation errors due to redefinitions of Windows API types and functions.

This commit resolves these conflicts by wrapping the manual API declarations in `subprocess.hpp` with an `#ifndef _WINDEF_` guard. This ensures the library's lightweight declarations are only used if the official Windows headers haven't been included, making it safe to use both.

The `test_ret_code.cc` unit test has been updated to include `<Windows.h>` directly for the `Sleep` function, which also serves to validate this fix.

* fix(windows): Add platform-specific include for Windows.h

Wraps the Windows.h include in a platform guard to prevent breaking non-Windows builds.
2025-09-12 20:14:39 +05:30
ImJotaM
60d4ad8d52
Refactor: Isolate WinAPI declarations. (#122)
This commit replaces the direct inclusion of `<windows.h>` with a minimal set of manual forward declarations.

This is a deliberate architectural change to:
- **Improve Compilation Speed:** Avoids parsing the notoriously large Windows header.
- **Eliminate Naming Pollution:** Prevents name clashes with common names like `min`/`max` which conflict with the C++ standard library.
- **Enhance Encapsulation:** Makes the library more self-contained by not exposing the entire Windows API.
2025-09-11 09:47:33 +05:30
Mara van der Laan
777cfa77d1
windows: Don't add an extra whitespace at end of Windows command line (#119)
The windows code adds an unnecessary extra space to the command line.
This can cause subtle issues, so avoid it.
2025-05-20 16:12:18 +05:30
Haowen Liu
f6232a7f26
Improve CMake setup (#118)
* 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
2025-05-04 20:22:13 +05:30
Tomás Andróil
9974ff69cd
fix(subprocess): check and handle fcntl(F_GETFD) failure (#117)
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.
2025-04-28 21:32:14 +05:30
Haowen Liu
625a877579
Proper implementation of wait() on Windows (#116)
This commit makes sure:
1. WaitForSingleObject returns with expected
code before proceeding.
2. Process handle is properly closed.
2025-04-27 19:32:35 +05:30
Hennadii Stepanov
ed313971c0
Do not escape double quotes for command line arguments on Windows (#113)
* 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.
2025-04-24 21:54:41 +05:30
Hennadii Stepanov
98a88e2499
Fix spelling (#101) 2025-04-24 11:16:27 +05:30
Shunsuke Shimizu
38d98d9d20
Explicitly define move constructor of Streams class. (#107)
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.
2025-04-24 11:15:32 +05:30
Hennadii Stepanov
778543b2f2
Avoid leaking POSIX name aliases beyond subprocess.hpp (#112)
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>
2025-04-24 11:14:22 +05:30
Hennadii Stepanov
34033d03de
Fix cross-compiling with mingw toolchain (#99)
Co-authored-by: Arun Muralidharan <arun11299@gmail.com>
2025-04-22 17:37:43 +05:30
Haowen Liu
04b015a8e5
Get Windows return code in wait() (#109)
Currently, wait() returns 0 on windows regardless
of the actual return code of processes.
2025-04-22 17:32:49 +05:30
Haowen Liu
2d8a8eebb0
Fix string_arg when used with rref (#110)
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.
2025-04-22 17:32:05 +05:30
Haowen Liu
beda7b5244
Support cwd on Windows (#108)
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.
2025-04-22 17:31:03 +05:30
Haoran Peng
3afe581c1f
Fix issues #103 and #104 (#106) 2025-03-22 23:25:37 +05:30
Hennadii Stepanov
4025693dec
Fix exception when CreateProcessW fails and add test for it (#98)
* Fix exception when `CreateProcessW` fails

This change makes the behavior on Windows consistent with the behavior
on Linux.

* test: Add `test_exception`
2024-01-25 12:01:40 +05:30
SoraKatadzuma
40cd59c097
Install guard (#93) 2023-12-03 18:11:52 +05:30
Hennadii Stepanov
40bcc2daa9
Fix redirection from StdError on Windows (#96)
* Fix redirection from StdError on Windows

* test: Add cross-platform test for error redirection
2023-12-03 18:10:57 +05:30
Hennadii Stepanov
06858e5fd7
test: Adjust test_ret_code for Windows and re-enable it (#97) 2023-12-03 18:10:05 +05:30
Hennadii Stepanov
d19cce111c
msvc: Fix "warning C4101: 'status': unreferenced local variable" (#94)
This change is required to build downstream projects using MSVC with
the `/WX` compiler option.

No behavior changes.
2023-12-01 09:43:14 +05:30
Hennadii Stepanov
46d2b244c4
ci: Switch CMake build configuration from Release to Debug (#95)
By default, CMake defines `NDEBUG` for the Release build configuration,
which effectively disables assertions and makes CI jobs less useful.
2023-12-01 09:42:28 +05:30
Anton Nikolaev
d809374661
fix overload thread (#92)
Co-authored-by: Anton Nikolayev <anton.nikolayev@ringcentral.com>
2023-08-28 09:57:30 +05:30
xoviat
64f4dfc2e5
remove changes to quote_argument and bring back ci for all platforms (#91)
* revert channges to quote argument

* ci: add cmake draft

* ci: enable suprocess tests

* cleanup some warnings

* ci: disable nonworking windows tests

* ci: set timeout

* ci: disable nonworking tests on windows

* ci: remove travis

---------

Co-authored-by: xoviat <xoviat@users.noreply.github.com>
2023-08-28 09:54:20 +05:30
Santiago
5a8adeb758
Added 'call' overload (#89)
* 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>
2023-08-19 12:25:29 +05:30
Jan Christoph Uhde
af23f33880
add: .editorconfig (#87) 2023-03-21 08:40:39 +05:30
Jan Christoph Uhde
d9ed0d606f
chore: improve CMake files (#85) 2023-03-21 08:39:25 +05:30
Mikhail Sokolovskiy
1392c47cbb
Create Popen::communicate overload for std::string (#82)
* Create Popen::communicate overload for std::string

* Create Popen::send overload for std::string
2022-06-25 20:50:38 +05:30
damageboy
cbc9e82158
Expose headers in cmake project (#77)
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
2022-06-11 11:02:37 +05:30
IbrahimSaad
399616b408
link tests targets with thread library (#79) 2022-06-11 11:02:05 +05:30
Jake Zimmerman
2ef9f168d3
Add retcode to CalledProcessError (#81)
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.
2022-05-29 09:48:20 +05:30
philippewarren
bf4289c1a0
Fix execute_process blocking on Windows (#76) 2022-05-15 21:40:30 +05:30
Elazar Leibovich
2bec886e79
Clang tidy (#73)
* clang-tidy: reorder includes

* clang-tidy: explicit one argument constructors

Co-authored-by: Elazar Leibovich <elazar.leibovich@nextsilicon.com>
2022-03-18 09:21:04 +05:30
Elazar Leibovich
0218c51b8c
Namespace library with github name. (#72)
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>
2022-03-18 09:20:06 +05:30
Dean Sands
fa58db9d6e
Issues #43, #58 and #60 (#59)
* Issue 43: some functions are not inline

* Issue #60: MinGW support
2020-09-29 19:19:47 +05:30
Dean Sands
5f43a237dc
Patched code to handle environment modifications (#62) 2020-09-29 19:19:02 +05:30
Arun M
718d0c816a merge 2020-06-13 13:06:28 +05:30
Arun M
dafa7dd36b string comparison issue on win32 #55 2020-06-13 13:06:00 +05:30
Németh Ádám
2f6bb248c6
fix for poll() function where the _child_created value is only ever set when not compiling on windows so on windows it should be omitted (#56) 2020-05-03 22:24:29 +05:30
Dilshod Mukhtarov
f1a698b50e
Forked subprocess not exited if execve failed (#54)
* 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.
2020-04-14 20:29:25 +05:30
Arun Muralidharan
a458bcfc4a
Update README.md 2020-04-09 17:34:16 +05:30
xoviat
9c624ce4e3 Enable basic windows support (#33)
* windows: fix implementation error
- repair configure_pipe
- allow vector initialization
- add draft destructor
- close pipes correctly

* windows: add some test compatibility

* windows: update readme

* windows: add vector args to check_output
2020-01-18 20:01:03 +05:30
CrashOverride
126ee2978f Update subprocess.hpp (#51)
fixed type
2019-11-23 22:49:14 +05:30
Adam Kewley
a5cc965f72 Remove trailing semicolons on namespace decls: required to build with -pedantic compiler flag (#45) 2019-08-28 22:22:11 +05:30
bibermann
6931e3d69f Add option to disable building tests (#41) 2019-07-11 15:10:02 +05:30
bibermann
3ab04aecaa Add install target (#40) 2019-07-10 22:55:50 +05:30
bibermann
785ff230a8 Allow usage in multiple object files (#39) 2019-07-10 17:13:15 +05:30
xoviat
f6799fcc34 WIP: add windows compatibility (#32)
* windows: add util functions

* windows: add cmake files

* windows: add travis.yml

* windows: address compatibility
- set cxx standard
- conditionally exclude codecvt

* windows: improve test coverage

* windows: improve test coverage

* windows: consolidate tests

* windows: disable failing test

* windows: modify read_atmost_n to use file object

* windows: modify read_all to use file object

* windows: update read_all test to use new api

* windows: implement main subprocess logic

* windows: add macro names

* windows: setup comm channels

* windows: compatibility fixes
2019-05-09 22:02:58 +05:30
Arun Muralidharan
871577770a
Revert "WIP: Add windows compatibility (#30)" (#31)
This reverts commit 5d92f4849276aa313dc1ba56c1556f4cca27fc7f.
2019-05-02 12:24:40 +05:30
xoviat
5d92f48492 WIP: Add windows compatibility (#30)
* add package files

* add windows compat to subprocess.hpp

* add test modifications

* repair test_read_all
2019-05-02 12:02:17 +05:30
jvilk-stripe
de5f791d04 Add virtual destructor to HolderBase (#28) 2019-03-11 12:44:12 +05:30
Gerardwx
3e29149ad1 Change rethrow statements (#25) 2019-01-26 13:08:44 +05:30
Arun M
e6cb89cb6e readme minor missing content #22 2019-01-03 22:06:20 +05:30
mohamedAlaaK
d74481c564 fixed a bug in util::read_all() function and added a test for it (#23) 2019-01-03 21:59:49 +05:30
Arun M
7ebcd80c05 Merge conflict 2018-12-31 17:43:15 +05:30
Arun M
ced3d53e3a I can send() but I have no way of closing the input channel #21 2018-12-31 17:41:24 +05:30
ilue
0894e7f6db Fix for preexec_func (#19) 2018-12-22 00:37:51 +05:30
Ben Dang
899e9b997d Replace throw with noexcept for C17 compatibility (#17)
C++11 started to deprecate the use of throw function identifiers.  In
C++17, this header no longer compiles due to the deprecation.

Signed-off-by: Ben Dang <me@bdang.it>
2018-10-08 12:45:10 +05:30
20 changed files with 1207 additions and 142 deletions

20
.editorconfig Normal file
View File

@ -0,0 +1,20 @@
# EditorConfig is awesome: http://EditorConfig.org
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
charset = utf-8
indent_style = space
indent_size = 4
end_of_line = lf
[*.{c,cppi,cc,h,hpp}]
indent_size = 2
# Tab indentation (no size specified)
[Makefile,makefile,GNUmakefile]
indent_style = tab
#[*.{js,py}]

View File

@ -0,0 +1,76 @@
# This starter workflow is for a CMake project running on multiple platforms. There is a different starter workflow if you just want a single platform.
# See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-single-platform.yml
name: CMake on multiple platforms
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
# Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. Consider changing this to true when your workflow is stable.
fail-fast: false
# Set up a matrix to run the following 3 configurations:
# 1. <Windows, Debug, latest MSVC compiler toolchain on the default runner image, default generator>
# 2. <Linux, Debug, latest GCC compiler toolchain on the default runner image, default generator>
# 3. <Linux, Debug, latest Clang compiler toolchain on the default runner image, default generator>
#
# To add more build types (Release, Debug, RelWithDebInfo, etc.) customize the build_type list.
matrix:
os: [ubuntu-latest, windows-latest]
build_type: [Debug]
c_compiler: [gcc, clang, cl]
include:
- os: windows-latest
c_compiler: cl
cpp_compiler: cl
- os: ubuntu-latest
c_compiler: gcc
cpp_compiler: g++
- os: ubuntu-latest
c_compiler: clang
cpp_compiler: clang++
exclude:
- os: windows-latest
c_compiler: gcc
- os: windows-latest
c_compiler: clang
- os: ubuntu-latest
c_compiler: cl
steps:
- uses: actions/checkout@v3
- name: Set reusable strings
# Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file.
id: strings
shell: bash
run: |
echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT"
- name: Configure CMake
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
run: >
cmake -B ${{ steps.strings.outputs.build-output-dir }}
-DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }}
-DCMAKE_C_COMPILER=${{ matrix.c_compiler }}
-DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
-DSUBPROCESS_TESTS=ON
-S ${{ github.workspace }}
- name: Build
# Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator).
run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ matrix.build_type }}
- name: Test
working-directory: ${{ steps.strings.outputs.build-output-dir }}
# Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator).
# See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
run: ctest --build-config ${{ matrix.build_type }} --timeout 10 -j4

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
build
.vscode

63
CMakeLists.txt Normal file
View File

@ -0,0 +1,63 @@
cmake_minimum_required(VERSION 3.5)
project(subprocess VERSION 2.2 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 11 CACHE STRING "C++ standard to use")
option(EXPORT_COMPILE_COMMANDS "create clang compile database" ON)
option(SUBPROCESS_TESTS "enable subprocess tests" OFF)
option(SUBPROCESS_INSTALL "enable subprocess install" OFF)
find_package(Threads REQUIRED)
add_library(subprocess INTERFACE)
target_link_libraries(subprocess INTERFACE Threads::Threads)
target_sources(subprocess PUBLIC
FILE_SET HEADERS
FILES
cpp-subprocess/subprocess.hpp
BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/
)
add_library(cpp-subprocess::subprocess ALIAS subprocess)
if(SUBPROCESS_INSTALL)
install(
TARGETS subprocess COMPONENT subprocess
EXPORT subprocess
FILE_SET HEADERS DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
include(CMakePackageConfigHelpers)
configure_package_config_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/subprocess-config.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/subprocess-config.cmake"
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/subprocess"
PATH_VARS PROJECT_NAME PROJECT_VERSION
)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/beman.exemplar-version.cmake"
VERSION ${PROJECT_VERSION}
COMPATIBILITY ExactVersion
)
install(
FILES
"${CMAKE_CURRENT_BINARY_DIR}/subprocess-config.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/subprocess-version.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/subprocess"
COMPONENT subprocess
)
install(
EXPORT subprocess
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/beman.exemplar"
NAMESPACE cpp-subprocess::
FILE subprocess-targets.cmake
COMPONENT subprocess
)
endif()
if(SUBPROCESS_TESTS)
include(CTest)
add_subdirectory(test)
endif()

View File

@ -1,4 +1,4 @@
![Subprocessing in C++]
## [Subprocessing in C++]
## Design Goals
The only goal was to develop something that is as close as python2.7 subprocess module in dealing with processes.
@ -13,14 +13,16 @@ This library had these design goals:
## Supported Platforms
Unlike python2.7 subprocess module, this library currently only supports MAC OS and Linux.
It has no support for Windows in its current state.
This library supports MAC OS and Linux.
Support for Windows is limited at this time. Please report any specific use-cases that fail,
and they will be fixed as they are reported.
## Integration
Subprocess library has just a single source `subprocess.hpp` present at the top directory of this repository. All you need to do is add
```cpp
#inlcude "subprocess.hpp"
#include "cpp-subprocess/subprocess.hpp"
using namespace subprocess;
// OR
@ -31,9 +33,24 @@ to the files where you want to make use of subprocessing. Make sure to add neces
Checkout http://templated-thoughts.blogspot.in/2016/03/sub-processing-with-modern-c.html as well.
## CMake Projects
```cmake
include(FetchContent)
FetchContent_Declare(
cpp-subprocess
GIT_REPOSITORY https://github.com/arun11299/cpp-subprocess.git
GIT_TAG v2.2
)
FetchContent_MakeAvailable(cpp-subprocess)
target_link_libraries(<your_target> PRIVATE cpp-subprocess::subprocess)
```
## Compiler Support
Linux - g++ 4.8 and above
Mac OS - Clang 3.4 and later
Windows - MSVC 2015 and above
## Examples
Here are few examples to show how to get started:
@ -48,7 +65,7 @@ std::cout << "Data len: " << obuf.length << std::endl;
More detailed way:
```cpp
auto p = Popen({"ls", "-l"});
auto p = Popen({"ls", "-l"}, output{PIPE});
auto obuf = p.communicate().first;
std::cout << "Data : " << obuf.buf.data() << std::endl;
std::cout << "Data len: " << obuf.length << std::endl;

View File

@ -0,0 +1,7 @@
set(SUBPROCESS_VERSION @PROJECT_VERSION@)
@PACKAGE_INIT@
include(${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@-targets.cmake)
check_required_components(@PROJECT_NAME@)

913
subprocess.hpp → cpp-subprocess/subprocess.hpp Executable file → Normal file

File diff suppressed because it is too large Load Diff

35
test/CMakeLists.txt Normal file
View File

@ -0,0 +1,35 @@
set(test_names test_subprocess test_cat test_double_quotes test_env test_err_redirection test_exception test_split test_main test_ret_code)
set(test_files env_script.sh write_err.sh write_err.txt)
foreach(test_name IN LISTS test_names)
add_executable(${test_name} ${test_name}.cc)
target_link_libraries(${test_name} PRIVATE subprocess)
add_test(
NAME ${test_name}
COMMAND $<TARGET_FILE:${test_name}>
)
endforeach()
foreach(test_file IN LISTS test_files)
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/${test_file}
${CMAKE_CURRENT_BINARY_DIR}/${test_file}
COPYONLY
)
endforeach()
set(TEST_REDIRECTION_PYTHON_SCRIPT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/test_redirection.py)
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/test_redirection.cc.in
${CMAKE_CURRENT_BINARY_DIR}/test_redirection.cc
@ONLY
)
add_executable(test_redirection ${CMAKE_CURRENT_BINARY_DIR}/test_redirection.cc)
target_link_libraries(test_redirection PRIVATE subprocess)
add_test(
NAME test_redirection
COMMAND $<TARGET_FILE:test_redirection>
)

View File

@ -1 +1 @@
through stdin to stdoutthrough stdin to stdoutthrough stdin to stdoutthrough stdin to stdout
through stdin to stdout

View File

@ -1,5 +1,5 @@
#include <iostream>
#include "../subprocess.hpp"
#include <cpp-subprocess/subprocess.hpp>
namespace sp = subprocess;
@ -23,6 +23,24 @@ void test_cat_file_redirection()
std::cout << "END_TEST" << std::endl;
}
void test_cat_send_terminate()
{
std::cout << "Test::test_cat_send_terminate" << std::endl;
std::vector<sp::Popen> pops;
for (int i=0; i < 5; i++) {
pops.emplace_back(sp::Popen({"cat", "-"}, sp::input{sp::PIPE}));
pops[i].send("3 5\n", 5);
pops[i].close_input();
}
for (int i=0; i < 5; i++) {
pops[i].wait();
}
std::cout << "END_TEST" << std::endl;
}
void test_buffer_growth()
{
auto obuf = sp::check_output({"cat", "../subprocess.hpp"});
@ -40,9 +58,17 @@ void test_buffer_growth_threaded_comm()
}
int main() {
test_cat_pipe_redirection();
#ifndef __USING_WINDOWS__
// test_cat_pipe_redirection();
test_cat_send_terminate();
/*
test_cat_file_redirection();
test_buffer_growth();
test_buffer_growth_threaded_comm();
*/
#endif
return 0;
}

View File

@ -0,0 +1,30 @@
#include <cpp-subprocess/subprocess.hpp>
#include <cassert>
#include <string>
namespace sp = subprocess;
// JSON requires the use of double quotes (see: https://json.org/).
// This test verifies proper handling of them.
void test_double_quotes()
{
// A simple JSON object.
const std::string expected{"{\"name\": \"value\"}"};
#ifdef __USING_WINDOWS__
const std::string command{"cmd.exe /c echo "};
#else
const std::string command{"echo "};
#endif
auto p = sp::Popen(command + expected, sp::output{sp::PIPE});
const auto out = p.communicate().first;
std::string result{out.buf.begin(), out.buf.end()};
// The `echo` command appends a newline.
result.erase(result.find_last_not_of(" \n\r\t") + 1);
assert(result == expected);
}
int main() {
test_double_quotes();
return 0;
}

View File

@ -1,8 +1,10 @@
#include <iostream>
#include "../subprocess.hpp"
#include <cpp-subprocess/subprocess.hpp>
using namespace subprocess;
#ifndef __USING_WINDOWS__
void test_env()
{
int st= Popen("./env_script.sh", environment{{
@ -13,7 +15,11 @@ void test_env()
assert (st == 0);
}
#endif
int main() {
#ifndef __USING_WINDOWS__
test_env();
#endif
return 0;
}

View File

@ -1,5 +1,5 @@
#include <iostream>
#include "../subprocess.hpp"
#include <cpp-subprocess/subprocess.hpp>
using namespace subprocess;
@ -11,6 +11,8 @@ void test_redirect()
}
int main() {
#ifndef __USING_WINDOWS__
test_redirect();
#endif
return 0;
}

27
test/test_exception.cc Normal file
View File

@ -0,0 +1,27 @@
#include <cassert>
#include <cstring>
#include <cpp-subprocess/subprocess.hpp>
namespace sp = subprocess;
void test_exception()
{
bool caught = false;
try {
auto p = sp::Popen("invalid_command");
assert(false); // Expected to throw
} catch (sp::CalledProcessError& e) {
#ifdef __USING_WINDOWS__
assert(std::strstr(e.what(), "CreateProcess failed: The system cannot find the file specified."));
#else
assert(std::strstr(e.what(), "execve failed: No such file or directory"));
#endif
caught = true;
}
assert(caught);
}
int main() {
test_exception();
return 0;
}

View File

@ -0,0 +1,23 @@
#include <cpp-subprocess/subprocess.hpp>
#include <cstdlib>
#include <string>
#include <tuple>
using namespace subprocess;
int main() {
auto p = Popen("python3 @TEST_REDIRECTION_PYTHON_SCRIPT_PATH@", output{PIPE}, error{PIPE});
OutBuffer out_buf;
ErrBuffer err_buf;
std::tie(out_buf, err_buf) = p.communicate();
std::string out{out_buf.buf.data()};
std::string err{err_buf.buf.data()};
if (out.find("Hello message.") == std::string::npos) return EXIT_FAILURE;
if (err.find("Hello message.") != std::string::npos) return EXIT_FAILURE;
if (out.find("Error report.") != std::string::npos) return EXIT_FAILURE;
if (err.find("Error report.") == std::string::npos) return EXIT_FAILURE;
return EXIT_SUCCESS;
}

7
test/test_redirection.py Executable file
View File

@ -0,0 +1,7 @@
#!/usr/bin/env python3
import sys
if __name__ == "__main__":
print("Hello message.")
print("Error report.", file=sys.stderr)

View File

@ -1,14 +1,26 @@
#include <iostream>
#include "../subprocess.hpp"
#include <cpp-subprocess/subprocess.hpp>
#ifdef __USING_WINDOWS__
#include <Windows.h>
#endif
namespace sp = subprocess;
void test_ret_code()
{
std::cout << "Test::test_poll_ret_code" << std::endl;
#ifdef __USING_WINDOWS__
auto p = sp::Popen({"cmd.exe", "/c", "exit", "1"});
#else
auto p = sp::Popen({"/usr/bin/false"});
#endif
while (p.poll() == -1) {
#ifdef __USING_WINDOWS__
Sleep(100);
#else
usleep(1000 * 100);
#endif
}
assert (p.retcode() == 1);
}
@ -25,9 +37,27 @@ void test_ret_code_comm()
std::cout << "retcode: " << cut.retcode() << std::endl;
}
void test_ret_code_check_output()
{
using namespace sp;
bool caught = false;
try {
auto obuf = check_output({"/bin/false"}, shell{false});
assert(false); // Expected to throw
} catch (CalledProcessError &e) {
std::cout << "retcode: " << e.retcode << std::endl;
assert (e.retcode == 1);
caught = true;
}
assert(caught);
}
int main() {
test_ret_code();
#ifndef __USING_WINDOWS__
test_ret_code_comm();
test_ret_code_check_output();
#endif
return 0;
}

Binary file not shown.

View File

@ -1,11 +1,15 @@
#include <iostream>
#include "../subprocess.hpp"
#include <cpp-subprocess/subprocess.hpp>
using namespace subprocess;
void test_exename()
{
#ifdef __USING_WINDOWS__
auto ret = call({"--version"}, executable{"cmake"}, shell{false});
#else
auto ret = call({"-l"}, executable{"ls"}, shell{false});
#endif
std::cout << ret << std::endl;
}
@ -35,7 +39,11 @@ void test_easy_piping()
void test_shell()
{
auto obuf = check_output({"ls", "-l"}, shell{false});
#ifdef __USING_WINDOWS__
auto obuf = check_output({"cmake", "--version"}, shell{false});
#else
auto obuf = check_output({"ls", "-l"}, shell{false});
#endif
std::cout << obuf.buf.data() << std::endl;
}
@ -43,20 +51,49 @@ void test_sleep()
{
auto p = Popen({"sleep", "30"}, shell{true});
while (p.poll() == -1) {
while (p.poll() == -1)
{
std::cout << "Waiting..." << std::endl;
#ifdef __USING_WINDOWS__
#else
sleep(1);
#endif
}
std::cout << "Sleep ended: ret code = " << p.retcode() << std::endl;
}
void test_read_all()
{
Popen p = Popen({"echo", "12345678"}, output{PIPE});
std::vector<char> buf(6);
int rbytes = util::read_all(p.output(), buf);
std::string out(buf.begin(), buf.end());
assert(out == "12345678\n" && rbytes == 9); // echo puts a new line at the end of output
std::cout << "read_all() succeeded" << std::endl;
}
void test_simple_cmd()
{
auto p = Popen({"ls", "-l"}, output{PIPE});
auto obuf = p.communicate().first;
std::cout << "Data : " << obuf.buf.data() << std::endl;
std::cout << "Data len: " << obuf.length << std::endl;
}
int main() {
/*
test_exename();
test_input();
test_piping();
test_easy_piping();
test_shell();
test_sleep();
test_read_all();
*/
test_simple_cmd();
return 0;
}