123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425 |
- [def bp::system [funcref boost::process::system bp::system]]
- [def bp::async_system [funcref boost::process::async_system bp::async_system]]
- [def bp::spawn [funcref boost::process::spawn bp::spawn]]
- [def bp::child [classref boost::process::child bp::child]]
- [def bp::cmd [classref boost::process::cmd bp::cmd]]
- [def bp::group [classref boost::process::group bp::group]]
- [def bp::ipstream [classref boost::process::ipstream bp::ipstream]]
- [def bp::opstream [classref boost::process::opstream bp::opstream]]
- [def bp::pstream [classref boost::process::pstream bp::pstream]]
- [def bp::pipe [classref boost::process::pipe bp::pipe]]
- [def bp::async_pipe [classref boost::process::async_pipe bp::async_pipe]]
- [def bp::search_path [funcref boost::process::search_path bp::search_path]]
- [def boost_org [@www.boost.org "www.boost.org"]]
- [def std::system [@http://en.cppreference.com/w/cpp/utility/program/system std::system]]
- [def child_running [memberref boost::process::child::running running]]
- [def child_wait [memberref boost::process::child::wait wait]]
- [def child_wait_for [memberref boost::process::child::wait_for wait_for]]
- [def child_exit_code [memberref boost::process::child::exit_code exit_code]]
- [def group_wait_for [memberref boost::process::group::wait_for wait_for]]
- [def bp::on_exit [globalref boost::process::on_exit bp::on_exit]]
- [def bp::null [globalref boost::process::null bp::null]]
- [def child_terminate [memberref boost::process::child::terminate terminate]]
- [def group_terminate [memberref boost::process::group::terminate terminate]]
- [def group_wait [memberref boost::process::group::wait wait]]
- [def bp::std_in [globalref boost::process::std_in bp::std_in]]
- [def bp::std_out [globalref boost::process::std_out bp::std_out]]
- [def bp::std_err [globalref boost::process::std_err bp::std_err]]
- [def io_service [@http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/io_service.html boost::asio::io_service]]
- [def asio_buffer [@http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/buffer.html boost::asio::buffer]]
- [def asio_async_read [@http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/async_read.html boost::asio::async_read]]
- [def bp::environment [classref boost::process::basic_environment bp::environment]]
- [def bp::native_environment [classref boost::process::basic_native_environment bp::native_environment]]
- [def boost::this_process::environment [funcref boost::this_process::environment boost::this_process:deadlock :environment]]
- [def std::chrono::seconds [@http://en.cppreference.com/w/cpp/chrono/duration std::chrono::seconds]]
- [def std::vector [@http://en.cppreference.com/w/cpp/container/vector std::vector]]
- [def __wait_for__ [memberref boost::process::child::wait_for wait_for]]
- [def __wait_until__ [memberref boost::process::child::wait_until wait_until]]
- [def __detach__ [memberref boost::process::child::detach detach]]
- [def __reference__ [link process.reference reference]]
- [def __concepts__ [link boost_process.concepts concepts]]
- [def boost::asio::yield_context [@http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/yield_context.html boost::asio::yield_context]]
- [def boost::asio::coroutine [@http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/coroutine.html boost::asio::coroutine]]
- [def bp::env [globalref boost::process::env bp::env]]
- [section:tutorial Tutorial]
- In this section we will go step by step through the different features of
- boost.process. For a full description see the __reference__ and the __concepts__ sections.
- [section Starting a process]
- We want to start a process, so let's start with a simple process. We will
- invoke the gcc compiler to compile a simple program.
- With the standard library this looks like this.
- ```
- int result = std::system("g++ main.cpp");
- ```
- Which we can write exactly like this in boost.process.
- ```
- namespace bp = boost::process; //we will assume this for all further examples
- int result = bp::system("g++ main.cpp");
- ```
- If a single string (or the explicit form bp::cmd), it will be interpreted as a command line.
- That will cause the execution function to search the `PATH` variable to find the executable.
- The alternative is the `exe-args` style, where the first string will be interpreted as a filename (including the path),
- and the rest as arguments passed to said function.
- [note For more details on the `cmd`/`exe-args` style look [link boost_process.design.arg_cmd_style here]]
- So as a first step, we'll use the `exe-args` style.
- ```
- int result = bp::system("/usr/bin/g++", "main.cpp");
- ```
- With that syntax we still have "g++" hard-coded, so let's assume we get the string
- from an external source as `boost::filesystem::path`, we can do this too.
- ```
- boost::filesystem::path p = "/usr/bin/g++"; //or get it from somewhere else.
- int result = bp::system(p, "main.cpp");
- ```
- Now we might want to find the `g++` executable in the `PATH`-variable, as the `cmd` syntax would do.
- `Boost.process` provides a function to this end: bp::search_path.
- ```
- boost::filesystem::path p = bp::search_path("g++"); //or get it from somewhere else.
- int result = bp::system(p, "main.cpp");
- ```
- [note [funcref boost::process::search_path search_path] will search for any executable with that name.
- This also includes to add a file suffix on windows, such as `.exe` or `.bat`.]
- [endsect]
- [section:launch_mode Launch functions]
- Given that our example used the [funcref boost::process::system system] function,
- our program will wait until the child process is completed. This may be unwanted,
- especially since compiling can take a while.
- In order to avoid that, boost.process provides several ways to launch a process.
- Besides the already mentioned [funcref boost::process::system system] function and its
- asynchronous version [funcref boost::process::async_system async_system],
- we can also use the [funcref boost::process::spawn spawn] function or the
- [classref boost::process::child child] class.
- The [funcref boost::process::spawn spawn] function launches a process and
- immediately detaches it, so no handle will be returned and the process will be ignored.
- This is not what we need for compiling, but maybe we want to entertain the user,
- while compiling:
- ```
- bp::spawn(bp::search_path("chrome"), boost_org);
- ```
- Now for the more sensible approach for compiling: a non-blocking execution.
- To implement that, we directly call the constructor of [classref boost::process::child child].
- ```
- bp::child c(bp::search_path("g++"), "main.cpp");
- while (c.child_running())
- do_some_stuff();
- c.child_wait(); //wait for the process to exit
- int result = c.child_exit_code();
- ```
- So we launch the process, by calling the child constructor. Then we check and do other
- things while the process is running and afterwards get the exit code. The call
- to child_wait is necessary, to obtain it and tell the operating system, that no
- one is waiting for the process anymore.
- [note You can also wait for a time span or a until a time point with __wait_for__ and __wait_until__]
- [warning If you don't call wait on a child object, it will be terminated on destruction.
- This can be avoided by calling __detach__ beforehand]
- [endsect]
- [section:error_handling Error]
- Until now, we have assumed that everything works out, but it is not impossible,
- that "g++" is not present. That will cause the launch of the process to fail.
- The default behaviour of all functions is to throw a
- [@http://en.cppreference.com/w/cpp/error/system_error std::system_error] on failure.
- As with many other functions in this library, passing an [@http://en.cppreference.com/w/cpp/error/error_code std::error_code]
- will change the behaviour, so that instead of throwing an exception, the error will be assigned to the error code.
- ```
- std::error_code ec;
- bp::system("g++ main.cpp", ec);
- ```
- [endsect]
- [section:io Synchronous I/O]
- In the examples given above, we have only started a program, but did not consider the output.
- The default depends on the system, but usually this will just write it to the same output as the launching process.
- If this shall be guaranteed, the streams can be explicitly forwarded like this.
- ```
- bp::system("g++ main.cpp", bp::std_out > stdout, bp::std_err > stderr, bp::std_in < stdin);
- ```
- Now for the first example, we might want to just ignore the output, which can be done by redirecting it to the null-device.
- This can be achieved this way:
- ```
- bp::system("g++ main.cpp", bp::std_out > bp::null);
- ```
- Alternatively we can also easily redirect the output to a file:
- ```
- bp::system("g++ main.cpp", bp::std_out > "gcc_out.log");
- ```
- Now, let's take a more visual example for reading data.
- [@http://pubs.opengroup.org/onlinepubs/009696699/utilities/nm.html nm] is a tool on posix,
- which reads the outline, i.e. a list of all entry points, of a binary.
- Every entry point will be put into a single line, and we will use a pipe to read it.
- At the end an empty line is appended, which we use as the indication to stop reading.
- Boost.process provides the pipestream ([classref boost::process::ipstream ipstream],
- [classref boost::process::opstream opstream], [classref boost::process::pstream pstream]) to
- wrap around the [classref boost::process::pipe pipe] and provide an implementation of the
- [@http://en.cppreference.com/w/cpp/io/basic_istream std::istream],
- [@http://en.cppreference.com/w/cpp/io/basic_ostream std::ostream] and
- [@http://en.cppreference.com/w/cpp/io/basic_iostream std::iostream] interface.
- ```
- std::vector<std::string> read_outline(std::string & file)
- {
- bp::ipstream is; //reading pipe-stream
- bp::child c(bp::search_path("nm"), file, bp::std_out > is);
-
- std::vector<std::string> data;
- std::string line;
-
- while (c.child_running() && std::getline(is, line) && !line.empty())
- data.push_back(line);
-
- c.child_wait();
-
- return data;
- }
- ```
- What this does is redirect the `stdout` of the process into a pipe and we read this
- synchronously.
- [note You can do the same thing with [globalref boost::process::std_err std_err]]
- Now we get the name from `nm` and we might want to demangle it, so we use input and output.
- `nm` has a demangle option, but for the sake of the example, we'll use
- [@https://sourceware.org/binutils/docs/binutils/c_002b_002bfilt.html c++filt] for this.
- ```
- bp::opstream in;
- bp::ipstream out;
- bp::child c("c++filt", std_out > out, std_in < in);
- in << "_ZN5boost7process8tutorialE" << endl;
- std::string value;
- out >> value;
- c.child_terminate();
- ```
- Now you might want to forward output from one process to another processes input.
- ```
- std::vector<std::string> read_demangled_outline(const std::string & file)
- {
- bp::pipe p;
- bp::ipstream is;
-
- std::vector<std::string> outline;
-
- //we just use the same pipe, so the
- bp::child nm(bp::search_path("nm"), file, bp::std_out > p);
- bp::child filt(bp::search_path("c++filt"), bp::std_in < p, bp::std_out > is);
-
- std::string line;
- while (filt.running() && std::getline(is, line)) //when nm finished the pipe closes and c++filt exits
- outline.push_back(line);
- nm.child_wait();
- filt.wait();
- }
- ```
- This forwards the data from `nm` to `c++filt` without your process needing to do anything.
- [endsect]
- [section:async_io Asynchronous I/O]
- Boost.process allows the usage of boost.asio to implement asynchronous I/O.
- If you are familiar with [@http://www.boost.org/doc/libs/release/libs/asio/ boost.asio] (which we highly recommend),
- you can use [classref boost::process::async_pipe async_pipe] which is implemented
- as an I/O-Object and can be used like [classref boost::process::pipe pipe] as shown above.
- Now we get back to our compiling example. `nm` we might analyze it line by line,
- but the compiler output will just be put into one large buffer.
- With [@http://www.boost.org/doc/libs/release/libs/asio/ boost.asio] this is what it looks like.
- ```
- io_service ios;
- std::vector<char> buf(4096);
- bp::async_pipe ap(ios);
- bp::child c(bp::search_path("g++"), "main.cpp", bp::std_out > ap);
- asio_async_read(ap, asio_buffer(buf),
- [](const boost::system::error_code &ec, std::size_t size){});
- ios.run();
- int result = c.exit_code();
- ```
- To make it easier, boost.process provides simpler interface for that, so that the buffer can be passed directly,
- provided we also pass a reference to an io_service.
- ```
- io_service ios;
- std::vector<char> buf(4096);
- bp::child c(bp::search_path("g++"), "main.cpp", bp::std_out > asio_buffer(buf), ios);
- ios.run();
- int result = c.exit_code();
- ```
- [note Passing an instance of io_service to the launching function automatically cause it to wait asynchronously for the exit, so no call of
- [memberref boost::process::child::wait wait] is needed]
-
- To make it even easier, you can use [@http://en.cppreference.com/w/cpp/thread/future std::future] for asynchronous operations
- (you will still need to pass a reference to a io_service) to the launching function, unless you use bp::system or bp::async_system.
- Now we will revisit our first example and read the compiler output asynchronously:
- ```
- boost::asio::io_service ios;
- std::future<std::string> data;
- child c("g++", "main.cpp", //set the input
- bp::std_in.close(),
- bp::std_out > bp::null, //so it can be written without anything
- bp::std_err > data,
- ios);
- ios.run(); //this will actually block until the compiler is finished
- auto err = data.get();
- ```
- [endsect]
- [section:group Groups]
- When launching several processes, processes can be grouped together.
- This will also apply for a child process, that launches other processes,
- if they do not modify the group membership. E.g. if you call `make` which
- launches other processes and call terminate on it,
- it will not terminate all the child processes of the child unless you use a group.
- The two main reasons to use groups are:
- # Being able to terminate child processes of the child process
- # Grouping several processes into one, just so they can be terminated at once
- If we have program like `make`, which does launch its own child processes,
- a call of child_terminate might not suffice. I.e. if we have a makefile launching `gcc`
- and use the following code, the `gcc` process will still run afterwards:
- ```
- bp::child c("make");
- if (!c.child_wait_for(std::chrono::seconds(10)) //give it 10 seconds
- c.child_terminate(); //then terminate
- ```
- So in order to also terminate `gcc` we can use a group.
- ```
- bp::group g;
- bp::child c("make", g);
- if (!g.group_wait_for(std::chrono::seconds(10))
- g.group_terminate();
-
- c.child_wait(); //to avoid a zombie process & get the exit code
- ```
- Now given the example, we still call child_wait to avoid a zombie process.
- An easier solution for that might be to use [funcref boost::process::spawn spawn].
- To put two processes into one group, the following code suffices. Spawn already
- launches a detached process (i.e. without a child-handle), but they can be grouped,
- to that in the case of a problem, RAII is still a given.
- ```
- void f()
- {
- bp::group g;
- bp::spawn("foo", g);
- bp::spawn("bar", g);
-
- do_something();
-
- g.group_wait();
- };
- ```
- In the example, it will wait for both processes at the end of the function unless
- an exception occurs. I.e. if an exception is thrown, the group will be terminated.
- Please see the [headerref boost/process/group.hpp reference] for more information.
- [endsect]
- [section:env Environment]
- This library provides access to the environment of the current process and allows
- setting it for the child process.
- ```
- //get a handle to the current environment
- auto env = boost::this_process::environment();
- //add a variable to the current environment
- env["VALUE_1"] = "foo";
- //copy it into an environment separate to the one of this process
- bp::environment env_ = env;
- //append two values to a variable in the new env
- env_["VALUE_2"] += {"bar1", "bar2"};
- //launch a process with `env_`
- bp::system("stuff", env_);
- ```
- A more convenient way to modify the environment for the child is the
- [globalref boost::process::env env] property, which the example as following:
- ```
- bp::system("stuff", bp::env["VALUE_1"]="foo", bp::env["VALUE_2"]+={"bar1", "bar2"});
- ```
- Please see to the [headerref boost/process/environment.hpp reference] for more information.
- [endsect]
- [endsect]
|