aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-09-01 13:38:30 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-09-01 13:38:30 +0200
commit42e1843a5f42455a8a5438551e363ee989483d64 (patch)
tree7d6cb638bdf97a185c5848e6a89d5e5bdb744fc5
parenteabb1afc66f710880ba43d6bfcfb9c7be6e97026 (diff)
Add diagnostics facility
-rw-r--r--bpkg/bpkg.cxx22
-rw-r--r--bpkg/buildfile2
-rw-r--r--bpkg/diagnostics329
-rw-r--r--bpkg/diagnostics.cxx70
4 files changed, 422 insertions, 1 deletions
diff --git a/bpkg/bpkg.cxx b/bpkg/bpkg.cxx
index 6e35544..3492705 100644
--- a/bpkg/bpkg.cxx
+++ b/bpkg/bpkg.cxx
@@ -5,9 +5,31 @@
#include <iostream>
#include <exception>
+#include <bpkg/diagnostics>
+
using namespace std;
+using namespace bpkg;
int
main ()
{
+ tracer trace ("main");
+
+ try
+ {
+ // Trace verbosity.
+ //
+ verb = 0;
+ }
+ catch (const failed&)
+ {
+ return 1; // Diagnostics has already been issued.
+ }
+ /*
+ catch (const std::exception& e)
+ {
+ error << e.what ();
+ return 1;
+ }
+ */
}
diff --git a/bpkg/buildfile b/bpkg/buildfile
index 2c22069..8437c86 100644
--- a/bpkg/buildfile
+++ b/bpkg/buildfile
@@ -5,4 +5,4 @@
import libs = libbutl%lib{butl}
import libs += libbpkg%lib{bpkg}
-exe{bpkg}: cxx{bpkg} $libs
+exe{bpkg}: cxx{bpkg diagnostics} $libs
diff --git a/bpkg/diagnostics b/bpkg/diagnostics
new file mode 100644
index 0000000..94cb0ba
--- /dev/null
+++ b/bpkg/diagnostics
@@ -0,0 +1,329 @@
+// file : bpkg/diagnostics -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BPKG_DIAGNOSTICS
+#define BPKG_DIAGNOSTICS
+
+#include <string>
+#include <cstdint>
+#include <utility> // move(), forward()
+#include <cassert>
+#include <sstream>
+#include <ostream>
+#include <exception>
+
+namespace bpkg
+{
+ // Throw this exception to terminate the process. The handler should
+ // assume that the diagnostics has already been issued.
+ //
+ class failed: public std::exception {};
+
+ // Trace verbosity level.
+ //
+ // 0 - tracing disabled.
+ // 1 - TODO
+ // 2 - TODO
+ // 3 - TODO
+ // 4 - TODO
+ // 5 - TODO
+ //
+ // While uint8 is more than enough, use uint16 for the ease of printing.
+ //
+ extern std::uint16_t verb;
+
+ template <typename F> inline void level1 (const F& f) {if (verb >= 1) f ();}
+ template <typename F> inline void level2 (const F& f) {if (verb >= 2) f ();}
+ template <typename F> inline void level3 (const F& f) {if (verb >= 3) f ();}
+ template <typename F> inline void level4 (const F& f) {if (verb >= 4) f ();}
+ template <typename F> inline void level5 (const F& f) {if (verb >= 5) f ();}
+
+ // Diagnostic facility, base infrastructure (potentially reusable).
+ //
+ extern std::ostream* diag_stream;
+
+ struct diag_record;
+ template <typename> struct diag_prologue;
+ template <typename> struct diag_mark;
+
+ typedef void (*diag_epilogue) (const diag_record&);
+
+ struct diag_record
+ {
+ template <typename T>
+ friend const diag_record&
+ operator<< (const diag_record& r, const T& x)
+ {
+ r.os_ << x;
+ return r;
+ }
+
+ diag_record (): empty_ (true), epilogue_ (nullptr) {}
+
+ template <typename B>
+ explicit
+ diag_record (const diag_prologue<B>& p)
+ : empty_ (true), epilogue_ (nullptr) {*this << p;}
+
+ template <typename B>
+ explicit
+ diag_record (const diag_mark<B>& m)
+ : empty_ (true), epilogue_ (nullptr) {*this << m;}
+
+ ~diag_record () noexcept(false);
+
+ void
+ append (diag_epilogue e) const
+ {
+ if (e != nullptr)
+ {
+ assert (epilogue_ == nullptr); // No multiple epilogues support.
+ epilogue_ = e;
+ }
+
+ if (empty_)
+ empty_ = false;
+ else
+ os_ << "\n ";
+ }
+
+ // Move constructible-only type.
+ //
+ /*
+ @@ libstdc++ doesn't yet have the ostringstream move support.
+
+ diag_record (diag_record&& r)
+ : os_ (std::move (r.os_))
+ {
+ empty_ = r.empty_;
+ r.empty_ = true;
+
+ epilogue_ = r.epilogue_;
+ r.epilogue_ = nullptr;
+ }
+ */
+
+ diag_record (diag_record&& r)
+ {
+ empty_ = r.empty_;
+ epilogue_ = r.epilogue_;
+
+ if (!empty_)
+ {
+ os_ << r.os_.str ();
+ r.empty_ = true;
+ r.epilogue_ = nullptr;
+ }
+ }
+
+ diag_record& operator= (diag_record&&) = delete;
+
+ diag_record (const diag_record&) = delete;
+ diag_record& operator= (const diag_record&) = delete;
+
+ public:
+ mutable std::ostringstream os_;
+
+ private:
+ mutable bool empty_;
+ mutable diag_epilogue epilogue_;
+ };
+
+ template <typename B>
+ struct diag_prologue: B
+ {
+ diag_prologue (diag_epilogue e = nullptr): B (), epilogue_ (e) {}
+
+ template <typename... A>
+ diag_prologue (A&&... a)
+ : B (std::forward<A> (a)...), epilogue_ (nullptr) {}
+
+ template <typename... A>
+ diag_prologue (diag_epilogue e, A&&... a)
+ : B (std::forward<A> (a)...), epilogue_ (e) {}
+
+ template <typename T>
+ diag_record
+ operator<< (const T& x) const
+ {
+ diag_record r;
+ r.append (epilogue_);
+ B::operator() (r);
+ r << x;
+ return r;
+ }
+
+ friend const diag_record&
+ operator<< (const diag_record& r, const diag_prologue& p)
+ {
+ r.append (p.epilogue_);
+ p (r);
+ return r;
+ }
+
+ private:
+ diag_epilogue epilogue_;
+ };
+
+ template <typename B>
+ struct diag_mark: B
+ {
+ diag_mark (): B () {}
+
+ template <typename... A>
+ diag_mark (A&&... a): B (std::forward<A> (a)...) {}
+
+ template <typename T>
+ diag_record
+ operator<< (const T& x) const
+ {
+ return B::operator() () << x;
+ }
+
+ friend const diag_record&
+ operator<< (const diag_record& r, const diag_mark& m)
+ {
+ return r << m ();
+ }
+ };
+
+ // Diagnostic facility, project specifics.
+ //
+ struct simple_prologue_base
+ {
+ explicit
+ simple_prologue_base (const char* type, const char* name)
+ : type_ (type), name_ (name) {}
+
+ void
+ operator() (const diag_record& r) const;
+
+ private:
+ const char* type_;
+ const char* name_;
+ };
+ typedef diag_prologue<simple_prologue_base> simple_prologue;
+
+ class location
+ {
+ public:
+ location () {}
+ location (std::string f, std::uint64_t l, std::uint64_t c)
+ : file (std::move (f)), line (l), column (c) {}
+
+ std::string file;
+ std::uint64_t line;
+ std::uint64_t column;
+ };
+
+ struct location_prologue_base
+ {
+ location_prologue_base (const char* type,
+ const char* name,
+ const location& l)
+ : type_ (type), name_ (name), loc_ (l) {}
+
+ void
+ operator() (const diag_record& r) const;
+
+ private:
+ const char* type_;
+ const char* name_;
+ const location loc_;
+ };
+ typedef diag_prologue<location_prologue_base> location_prologue;
+
+ struct basic_mark_base
+ {
+ explicit
+ basic_mark_base (const char* type,
+ const char* name = nullptr,
+ const void* data = nullptr)
+ : type_ (type), name_ (name), data_ (data) {}
+
+ simple_prologue
+ operator() () const
+ {
+ return simple_prologue (type_, name_);
+ }
+
+ location_prologue
+ operator() (const location& l) const
+ {
+ return location_prologue (type_, name_, l);
+ }
+
+ template <typename L>
+ location_prologue
+ operator() (const L& l) const
+ {
+ return location_prologue (type_, name_, get_location (l, data_));
+ }
+
+ protected:
+ const char* type_;
+ const char* name_;
+ const void* data_;
+ };
+ typedef diag_mark<basic_mark_base> basic_mark;
+
+ extern const basic_mark error;
+ extern const basic_mark warn;
+ extern const basic_mark info;
+ extern const basic_mark text;
+
+ // trace
+ //
+ struct trace_mark_base: basic_mark_base
+ {
+ explicit
+ trace_mark_base (const char* name, const void* data = nullptr)
+ : basic_mark_base ("trace", name, data) {}
+ };
+ typedef diag_mark<trace_mark_base> trace_mark;
+
+ typedef trace_mark tracer;
+
+ // fail
+ //
+ template <typename E>
+ struct fail_mark_base
+ {
+ explicit
+ fail_mark_base (const void* data = nullptr): data_ (data) {}
+
+ simple_prologue
+ operator() () const
+ {
+ return simple_prologue (&epilogue, "error", nullptr);
+ }
+
+ location_prologue
+ operator() (const location& l) const
+ {
+ return location_prologue (&epilogue, "error", nullptr, l);
+ }
+
+ template <typename L>
+ location_prologue
+ operator() (const L& l) const
+ {
+ return location_prologue (
+ &epilogue, "error", nullptr, get_location (l, data_));
+ }
+
+ static void
+ epilogue (const diag_record&) {throw E ();}
+
+ private:
+ const void* data_;
+ };
+
+ template <typename E>
+ using fail_mark = diag_mark<fail_mark_base<E>>;
+
+ extern const fail_mark<failed> fail;
+}
+
+#endif // BPKG_DIAGNOSTICS
diff --git a/bpkg/diagnostics.cxx b/bpkg/diagnostics.cxx
new file mode 100644
index 0000000..2bee18e
--- /dev/null
+++ b/bpkg/diagnostics.cxx
@@ -0,0 +1,70 @@
+// file : bpkg/diagnostics.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <bpkg/diagnostics>
+
+#include <iostream>
+
+using namespace std;
+
+namespace bpkg
+{
+ // Trace verbosity level.
+ //
+ uint16_t verb;
+
+ // Diagnostic facility, base infrastructure.
+ //
+ ostream* diag_stream = &cerr;
+
+ diag_record::
+ ~diag_record () noexcept(false)
+ {
+ // Don't flush the record if this destructor was called as part of
+ // the stack unwinding. Right now this means we cannot use this
+ // mechanism in destructors, which is not a big deal, except for
+ // one place: exception_guard. So for now we are going to have
+ // this ugly special check which we will be able to get rid of
+ // once C++17 uncaught_exceptions() becomes available.
+ //
+ if (!empty_ && (!uncaught_exception () /*|| exception_unwinding_dtor*/))
+ {
+ *diag_stream << os_.str () << endl;
+
+ if (epilogue_ != nullptr)
+ epilogue_ (*this); // Can throw.
+ }
+ }
+
+ // Diagnostic facility, project specifics.
+ //
+
+ void simple_prologue_base::
+ operator() (const diag_record& r) const
+ {
+ if (type_ != nullptr)
+ r << type_ << ": ";
+
+ if (name_ != nullptr)
+ r << name_ << ": ";
+ }
+
+ void location_prologue_base::
+ operator() (const diag_record& r) const
+ {
+ r << loc_.file << ':' << loc_.line << ':' << loc_.column << ": ";
+
+ if (type_ != nullptr)
+ r << type_ << ": ";
+
+ if (name_ != nullptr)
+ r << name_ << ": ";
+ }
+
+ const basic_mark error ("error");
+ const basic_mark warn ("warning");
+ const basic_mark info ("info");
+ const basic_mark text (nullptr);
+ const fail_mark<failed> fail;
+}