From 42e1843a5f42455a8a5438551e363ee989483d64 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 1 Sep 2015 13:38:30 +0200 Subject: Add diagnostics facility --- bpkg/bpkg.cxx | 22 ++++ bpkg/buildfile | 2 +- bpkg/diagnostics | 329 +++++++++++++++++++++++++++++++++++++++++++++++++++ bpkg/diagnostics.cxx | 70 +++++++++++ 4 files changed, 422 insertions(+), 1 deletion(-) create mode 100644 bpkg/diagnostics create mode 100644 bpkg/diagnostics.cxx 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 #include +#include + 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 +#include +#include // move(), forward() +#include +#include +#include +#include + +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 inline void level1 (const F& f) {if (verb >= 1) f ();} + template inline void level2 (const F& f) {if (verb >= 2) f ();} + template inline void level3 (const F& f) {if (verb >= 3) f ();} + template inline void level4 (const F& f) {if (verb >= 4) f ();} + template 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 struct diag_prologue; + template struct diag_mark; + + typedef void (*diag_epilogue) (const diag_record&); + + struct diag_record + { + template + friend const diag_record& + operator<< (const diag_record& r, const T& x) + { + r.os_ << x; + return r; + } + + diag_record (): empty_ (true), epilogue_ (nullptr) {} + + template + explicit + diag_record (const diag_prologue& p) + : empty_ (true), epilogue_ (nullptr) {*this << p;} + + template + explicit + diag_record (const diag_mark& 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 + struct diag_prologue: B + { + diag_prologue (diag_epilogue e = nullptr): B (), epilogue_ (e) {} + + template + diag_prologue (A&&... a) + : B (std::forward (a)...), epilogue_ (nullptr) {} + + template + diag_prologue (diag_epilogue e, A&&... a) + : B (std::forward (a)...), epilogue_ (e) {} + + template + 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 + struct diag_mark: B + { + diag_mark (): B () {} + + template + diag_mark (A&&... a): B (std::forward (a)...) {} + + template + 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; + + 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; + + 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 + 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; + + 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; + + typedef trace_mark tracer; + + // fail + // + template + 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 + 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 + using fail_mark = diag_mark>; + + extern const fail_mark 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 + +#include + +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 fail; +} -- cgit v1.1