aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2014-12-11 09:45:26 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2014-12-11 09:45:26 +0200
commitfdc21950905d64b2ca1df5a0b2622022beffe922 (patch)
tree767f87d249a116f896c1de728e8dc32f899f0f4d
parentf4ed3e569cb5ebae855ea5309bfc17aa6b35874a (diff)
Improve diagnostics and error handling
g++-4.9 -std=c++14 -g -I.. -o bd bd.cxx target.cxx native.cxx rule.cxx cxx/rule.cxx cxx/target.cxx process.cxx timestamp.cxx path.cxx
-rw-r--r--build/bd.cxx88
-rw-r--r--build/cxx/rule2
-rw-r--r--build/cxx/rule.cxx55
-rw-r--r--build/diagnostics54
4 files changed, 143 insertions, 56 deletions
diff --git a/build/bd.cxx b/build/bd.cxx
index faae4c4..c592d64 100644
--- a/build/bd.cxx
+++ b/build/bd.cxx
@@ -5,7 +5,6 @@
#include <time.h> // tzset()
#include <vector>
-#include <cstdlib> // exit
#include <cassert>
#include <iostream>
#include <typeinfo>
@@ -14,6 +13,7 @@
#include <build/target>
#include <build/rule>
#include <build/process>
+#include <build/diagnostics>
using namespace std;
@@ -38,7 +38,21 @@ namespace build
{
const rule& ru (rs.first->second);
- if (recipe re = ru.match (t))
+ recipe re;
+
+ {
+ auto g (
+ make_exception_guard (
+ [] (target& t)
+ {
+ cerr << "info: while matching a rule for target " << t << endl;
+ },
+ t));
+
+ re = ru.match (t);
+ }
+
+ if (re)
{
t.recipe (re);
break;
@@ -83,21 +97,23 @@ namespace build
}
}
- try
- {
- t.state ((ts = t.recipe () (t)));
- assert (ts != target_state::unknown);
- return ts;
- }
- catch (const process_error& e)
+ const recipe& r (t.recipe ());
+
{
- // Take care of failed children. In a multi-threaded program that
- // fork()'ed but did not exec(), it is unwise to try to do any kind
- // of cleanup (like unwinding the stack and running destructors).
- //
- assert (e.child ());
- exit (1);
+ auto g (
+ make_exception_guard (
+ [] (target& t)
+ {
+ cerr << "info: while building target " << t << endl;
+ },
+ t));
+
+ ts = r (t);
}
+
+ assert (ts != target_state::unknown);
+ t.state (ts);
+ return ts;
}
}
@@ -140,24 +156,36 @@ main (int argc, char* argv[])
//
//
- if (!match (bd))
- return 1; // Diagnostics has already been issued.
-
- switch (update (bd))
+ try
{
- case target_state::uptodate:
+ if (!match (bd))
+ return 1; // Diagnostics has already been issued.
+
+ switch (update (bd))
{
- cerr << "info: target " << bd << " is up to date" << endl;
+ case target_state::uptodate:
+ {
+ cerr << "info: target " << bd << " is up to date" << endl;
+ break;
+ }
+ case target_state::updated:
break;
+ case target_state::failed:
+ {
+ cerr << "error: failed to update target " << bd << endl;
+ return 1;
+ }
+ case target_state::unknown:
+ assert (false);
}
- case target_state::updated:
- break;
- case target_state::failed:
- {
- cerr << "error: failed to update target " << bd << endl;
- return 1;
- }
- case target_state::unknown:
- assert (false);
+ }
+ catch (const error&)
+ {
+ return 1; // Diagnostics has already been issued.
+ }
+ catch (const std::exception& e)
+ {
+ cerr << "error: " << e.what () << endl;
+ return 1;
}
}
diff --git a/build/cxx/rule b/build/cxx/rule
index 85352b7..4900180 100644
--- a/build/cxx/rule
+++ b/build/cxx/rule
@@ -28,7 +28,7 @@ namespace build
update (target&);
private:
- bool
+ void
inject_prerequisites (obj&, const cxx&) const;
};
diff --git a/build/cxx/rule.cxx b/build/cxx/rule.cxx
index 31c27dc..ccc9bb5 100644
--- a/build/cxx/rule.cxx
+++ b/build/cxx/rule.cxx
@@ -14,6 +14,7 @@
#include <build/process>
#include <build/timestamp>
+#include <build/diagnostics>
using namespace std;
@@ -63,11 +64,7 @@ namespace build
// Inject additional prerequisites.
//
- // @@ If this failed, saying that the rule did not match is
- // not quite correct.
- //
- if (!inject_prerequisites (o, *s))
- return recipe ();
+ inject_prerequisites (o, *s);
return recipe (&update);
}
@@ -114,12 +111,12 @@ namespace build
return r;
}
- bool compile::
+ void compile::
inject_prerequisites (obj& o, const cxx& s) const
{
const char* args[] = {
"g++-4.9",
- "-std=c++11",
+ "-std=c++14",
"-I..",
"-M",
"-MG", // Treat missing headers as generated.
@@ -130,7 +127,6 @@ namespace build
try
{
process pr (args, false, false, true);
- bool r (true);
__gnu_cxx::stdio_filebuf<char> fb (pr.in_ofd, ios_base::in);
istream is (&fb);
@@ -142,27 +138,24 @@ namespace build
if (is.fail () && !is.eof ())
{
- cerr << "warning: io error while parsing output" << endl;
- r = false;
- break;
+ cerr << "error: io error while parsing g++ -M output" << endl;
+ throw error ();
}
size_t p (0);
if (first)
{
- // Empty output usually means the wait() call below will return
+ // Empty output should mean the wait() call below will return
// false.
//
if (l.empty ())
- {
- r = false;
break;
- }
- first = false;
assert (l[0] == '*' && l[1] == ':' && l[2] == ' ');
next (l, (p = 3)); // Skip the source file.
+
+ first = false;
}
while (p != l.size ())
@@ -185,20 +178,24 @@ namespace build
}
}
- //@@ Any diagnostics if wait() returns false. Or do we assume
- // the child process issued something?
+ // We assume the child process issued some diagnostics.
//
- return pr.wait () && r;
+ if (!pr.wait ())
+ throw error ();
}
catch (const process_error& e)
{
- cerr << "warning: unable to execute '" << args[0] << "': " <<
+ cerr << "error: unable to execute '" << args[0] << "': " <<
e.what () << endl;
+ // In a multi-threaded program that fork()'ed but did not exec(),
+ // it is unwise to try to do any kind of cleanup (like unwinding
+ // the stack and running destructors).
+ //
if (e.child ())
exit (1);
- return false;
+ throw error ();
}
}
@@ -243,7 +240,7 @@ namespace build
const char* args[] = {
"g++-4.9",
- "-std=c++11",
+ "-std=c++14",
"-I..",
"-c",
"-o", o.path ().string ().c_str (),
@@ -272,8 +269,12 @@ namespace build
cerr << "error: unable to execute '" << args[0] << "': " <<
e.what () << endl;
+ // In a multi-threaded program that fork()'ed but did not exec(),
+ // it is unwise to try to do any kind of cleanup (like unwinding
+ // the stack and running destructors).
+ //
if (e.child ())
- throw; // Let caller terminate us quickly without causing a scene.
+ exit (1);
return target_state::failed;
}
@@ -356,7 +357,7 @@ namespace build
if (!u)
return target_state::uptodate;
- vector<const char*> args {"g++-4.9", "-std=c++11", "-o"};
+ vector<const char*> args {"g++-4.9", "-std=c++14", "-o"};
args.push_back (e.path ().string ().c_str ());
@@ -390,8 +391,12 @@ namespace build
cerr << "error: unable to execute '" << args[0] << "': " <<
e.what () << endl;
+ // In a multi-threaded program that fork()'ed but did not exec(),
+ // it is unwise to try to do any kind of cleanup (like unwinding
+ // the stack and running destructors).
+ //
if (e.child ())
- throw; // Let caller terminate us quickly without causing a scene.
+ exit (1);
return target_state::failed;
}
diff --git a/build/diagnostics b/build/diagnostics
new file mode 100644
index 0000000..c8fb169
--- /dev/null
+++ b/build/diagnostics
@@ -0,0 +1,54 @@
+// file : build/diagnostics -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BUILD_DIAGNOSTICS
+#define BUILD_DIAGNOSTICS
+
+#include <tuple>
+#include <utility>
+#include <exception>
+
+namespace build
+{
+ // Throw this exception to terminate the build. The handler should
+ // assume that the diagnostics has already been issued.
+ //
+ class error: public std::exception {};
+
+ // Call a function if there is an exception.
+ //
+ template <typename F, typename T>
+ struct exception_guard;
+
+ template <typename F, typename... A>
+ inline exception_guard<F, std::tuple<A&&...>>
+ make_exception_guard (F f, A&&... a)
+ {
+ return exception_guard<F, std::tuple<A&&...>> (
+ std::move (f), std::forward_as_tuple (a...));
+ }
+
+ template <typename F, typename... A>
+ struct exception_guard<F, std::tuple<A...>>
+ {
+ typedef std::tuple<A...> T;
+
+ exception_guard (F f, T a): f_ (std::move (f)), a_ (std::move (a)) {}
+ ~exception_guard ()
+ {
+ if (std::uncaught_exception ())
+ call (std::index_sequence_for<A...> ());
+ }
+
+ private:
+ template <std::size_t... I>
+ void
+ call (std::index_sequence<I...>) {f_ (std::get<I> (a_)...);}
+
+ F f_;
+ T a_;
+ };
+}
+
+#endif // BUILD_DIAGNOSTICS