// file : build2/diagnostics -*- C++ -*- // copyright : Copyright (c) 2014-2016 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file #ifndef BUILD2_DIAGNOSTICS #define BUILD2_DIAGNOSTICS #include // size_t #include #include #include #include #include #include #include #include #include #include namespace build2 { struct 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, std::size_t n = 0); void print_process (const char* const* args, std::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 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 ();} template inline void level6 (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 (std::ostream& os) { uint16_t v (static_cast (os.iword (stream_verb_index))); return v == 0 ? stream_verb_max : v - 1; } inline void stream_verb (std::ostream& os, uint16_t v) { os.iword (stream_verb_index) = static_cast (v + 1); } // Diagnostic facility, base infrastructure (potentially reusable). // extern std::ostream* diag_stream; 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_) { assert (false); //@@ Stream verbosity will not be transferred. 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, uint16_t sverb) : type_ (type), name_ (name), sverb_ (sverb) {} void operator() (const diag_record& r) const; private: const char* type_; const char* name_; const uint16_t sverb_; }; typedef diag_prologue simple_prologue; class location { public: // Note that location maintains a shallow reference to path. // location (): file (nullptr), line (0), column (0) {} location (const path* f, std::uint64_t l, std::uint64_t c) : file (f), line (l), column (c) {} const path* 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, uint16_t sverb) : type_ (type), name_ (name), loc_ (l), sverb_ (sverb) {} void operator() (const diag_record& r) const; private: const char* type_; const char* name_; const location loc_; const uint16_t sverb_; }; typedef diag_prologue location_prologue; struct basic_mark_base { explicit basic_mark_base (uint16_t (*sverb) (), const char* type, const char* name = nullptr, const void* data = nullptr, diag_epilogue epilogue = nullptr) : sverb_ (sverb), type_ (type), name_ (name), data_ (data), epilogue_ (epilogue) {} simple_prologue operator() () const { return simple_prologue (epilogue_, type_, name_, sverb_ ()); } location_prologue operator() (const location& l) const { return location_prologue (epilogue_, type_, name_, l, sverb_ ()); } template location_prologue operator() (const L& l) const { return location_prologue ( epilogue_, type_, name_, get_location (l, data_), sverb_ ()); } protected: uint16_t (*sverb_) (); const char* type_; const char* name_; const void* data_; const diag_epilogue epilogue_; }; 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 ([]() {return stream_verb_max;}, "trace", name, data) {} }; typedef diag_mark trace_mark; typedef trace_mark tracer; // fail // template struct fail_mark_base: basic_mark_base { explicit fail_mark_base (const void* data = nullptr) : basic_mark_base (&stream_verb_map, "error", nullptr, data, &epilogue) {} static void epilogue (const diag_record&) {throw E ();} }; template using fail_mark = diag_mark>; extern const fail_mark fail; } #endif // BUILD2_DIAGNOSTICS