// 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 <vector> #include <cstdint> #include <utility> #include <cassert> #include <sstream> #include <ostream> #include <exception> #include <type_traits> #include <build/path> namespace build { // Throw this exception to terminate the build. The handler should // assume that the diagnostics has already been issued. // class failed: public std::exception {}; // In addition to calling relative_work(), this function also uses // shorter notations such as '~/'. If the path is the same as work, // it returns '.'. // std::string diag_relative_work (const path&); inline std::ostream& operator<< (std::ostream& os, const path& p) { return os << diag_relative_work (p); } // Print process commmand line. // void print_process (const char* const* args); inline void print_process (const std::vector<const char*>& args) { print_process (args.data ()); } // Trace verbosity level. // // 1 - command lines to update explicit targets (e.g., .o) // 2 - command lines to update implicit targets (e.g., .d) // 3 - things didn't work out (e.g., rule did not match) // 4 - additional information // 5 - more additional information // extern std::uint8_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; template <typename> struct diag_prologue; template <typename> struct diag_mark; struct diag_record; 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; private: mutable bool empty_; mutable std::ostringstream os_; 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; struct location { location () {} location (const char* f, std::uint64_t l, std::uint64_t c) : file (f), line (l), column (c) {} const char* 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_)); } private: 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; 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; 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 // BUILD_DIAGNOSTICS