CLI tool
daandemeyer/reproc avatar
daandemeyer/reproc

reproc: starting child processes from C and C++

A cross-platform (C99/C++11) process library

660 stars85 forksCMIT

At a glance

What is it?
reproc is a cross-platform C99 library, with a C++11 wrapper on top, for starting external programs, talking to them over their standard streams and stopping them, either cleanly or immediately. The whole surface is a handful of functions and a CMake integration, and the trade-off is that it wraps the platform APIs rather than extending them.
Who is it for?
reproc suits a C or C++ codebase that has to launch a child process and read its output on both POSIX and Windows without writing two implementations, and it suits a codebase that only needs a shell-out less well, since popen covers that case with less machinery.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 7 days ago.
What is it written in?
Mainly C, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A redirected process, started and read from C

reproc, short for Redirected Process, is a cross-platform C and C++ library for starting, stopping and communicating with external programs. The stated use case is executing command line applications directly from C or C++ code and retrieving their output, and the feature list is short enough to state in full. Start any program from your code, communicate with it through its standard streams, wait for it to exit, or stop it yourself, either allowing the process to clean up its resources or stopping it immediately.

That last capability is the one that distinguishes it from the obvious alternatives. A child process that writes to standard output and standard error, that you need to read without deadlocking, and that you may need to kill when it hangs, is a genuinely awkward thing to get right. The two usual shortcuts, system and popen, give you a merged stream and no way to signal the child that it should stop.

The usage example in the README is the whole API in a few lines:

c
#include <reproc/run.h>

int main(void)
{
  const char *args[] = { "echo", "Hello, world!", NULL };
  return reproc_run(args, (reproc_options) { 0 });
}

The call takes an argv-style array with a NULL terminator and a reproc_options struct, which is zero-initialised here. Zeroing the options struct is the expected way to express defaults, a C99 compound literal rather than an initialiser function, and it means a struct with many fields can be used without naming any of them.

The library is MIT licensed, written in C99 for the core with an optional C++11 wrapper, and the repository carries a .clang-format, a .clang-tidy configuration, a CHANGELOG and a cmake directory, so the project has the tooling you would expect from a long-lived C library.

reproc and reproc++ are two separate libraries

The project ships two libraries, and the README is careful about the distinction. reproc is the C99 library that contains the actual code for working with external programs. reproc++ depends on reproc and adapts its API to an idiomatic C++11 API, and it adds a few extras that simplify working with external programs from C++.

The word idiomatic is doing real work in that sentence. The C interface is a set of functions taking handles and options structs, which is what a C99 library can be. The C++ side wraps the same behaviour in a class-shaped API, and the extras mentioned are the sort of thing that only makes sense in C++, for example RAII-style handling of a process handle that closes itself.

This split has a practical consequence that appears later in the build instructions. Because they are separate libraries, they have separate config files as well, and the README warns you to make sure you search for the one you actually want to use. There is a find_package call for each:

cmake
find_package(reproc) # Find reproc.
find_package(reproc++) # Find reproc++.

And separate link targets, so a project that uses both links both:

cmake
target_link_libraries(myapp reproc) # Link against reproc.
target_link_libraries(myapp reproc++) # Link against reproc++.

The C++ wrapper is optional, and the build option that controls it is REPROC++, which defaults to the value of REPROC_DEVELOP. The core library is C99, so it drops into a C project without dragging in a C++ toolchain, and the wrapper is what makes the library pleasant in a C++ codebase rather than an exercise in manual handle management.

Getting the source into your build, three ways

The README presents several ways to get reproc into a project, and the choice depends on how much control you want over the version. Building reproc as part of your project using CMake is the common path, and the first step is getting the source.

The first option is the CMake FetchContent API, available when using CMake 3.11 or later, which downloads reproc when CMake runs, with a link to the modern CMake guide for an example. The second is to include the repository as a git submodule, again with a guide. The third is the simplest and the crudest, downloading a zip of the source without the git history and adding it to your own repository in a separate directory.

Once the source is in place, it is added from the root CMakeLists.txt:

cmake
add_subdirectory(<path-to-reproc>) # For example: add_subdirectory(external/reproc)

And options are set before that call:

cmake
set(REPROC++ ON)
add_subdirectory(<path-to-reproc>)

The ordering requirement comes with a warning worth heeding. If the option has already been cached in a previous CMake run, you will have to clear CMake's cache to apply the new default value. That is the usual CMake behaviour with cached options, but it catches people who set an option in a CMakeLists.txt and then wonder why nothing changed, because the cached value from the earlier configure wins.

The FetchContent route and the vendored zip have different upgrade profiles. FetchContent pins to whatever commit or tag you point it at and needs a network at configure time, the submodule gives you an explicit commit to review and update, and the zip freezes the code in your own history. For a library this small, all three are reasonable, and the choice is mostly about how your project handles third-party code.

Building, installing and then finding reproc

If you would rather consume reproc as an installed library, the README gives both the package manager route and the source route. reproc is available in the Arch User Repository and in vcpkg, which covers the two commonest ways a C project acquires dependencies on Windows and on Arch based systems.

Building and installing from source takes three commands, and the README notes that this path needs CMake 3.13 or newer:

sh
cmake -B build
cmake --build build
cmake --install build

The tests are built with the REPROC_TEST option, and the test target runs them:

sh
cmake -B build -DREPROC_TEST=ON
cmake --build build
cmake --build build --target test

