aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2018-01-08 11:07:17 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2018-01-08 11:07:17 +0200
commitf6c3788de3d148c90aba705d045b1d92e7fea20a (patch)
tree3e99d4a3d87ab4f6c68cbfd0060e28b465bee4de
parentf1b3c0e86daec1f5b7ed81a60edbe548fcbb5f42 (diff)
Complete runtime/stdlib detection
-rw-r--r--build2/cc/gcc.cxx7
-rw-r--r--build2/cc/guess.cxx334
-rw-r--r--build2/cc/guess.hxx23
-rw-r--r--build2/cc/module.cxx16
-rw-r--r--build2/cc/msvc.cxx6
-rw-r--r--build2/diagnostics.hxx7
-rw-r--r--build2/types.hxx1
-rw-r--r--build2/utility.cxx3
-rw-r--r--build2/utility.hxx83
-rw-r--r--build2/utility.txx8
-rw-r--r--build2/version/snapshot-git.cxx5
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 <stddef.h> /* Forces defining __KLIBC__ for klibc. */ \n"
+"# include <limits.h> /* Includes features.h for glibc. */ \n"
+"# include <sys/types.h> /* 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<guess_result> (3, pp, "-v", f, false, false, &cs);
+ r = run<guess_result> (3, xp, "-v", f, false, false, &cs);
if (!r.empty ())
{
@@ -284,7 +405,7 @@ namespace build2
return guess_result ();
};
- r = run<guess_result> (3, pp, "--version", f, false);
+ r = run<guess_result> (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<guess_result> (3, pp, f, false);
+ r = run<guess_result> (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<string> (3, args.data (), f, false));
+ string t (run<string> (3, xp, args.data (), f, false));
if (t.empty ())
{
@@ -495,7 +619,7 @@ namespace build2
<< "falling back to -dumpmachine";});
args[1] = "-dumpmachine";
- t = run<string> (3, args.data (), f);
+ t = run<string> (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 <bits/c++config.h> \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<string> (3, args.data (), [](string& l) {return move (l);}));
+ auto f = [] (string& l) {return move (l);};
+ string t (run<string> (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(<bits/c++config.h>) \n"
+ " #include <bits/c++config.h> \n"
+ " stdlib:=\"libstdc++\" \n"
+ "#else \n"
+ " #include <cstddef> \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<string> (3, xc, "-V", f, false);
+ s = run<string> (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<string> (3, args.data (), f, false));
+ string t (run<string> (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<string> (3, xc, "-dumpmachine", [](string& l) {return move (l);});
+ {
+ auto f = [] (string& l) {return move (l);};
+ t = run<string> (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<path> (*p.first));
ci = build2::cc::guess (x_lang,
xc,
+ cast_null<strings> (rs[config_c_poptions]),
+ cast_null<strings> (rs[config_x_poptions]),
cast_null<strings> (rs[config_c_coptions]),
- cast_null<strings> (rs[config_x_coptions]));
+ cast_null<strings> (rs[config_x_coptions]),
+ cast_null<strings> (rs[config_c_loptions]),
+ cast_null<strings> (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 <typename F> inline void l1 (const F& f) {if (verb >= 1) f ();}
template <typename F> 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;
// <libbutl/target-triplet.mxx>
//
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 <typename T, typename F>
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 <typename T, typename F>
+ T
+ run (const process_path& pp,
+ const char* args[],
+ F&& f,
+ bool error = true,
+ bool ignore_exit = false,
+ sha256* checksum = nullptr)
+ {
+ return run<T> (
+ verb_never, pp, args, forward<F> (f), error, ignore_exit, checksum);
+ }
+
+ template <typename T, typename F>
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<T> (pp, args, forward<F> (f), error, ignore_exit, checksum);
+ return run<T> (
+ verbosity, pp, args, forward<F> (f), error, ignore_exit, checksum);
}
// run <prog>
//
template <typename T, typename F>
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<T> (verb, args, forward<F> (f), error, ignore_exit, checksum);
+ return run<T> (
+ verbosity, args, forward<F> (f), error, ignore_exit, checksum);
}
template <typename T, typename F>
@@ -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<T> (pp, args, forward<F> (f), error, ignore_exit, checksum);
+ return run<T> (
+ verbosity, pp, args, forward<F> (f), error, ignore_exit, checksum);
}
// run <prog> <arg>
//
template <typename T, typename F>
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<T> (verb, args, forward<F> (f), error, ignore_exit, checksum);
+ return run<T> (
+ verbosity, args, forward<F> (f), error, ignore_exit, checksum);
}
template <typename T, typename F>
@@ -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<T> (pp, args, forward<F> (f), error, ignore_exit, checksum);
+ return run<T> (
+ verbosity, pp, args, forward<F> (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 <typename T, typename F>
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
{