aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/cc
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2022-10-27 11:00:47 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2022-10-27 11:00:47 +0200
commit72e9ec3d7028765836851b515d80816f2da74060 (patch)
treec3aea51139b9e0a071455b83a5ab067542a7e839 /libbuild2/cc
parent1c08fae61a1e9a01d56735768ad7f8f3d0b45f41 (diff)
Initial work on child process diagnostics buffering
Currently this is implemented for C/C++ compile and link rules.
Diffstat (limited to 'libbuild2/cc')
-rw-r--r--libbuild2/cc/compile-rule.cxx91
-rw-r--r--libbuild2/cc/link-rule.cxx106
-rw-r--r--libbuild2/cc/msvc.cxx25
3 files changed, 141 insertions, 81 deletions
diff --git a/libbuild2/cc/compile-rule.cxx b/libbuild2/cc/compile-rule.cxx
index fda97a0..8d507e7 100644
--- a/libbuild2/cc/compile-rule.cxx
+++ b/libbuild2/cc/compile-rule.cxx
@@ -264,6 +264,10 @@ namespace build2
compile_rule::
compile_rule (data&& d, const scope& rs)
: common (move (d)),
+ //
+ // @@ TODO: split into rule_name (string) and rule_version (integer)
+ // on next increment.
+ //
rule_id (string (x) += ".compile 6")
{
// Locate the header cache (see enter_header() for details).
@@ -3961,6 +3965,8 @@ namespace build2
// diagnostics (if things go badly we will restart with this
// support).
//
+ // @@ TODO: diag_buffer
+ //
if (drmp == nullptr) // Dependency info goes to stdout.
{
assert (!sense_diag); // Note: could support with fdselect().
@@ -3970,7 +3976,7 @@ namespace build2
//
pr = process (
cpath,
- args.data (),
+ args,
0,
-1,
cclass == compiler_class::msvc ? 1 : gen ? 2 : -2,
@@ -3980,7 +3986,7 @@ namespace build2
else // Dependency info goes to a temporary file.
{
pr = process (cpath,
- args.data (),
+ args,
mod_mapper ? -1 : 0,
mod_mapper ? -1 : 2, // Send stdout to stderr.
gen ? 2 : sense_diag ? -1 : -2,
@@ -4890,10 +4896,10 @@ namespace build2
print_process (args);
// We don't want to see warnings multiple times so ignore all
- // diagnostics.
+ // diagnostics (thus no need for diag_buffer).
//
pr = process (cpath,
- args.data (),
+ args,
0, -1, -2,
nullptr, // CWD
env.empty () ? nullptr : env.data ());
@@ -6327,7 +6333,7 @@ namespace build2
// Filter cl.exe noise (msvc.cxx).
//
void
- msvc_filter_cl (ifdstream&, const path& src);
+ msvc_filter_cl (diag_buffer&, const path& src);
// Append header unit-related options.
//
@@ -6674,7 +6680,7 @@ namespace build2
small_vector<string, 2> module_args; // Module options storage.
size_t out_i (0); // Index of the -o option.
- size_t lang_n (0); // Number of lang options.
+ //size_t lang_n (0); // Number of lang options. @@ TMP
switch (cclass)
{
@@ -7025,7 +7031,7 @@ namespace build2
args.push_back ("-c");
}
- lang_n = append_lang_options (args, md);
+ /*lang_n = */append_lang_options (args, md); // @@ TMP
if (md.pp == preprocessed::all)
{
@@ -7074,6 +7080,8 @@ namespace build2
// the source file, not its preprocessed version (so that it's easy to
// copy and re-run, etc). Only at level 3 and above print the real deal.
//
+ // @@ TODO: why don't we print env (here and/or below)? Also link rule.
+ //
if (verb == 1)
text << x_name << ' ' << s;
else if (verb == 2)
@@ -7081,12 +7089,17 @@ namespace build2
// If we have the (partially) preprocessed output, switch to that.
//
+ // But we remember the original source/position to restore later.
+ //
bool psrc (md.psrc);
bool ptmp (psrc && md.psrc.temporary);
+ pair<size_t, const char*> osrc;
if (psrc)
{
args.pop_back (); // nullptr
+ osrc.second = args.back ();
args.pop_back (); // sp
+ osrc.first = args.size ();
sp = &md.psrc.path ();
@@ -7096,6 +7109,8 @@ namespace build2
{
case compiler_type::gcc:
{
+ // @@ TMP
+#if 0
// The -fpreprocessed is implied by .i/.ii. But not when compiling
// a header unit (there is no .hi/.hii).
//
@@ -7109,6 +7124,15 @@ namespace build2
//
for (; lang_n != 0; --lang_n)
args.pop_back ();
+#else
+ // -fpreprocessed is implied by .i/.ii unless compiling a header
+ // unit (there is no .hi/.hii). Also, we would need to pop -x
+ // since it takes precedence over the extension, which would mess
+ // up our osrc logic. So in the end it feels like always passing
+ // explicit -fpreprocessed is the way to go.
+ //
+ args.push_back ("-fpreprocessed");
+#endif
args.push_back ("-fdirectives-only");
break;
@@ -7145,6 +7169,8 @@ namespace build2
if (verb >= 3)
print_process (args);
+ diag_buffer dbuf (ctx);
+
// @@ DRYRUN: Currently we discard the (partially) preprocessed file on
// dry-run which is a waste. Even if we keep the file around (like we do
// for the error case; see above), we currently have no support for
@@ -7163,45 +7189,36 @@ namespace build2
file_cache::read psrcr (psrc ? md.psrc.open () : file_cache::read ());
// VC cl.exe sends diagnostics to stdout. It also prints the file
- // name being compiled as the first line. So for cl.exe we redirect
- // stdout to a pipe, filter that noise out, and send the rest to
- // stderr.
+ // name being compiled as the first line. So for cl.exe we filter
+ // that noise out.
//
- // For other compilers redirect stdout to stderr, in case any of
- // them tries to pull off something similar. For sane compilers this
- // should be harmless.
+ // For other compilers also redirect stdout to stderr, in case any
+ // of them tries to pull off something similar. For sane compilers
+ // this should be harmless.
//
bool filter (ctype == compiler_type::msvc);
process pr (cpath,
- args.data (),
- 0, (filter ? -1 : 2), 2,
+ args,
+ 0, 2, dbuf.open (args[0], filter),
nullptr, // CWD
env.empty () ? nullptr : env.data ());
if (filter)
- {
- try
- {
- ifdstream is (
- move (pr.in_ofd), fdstream_mode::text, ifdstream::badbit);
-
- msvc_filter_cl (is, *sp);
+ msvc_filter_cl (dbuf, *sp);
- // If anything remains in the stream, send it all to stderr.
- // Note that the eof check is important: if the stream is at
- // eof, this and all subsequent writes to the diagnostics stream
- // will fail (and you won't see a thing).
- //
- if (is.peek () != ifdstream::traits_type::eof ())
- diag_stream_lock () << is.rdbuf ();
+ dbuf.read ();
- is.close ();
- }
- catch (const io_error&) {} // Assume exits with error.
+ // Restore the original source if we switched to preprocessed.
+ //
+ if (psrc)
+ {
+ args.resize (osrc.first);
+ args.push_back (osrc.second);
+ args.push_back (nullptr);
}
- run_finish (args, pr);
+ dbuf.finish (args, pr);
}
catch (const process_error& e)
{
@@ -7252,12 +7269,14 @@ namespace build2
try
{
process pr (cpath,
- args.data (),
- 0, 2, 2,
+ args,
+ 0, 2, dbuf.open (args[0]),
nullptr, // CWD
env.empty () ? nullptr : env.data ());
- run_finish (args, pr);
+ dbuf.read ();
+
+ dbuf.finish (args, pr);
}
catch (const process_error& e)
{
diff --git a/libbuild2/cc/link-rule.cxx b/libbuild2/cc/link-rule.cxx
index a5fa7bc..5d212e6 100644
--- a/libbuild2/cc/link-rule.cxx
+++ b/libbuild2/cc/link-rule.cxx
@@ -254,6 +254,10 @@ namespace build2
link_rule::
link_rule (data&& d)
: common (move (d)),
+ //
+ // @@ TODO: split into rule_name (string) and rule_version (integer)
+ // on next increment.
+ //
rule_id (string (x) += ".link 3")
{
}
@@ -2671,7 +2675,7 @@ namespace build2
// Filter link.exe noise (msvc.cxx).
//
void
- msvc_filter_link (ifdstream&, const file&, otype);
+ msvc_filter_link (diag_buffer&, const file&, otype);
// Translate target CPU to the link.exe/lib.exe /MACHINE option.
//
@@ -2879,6 +2883,8 @@ namespace build2
env_ptrs.push_back (nullptr);
}
+ diag_buffer dbuf (ctx);
+
// If targeting Windows, take care of the manifest.
//
path manifest; // Manifest itself (msvc) or compiled object file.
@@ -2929,12 +2935,15 @@ namespace build2
try
{
+ // We assume that what we write to stdin is small enough to
+ // fit into the pipe's buffer without blocking.
+ //
process pr (rc,
args,
- -1 /* stdin */,
- 1 /* stdout */,
- 2 /* stderr */,
- nullptr /* cwd */,
+ -1 /* stdin */,
+ 1 /* stdout */,
+ dbuf.open (args[0]) /* stderr */,
+ nullptr /* cwd */,
env_ptrs.empty () ? nullptr : env_ptrs.data ());
try
@@ -2960,7 +2969,8 @@ namespace build2
// was caused by that and let run_finish() deal with it.
}
- run_finish (args, pr);
+ dbuf.read ();
+ dbuf.finish (args, pr, 2 /* verbosity */);
}
catch (const process_error& e)
{
@@ -3823,8 +3833,21 @@ namespace build2
else if (verb == 2)
print_process (args);
+ // Do any necessary fixups to the command line to make it runnable.
+ //
+ // Notice the split in the diagnostics: at verbosity level 1 we print
+ // the "logical" command line while at level 2 and above -- what we are
+ // actually executing.
+ //
+ // We also need to save the original for the diag_buffer::close() call
+ // below if at verbosity level 1.
+ //
+ cstrings oargs;
+
// Adjust linker parallelism.
//
+ // Note that we are not going to bother with oargs for this.
+ //
string jobs_arg;
scheduler::alloc_guard jobs_extra;
@@ -3875,12 +3898,6 @@ namespace build2
}
}
- // Do any necessary fixups to the command line to make it runnable.
- //
- // Notice the split in the diagnostics: at verbosity level 1 we print
- // the "logical" command line while at level 2 and above -- what we are
- // actually executing.
- //
// On Windows we need to deal with the command line length limit. The
// best workaround seems to be passing (part of) the command line in an
// "options file" ("response file" in Microsoft's terminology). Both
@@ -3966,14 +3983,15 @@ namespace build2
fail << "unable to write to " << f << ": " << e;
}
+ if (verb == 1)
+ oargs = args;
+
// Replace input arguments with @file.
//
targ = '@' + f.string ();
args.resize (args_input);
args.push_back (targ.c_str());
args.push_back (nullptr);
-
- //@@ TODO: leave .t file if linker failed and verb > 2?
}
}
#endif
@@ -3996,51 +4014,48 @@ namespace build2
{
// VC tools (both lib.exe and link.exe) send diagnostics to stdout.
// Also, link.exe likes to print various gratuitous messages. So for
- // link.exe we redirect stdout to a pipe, filter that noise out, and
- // send the rest to stderr.
+ // link.exe we filter that noise out.
//
// For lib.exe (and any other insane linker that may try to pull off
// something like this) we are going to redirect stdout to stderr.
// For sane compilers this should be harmless.
//
// Note that we don't need this for LLD's link.exe replacement which
- // is quiet.
+ // is thankfully quiet.
//
bool filter (tsys == "win32-msvc" &&
!lt.static_library () &&
cast<string> (rs["bin.ld.id"]) != "msvc-lld");
process pr (*ld,
- args.data (),
- 0 /* stdin */,
- (filter ? -1 : 2) /* stdout */,
- 2 /* stderr */,
- nullptr /* cwd */,
+ args,
+ 0 /* stdin */,
+ 2 /* stdout */,
+ dbuf.open (args[0], filter) /* stderr */,
+ nullptr /* cwd */,
env_ptrs.empty () ? nullptr : env_ptrs.data ());
if (filter)
+ msvc_filter_link (dbuf, t, ot);
+
+ dbuf.read ();
+
{
- try
- {
- ifdstream is (
- move (pr.in_ofd), fdstream_mode::text, ifdstream::badbit);
+ bool e (pr.wait ());
- msvc_filter_link (is, t, ot);
+#ifdef _WIN32
+ // Keep the options file if we have shown it.
+ //
+ if (!e && verb > 2)
+ trm.cancel ();
+#endif
- // If anything remains in the stream, send it all to stderr.
- // Note that the eof check is important: if the stream is at
- // eof, this and all subsequent writes to the diagnostics stream
- // will fail (and you won't see a thing).
- //
- if (is.peek () != ifdstream::traits_type::eof ())
- diag_stream_lock () << is.rdbuf ();
+ dbuf.close (oargs.empty () ? args : oargs, *pr.exit);
- is.close ();
- }
- catch (const io_error&) {} // Assume exits with error.
+ if (!e)
+ throw failed ();
}
- run_finish (args, pr);
jobs_extra.deallocate ();
}
catch (const process_error& e)
@@ -4100,10 +4115,25 @@ namespace build2
print_process (args);
if (!ctx.dry_run)
+ {
+#if 0
run (rl,
args,
dir_path () /* cwd */,
env_ptrs.empty () ? nullptr : env_ptrs.data ());
+#else
+ process pr (rl,
+ args,
+ 0 /* stdin */,
+ 1 /* stdout */,
+ dbuf.open (args[0]) /* stderr */,
+ nullptr /* cwd */,
+ env_ptrs.empty () ? nullptr : env_ptrs.data ());
+
+ dbuf.read ();
+ dbuf.finish (args, pr);
+#endif
+ }
}
// For Windows generate (or clean up) rpath-emulating assembly.
diff --git a/libbuild2/cc/msvc.cxx b/libbuild2/cc/msvc.cxx
index f95cab0..9aff347 100644
--- a/libbuild2/cc/msvc.cxx
+++ b/libbuild2/cc/msvc.cxx
@@ -164,18 +164,21 @@ namespace build2
// Filter cl.exe and link.exe noise.
//
+ // Note: must be followed with the dbuf.read() call.
+ //
void
- msvc_filter_cl (ifdstream& is, const path& src)
+ msvc_filter_cl (diag_buffer& dbuf, const path& src)
+ try
{
// While it appears VC always prints the source name (event if the
// file does not exist), let's do a sanity check. Also handle the
// command line errors/warnings which come before the file name.
//
- for (string l; !eof (getline (is, l)); )
+ for (string l; !eof (getline (dbuf.is, l)); )
{
if (l != src.leaf ().string ())
{
- diag_stream_lock () << l << endl;
+ dbuf.write (l, true /* newline */);
if (msvc_sense_diag (l, 'D').first != string::npos)
continue;
@@ -184,14 +187,19 @@ namespace build2
break;
}
}
+ catch (const io_error&)
+ {
+ // Let the following diag_buffer::read() call deal with this.
+ }
void
- msvc_filter_link (ifdstream& is, const file& t, otype lt)
+ msvc_filter_link (diag_buffer& dbuf, const file& t, otype lt)
+ try
{
// Filter lines until we encounter something we don't recognize. We also
// have to assume the messages can be translated.
//
- for (string l; getline (is, l); )
+ for (string l; getline (dbuf.is, l); )
{
// " Creating library foo\foo.dll.lib and object foo\foo.dll.exp"
//
@@ -216,12 +224,15 @@ namespace build2
// /INCREMENTAL causes linker to sometimes issue messages but now I
// can't quite reproduce it.
- //
- diag_stream_lock () << l << endl;
+ dbuf.write (l, true /* newline */);
break;
}
}
+ catch (const io_error&)
+ {
+ // Let the following diag_buffer::read() call deal with this.
+ }
void
msvc_extract_header_search_dirs (const strings& v, dir_paths& r)