The README also notes that running the test binary directly from the build directory is possible after building, which is what you do when you want to run a single case.

After installation, your build system has to find the library, and reproc helps with that by providing both CMake config files and pkg-config files. The pkg-config route matters more than it used to, since it is what lets a non-CMake build, or a project that mixes build systems, link against an installed reproc without hand-written flags.

The CMake subproject integration extends beyond CMake itself. From Meson 0.53.2 onwards, reproc can be included as a CMake subproject in Meson build scripts, with a link to the Meson documentation. For a project that has moved to Meson but wants this library, that path avoids vendoring the source by hand.

The CMake options that change what ends up in your binary

The build is configurable in three tiers, and the middle one is the interesting part for anyone embedding reproc.

The user options are REPROC++ for the C++ wrapper, REPROC_TEST for the tests, and REPROC_EXAMPLES for the example binaries, all defaulting to the value of REPROC_DEVELOP. The advanced options are where the engineering decisions sit. REPROC_INSTALL controls whether installation rules are generated, and it defaults to ON unless object libraries are enabled. REPROC_INSTALL_CMAKECONFIGDIR and REPROC_INSTALL_PKGCONFIGDIR set where the config and pkg-config files land, and REPROC_INSTALL_PKGCONFIG turns the pkg-config files on. REPROC_MULTITHREADED is on by default and is described as using pthread_sigmask and linking against the system's thread library.

REPROC_OBJECT_LIBRARIES is the option that changes the distribution story. The README explains why: when reproc is built as a static or shared library, it has to be installed alongside the consuming library, which makes distributing that consuming library harder. With object libraries, reproc's object files are included directly into the consuming library and no extra installation is necessary. Two caveats follow. The object libraries only link correctly from CMake 3.14 onwards, and the option overrides BUILD_SHARED_LIBS.

That is a well-chosen set of knobs for a library that has to live inside other people's build systems, and the defaults are sensible. The one to think about early is REPROC_OBJECT_LIBRARIES, because it changes what you have to ship.

The repository layout is plain: reproc holds the C sources, reproc++ the C++ wrapper, cmake the CMake modules, and CMakeLists.txt the top-level build.

One dependency per platform, and the signal question

The dependency story is short. By default reproc depends on pthreads on POSIX systems, with the -pthread flag, and on Winsock 2.2 on Windows systems, with -lws2_32. CMake and pkg-config handle both automatically, which is the point of shipping the config and pkg-config files in the first place.

Winsock is the tell. Socket programming is not something a process library fundamentally needs, but on Windows the handle inheritance and pipe plumbing around standard streams goes through the socket layer, so Winsock 2.2 comes along. On POSIX the equivalent plumbing is pipes and file descriptors, with no extra library beyond threads.

The signal handling is where the two platforms differ most visibly. REPROC_MULTITHREADED, on by default, uses pthread_sigmask and links against the system thread library, which is a POSIX concept. That is the mechanism that keeps a signal delivered on one thread from landing in the middle of a wait for a child process, and it is also the option a Windows port has no use for.

The practical consequence for an adopter is that reproc is not a pure abstraction. It is a portable surface over two genuinely different operating system models, and some of its options exist only on one of them. That is not a criticism, since the alternative is writing that difference yourself, but it does mean the option list is worth reading rather than accepting the defaults blindly.

The releases show steady maintenance. Version 14.2.8 was published on 2026-08-28, preceded by 14.2.7 and 14.2.6 on the same day in April 2026, and the last push was on 2026-10-16. Two patch releases landing within a minute of each other is the usual pattern when a fix is published for two supported versions at once.

Editorial conclusion

reproc suits a C or C++ codebase that has to launch a child process and read its output on both POSIX and Windows without writing two implementations, and it suits a codebase that only needs a shell-out less well, since popen covers that case with less machinery. Before adopting it, decide whether you want the C core or the C++ wrapper, because they are separate libraries with separate config files, and read the CMake options, since REPROC_OBJECT_LIBRARIES changes how the library is distributed and only links correctly from CMake 3.14 onwards. The project is MIT licensed, the last push was on 2026-10-16, and the newest tagged release is 14.2.8 from 2026-08-28.

Frequently asked questions

What is reproc used for?

It is a cross-platform C99 and C++11 library for starting external programs, communicating with them over their standard streams, waiting for them to exit, and stopping them either cleanly or immediately. The main use case is running command line applications from C or C++ and reading their output.

How do I add reproc to a CMake project?

Get the source with FetchContent, a git submodule or a vendored zip, then call add_subdirectory on the path to it. Options such as REPROC++ must be set before that call, and a cached option from an earlier configure has to be cleared to apply a new value.

What are the dependencies of reproc?

By default pthreads on POSIX systems with the -pthread flag, and Winsock 2.2 on Windows systems with -lws2_32. CMake and pkg-config handle both automatically.

Can I use reproc from a Meson build?

Yes. From Meson 0.53.2 onwards, reproc can be included as a CMake subproject in Meson build scripts, using the Meson CMake module.

What is the difference between reproc and reproc++?

reproc is the C99 core that contains the code for working with external programs, and reproc++ is a C++11 wrapper that adapts the API and adds a few extras. They are separate libraries with separate CMake config files, so you search for the one you need.

Official sources

  1. daandemeyer/reproc on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/daandemeyer-reproc.svg)](https://hysenlabs.com/projects/daandemeyer-reproc)

Was this useful?