aboutsummaryrefslogtreecommitdiff
path: root/build2/diagnostics
diff options
context:
space:
mode:
Diffstat (limited to 'build2/diagnostics')
-rw-r--r--build2/diagnostics402
1 files changed, 402 insertions, 0 deletions
diff --git a/build2/diagnostics b/build2/diagnostics
new file mode 100644
index 0000000..52f999a
--- /dev/null
+++ b/build2/diagnostics
@@ -0,0 +1,402 @@
+// file : build2/diagnostics -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BUILD2_DIAGNOSTICS
+#define BUILD2_DIAGNOSTICS
+
+#include <cstddef> // size_t
+#include <cstdint>
+#include <utility>
+#include <cassert>
+#include <sstream>
+#include <ostream>
+#include <exception>
+#include <type_traits>
+
+#include <butl/path>
+
+#include <build2/types>
+#include <build2/path-io>
+
+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 {};
+
+ // Flag that indicates whether paths should be inserted relative
+ // into this stream.
+ //
+ extern const int relative_index;
+
+ inline bool
+ relative (std::ostream& os) {return os.iword (relative_index);}
+
+ inline void
+ relative (std::ostream& os, bool v) {os.iword (relative_index) = v ? 1 : 0;}
+
+ // 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 ());
+ }
+
+ // Verbosity level.
+ //
+ // 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 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 ();}
+ template <typename F> inline void level6 (const F& f) {if (verb >= 6) f ();}
+
+ // Diagnostic facility, base infrastructure (potentially reusable).
+ //
+ extern std::ostream* diag_stream;
+
+ 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_)
+ {
+ assert (false); //@@ Relative flag 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 <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, bool rel)
+ : type_ (type), name_ (name), relative_ (rel) {}
+
+ void
+ operator() (const diag_record& r) const;
+
+ private:
+ const char* type_;
+ const char* name_;
+ const bool relative_;
+ };
+ typedef diag_prologue<simple_prologue_base> simple_prologue;
+
+ class location
+ {
+ public:
+ 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,
+ bool rel)
+ : type_ (type), name_ (name), loc_ (l), relative_ (rel) {}
+
+ void
+ operator() (const diag_record& r) const;
+
+ private:
+ const char* type_;
+ const char* name_;
+ const location loc_;
+ const bool relative_;
+ };
+ typedef diag_prologue<location_prologue_base> location_prologue;
+
+ // Here is the absolute/relative path rationale: we want it absolute
+ // in the error/warning/info streams to give the user the complete
+ // picture. But in the text stream (e.g., command lines), we print
+ // relative unless verbosity is greater than 1.
+ //
+ 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_, false);
+ }
+
+ location_prologue
+ operator() (const location& l) const
+ {
+ return location_prologue (type_, name_, l, false);
+ }
+
+ template <typename L>
+ location_prologue
+ operator() (const L& l) const
+ {
+ return location_prologue (type_, name_, get_location (l, data_), false);
+ }
+
+ 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;
+
+ // text
+ //
+ struct text_mark_base: basic_mark_base
+ {
+ text_mark_base (): basic_mark_base (nullptr) {}
+
+ simple_prologue
+ operator() () const
+ {
+ return simple_prologue (type_, name_, verb <= 1);
+ }
+ };
+ typedef diag_mark<text_mark_base> text_mark;
+
+ extern const text_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, false);
+ }
+
+ location_prologue
+ operator() (const location& l) const
+ {
+ return location_prologue (&epilogue, "error", nullptr, l, false);
+ }
+
+ template <typename L>
+ location_prologue
+ operator() (const L& l) const
+ {
+ return location_prologue (
+ &epilogue, "error", nullptr, get_location (l, data_), false);
+ }
+
+ 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 // BUILD2_DIAGNOSTICS