From f6c3788de3d148c90aba705d045b1d92e7fea20a Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 8 Jan 2018 11:07:17 +0200 Subject: Complete runtime/stdlib detection --- build2/cc/gcc.cxx | 7 +- build2/cc/guess.cxx | 334 +++++++++++++++++++++++++++++++++------- build2/cc/guess.hxx | 23 ++- build2/cc/module.cxx | 16 +- build2/cc/msvc.cxx | 6 +- build2/diagnostics.hxx | 7 +- build2/types.hxx | 1 + build2/utility.cxx | 3 +- build2/utility.hxx | 83 ++++++---- build2/utility.txx | 8 +- build2/version/snapshot-git.cxx | 5 +- 11 files changed, 389 insertions(+), 104 deletions(-) diff --git a/build2/cc/gcc.cxx b/build2/cc/gcc.cxx index 0ce4c4f..ec69524 100644 --- a/build2/cc/gcc.cxx +++ b/build2/cc/gcc.cxx @@ -180,7 +180,12 @@ namespace build2 if (verb >= 3) print_process (args); - process pr (run_start (xc, args.data (), -1)); // Open pipe to stdout. + // Open pipe to stdout. + // + process pr (run_start (xc, + args.data (), + 0, /* stdin */ + -1 /* stdout */)); string l; try diff --git a/build2/cc/guess.cxx b/build2/cc/guess.cxx index e3eeb82..32c3166 100644 --- a/build2/cc/guess.cxx +++ b/build2/cc/guess.cxx @@ -37,6 +37,127 @@ namespace build2 return string (); // Never reached. } + // Standard library detection for GCC-class compilers. + // + // The src argument should detect the standard library based on the + // preprocessor macros and output the result in the stdlib:="XXX" form. + // + static string + stdlib (lang xl, + const process_path& xc, + const strings* c_po, const strings* x_po, + const strings* c_co, const strings* x_co, + const char* src) + { + cstrings args {xc.recall_string ()}; + if (c_po != nullptr) append_options (args, *c_po); + if (x_po != nullptr) append_options (args, *x_po); + if (c_co != nullptr) append_options (args, *c_co); + if (x_co != nullptr) append_options (args, *x_co); + args.push_back ("-x"); + switch (xl) + { + case lang::c: args.push_back ("c"); break; + case lang::cxx: args.push_back ("c++"); break; + } + args.push_back ("-E"); + args.push_back ("-"); // Read stdin. + args.push_back (nullptr); + + // The source we are going to preprocess may contains #include's which + // may fail to resolve if, for example, there is no standard library + // (-nostdinc/-nostdinc++). So we are going to suppress diagnostics and + // assume the error exit code means no standard library (of course it + // could also be because there is something wrong with the compiler or + // options but that we simply leave to blow up later). + // + process pr (run_start (3 /* verbosity */, + args.data (), + -1 /* stdin */, + -1 /* stdout */, + false /* error */)); + string l, r; + try + { + // Here we have to simultaneously write to stdin and read from stdout + // with both operations having the potential to block. For now we + // assume that src fits into the pipe's buffer. + // + ofdstream os (move (pr.out_fd)); + ifdstream is (move (pr.in_ofd), + fdstream_mode::skip, + ifdstream::badbit); + + os << src << endl; + os.close (); + + while (!eof (getline (is, l))) + { + size_t p (l.find_first_not_of (' ')); + + if (p != string::npos && l.compare (p, 9, "stdlib:=\"") == 0) + { + p += 9; + r = string (l, p, l.size () - p - 1); // One for closing \". + break; + } + } + + is.close (); + } + catch (const io_error&) + { + // Presumably the child process failed. Let run_finish() deal with + // that. + } + + if (!run_finish (args.data (), pr, false /* error */, l)) + r = "none"; + + if (r.empty ()) + fail << "unable to determine " << xl << " standard library"; + + return r; + } + + // C standard library detection on POSIX (i.e., non-Windows) systems. + // Notes: + // + // - We place platform macro-based checks (__FreeBSD__, __APPLE__, etc) + // after library macro-based ones in case a non-default libc is used. + // + static const char* c_stdlib_src = +"#if !defined(__STDC_HOSTED__) || __STDC_HOSTED__ == 1 \n" +"# include /* Forces defining __KLIBC__ for klibc. */ \n" +"# include /* Includes features.h for glibc. */ \n" +"# include /* Includes sys/cdefs.h for bionic. */ \n" +" /* Includes sys/features.h for newlib. */ \n" +" /* Includes features.h for uclibc. */ \n" +"# if defined(__KLIBC__) \n" +" stdlib:=\"klibc\" \n" +"# elif defined(__BIONIC__) \n" +" stdlib:=\"bionic\" \n" +"# elif defined(__NEWLIB__) \n" +" stdlib:=\"newlib\" \n" +"# elif defined(__UCLIBC__) \n" +" stdlib:=\"uclibc\" \n" +"# elif defined(__dietlibc__) /* Also has to be defined manually by */ \n" +" stdlib:=\"dietlibc\" /* or some wrapper. */ \n" +"# elif defined(__MUSL__) /* This libc refuses to define __MUSL__ */ \n" +" stdlib:=\"musl\" /* so it has to be defined by user. */ \n" +"# elif defined(__GLIBC__) /* Check for glibc last since some libc's */ \n" +" stdlib:=\"glibc\" /* pretend to be it. */ \n" +"# elif defined(__FreeBSD__) \n" +" stdlib:=\"freebsd\" \n" +"# elif defined(__APPLE__) \n" +" stdlib:=\"apple\" \n" +"# else \n" +" stdlib:=\"other\" \n" +"# endif \n" +"#else \n" +" stdlib:=\"none\" \n" +"#endif \n"; + // Pre-guess the compiler type based on the compiler executable name. // Return empty string if can't make a guess (for example, because the // compiler name is a generic 'c++'). Note that it only guesses the type, @@ -147,7 +268,7 @@ namespace build2 guess_result r; - process_path pp (run_search (xc, true)); + process_path xp (run_search (xc, true)); // Start with -v. This will cover gcc and clang. // @@ -248,7 +369,7 @@ namespace build2 // Suppress all the compiler errors because we may be trying an // unsupported option. // - r = run (3, pp, "-v", f, false, false, &cs); + r = run (3, xp, "-v", f, false, false, &cs); if (!r.empty ()) { @@ -284,7 +405,7 @@ namespace build2 return guess_result (); }; - r = run (3, pp, "--version", f, false); + r = run (3, xp, "--version", f, false); } // Finally try to run it without any options to detect msvc. @@ -317,7 +438,7 @@ namespace build2 return guess_result (); }; - r = run (3, pp, f, false); + r = run (3, xp, f, false); } if (!r.empty ()) @@ -335,7 +456,7 @@ namespace build2 l5 ([&]{trace << xc << " is " << r.id << ": '" << r.signature << "'";}); - r.path = move (pp); + r.path = move (xp); } } else @@ -397,12 +518,15 @@ namespace build2 static compiler_info guess_gcc (lang xl, const path& xc, - const strings* c_coptions, - const strings* x_coptions, + const strings* c_po, const strings* x_po, + const strings* c_co, const strings* x_co, + const strings*, const strings*, guess_result&& gr) { tracer trace ("cc::guess_gcc"); + const process_path& xp (gr.path); + // Extract the version. The signature line has the following format // though language words can be translated and even rearranged (see // examples above). @@ -478,8 +602,8 @@ namespace build2 // -dumpmachine (older gcc or not multi-arch). // cstrings args {xc.string ().c_str (), "-print-multiarch"}; - if (c_coptions != nullptr) append_options (args, *c_coptions); - if (x_coptions != nullptr) append_options (args, *x_coptions); + if (c_co != nullptr) append_options (args, *c_co); + if (x_co != nullptr) append_options (args, *x_co); args.push_back (nullptr); // The output of both -print-multiarch and -dumpmachine is a single line @@ -487,7 +611,7 @@ namespace build2 // auto f = [] (string& l) {return move (l);}; - string t (run (3, args.data (), f, false)); + string t (run (3, xp, args.data (), f, false)); if (t.empty ()) { @@ -495,7 +619,7 @@ namespace build2 << "falling back to -dumpmachine";}); args[1] = "-dumpmachine"; - t = run (3, args.data (), f); + t = run (3, xp, args.data (), f); } if (t.empty ()) @@ -504,6 +628,12 @@ namespace build2 string ot (t); + // Parse the target into triplet (for further tests) ignoring any + // failures. + // + target_triplet tt; + try {tt = target_triplet (t);} catch (const invalid_argument&) {} + // Derive the toolchain pattern. Try cc/c++ as a fallback. // string pat (pattern (xc, xl == lang::c ? "gcc" : "g++")); @@ -511,11 +641,32 @@ namespace build2 if (pat.empty ()) pat = pattern (xc, xl == lang::c ? "cc" : "c++"); - + // Runtime and standard library. + // // GCC always uses libgcc (even on MinGW). Even with -nostdlib GCC's // documentation says that you should usually specify -lgcc. // - string rt ("libgcc"); + string rt ("libgcc"); + string csl (tt.system == "mingw32" + ? "msvc" + : stdlib (xl, xp, c_po, x_po, c_co, x_co, c_stdlib_src)); + string xsl; + switch (xl) + { + case lang::c: xsl = csl; break; + case lang::cxx: + { + // While GCC only supports it's own C++ standard library (libstdc++) + // we still run the test to detect the "none" case (-nostdinc++). + // + const char* src = + "#include \n" + "stdlib:=\"libstdc++\" \n"; + + xsl = stdlib (xl, xp, c_po, x_po, c_co, x_co, src); + break; + } + } return compiler_info { move (gr.path), @@ -529,17 +680,20 @@ namespace build2 move (pat), "", move (rt), - "", - ""}; + move (csl), + move (xsl)}; } static compiler_info guess_clang (lang xl, const path& xc, - const strings* c_coptions, - const strings* x_coptions, + const strings* c_po, const strings* x_po, + const strings* c_co, const strings* x_co, + const strings* c_lo, const strings* x_lo, guess_result&& gr) { + const process_path& xp (gr.path); + // Extract the version. Here we will try to handle both vanilla and // Apple clang since the signature lines are fairly similar. They have // the following format though language words can probably be translated @@ -613,15 +767,16 @@ namespace build2 // Unlike gcc, clang doesn't have -print-multiarch. Its -dumpmachine, // however, respects the compile options (e.g., -m32). // - cstrings args {xc.string ().c_str (), "-dumpmachine"}; - if (c_coptions != nullptr) append_options (args, *c_coptions); - if (x_coptions != nullptr) append_options (args, *x_coptions); + cstrings args {xp.recall_string (), "-dumpmachine"}; + if (c_co != nullptr) append_options (args, *c_co); + if (x_co != nullptr) append_options (args, *x_co); args.push_back (nullptr); // The output of -dumpmachine is a single line containing just the // target triplet. // - string t (run (3, args.data (), [](string& l) {return move (l);})); + auto f = [] (string& l) {return move (l);}; + string t (run (3, xp, args.data (), f)); if (t.empty ()) fail << "unable to extract target architecture from " << xc @@ -664,14 +819,16 @@ namespace build2 if (pat.empty ()) pat = pattern (xc, xl == lang::c ? "cc" : "c++"); + // Runtime and standard library. + // // Clang can use libgcc, its own compiler-rt, or, on Windows targeting // MSVC, the VC's runtime. As usual, there is no straightforward way // to query this and silence on the mailing list. See: // // http://lists.llvm.org/pipermail/cfe-dev/2018-January/056494.html // - // So for now we will just look for --rtlib and if none specified, - // assume some platform-specific defaults. + // So for now we will just look for --rtlib (note: linker option) and if + // none specified, assume some platform-specific defaults. // string rt; { @@ -683,19 +840,53 @@ namespace build2 }; const string* o; - if ((o = find_rtlib (x_coptions)) != nullptr || - (o = find_rtlib (c_coptions)) != nullptr) + if ((o = find_rtlib (x_lo)) != nullptr || + (o = find_rtlib (c_lo)) != nullptr) { rt = string (*o, 8); } - // Defaults. - // else if (tt.system == "win32-msvc") rt = "msvc"; else if (tt.system == "linux-gnu" || tt.system == "freebsd") rt = "libgcc"; else /* Mac OS, etc. */ rt = "compiler-rt"; } + string csl (tt.system == "win32-msvc" || tt.system == "mingw32" + ? "msvc" + : stdlib (xl, xp, c_po, x_po, c_co, x_co, c_stdlib_src)); + + string xsl; + switch (xl) + { + case lang::c: xsl = csl; break; + case lang::cxx: + { + // All Clang versions that we care to support have __has_include() + // so we use it to determine which standard library is available. + // + // Note that we still include the corresponding headers to verify + // things are usable. For the "other" case we include some + // standard header to detect the "none" case (e.g, -nostdinc++). + // + const char* src = + "#if __has_include(<__config>) \n" + " #include <__config> \n" + " stdlib:=\"libc++\" \n" + "#elif __has_include() \n" + " #include \n" + " stdlib:=\"libstdc++\" \n" + "#else \n" + " #include \n" + " stdlib:=\"other\" \n" + "#endif \n"; + + xsl = tt.system == "win32-msvc" + ? "msvcp" + : stdlib (xl, xp, c_po, x_po, c_co, x_co, src); + break; + } + } + return compiler_info { move (gr.path), move (gr.id), @@ -708,17 +899,20 @@ namespace build2 move (pat), "", move (rt), - "", - ""}; + move (csl), + move (xsl)}; } static compiler_info guess_icc (lang xl, const path& xc, - const strings* c_coptions, - const strings* x_coptions, + const strings* c_po, const strings* x_po, + const strings* c_co, const strings* x_co, + const strings*, const strings*, guess_result&& gr) { + const process_path& xp (gr.path); + // Extract the version. If the version has the fourth component, then // the signature line (extracted with --version) won't include it. So we // will have to get a more elaborate line with -V. We will also have to @@ -750,13 +944,13 @@ namespace build2 auto f = [] (string& l) { return l.compare (0, 5, "Intel") == 0 && (l[5] == '(' || l[5] == ' ') - ? move (l) - : string (); + ? move (l) + : string (); }; // The -V output is sent to STDERR. // - s = run (3, xc, "-V", f, false); + s = run (3, xp, "-V", f, false); if (s.empty ()) fail << "unable to extract signature from " << xc << " -V output"; @@ -849,13 +1043,13 @@ namespace build2 // "Intel(R)" "MIC" (-dumpmachine says: x86_64-k1om-linux) // cstrings args {xc.string ().c_str (), "-V"}; - if (c_coptions != nullptr) append_options (args, *c_coptions); - if (x_coptions != nullptr) append_options (args, *x_coptions); + if (c_co != nullptr) append_options (args, *c_co); + if (x_co != nullptr) append_options (args, *x_co); args.push_back (nullptr); // The -V output is sent to STDERR. // - string t (run (3, args.data (), f, false)); + string t (run (3, xp, args.data (), f, false)); if (t.empty ()) fail << "unable to extract target architecture from " << xc @@ -897,7 +1091,10 @@ namespace build2 // on which we are running), who knows what will happen in the future. // So instead we are going to use -dumpmachine and substitute the CPU. // - t = run (3, xc, "-dumpmachine", [](string& l) {return move (l);}); + { + auto f = [] (string& l) {return move (l);}; + t = run (3, xp, "-dumpmachine", f); + } if (t.empty ()) fail << "unable to extract target architecture from " << xc @@ -931,7 +1128,23 @@ namespace build2 // Runtime and standard library. // - string rt (tt.system == "win32-msvc" ? "msvc" : "libgcc"); + // For now we assume that unless it is Windows, we are targeting + // Linux/GCC. + // + string rt (tt.system == "win32-msvc" ? "msvc" : "libgcc"); + string csl (tt.system == "win32-msvc" + ? "msvc" + : stdlib (xl, xp, c_po, x_po, c_co, x_co, c_stdlib_src)); + string xsl; + switch (xl) + { + case lang::c: xsl = csl; break; + case lang::cxx: + { + xsl = tt.system == "win32-msvc" ? "msvcp" : "libstdc++"; + break; + } + } return compiler_info { move (gr.path), @@ -945,15 +1158,16 @@ namespace build2 move (pat), "", move (rt), - "", - ""}; + move (csl), + move (xsl)}; } static compiler_info - guess_msvc (lang, + guess_msvc (lang xl, const path& xc, - const strings*, - const strings*, + const strings*, const strings*, + const strings*, const strings*, + const strings*, const strings*, guess_result&& gr) { // Extract the version. The signature line has the following format @@ -1170,6 +1384,13 @@ namespace build2 // Runtime and standard library. // string rt ("msvc"); + string csl ("msvc"); + string xsl; + switch (xl) + { + case lang::c: xsl = csl; break; + case lang::cxx: xsl = "msvcp"; break; + } return compiler_info { move (gr.path), @@ -1183,15 +1404,16 @@ namespace build2 move (cpat), move (bpat), move (rt), - "", - ""}; + move (csl), + move (xsl)}; } compiler_info guess (lang xl, const path& xc, - const strings* c_coptions, - const strings* x_coptions) + const strings* c_po, const strings* x_po, + const strings* c_co, const strings* x_co, + const strings* c_lo, const strings* x_lo) { string pre (pre_guess (xl, xc)); guess_result gr; @@ -1223,26 +1445,34 @@ namespace build2 case compiler_id::gcc: { assert (id.variant.empty ()); - r = guess_gcc (xl, xc, c_coptions, x_coptions, move (gr)); + r = guess_gcc (xl, xc, + c_po, x_po, c_co, x_co, c_lo, x_lo, + move (gr)); break; } case compiler_id::clang: case compiler_id::clang_apple: { assert (id.variant.empty () || id.variant == "apple"); - r = guess_clang (xl, xc, c_coptions, x_coptions, move (gr)); + r = guess_clang (xl, xc, + c_po, x_po, c_co, x_co, c_lo, x_lo, + move (gr)); break; } case compiler_id::msvc: { assert (id.variant.empty ()); - r = guess_msvc (xl, xc, c_coptions, x_coptions, move (gr)); + r = guess_msvc (xl, xc, + c_po, x_po, c_co, x_co, c_lo, x_lo, + move (gr)); break; } case compiler_id::icc: { assert (id.variant.empty ()); - r = guess_icc (xl, xc, c_coptions, x_coptions, move (gr)); + r = guess_icc (xl, xc, + c_po, x_po, c_co, x_co, c_lo, x_lo, + move (gr)); break; } } diff --git a/build2/cc/guess.hxx b/build2/cc/guess.hxx index 9d87cb4..d6ca28d 100644 --- a/build2/cc/guess.hxx +++ b/build2/cc/guess.hxx @@ -167,15 +167,21 @@ namespace build2 // msvc // // The C standard library is normally the library/project name (e.g, - // glibc, musl, newlib, klibc, etc) but if there is none, then we - // fallback to the vendor name (e.g., freebsd). Proposed values are (any - // BSD-derived libc should end with the *bsd suffix): + // glibc, klibc, newlib, etc) but if there is none, then we fallback to + // the vendor name (e.g., freebsd, apple). Current values are: // // glibc // msvc (msvcrt.lib/msvcrNNN.dll) // freebsd - // applebsd - // cygwin? (apparently newlib) + // apple + // newlib (also used by Cygwin) + // klibc + // bionic + // uclibc + // musl + // dietlibc + // other + // none // // The C++ standard library is normally the library/project name. // Current values are: @@ -183,6 +189,8 @@ namespace build2 // libstdc++ // libc++ // msvcp (msvcprt.lib/msvcpNNN.dll) + // other + // none // string runtime; string c_stdlib; @@ -197,8 +205,9 @@ namespace build2 compiler_info guess (lang, const path& xc, - const strings* c_coptions, - const strings* x_coptions); + const strings* c_poptions, const strings* x_poptions, + const strings* c_coptions, const strings* x_coptions, + const strings* c_loptions, const strings* x_loptions); // Given a language, toolchain id, and optionally (empty) a pattern, // return an appropriate default compiler path. diff --git a/build2/cc/module.cxx b/build2/cc/module.cxx index 5b1a79a..1020bd6 100644 --- a/build2/cc/module.cxx +++ b/build2/cc/module.cxx @@ -42,7 +42,9 @@ namespace build2 config::save_module (rs, x, 250); + const variable& config_c_poptions (var_pool["config.cc.poptions"]); const variable& config_c_coptions (var_pool["config.cc.coptions"]); + const variable& config_c_loptions (var_pool["config.cc.loptions"]); // config.x // @@ -107,8 +109,12 @@ namespace build2 const path& xc (cast (*p.first)); ci = build2::cc::guess (x_lang, xc, + cast_null (rs[config_c_poptions]), + cast_null (rs[config_x_poptions]), cast_null (rs[config_c_coptions]), - cast_null (rs[config_x_coptions])); + cast_null (rs[config_x_coptions]), + cast_null (rs[config_c_loptions]), + cast_null (rs[config_x_loptions])); // Split/canonicalize the target. First see if the user asked us to // use config.sub. @@ -171,7 +177,7 @@ namespace build2 rs.assign (x_pattern) = ci.pattern; if (!x_stdlib.aliases (c_stdlib)) - rs.assign (x_stdlib) = move (ci.x_stdlib); + rs.assign (x_stdlib) = ci.x_stdlib; new_ = p.second; @@ -351,11 +357,11 @@ namespace build2 if (ct != ci.original_target) dr << " (" << ci.original_target << ")"; - - // @@ Print c_stdlib? - // dr << "\n runtime " << ci.runtime << "\n stdlib " << ci.x_stdlib; + + if (!x_stdlib.aliases (c_stdlib)) + dr << "\n c stdlib " << ci.c_stdlib; } if (!tstd.empty ()) diff --git a/build2/cc/msvc.cxx b/build2/cc/msvc.cxx index 59bb55c..b418f78 100644 --- a/build2/cc/msvc.cxx +++ b/build2/cc/msvc.cxx @@ -157,7 +157,11 @@ namespace build2 // Link.exe seem to always dump everything to stdout but just in case // redirect stderr to stdout. // - process pr (run_start (ld, args, -1 /* stdout */, false /* error */)); + process pr (run_start (ld, + args, + 0 /* stdin */, + -1 /* stdout */, + false /* error */)); bool obj (false), dll (false); string s; diff --git a/build2/diagnostics.hxx b/build2/diagnostics.hxx index 9a020d3..653d9a6 100644 --- a/build2/diagnostics.hxx +++ b/build2/diagnostics.hxx @@ -59,8 +59,11 @@ namespace build2 // // While uint8 is more than enough, use uint16 for the ease of printing. // - extern uint16_t verb; - const uint16_t verb_never = 7; + + // Forward-declarated in utility.hxx. + // + // extern uint16_t verb; + // const uint16_t verb_never = 7; template inline void l1 (const F& f) {if (verb >= 1) f ();} template inline void l2 (const F& f) {if (verb >= 2) f ();} diff --git a/build2/types.hxx b/build2/types.hxx index 9ddc427..aba36a4 100644 --- a/build2/types.hxx +++ b/build2/types.hxx @@ -261,6 +261,7 @@ namespace build2 using butl::auto_fd; using butl::ifdstream; using butl::ofdstream; + using butl::fdstream_mode; // // diff --git a/build2/utility.cxx b/build2/utility.cxx index 280edd3..4196b03 100644 --- a/build2/utility.cxx +++ b/build2/utility.cxx @@ -210,13 +210,14 @@ namespace build2 process run_start (const process_path& pp, const char* args[], + int in, int out, bool err, const dir_path& cwd) try { assert (args[0] == pp.recall_string ()); - return process (pp, args, 0, out, (err ? 2 : 1), cwd.string ().c_str ()); + return process (pp, args, in, out, (err ? 2 : 1), cwd.string ().c_str ()); } catch (const process_error& e) { diff --git a/build2/utility.hxx b/build2/utility.hxx index cfdaee0..bbd6bc3 100644 --- a/build2/utility.hxx +++ b/build2/utility.hxx @@ -164,16 +164,19 @@ namespace build2 string diag_relative (const path&, bool current = true); + // Diagnostics forward declarations (see diagnostics.hxx). + // + extern uint16_t verb; + const uint16_t verb_never = 7; + + void + print_process (const char* const*, size_t); + // Basic process utilities. // // The run*() functions with process_path assume that you are printing // the process command line yourself. - extern uint16_t verb; // diagnostics.hxx - - void - print_process (const char* const*, size_t); // diagnostics.hxx - // Search for a process executable. Issue diagnostics and throw failed in // case of an error. // @@ -202,14 +205,17 @@ namespace build2 run_finish (args.data (), pr); } - // Start a process with the specified arguments. 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. 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. // process run_start (const process_path&, const char* args[], + int in, int out, bool error = true, const dir_path& cwd = dir_path ()); @@ -219,7 +225,7 @@ namespace build2 const char* args[], const dir_path& cwd = dir_path ()) { - process pr (run_start (p, args, 1 /* stdout */, true, cwd)); + process pr (run_start (p, args, 0 /* stdin */, 1 /* stdout */, true, cwd)); run_finish (args, pr); } @@ -237,6 +243,7 @@ namespace build2 inline process run_start (uint16_t verbosity, const char* args[], + int in, int out, bool error = true, const dir_path& cwd = dir_path ()) @@ -246,7 +253,7 @@ namespace build2 if (verb >= verbosity) print_process (args, 0); - return run_start (pp, args, out, error, cwd); + return run_start (pp, args, in, out, error, cwd); } inline void @@ -254,7 +261,12 @@ namespace build2 const char* args[], const dir_path& cwd = dir_path ()) { - process pr (run_start (verbosity, args, 1 /* stdout */, true, cwd)); + process pr (run_start (verbosity, + args, + 0 /* stdin */, + 1 /* stdout */, + true, + cwd)); run_finish (args, pr); } @@ -283,7 +295,8 @@ namespace build2 // template T - run (const process_path&, + run (uint16_t verbosity, + const process_path&, const char* args[], F&&, bool error = true, @@ -291,6 +304,19 @@ namespace build2 sha256* checksum = nullptr); template + T + run (const process_path& pp, + const char* args[], + F&& f, + bool error = true, + bool ignore_exit = false, + sha256* checksum = nullptr) + { + return run ( + verb_never, pp, args, forward (f), error, ignore_exit, checksum); + } + + template inline T run (uint16_t verbosity, const char* args[], @@ -300,18 +326,15 @@ namespace build2 sha256* checksum = nullptr) { process_path pp (run_search (args[0])); - - if (verb >= verbosity) - print_process (args, 0); - - return run (pp, args, forward (f), error, ignore_exit, checksum); + return run ( + verbosity, pp, args, forward (f), error, ignore_exit, checksum); } // run // template inline T - run (uint16_t verb, + run (uint16_t verbosity, const path& prog, F&& f, bool error = true, @@ -319,7 +342,8 @@ namespace build2 sha256* checksum = nullptr) { const char* args[] = {prog.string ().c_str (), nullptr}; - return run (verb, args, forward (f), error, ignore_exit, checksum); + return run ( + verbosity, args, forward (f), error, ignore_exit, checksum); } template @@ -332,18 +356,15 @@ namespace build2 sha256* checksum = nullptr) { const char* args[] = {pp.recall_string (), nullptr}; - - if (verb >= verbosity) - print_process (args, 0); - - return run (pp, args, forward (f), error, ignore_exit, checksum); + return run ( + verbosity, pp, args, forward (f), error, ignore_exit, checksum); } // run // template inline T - run (uint16_t verb, + run (uint16_t verbosity, const path& prog, const char* arg, F&& f, @@ -352,7 +373,8 @@ namespace build2 sha256* checksum = nullptr) { const char* args[] = {prog.string ().c_str (), arg, nullptr}; - return run (verb, args, forward (f), error, ignore_exit, checksum); + return run ( + verbosity, args, forward (f), error, ignore_exit, checksum); } template @@ -366,11 +388,8 @@ namespace build2 sha256* checksum = nullptr) { const char* args[] = {pp.recall_string (), arg, nullptr}; - - if (verb >= verbosity) - print_process (args, 0); - - return run (pp, args, forward (f), error, ignore_exit, checksum); + return run ( + verbosity, pp, args, forward (f), error, ignore_exit, checksum); } // Empty string and path. diff --git a/build2/utility.txx b/build2/utility.txx index 6ca245f..cea44ad 100644 --- a/build2/utility.txx +++ b/build2/utility.txx @@ -58,14 +58,18 @@ namespace build2 template T - run (const process_path& pp, + run (uint16_t verbosity, + const process_path& pp, const char* args[], F&& f, bool err, bool ignore_exit, sha256* checksum) { - process pr (run_start (pp, args, -1 /* stdout */, err)); + if (verb >= verbosity) + print_process (args, 0); + + process pr (run_start (pp, args, 0 /* stdin */, -1 /* stdout */, err)); T r; string l; // Last line of output. diff --git a/build2/version/snapshot-git.cxx b/build2/version/snapshot-git.cxx index bf863ce..dff5579 100644 --- a/build2/version/snapshot-git.cxx +++ b/build2/version/snapshot-git.cxx @@ -49,7 +49,10 @@ namespace build2 const char* args[] { "git", "-C", d, "cat-file", "commit", "HEAD", nullptr}; - process pr (run_start (3 /* verbosity */, args, -1 /* stdout */)); + process pr (run_start (3 /* verbosity */, + args, + 0 /* stdin */, + -1 /* stdout */)); try { -- cgit v1.1