From 70317569c6dcd9809ed4a8c425777e653ec6ca08 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Mon, 1 May 2017 18:24:31 +0300 Subject: Add hxx extension for headers --- build2/diagnostics.hxx | 351 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 351 insertions(+) create mode 100644 build2/diagnostics.hxx (limited to 'build2/diagnostics.hxx') diff --git a/build2/diagnostics.hxx b/build2/diagnostics.hxx new file mode 100644 index 0000000..692115b --- /dev/null +++ b/build2/diagnostics.hxx @@ -0,0 +1,351 @@ +// file : build2/diagnostics.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef BUILD2_DIAGNOSTICS_HXX +#define BUILD2_DIAGNOSTICS_HXX + +#include + +#include +#include + +namespace build2 +{ + using butl::diag_record; + + // Throw this exception to terminate the build. The handler should + // assume that the diagnostics has already been issued. + // + class failed: public std::exception {}; + + // Print process commmand line. If the number of elements is specified + // (or the second version is used), then it will print the piped multi- + // process command line, if present. In this case, the expected format + // is as follows: + // + // name1 arg arg ... nullptr + // name2 arg arg ... nullptr + // ... + // nameN arg arg ... nullptr nullptr + // + void + print_process (diag_record&, const char* const* args, size_t n = 0); + + void + print_process (const char* const* args, size_t n = 0); + + inline void + print_process (diag_record& dr, const cstrings& args) + { + print_process (dr, args.data (), args.size ()); + } + + inline void + print_process (const cstrings& args) + { + print_process (args.data (), args.size ()); + } + + // Program verbosity level (-v/--verbose). + // + // 0 - disabled + // 1 - high-level information messages + // 2 - essential underlying commands that are being executed + // 3 - all underlying commands that are being executed + // 4 - information helpful to the user (e.g., why a rule did not match) + // 5 - information helpful to the developer + // 6 - even more detailed information + // + // While uint8 is more than enough, use uint16 for the ease of printing. + // + extern uint16_t verb; + + template inline void l1 (const F& f) {if (verb >= 1) f ();} + template inline void l2 (const F& f) {if (verb >= 2) f ();} + template inline void l3 (const F& f) {if (verb >= 3) f ();} + template inline void l4 (const F& f) {if (verb >= 4) f ();} + template inline void l5 (const F& f) {if (verb >= 5) f ();} + template inline void l6 (const F& f) {if (verb >= 6) f ();} + + // Stream verbosity level. It is determined by the diagnostic type (e.g., + // trace always has maximum verbosity) as well as the program verbosity. It + // is used to decide whether to print relative/absolute paths, and default + // target extensions. + // + // 0 - minimum + // 1 - intermediate + // 2 - maximum + // + // Currently we have the following program to stream verbosity mapping: + // + // fail/error/warn/info <2:0 2:1 >2:2 + // trace *:2 + // + // A stream that hasn't been (yet) assigned any verbosity explicitly (e.g., + // ostringstream) defaults to maximum. + // + const uint16_t stream_verb_min = 0; + const uint16_t stream_verb_max = 2; + + // Default program to stream verbosity mapping, as outlined above. + // + inline uint16_t + stream_verb_map () {return verb < 2 ? 0 : (verb > 2 ? 2 : 1);} + + extern const int stream_verb_index; + + inline uint16_t + stream_verb (ostream& os) + { + uint16_t v (static_cast (os.iword (stream_verb_index))); + return v == 0 ? stream_verb_max : v - 1; + } + + inline void + stream_verb (ostream& os, uint16_t v) + { + os.iword (stream_verb_index) = static_cast (v + 1); + } + + // Diagnostic facility, base infrastructure. + // + using butl::diag_lock; + using butl::diag_stream; + using butl::diag_epilogue; + + // Diagnostics stack. Each frame is "applied" to the fail/error/warn/info + // diag record. + // + // Unfortunately most of our use-cases don't fit into the 2-pointer small + // object optimization of std::function. So we have to complicate things + // a bit here. + // + struct diag_frame + { + explicit + diag_frame (void (*f) (const diag_frame&, const diag_record&)) + : func_ (f), prev_ (stack) {stack = this;} + + // Start with an existing stack, for example, from another thread. + // + explicit + diag_frame (const diag_frame* prev) + : prev_ (stack) {stack = prev;} // Just a restore guard. + + static void + apply (const diag_record& r) + { + for (const diag_frame* f (stack); f != nullptr; f = f->prev_) + f->func_ (*f, r); + } + + ~diag_frame () {stack = prev_;} + + static +#ifdef __cpp_thread_local + thread_local +#else + __thread +#endif + const diag_frame* stack; // Tip of the stack. + + private: + void (*func_) (const diag_frame&, const diag_record&); + const diag_frame* prev_; + }; + + template + struct diag_frame_impl: diag_frame + { + explicit + diag_frame_impl (F f): diag_frame (&thunk), func_ (move (f)) {} + + private: + static void + thunk (const diag_frame& f, const diag_record& r) + { + static_cast (f).func_ (r); + } + + const F func_; + }; + + template + inline diag_frame_impl + make_diag_frame (F f) + { + return diag_frame_impl (move (f)); + } + + // Diagnostic facility, project specifics. + // + struct simple_prologue_base + { + explicit + simple_prologue_base (const char* type, + const char* mod, + const char* name, + uint16_t sverb) + : type_ (type), mod_ (mod), name_ (name), sverb_ (sverb) {} + + void + operator() (const diag_record& r) const; + + private: + const char* type_; + const char* mod_; + const char* name_; + const uint16_t sverb_; + }; + + class location + { + public: + // Note that location maintains a shallow reference to path. Zero lines + // or columns are not printed. + // + location (): file (nullptr), line (0), column (0) {} + location (const path* f, uint64_t l = 0, uint64_t c = 0) + : file (f), line (l), column (c) {} + + bool + empty () const {return file == nullptr;} + + const path* file; + uint64_t line; + uint64_t column; + }; + + struct location_prologue_base + { + location_prologue_base (const char* type, + const char* mod, + const char* name, + const location& l, + uint16_t sverb) + : type_ (type), mod_ (mod), name_ (name), loc_ (l), sverb_ (sverb) {} + + void + operator() (const diag_record& r) const; + + private: + const char* type_; + const char* mod_; + const char* name_; + const location loc_; + const uint16_t sverb_; + }; + + struct basic_mark_base + { + using simple_prologue = butl::diag_prologue; + using location_prologue = butl::diag_prologue; + + explicit + basic_mark_base (const char* type, + diag_epilogue* epilogue = &diag_frame::apply, + uint16_t (*sverb) () = &stream_verb_map, + const char* mod = nullptr, + const char* name = nullptr, + const void* data = nullptr) + : sverb_ (sverb), + type_ (type), mod_ (mod), name_ (name), data_ (data), + epilogue_ (epilogue) {} + + simple_prologue + operator() () const + { + return simple_prologue (epilogue_, type_, mod_, name_, sverb_ ()); + } + + location_prologue + operator() (const location& l) const + { + return location_prologue (epilogue_, type_, mod_, name_, l, sverb_ ()); + } + + template + location_prologue + operator() (const L& l) const + { + return location_prologue ( + epilogue_, type_, mod_, name_, get_location (l, data_), sverb_ ()); + } + + protected: + uint16_t (*sverb_) (); + const char* type_; + const char* mod_; + const char* name_; + const void* data_; + diag_epilogue* const epilogue_; + }; + using basic_mark = butl::diag_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) + : trace_mark_base (nullptr, name, data) {} + + trace_mark_base (const char* mod, + const char* name, + const void* data = nullptr) + : basic_mark_base ("trace", + nullptr, // No diag stack. + []() {return stream_verb_max;}, + mod, + name, + data) {} + }; + using trace_mark = butl::diag_mark; + using tracer = trace_mark; + + // fail + // + struct fail_mark_base: basic_mark_base + { + explicit + fail_mark_base (const char* type, + const void* data = nullptr) + : basic_mark_base (type, + [](const diag_record& r) + { + diag_frame::apply (r); + r.flush (); + throw failed (); + }, + &stream_verb_map, + nullptr, + nullptr, + data) {} + }; + using fail_mark = butl::diag_mark; + + struct fail_end_base + { + [[noreturn]] void + operator() (const diag_record& r) const + { + // If we just throw then the record's destructor will see an active + // exception and will not flush the record. + // + r.flush (); + throw failed (); + } + }; + using fail_end = butl::diag_noreturn_end; + + extern const fail_mark fail; + extern const fail_end endf; +} + +#endif // BUILD2_DIAGNOSTICS_HXX -- cgit v1.1