forked from OctaForge/libostd
initial impl of process calling api (posix only atm)
This commit is contained in:
parent
44d370183c
commit
1ecf921f1a
65
build.cc
65
build.cc
|
@ -110,35 +110,22 @@ static void print_help(ostd::string_range arg0) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void exec_command(std::string const &cmd, strvec const &args) {
|
static void exec_command(strvec const &args) {
|
||||||
auto argp = std::make_unique<char *[]>(args.size() + 2);
|
ostd::process_info pi;
|
||||||
argp[0] = const_cast<char *>(cmd.data());
|
pi.open(nullptr, ostd::iter(args));
|
||||||
for (std::size_t i = 0; i < args.size(); ++i) {
|
if (int ret; (ret = pi.close())) {
|
||||||
argp[i + 1] = const_cast<char *>(args[i].data());
|
|
||||||
}
|
|
||||||
argp[args.size() + 1] = nullptr;
|
|
||||||
|
|
||||||
auto pid = fork();
|
|
||||||
if (pid == -1) {
|
|
||||||
throw std::runtime_error{"command failed"};
|
|
||||||
} else if (!pid) {
|
|
||||||
if (execvp(argp[0], argp.get())) {
|
|
||||||
argp.reset();
|
|
||||||
std::exit(1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (int retc = -1; (waitpid(pid, &retc, 0) == -1) || retc) {
|
|
||||||
auto app = ostd::appender<std::string>();
|
auto app = ostd::appender<std::string>();
|
||||||
ostd::format(app, "command failed with code %d", retc);
|
ostd::format(app, "command failed with code %d", ret);
|
||||||
throw std::runtime_error{app.get()};
|
throw std::runtime_error{app.get()};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static std::string get_command(std::string const &cmd, strvec const &args) {
|
static std::string get_command(strvec const &args) {
|
||||||
std::string ret = cmd;
|
std::string ret;
|
||||||
for (auto &s: args) {
|
for (auto &s: args) {
|
||||||
|
if (!ret.empty()) {
|
||||||
ret += ' ';
|
ret += ' ';
|
||||||
|
}
|
||||||
ret += s;
|
ret += s;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -292,17 +279,17 @@ int main(int argc, char **argv) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto exec_v = [&io_msgs](std::string const &cmd, strvec const &args) {
|
auto exec_v = [&io_msgs](strvec const &args) {
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
io_msgs.put(get_command(cmd, args));
|
io_msgs.put(get_command(args));
|
||||||
}
|
}
|
||||||
exec_command(cmd, args);
|
exec_command(args);
|
||||||
};
|
};
|
||||||
|
|
||||||
auto call_cxx = [&](
|
auto call_cxx = [&](
|
||||||
fs::path const &input, fs::path const &output, bool shared
|
fs::path const &input, fs::path const &output, bool shared
|
||||||
) {
|
) {
|
||||||
strvec args;
|
strvec args = { cxx };
|
||||||
add_args(args, cxxflags);
|
add_args(args, cxxflags);
|
||||||
|
|
||||||
auto ifs = input.string();
|
auto ifs = input.string();
|
||||||
|
@ -322,7 +309,7 @@ int main(int argc, char **argv) {
|
||||||
args.push_back(outp.string());
|
args.push_back(outp.string());
|
||||||
args.push_back(ifs);
|
args.push_back(ifs);
|
||||||
|
|
||||||
exec_v(cxx, args);
|
exec_v(args);
|
||||||
|
|
||||||
return outp;
|
return outp;
|
||||||
};
|
};
|
||||||
|
@ -333,7 +320,7 @@ int main(int argc, char **argv) {
|
||||||
auto call_as = [&](
|
auto call_as = [&](
|
||||||
fs::path const &input, fs::path const &output, bool shared
|
fs::path const &input, fs::path const &output, bool shared
|
||||||
) {
|
) {
|
||||||
strvec args;
|
strvec args = { as };
|
||||||
add_args(args, asflags);
|
add_args(args, asflags);
|
||||||
|
|
||||||
auto ifs = input.string();
|
auto ifs = input.string();
|
||||||
|
@ -353,7 +340,7 @@ int main(int argc, char **argv) {
|
||||||
args.push_back(outp.string());
|
args.push_back(outp.string());
|
||||||
args.push_back(ifs);
|
args.push_back(ifs);
|
||||||
|
|
||||||
exec_v(as, args);
|
exec_v(args);
|
||||||
|
|
||||||
return outp;
|
return outp;
|
||||||
};
|
};
|
||||||
|
@ -363,7 +350,7 @@ int main(int argc, char **argv) {
|
||||||
) {
|
) {
|
||||||
echo_q("LD: %s", output);
|
echo_q("LD: %s", output);
|
||||||
|
|
||||||
strvec args;
|
strvec args = { cxx };
|
||||||
add_args(args, cxxflags);
|
add_args(args, cxxflags);
|
||||||
|
|
||||||
args.push_back("-o");
|
args.push_back("-o");
|
||||||
|
@ -375,24 +362,26 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
add_args(args, ldflags);
|
add_args(args, ldflags);
|
||||||
|
|
||||||
exec_v(cxx, args);
|
exec_v(args);
|
||||||
|
|
||||||
if (build_cfg == "release") {
|
if (build_cfg == "release") {
|
||||||
args.clear();
|
args.clear();
|
||||||
|
args.push_back(strip);
|
||||||
args.push_back(output.string());
|
args.push_back(output.string());
|
||||||
exec_v(strip, args);
|
exec_v(args);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto call_ldlib = [&](
|
auto call_ldlib = [&](
|
||||||
fs::path const &output, pathvec const &files, bool shared
|
fs::path const &output, pathvec const &files, bool shared
|
||||||
) {
|
) {
|
||||||
strvec args;
|
|
||||||
if (shared) {
|
if (shared) {
|
||||||
add_args(args, SHARED_CXXFLAGS);
|
strvec flags;
|
||||||
add_args(args, SHARED_LDFLAGS);
|
add_args(flags, SHARED_CXXFLAGS);
|
||||||
call_ld(output, files, args);
|
add_args(flags, SHARED_LDFLAGS);
|
||||||
|
call_ld(output, files, flags);
|
||||||
} else {
|
} else {
|
||||||
|
strvec args = { ar };
|
||||||
echo_q("AR: %s", output);
|
echo_q("AR: %s", output);
|
||||||
|
|
||||||
args.push_back("rcs");
|
args.push_back("rcs");
|
||||||
|
@ -401,7 +390,7 @@ int main(int argc, char **argv) {
|
||||||
args.push_back(p.string());
|
args.push_back(p.string());
|
||||||
}
|
}
|
||||||
|
|
||||||
exec_v(ar, args);
|
exec_v(args);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -538,7 +527,7 @@ int main(int argc, char **argv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (build_testsuite) {
|
if (build_testsuite) {
|
||||||
exec_v("./test_runner", { TEST_DIR.string() });
|
exec_v({ "./test_runner", TEST_DIR.string() });
|
||||||
}
|
}
|
||||||
|
|
||||||
io_msgs.close();
|
io_msgs.close();
|
||||||
|
|
|
@ -15,9 +15,16 @@
|
||||||
#define OSTD_PROCESS_HH
|
#define OSTD_PROCESS_HH
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <system_error>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "ostd/platform.hh"
|
#include "ostd/platform.hh"
|
||||||
#include "ostd/string.hh"
|
#include "ostd/string.hh"
|
||||||
|
#include "ostd/range.hh"
|
||||||
|
#include "ostd/io.hh"
|
||||||
|
|
||||||
namespace ostd {
|
namespace ostd {
|
||||||
|
|
||||||
|
@ -69,6 +76,88 @@ OutputRange &&split_args(OutputRange &&out, string_range str) {
|
||||||
return std::forward<OutputRange>(out);
|
return std::forward<OutputRange>(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct process_error: std::system_error {
|
||||||
|
using std::system_error::system_error;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class process_pipe {
|
||||||
|
DEFAULT = 0,
|
||||||
|
PIPE,
|
||||||
|
STDOUT
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OSTD_EXPORT process_info {
|
||||||
|
process_pipe use_in = process_pipe::DEFAULT;
|
||||||
|
process_pipe use_out = process_pipe::DEFAULT;
|
||||||
|
process_pipe use_err = process_pipe::DEFAULT;
|
||||||
|
file_stream in = file_stream{};
|
||||||
|
file_stream out = file_stream{};
|
||||||
|
file_stream err = file_stream{};
|
||||||
|
|
||||||
|
process_info() {}
|
||||||
|
process_info(
|
||||||
|
process_pipe in_use, process_pipe out_use, process_pipe err_use
|
||||||
|
):
|
||||||
|
use_in(in_use), use_out(out_use), use_err(err_use)
|
||||||
|
{}
|
||||||
|
|
||||||
|
process_info(process_info &&i):
|
||||||
|
use_in(i.use_in), use_out(i.use_out), use_err(i.use_err),
|
||||||
|
in(std::move(i.in)), out(std::move(i.out)), err(std::move(i.err)),
|
||||||
|
pid(i.pid), errno_fd(i.errno_fd)
|
||||||
|
{
|
||||||
|
i.pid = -1;
|
||||||
|
i.errno_fd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
process_info &operator=(process_info &&i) {
|
||||||
|
swap(i);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void swap(process_info &i) {
|
||||||
|
using std::swap;
|
||||||
|
swap(use_in, i.use_in);
|
||||||
|
swap(use_out, i.use_out);
|
||||||
|
swap(use_err, i.use_err);
|
||||||
|
swap(in, i.in);
|
||||||
|
swap(out, i.out);
|
||||||
|
swap(err, i.err);
|
||||||
|
swap(pid, i.pid);
|
||||||
|
swap(errno_fd, i.errno_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
~process_info();
|
||||||
|
|
||||||
|
template<typename InputRange>
|
||||||
|
void open(string_range cmd, InputRange args, bool use_path = true) {
|
||||||
|
static_assert(
|
||||||
|
std::is_constructible_v<std::string, range_reference_t<InputRange>>,
|
||||||
|
"The arguments must be strings"
|
||||||
|
);
|
||||||
|
std::vector<std::string> argv;
|
||||||
|
if (cmd.empty()) {
|
||||||
|
if (args.empty()) {
|
||||||
|
throw process_error{EINVAL, std::generic_category()};
|
||||||
|
}
|
||||||
|
cmd = args[0];
|
||||||
|
}
|
||||||
|
for (; !args.empty(); args.pop_front()) {
|
||||||
|
argv.emplace_back(args.front());
|
||||||
|
}
|
||||||
|
open_impl(std::string{cmd}, argv, use_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
int close();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void open_impl(
|
||||||
|
std::string const &cmd, std::vector<std::string> const &args,
|
||||||
|
bool use_path
|
||||||
|
);
|
||||||
|
int pid = -1, errno_fd = -1;
|
||||||
|
};
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
} /* namespace ostd */
|
} /* namespace ostd */
|
||||||
|
|
152
src/process.cc
152
src/process.cc
|
@ -4,6 +4,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cerrno>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <new>
|
#include <new>
|
||||||
|
@ -15,6 +17,8 @@
|
||||||
# include <windows.h>
|
# include <windows.h>
|
||||||
#else
|
#else
|
||||||
# include <unistd.h>
|
# include <unistd.h>
|
||||||
|
# include <fcntl.h>
|
||||||
|
# include <sys/wait.h>
|
||||||
# include <wordexp.h>
|
# include <wordexp.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -122,4 +126,152 @@ OSTD_EXPORT void split_args_impl(
|
||||||
}
|
}
|
||||||
|
|
||||||
} /* namespace detail */
|
} /* namespace detail */
|
||||||
|
|
||||||
|
static void stdstream_close(FILE *f) {
|
||||||
|
std::fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
OSTD_EXPORT void process_info::open_impl(
|
||||||
|
std::string const &cmd, std::vector<std::string> const &args,
|
||||||
|
bool use_path
|
||||||
|
) {
|
||||||
|
if (use_in == process_pipe::STDOUT) {
|
||||||
|
throw process_error{EINVAL, std::generic_category()};
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef OSTD_PLATFORM_WIN32
|
||||||
|
auto argp = std::make_unique<char *[]>(args.size() + 1);
|
||||||
|
for (std::size_t i = 0; i < args.size(); ++i) {
|
||||||
|
argp[i] = const_cast<char *>(args[i].data());
|
||||||
|
}
|
||||||
|
argp[args.size()] = nullptr;
|
||||||
|
|
||||||
|
int fd_errno[2]; /* used to detect if exec failed */
|
||||||
|
int fd_stdin[2];
|
||||||
|
int fd_stdout[2];
|
||||||
|
int fd_stderr[2];
|
||||||
|
|
||||||
|
if (
|
||||||
|
(pipe(fd_errno) < 0) ||
|
||||||
|
((use_in == process_pipe::PIPE) && (pipe(fd_stdin) < 0)) ||
|
||||||
|
((use_out == process_pipe::PIPE) && (pipe(fd_stdout) < 0)) ||
|
||||||
|
((use_err == process_pipe::PIPE) && (pipe(fd_stderr) < 0))
|
||||||
|
) {
|
||||||
|
throw process_error{errno, std::generic_category()};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto cpid = fork();
|
||||||
|
if (cpid == -1) {
|
||||||
|
throw process_error{errno, std::generic_category()};
|
||||||
|
} else if (!cpid) {
|
||||||
|
/* child process */
|
||||||
|
::close(fd_errno[0]);
|
||||||
|
/* fcntl fails, write the errno to be read from parent */
|
||||||
|
if (fcntl(fd_errno[1], F_SETFD, FD_CLOEXEC) < 0) {
|
||||||
|
write(fd_errno[1], int(errno), sizeof(int));
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
/* prepare standard streams */
|
||||||
|
if (use_in == process_pipe::PIPE) {
|
||||||
|
/* close writing end */
|
||||||
|
::close(fd_stdin[1]);
|
||||||
|
if (dup2(fd_stdin[0], STDIN_FILENO) < 0) {
|
||||||
|
write(fd_errno[1], int(errno), sizeof(int));
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (use_out == process_pipe::PIPE) {
|
||||||
|
/* close reading end */
|
||||||
|
::close(fd_stdout[0]);
|
||||||
|
if (dup2(fd_stdout[1], STDOUT_FILENO) < 0) {
|
||||||
|
write(fd_errno[1], int(errno), sizeof(int));
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (use_err == process_pipe::PIPE) {
|
||||||
|
/* close reading end */
|
||||||
|
::close(fd_stderr[0]);
|
||||||
|
if (dup2(fd_stderr[1], STDERR_FILENO) < 0) {
|
||||||
|
write(fd_errno[1], int(errno), sizeof(int));
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
} else if (use_err == process_pipe::STDOUT) {
|
||||||
|
if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0) {
|
||||||
|
write(fd_errno[1], int(errno), sizeof(int));
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int ret = 0;
|
||||||
|
if (use_path) {
|
||||||
|
ret = execvp(cmd.data(), argp.get());
|
||||||
|
} else {
|
||||||
|
ret = execv(cmd.data(), argp.get());
|
||||||
|
}
|
||||||
|
std::exit(1);
|
||||||
|
} else {
|
||||||
|
/* parent process */
|
||||||
|
::close(fd_errno[1]);
|
||||||
|
if (use_in == process_pipe::PIPE) {
|
||||||
|
/* close reading end */
|
||||||
|
::close(fd_stdin[0]);
|
||||||
|
in.open(fdopen(fd_stdin[1], "w"), stdstream_close);
|
||||||
|
}
|
||||||
|
if (use_out == process_pipe::PIPE) {
|
||||||
|
/* close writing end */
|
||||||
|
::close(fd_stdout[1]);
|
||||||
|
out.open(fdopen(fd_stdout[0], "r"), stdstream_close);
|
||||||
|
}
|
||||||
|
if (use_err == process_pipe::PIPE) {
|
||||||
|
/* close writing end */
|
||||||
|
::close(fd_stderr[1]);
|
||||||
|
err.open(fdopen(fd_stderr[0], "r"), stdstream_close);
|
||||||
|
}
|
||||||
|
pid = int(cpid);
|
||||||
|
errno_fd = fd_errno[1];
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/* stub for now */
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
OSTD_EXPORT int process_info::close() {
|
||||||
|
if (pid < 0) {
|
||||||
|
throw process_error{ECHILD, std::generic_category()};
|
||||||
|
}
|
||||||
|
#ifndef OSTD_PLATFORM_WIN32
|
||||||
|
int retc = 0;
|
||||||
|
if (pid_t wp; (wp = waitpid(pid, &retc, 0)) < 0) {
|
||||||
|
throw process_error{errno, std::generic_category()};
|
||||||
|
}
|
||||||
|
if (retc) {
|
||||||
|
int eno;
|
||||||
|
auto r = read(errno_fd, &eno, sizeof(int));
|
||||||
|
::close(errno_fd);
|
||||||
|
errno_fd = -1;
|
||||||
|
if (r < 0) {
|
||||||
|
throw process_error{errno, std::generic_category()};
|
||||||
|
} else if (r == sizeof(int)) {
|
||||||
|
throw process_error{eno, std::generic_category()};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retc;
|
||||||
|
#else
|
||||||
|
throw process_error{ECHILD, std::generic_category()};
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
OSTD_EXPORT process_info::~process_info() {
|
||||||
|
try {
|
||||||
|
close();
|
||||||
|
} catch (process_error const &) {}
|
||||||
|
#ifndef OSTD_PLATFORM_WIN32
|
||||||
|
if (errno_fd > 0) {
|
||||||
|
::close(errno_fd);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
} /* namespace ostd */
|
} /* namespace ostd */
|
||||||
|
|
Loading…
Reference in a new issue