diff options
Diffstat (limited to 'libbuild2/utility.hxx')
-rw-r--r-- | libbuild2/utility.hxx | 543 |
1 files changed, 391 insertions, 152 deletions
diff --git a/libbuild2/utility.hxx b/libbuild2/utility.hxx index b62d2ab..b534f41 100644 --- a/libbuild2/utility.hxx +++ b/libbuild2/utility.hxx @@ -4,14 +4,15 @@ #ifndef LIBBUILD2_UTILITY_HXX #define LIBBUILD2_UTILITY_HXX -#include <tuple> // make_tuple() -#include <memory> // make_shared() -#include <string> // to_string() -#include <utility> // move(), forward(), declval(), make_pair(), swap() -#include <cassert> // assert() -#include <iterator> // make_move_iterator() -#include <algorithm> // * -#include <functional> // ref(), cref() +#include <tuple> // make_tuple() +#include <memory> // make_shared() +#include <string> // to_string() +#include <utility> // move(), forward(), declval(), make_pair(), swap() +#include <cassert> // assert() +#include <iterator> // make_move_iterator(), back_inserter() +#include <algorithm> // * +#include <functional> // ref(), cref() +#include <type_traits> #include <libbutl/ft/lang.hxx> @@ -50,10 +51,17 @@ namespace build2 using std::make_tuple; using std::make_shared; using std::make_move_iterator; - using std::to_string; + using std::back_inserter; using std::stoul; using std::stoull; + using std::to_string; + + // Currently only supports base 10 and 16. Note: adds `0x` if base 16. + // + LIBBUILD2_SYMEXPORT string + to_string (uint64_t, int base, size_t width = 0); + // <libbutl/utility.hxx> // using butl::reverse_iterate; @@ -69,6 +77,7 @@ namespace build2 using butl::alpha; using butl::alnum; using butl::digit; + using butl::wspace; using butl::trim; using butl::next_word; @@ -90,12 +99,27 @@ namespace build2 // <libbutl/fdstream.hxx> // + using butl::fdopen_null; using butl::open_file_or_stdin; using butl::open_file_or_stdout; // <libbutl/path-pattern.hxx> // using butl::path_pattern; + using butl::path_match; + + // Perform process-wide initializations/adjustments/workarounds. Should be + // called once early in main(). In particular, besides other things, this + // functions does the following: + // + // - Sets PATH to include baseutils /bin on Windows. + // + // - Ignores SIGPIPE. + // + // - Calls tzset(). + // + LIBBUILD2_SYMEXPORT void + init_process (); // Diagnostics state (verbosity level, etc; see <libbuild2/diagnostics.hxx>). // @@ -113,6 +137,7 @@ namespace build2 init_diag (uint16_t verbosity, bool silent = false, optional<bool> progress = nullopt, + optional<bool> diag_color = nullopt, bool no_lines = false, bool no_columns = false, bool stderr_term = false); @@ -122,13 +147,21 @@ namespace build2 LIBBUILD2_SYMEXPORT extern bool silent; // --[no-]progress + // --[no-]diag-color // LIBBUILD2_SYMEXPORT extern optional<bool> diag_progress_option; + LIBBUILD2_SYMEXPORT extern optional<bool> diag_color_option; LIBBUILD2_SYMEXPORT extern bool diag_no_line; // --no-line LIBBUILD2_SYMEXPORT extern bool diag_no_column; // --no-column - LIBBUILD2_SYMEXPORT extern bool stderr_term; // True if stderr is a terminal. + // True if stderr is a terminal. + // + LIBBUILD2_SYMEXPORT extern bool stderr_term; + + // True if the color can be used on the stderr terminal. + // + LIBBUILD2_SYMEXPORT extern bool stderr_term_color; // Global state (verbosity, home/work directories, etc). @@ -138,6 +171,7 @@ namespace build2 LIBBUILD2_SYMEXPORT void init (void (*terminate) (bool), const char* argv0, + bool serial_stop, optional<bool> mtime_check = nullopt, optional<path> config_sub = nullopt, optional<path> config_guess = nullopt); @@ -156,11 +190,15 @@ namespace build2 LIBBUILD2_SYMEXPORT extern const standard_version build_version; LIBBUILD2_SYMEXPORT extern const string build_version_interface; - // Whether running installed build and, if so, the library installation - // directory (empty otherwise). + // Whether running installed build as well as the library installation + // directory (only if installed, empty otherwise), the exported buildfile + // installation directory (only if configured, empty otherwise), and data + // installation directory (only if installed, src_root otherwise). // LIBBUILD2_SYMEXPORT extern const bool build_installed; LIBBUILD2_SYMEXPORT extern const dir_path build_install_lib; // $install.lib + LIBBUILD2_SYMEXPORT extern const dir_path build_install_buildfile; // $install.buildfile + LIBBUILD2_SYMEXPORT extern const dir_path build_install_data; // $install.data // --[no-]mtime-check // @@ -211,7 +249,7 @@ namespace build2 // Basic process utilities. // - // The run*() functions with process_path assume that you are printing + // The run*() functions with process_path/_env assume that you are printing // the process command line yourself. // Search for a process executable. Issue diagnostics and throw failed in @@ -245,126 +283,55 @@ namespace build2 [[noreturn]] LIBBUILD2_SYMEXPORT void run_search_fail (const path&, const location& = location ()); - // Wait for process termination returning true if the process exited - // normally with a zero code and false otherwise. The latter case is - // normally followed up with a call to run_finish(). - // - LIBBUILD2_SYMEXPORT bool - run_wait (const char* args[], process&, const location& = location ()); - - bool - run_wait (cstrings& args, process&, const location& = location ()); - - // Wait for process termination. Issue diagnostics and throw failed in case - // of abnormal termination. If the process has terminated normally but with - // a non-zero exit status, then assume the diagnostics has already been - // issued and just throw failed. The last argument is used in cooperation - // with run_start() in case STDERR is redirected to STDOUT. - // - void - run_finish (const char* args[], - process&, - const string& = string (), - const location& = location ()); - - void - run_finish (cstrings& args, process& pr, const location& l = location ()); - - // As above but if the process has exited normally with a non-zero code, - // then return false rather than throwing. - // - bool - run_finish_code (const char* args[], - process&, - const string& = string (), - const location& = location ()); - - // Start a process with the specified arguments. If in is -1, then redirect - // STDIN to a pipe (can also be -2 to redirect to /dev/null or equivalent). - // If out is -1, redirect STDOUT to a pipe. If error is false, then - // redirecting STDERR to STDOUT (this can be used to suppress diagnostics - // from the child process). Issue diagnostics and throw failed in case of an - // error. + // Start a process with the specified arguments. Issue diagnostics and throw + // failed in case of an error. If in is -1, then redirect stdin to a pipe + // (can also be -2 to redirect it to /dev/null or equivalent). If out is -1, + // then redirect stdout to a pipe. If stderr is redirected to stdout (can + // be used to analyze diagnostics from the child process), then, in case of + // an error, the last line read from stdout must be passed to run_finish() + // below. // LIBBUILD2_SYMEXPORT process run_start (uint16_t verbosity, const process_env&, // Implicit-constructible from process_path. - const char* args[], + const char* const* args, int in = 0, int out = 1, - bool error = true, - const dir_path& cwd = dir_path (), - const location& = location ()); + int err = 2, + const location& = {}); inline process run_start (uint16_t verbosity, const process_env& pe, - cstrings& args, + const cstrings& args, int in = 0, int out = 1, - bool error = true, - const dir_path& cwd = dir_path (), - const location& l = location ()) + int err = 2, + const location& l = {}) { - return run_start (verbosity, pe, args.data (), in, out, error, cwd, l); + return run_start (verbosity, pe, args.data (), in, out, err, l); } inline process run_start (const process_env& pe, - const char* args[], + const char* const* args, int in = 0, int out = 1, - bool error = true, - const dir_path& cwd = dir_path (), - const location& l = location ()) + int err = 2, + const location& l = {}) { - return run_start (verb_never, pe, args, in, out, error, cwd, l); + return run_start (verb_never, pe, args, in, out, err, l); } inline process run_start (const process_env& pe, - cstrings& args, + const cstrings& args, int in = 0, int out = 1, - bool error = true, - const dir_path& cwd = dir_path (), - const location& l = location ()) - { - return run_start (pe, args.data (), in, out, error, cwd, l); - } - - inline void - run (const process_env& pe, // Implicit-constructible from process_path. - const char* args[]) + int err = 2, + const location& l = {}) { - process pr (run_start (pe, args)); - run_finish (args, pr); - } - - inline void - run (const process_env& pe, // Implicit-constructible from process_path. - cstrings& args) - { - run (pe, args.data ()); - } - - inline void - run (const process_path& p, - const char* args[], - const dir_path& cwd, - const char* const* env = nullptr) - { - process pr (run_start (process_env (p, env), args, 0, 1, true, cwd)); - run_finish (args, pr); - } - - inline void - run (const process_path& p, - cstrings& args, - const dir_path& cwd, - const char* const* env = nullptr) - { - run (p, args.data (), cwd, env); + return run_start (pe, args.data (), in, out, err, l); } // As above, but search for the process (including updating args[0]) and @@ -375,16 +342,16 @@ namespace build2 const char* args[], int in = 0, int out = 1, - bool error = true, - const dir_path& cwd = dir_path (), + int err = 2, const char* const* env = nullptr, - const location& l = location ()) + const dir_path& cwd = {}, + const location& l = {}) { process_path pp (run_search (args[0], l)); return run_start (verbosity, - process_env (pp, env), args, - in, out, error, - cwd, l); + process_env (pp, cwd, env), args, + in, out, err, + l); } inline process @@ -392,55 +359,215 @@ namespace build2 cstrings& args, int in = 0, int out = 1, - bool error = true, - const dir_path& cwd = dir_path (), + int err = 2, const char* const* env = nullptr, - const location& l = location ()) + const dir_path& cwd = {}, + const location& l = {}) { - return run_start (verbosity, args.data (), in, out, error, cwd, env, l); + return run_start (verbosity, args.data (), in, out, err, env, cwd, l); } + // Wait for process termination returning true if the process exited + // normally with a zero code and false otherwise. The latter case is + // normally followed up with a call to run_finish(). + // + LIBBUILD2_SYMEXPORT bool + run_wait (const char* const* args, process&, const location& = location ()); + + bool + run_wait (const cstrings& args, process&, const location& = location ()); + + // Wait for process termination, issues diagnostics, and throw failed. + // + // If the child process exited abnormally or normally with non-0 code, then + // print the error diagnostics to this effect. Additionally, if the + // verbosity level is between 1 and the specified value, then print the + // command line as info after the error. If omit_normal is true, then don't + // print either for the normal exit (usually used for custom diagnostics or + // when process failure can be tolerated). + // + // Normally the specified verbosity will be 1 and the command line args + // represent the verbosity level 2 (logical) command line. Or, to put it + // another way, it should be 1 less than what gets passed to run_start(). + // Note that args should only represent a single command in a pipe (see + // print_process() for details). + // + // See also diag_buffer::close(). + // + // The line argument is used in cooperation with run_start() to diagnose a + // failure to exec in case stderr is redirected to stdout (see the + // implementation for details). + // + void + run_finish (const char* const* args, + process&, + uint16_t verbosity, + bool omit_normal = false, + const location& = location ()); + + void + run_finish (const cstrings& args, + process&, + uint16_t verbosity, + bool omit_normal = false, + const location& = location ()); + + void + run_finish (const char* const* args, + process&, + const string& line, + uint16_t verbosity, + bool omit_normal = false, + const location& = location ()); + + // As above but if the process has exited normally with a non-zero code, + // then return false rather than throwing. + // + // Note that the normal non-0 exit diagnostics is omitted by default + // assuming appropriate custom diagnostics will be issued, if required. + // + bool + run_finish_code (const char* const* args, + process&, + uint16_t verbosity, + bool omit_normal = true, + const location& = location ()); + + bool + run_finish_code (const cstrings& args, + process&, + uint16_t verbosity, + bool omit_normal = true, + const location& = location ()); + + bool + run_finish_code (const char* const* args, + process&, + const string&, + uint16_t verbosity, + bool omit_normal = true, + const location& = location ()); + + // As above but with diagnostics buffering. + // + // Specifically, this version first waits for the process termination, then + // calls diag_buffer::close(verbosity, omit_normal), and finally throws + // failed if the process didn't exit with 0 code. + // + class diag_buffer; + + void + run_finish (diag_buffer&, + const char* const* args, + process&, + uint16_t verbosity, + bool omit_normal = false, + const location& = location ()); + + void + run_finish (diag_buffer&, + const cstrings& args, + process&, + uint16_t verbosity, + bool omit_normal = false, + const location& = location ()); + + // As above but if the process has exited normally with a non-zero code, + // then return false rather than throwing. + // + // Note that the normal non-0 exit diagnostics is omitted by default + // assuming appropriate custom diagnostics will be issued, if required. + // + bool + run_finish_code (diag_buffer&, + const char* const* args, + process&, + uint16_t verbosity, + bool omit_normal = true, + const location& = location ()); + + bool + run_finish_code (diag_buffer&, + const cstrings& args, + process&, + uint16_t verbosity, + bool omit_normal = true, + const location& = location ()); + + // Run the process with the specified arguments by calling the above start + // and finish functions. Buffer diagnostics unless in the load phase. + // + LIBBUILD2_SYMEXPORT void + run (context&, + const process_env& pe, // Implicit-constructible from process_path. + const char* const* args, + uint16_t finish_verbosity); + inline void - run (uint16_t verbosity, - const char* args[], - const dir_path& cwd = dir_path (), - const char* const* env = nullptr) + run (context& ctx, + const process_env& pe, + const cstrings& args, + uint16_t finish_verbosity) { - process pr (run_start (verbosity, args, 0, 1, true, cwd, env)); - run_finish (args, pr); + run (ctx, pe, args.data (), finish_verbosity); } + // As above but pass cwd/env vars as arguments rather than as part of + // process_env. + // inline void - run (uint16_t verbosity, - cstrings& args, - const dir_path& cwd = dir_path (), - const char* const* env = nullptr) + run (context& ctx, + const process_path& p, + const char* const* args, + uint16_t finish_verbosity, + const char* const* env, + const dir_path& cwd = {}) { - run (verbosity, args.data (), cwd, env); + run (ctx, process_env (p, cwd, env), args, finish_verbosity); + } + + inline void + run (context& ctx, + const process_path& p, + const cstrings& args, + uint16_t finish_verbosity, + const char* const* env, + const dir_path& cwd = {}) + { + run (ctx, p, args.data (), finish_verbosity, env, cwd); } // Start the process as above and then call the specified function on each // trimmed line of the output until it returns a non-empty object T (tested // with T::empty()) which is then returned to the caller. // + // If verbosity is specified, print the process commands line at that level + // (with the verbosite-1 value passed run_finish()). + // + // If error is false, then redirecting stderr to stdout and don't fail if + // the process exits normally but with non-0 code (can be used to suppress + // and/or analyze diagnostics from the child process). Otherwise, buffer + // diagnostics unless in the load phase. + // // The predicate can move the value out of the passed string but, if error // is false, only in case of a "content match" (so that any diagnostics // lines are left intact). The function signature should be: // // T (string& line, bool last) // - // If ignore_exit is true, then the program's exit status is ignored (if it - // is false and the program exits with the non-zero status, then an empty T - // instance is returned). + // If, in addition to error being false, ignore_exit is true, then the + // program's normal exit status is ignored (if it is false and the program + // exits with the non-zero status, then an empty T instance is returned). // // If checksum is not NULL, then feed it the content of each trimmed line // (including those that come after the callback returns non-empty object). // template <typename T, typename F> T - run (uint16_t verbosity, + run (context&, + uint16_t verbosity, const process_env&, // Implicit-constructible from process_path. - const char* args[], + const char* const* args, F&&, bool error = true, bool ignore_exit = false, @@ -448,20 +575,55 @@ namespace build2 template <typename T, typename F> inline T - run (const process_env& pe, // Implicit-constructible from process_path. - const char* args[], + run (context& ctx, + uint16_t verbosity, + const process_env& pe, + const cstrings& args, + F&& f, + bool error = true, + bool ignore_exit = false, + sha256* checksum = nullptr) + { + return run<T> (ctx, + verbosity, + pe, args.data (), + forward<F> (f), + error, ignore_exit, checksum); + } + + template <typename T, typename F> + inline T + run (context&, + const process_env&, + const char* const* args, + uint16_t finish_verbosity, + F&&, + bool error = true, + bool ignore_exit = false, + sha256* checksum = nullptr); + + template <typename T, typename F> + inline T + run (context& ctx, + const process_env& pe, + const cstrings& args, + uint16_t finish_verbosity, F&& f, bool error = true, bool ignore_exit = false, sha256* checksum = nullptr) { - return run<T> ( - verb_never, pe, args, forward<F> (f), error, ignore_exit, checksum); + return run<T> (ctx, + pe, args.data (), + finish_verbosity, + forward<F> (f), + error, ignore_exit, checksum); } template <typename T, typename F> inline T - run (uint16_t verbosity, + run (context& ctx, + uint16_t verbosity, const char* args[], F&& f, bool error = true, @@ -469,15 +631,38 @@ namespace build2 sha256* checksum = nullptr) { process_path pp (run_search (args[0])); - return run<T> ( - verbosity, pp, args, forward<F> (f), error, ignore_exit, checksum); + return run<T> (ctx, + verbosity, + pp, args, + forward<F> (f), + error, ignore_exit, checksum); + } + + template <typename T, typename F> + inline T + run (context& ctx, + uint16_t verbosity, + cstrings& args, + F&& f, + bool error = true, + bool ignore_exit = false, + sha256* checksum = nullptr) + { + return run<T> (ctx, + verbosity, + args.data (), + forward<F> (f), + error, ignore_exit, checksum); } + // As above but run a program without any arguments or with one argument. + // // run <prog> // template <typename T, typename F> inline T - run (uint16_t verbosity, + run (context& ctx, + uint16_t verbosity, const path& prog, F&& f, bool error = true, @@ -485,13 +670,20 @@ namespace build2 sha256* checksum = nullptr) { const char* args[] = {prog.string ().c_str (), nullptr}; - return run<T> ( - verbosity, args, forward<F> (f), error, ignore_exit, checksum); + return run<T> (ctx, + verbosity, + args, + forward<F> (f), + error, ignore_exit, checksum); } template <typename T, typename F> - inline T - run (uint16_t verbosity, + inline typename std::enable_if< + (!std::is_same<typename std::decay<F>::type, const char**>::value && + !std::is_same<typename std::remove_reference<F>::type, cstrings>::value), + T>::type + run (context& ctx, + uint16_t verbosity, const process_env& pe, // Implicit-constructible from process_path. F&& f, bool error = true, @@ -499,15 +691,19 @@ namespace build2 sha256* checksum = nullptr) { const char* args[] = {pe.path->recall_string (), nullptr}; - return run<T> ( - verbosity, pe, args, forward<F> (f), error, ignore_exit, checksum); + return run<T> (ctx, + verbosity, + pe, args, + forward<F> (f), + error, ignore_exit, checksum); } // run <prog> <arg> // template <typename T, typename F> inline T - run (uint16_t verbosity, + run (context& ctx, + uint16_t verbosity, const path& prog, const char* arg, F&& f, @@ -516,13 +712,17 @@ namespace build2 sha256* checksum = nullptr) { const char* args[] = {prog.string ().c_str (), arg, nullptr}; - return run<T> ( - verbosity, args, forward<F> (f), error, ignore_exit, checksum); + return run<T> (ctx, + verbosity, + args, + forward<F> (f), + error, ignore_exit, checksum); } template <typename T, typename F> inline T - run (uint16_t verbosity, + run (context& ctx, + uint16_t verbosity, const process_env& pe, // Implicit-constructible from process_path. const char* arg, F&& f, @@ -531,8 +731,47 @@ namespace build2 sha256* checksum = nullptr) { const char* args[] = {pe.path->recall_string (), arg, nullptr}; - return run<T> ( - verbosity, pe, args, forward<F> (f), error, ignore_exit, checksum); + return run<T> (ctx, + verbosity, + pe, args, + forward<F> (f), + error, ignore_exit, checksum); + } + + // As above but a lower-level interface that erases T and F and can also be + // used to suppress trimming. + // + // The passed function should return true if it should be called again + // (i.e., the object is still empty in the T & F interface) and false + // otherwise. + // + // The first version ruturn true if the result is usable and false + // otherwise, depending on the process exit code and error/ignore_exit + // values. (In the latter case, the T & F interface makes the resulting + // object empty). + // + LIBBUILD2_SYMEXPORT bool + run (context&, + uint16_t verbosity, + const process_env&, + const char* const* args, + uint16_t finish_verbosity, + const function<bool (string& line, bool last)>&, + bool trim = true, + bool error = true, + bool ignore_exit = false, + sha256* checksum = nullptr); + + // Concatenate the program path and arguments into a shallow NULL-terminated + // vector of C-strings. + // + LIBBUILD2_SYMEXPORT cstrings + process_args (const char* program, const strings& args); + + inline cstrings + process_args (const string& program, const strings& args) + { + return process_args (program.c_str (), args); } // File descriptor streams. |