From db0edaafe15831ba6fa9c2109da37942506c62b1 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Sat, 23 Jan 2016 11:14:05 +0200 Subject: Cleanup absolute/relative path diagnostics by introducing stream verbosity --- build2/b.cxx | 7 ++- build2/context | 49 +++++++++++++--- build2/context.cxx | 25 ++------ build2/diagnostics | 150 ++++++++++++++++++++++++------------------------ build2/diagnostics.cxx | 19 +++--- build2/file.cxx | 6 +- build2/lexer | 8 +-- build2/lexer.cxx | 2 +- build2/parser | 9 +-- build2/parser.cxx | 26 ++++----- build2/path-io.cxx | 6 +- build2/prerequisite.cxx | 22 +++++-- build2/spec.cxx | 16 ++++-- build2/target.cxx | 12 +++- build2/types | 13 ++++- 15 files changed, 212 insertions(+), 158 deletions(-) diff --git a/build2/b.cxx b/build2/b.cxx index cc87cec..c6a118e 100644 --- a/build2/b.cxx +++ b/build2/b.cxx @@ -151,7 +151,7 @@ main (int argc, char* argv[]) istringstream is (s); is.exceptions (istringstream::failbit | istringstream::badbit); - lexer l (is, ""); + lexer l (is, path ("")); token t (l.next ()); if (t.type == token_type::eos) @@ -202,7 +202,7 @@ main (int argc, char* argv[]) is.exceptions (istringstream::failbit | istringstream::badbit); parser p; - bspec = p.parse_buildspec (is, ""); + bspec = p.parse_buildspec (is, path ("")); } catch (const istringstream::failure&) { @@ -227,7 +227,8 @@ main (int argc, char* argv[]) for (opspec& os: ms) { - const location l ("", 1, 0); //@@ TODO + const path p (""); + const location l (&p, 1, 0); //@@ TODO if (os.empty ()) // Default target: dir{}. os.push_back (targetspec (name ("dir", string ()))); diff --git a/build2/context b/build2/context index 7bae8c3..e2732db 100644 --- a/build2/context +++ b/build2/context @@ -23,7 +23,6 @@ namespace build2 extern dir_path work; extern dir_path home; - extern string_pool path_pool; extern string_pool extension_pool; extern string_pool project_name_pool; @@ -146,18 +145,52 @@ namespace build2 diag_relative (const dir_path&, bool current = true); // Action phrases, e.g., "configure update exe{foo}", "updating exe{foo}", - // and "updating exe{foo} is configured". + // and "updating exe{foo} is configured". Use like this: + // + // info << "while " << diag_doing (a, t); // class target; - std::string - diag_do (const action&, const target&); + struct diag_phrase + { + const action& a; + const target& t; + void (*f) (ostream&, const action&, const target&); + }; - std::string - diag_doing (const action&, const target&); + inline ostream& + operator<< (ostream& os, const diag_phrase& p) + { + p.f (os, p.a, p.t); + return os; + } - std::string - diag_done (const action&, const target&); + void + diag_do (ostream&, const action&, const target&); + + inline diag_phrase + diag_do (const action& a, const target& t) + { + return diag_phrase {a, t, &diag_do}; + } + + void + diag_doing (ostream&, const action&, const target&); + + inline diag_phrase + diag_doing (const action& a, const target& t) + { + return diag_phrase {a, t, &diag_doing}; + } + + void + diag_done (ostream&, const action&, const target&); + + inline diag_phrase + diag_done (const action& a, const target& t) + { + return diag_phrase {a, t, &diag_done}; + } } #include diff --git a/build2/context.cxx b/build2/context.cxx index 0579545..87a6d71 100644 --- a/build2/context.cxx +++ b/build2/context.cxx @@ -5,7 +5,6 @@ #include #include -#include #include #include @@ -22,7 +21,6 @@ namespace build2 dir_path work; dir_path home; - string_pool path_pool; string_pool extension_pool; string_pool project_name_pool; @@ -35,7 +33,6 @@ namespace build2 void reset () { - path_pool.clear (); extension_pool.clear (); project_name_pool.clear (); @@ -299,15 +296,13 @@ namespace build2 // diag_do(), etc. // - string - diag_do (const action&, const target& t) + void + diag_do (ostream& os, const action&, const target& t) { const meta_operation_info& m (*current_mif); const operation_info& io (*current_inner_oif); const operation_info* oo (current_outer_oif); - ostringstream os; - // perform(update(x)) -> "update x" // configure(update(x)) -> "configure updating x" // @@ -325,18 +320,15 @@ namespace build2 os << "(for " << oo->name << ") "; os << t; - return os.str (); } - string - diag_doing (const action&, const target& t) + void + diag_doing (ostream& os, const action&, const target& t) { const meta_operation_info& m (*current_mif); const operation_info& io (*current_inner_oif); const operation_info* oo (current_outer_oif); - ostringstream os; - // perform(update(x)) -> "updating x" // configure(update(x)) -> "configuring updating x" // @@ -350,18 +342,15 @@ namespace build2 os << "(for " << oo->name << ") "; os << t; - return os.str (); } - string - diag_done (const action&, const target& t) + void + diag_done (ostream& os, const action&, const target& t) { const meta_operation_info& m (*current_mif); const operation_info& io (*current_inner_oif); const operation_info* oo (current_outer_oif); - ostringstream os; - // perform(update(x)) -> "x is up to date" // configure(update(x)) -> "updating x is configured" // @@ -385,7 +374,5 @@ namespace build2 os << t << " " << m.name_done; } - - return os.str (); } } diff --git a/build2/diagnostics b/build2/diagnostics index 4aef6dd..27c71fa 100644 --- a/build2/diagnostics +++ b/build2/diagnostics @@ -28,17 +28,6 @@ namespace build2 // 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 @@ -67,7 +56,7 @@ namespace build2 print_process (args.data (), args.size ()); } - // Verbosity level. + // Program verbosity level (-v/--verbose). // // 0 - disabled // 1 - high-level information messages @@ -79,7 +68,7 @@ namespace build2 // // While uint8 is more than enough, use uint16 for the ease of printing. // - extern std::uint16_t verb; + 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 ();} @@ -88,6 +77,46 @@ namespace build2 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; @@ -159,7 +188,7 @@ namespace build2 if (!empty_) { - assert (false); //@@ Relative flag will not be transferred. + assert (false); //@@ Stream verbosity will not be transferred. os_ << r.os_.str (); r.empty_ = true; @@ -243,8 +272,8 @@ namespace build2 struct simple_prologue_base { explicit - simple_prologue_base (const char* type, const char* name, bool rel) - : type_ (type), name_ (name), relative_ (rel) {} + 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; @@ -252,18 +281,20 @@ namespace build2 private: const char* type_; const char* name_; - const bool relative_; + const uint16_t sverb_; }; typedef diag_prologue simple_prologue; class location { public: - location () {} - location (const char* f, std::uint64_t l, std::uint64_t c) + // 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 char* file; + const path* file; std::uint64_t line; std::uint64_t column; }; @@ -273,8 +304,8 @@ namespace build2 location_prologue_base (const char* type, const char* name, const location& l, - bool rel) - : type_ (type), name_ (name), loc_ (l), relative_ (rel) {} + uint16_t sverb) + : type_ (type), name_ (name), loc_ (l), sverb_ (sverb) {} void operator() (const diag_record& r) const; @@ -283,68 +314,55 @@ namespace build2 const char* type_; const char* name_; const location loc_; - const bool relative_; + const uint16_t sverb_; }; typedef diag_prologue 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, + basic_mark_base (uint16_t (*sverb) (), + const char* type, const char* name = nullptr, - const void* data = nullptr) - : type_ (type), name_ (name), data_ (data) {} + 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 (type_, name_, false); + return simple_prologue (epilogue_, type_, name_, sverb_ ()); } location_prologue operator() (const location& l) const { - return location_prologue (type_, name_, l, false); + return location_prologue (epilogue_, type_, name_, l, sverb_ ()); } template location_prologue operator() (const L& l) const { - return location_prologue (type_, name_, get_location (l, data_), false); + 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; - - // 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; - - extern const text_mark text; + extern const basic_mark text; // trace // @@ -352,7 +370,8 @@ namespace build2 { explicit trace_mark_base (const char* name, const void* data = nullptr) - : basic_mark_base ("trace", name, data) {} + : basic_mark_base ([]() {return stream_verb_max;}, "trace", name, data) + {} }; typedef diag_mark trace_mark; @@ -361,36 +380,15 @@ namespace build2 // fail // template - struct fail_mark_base + struct fail_mark_base: basic_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 - location_prologue - operator() (const L& l) const - { - return location_prologue ( - &epilogue, "error", nullptr, get_location (l, data_), false); - } + 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 ();} - - private: - const void* data_; }; template diff --git a/build2/diagnostics.cxx b/build2/diagnostics.cxx index 50e4578..fb108dc 100644 --- a/build2/diagnostics.cxx +++ b/build2/diagnostics.cxx @@ -13,9 +13,9 @@ using namespace std; namespace build2 { - // Relative stream. + // Stream verbosity. // - const int relative_index = ostream::xalloc (); + const int stream_verb_index = ostream::xalloc (); void print_process (const char* const* args, size_t n) @@ -94,7 +94,7 @@ namespace build2 void simple_prologue_base:: operator() (const diag_record& r) const { - relative (r.os_, relative_); + stream_verb (r.os_, sverb_); if (type_ != nullptr) r << type_ << ": "; @@ -106,9 +106,9 @@ namespace build2 void location_prologue_base:: operator() (const diag_record& r) const { - relative (r.os_, relative_); + stream_verb (r.os_, sverb_); - r << loc_.file << ':' << loc_.line << ':' << loc_.column << ": "; + r << *loc_.file << ':' << loc_.line << ':' << loc_.column << ": "; if (type_ != nullptr) r << type_ << ": "; @@ -117,9 +117,10 @@ namespace build2 r << name_ << ": "; } - const basic_mark error ("error"); - const basic_mark warn ("warning"); - const basic_mark info ("info"); - const text_mark text; + const basic_mark error (&stream_verb_map, "error"); + const basic_mark warn (&stream_verb_map, "warning"); + const basic_mark info (&stream_verb_map, "info"); + const basic_mark text (&stream_verb_map, nullptr); + const fail_mark fail; } diff --git a/build2/file.cxx b/build2/file.cxx index 68b655e..9a5ab80 100644 --- a/build2/file.cxx +++ b/build2/file.cxx @@ -275,9 +275,7 @@ namespace build2 ifs.exceptions (ifstream::failbit | ifstream::badbit); - path rbf (diag_relative (bf)); - - lexer lex (ifs, rbf.string ()); + lexer lex (ifs, bf); token t (lex.next ()); token_type tt; @@ -286,7 +284,7 @@ namespace build2 tt != token_type::prepend && tt != token_type::append)) { - error << "variable '" << var << "' expected as first line in " << rbf; + error << "variable '" << var << "' expected as first line in " << bf; throw failed (); // Suppress "used uninitialized" warning. } diff --git a/build2/lexer b/build2/lexer index 69ca0a8..06f2b24 100644 --- a/build2/lexer +++ b/build2/lexer @@ -44,14 +44,14 @@ namespace build2 { public: lexer (std::istream& is, - const std::string& name, + const path& name, void (*processor) (token&, const lexer&) = nullptr) : char_scanner (is), fail (name), processor_ (processor), sep_ (false) { mode_.push (lexer_mode::normal); } - const std::string& + const path& name () const {return fail.name_;} // Note: sets mode for the next token. If mode is pairs, then @@ -115,12 +115,12 @@ namespace build2 private: struct fail_mark_base: build2::fail_mark_base { - fail_mark_base (const std::string& n): name_ (n) {} + fail_mark_base (const path& n): name_ (n) {} location_prologue operator() (const xchar&) const; - std::string name_; + path name_; }; typedef diag_mark fail_mark; diff --git a/build2/lexer.cxx b/build2/lexer.cxx index da3d64e..c73017e 100644 --- a/build2/lexer.cxx +++ b/build2/lexer.cxx @@ -458,6 +458,6 @@ namespace build2 operator() (const xchar& c) const { return build2::fail_mark_base::operator() ( - location (name_.c_str (), c.line, c.column)); + location (&name_, c.line, c.column)); } } diff --git a/build2/parser b/build2/parser index a379bd7..ae3add9 100644 --- a/build2/parser +++ b/build2/parser @@ -36,13 +36,14 @@ namespace build2 // Issue diagnostics and throw failed in case of an error. // void - parse_buildfile (std::istream&, const path&, scope& root, scope& base); + parse_buildfile (std::istream&, const path& name, + scope& root, scope& base); buildspec - parse_buildspec (std::istream&, const std::string& name); + parse_buildspec (std::istream&, const path& name); token - parse_variable (lexer&, scope&, std::string name, token_type kind); + parse_variable (lexer&, scope&, std::string var_name, token_type kind); names_type parse_export_stub (std::istream& is, const path& p, scope& r, scope& b) @@ -285,7 +286,7 @@ namespace build2 protected: bool boot_; - const std::string* path_; // Path processed by diag_relative() and pooled. + const path* path_; // Current path. lexer* lexer_; target* target_; // Current target, if any. scope* scope_; // Current base scope (out_base). diff --git a/build2/parser.cxx b/build2/parser.cxx index 187c47e..7e07834 100644 --- a/build2/parser.cxx +++ b/build2/parser.cxx @@ -39,7 +39,7 @@ namespace build2 { enter_buildfile (p); - path_ = &path_pool.find (diag_relative (p)); // Relative to work. + path_ = &p; lexer l (is, *path_); lexer_ = &l; @@ -63,14 +63,14 @@ namespace build2 token parser:: parse_variable (lexer& l, scope& s, string name, type kind) { - path_ = &l.name (); // Note: not pooled. + path_ = &l.name (); lexer_ = &l; target_ = nullptr; scope_ = &s; type tt; token t (type::eos, false, 0, 0); - variable (t, tt, name, kind); + variable (t, tt, move (name), kind); return t; } @@ -539,8 +539,8 @@ namespace build2 enter_buildfile (p); - const string* op (path_); - path_ = &path_pool.find (diag_relative (p)); // Relative to work. + const path* op (path_); + path_ = &p; lexer l (ifs, *path_); lexer* ol (lexer_); @@ -675,8 +675,8 @@ namespace build2 enter_buildfile (p); - const string* op (path_); - path_ = &path_pool.find (diag_relative (p)); // Relative to work. + const path* op (path_); + path_ = &p; lexer l (ifs, *path_); lexer* ol (lexer_); @@ -1899,11 +1899,11 @@ namespace build2 } buildspec parser:: - parse_buildspec (istream& is, const std::string& name) + parse_buildspec (istream& is, const path& name) { - path_ = &name; // Note: caller pools. + path_ = &name; - lexer l (is, name, &paren_processor); + lexer l (is, *path_, &paren_processor); lexer_ = &l; target_ = nullptr; scope_ = root_ = global_scope; @@ -2232,8 +2232,8 @@ namespace build2 static location get_location (const token& t, const void* data) { - assert (data != nullptr); - const string& p (**static_cast (data)); - return location (p.c_str (), t.line, t.column); + assert (data != nullptr); // &parser::path_ + const path* p (*static_cast (data)); + return location (p, t.line, t.column); } } diff --git a/build2/path-io.cxx b/build2/path-io.cxx index 81b981b..845b0a3 100644 --- a/build2/path-io.cxx +++ b/build2/path-io.cxx @@ -16,14 +16,14 @@ namespace build2 ostream& operator<< (ostream& os, const path& p) { - return os << (relative (os) ? diag_relative (p) : p.string ()); + return os << (stream_verb (os) < 2 ? diag_relative (p) : p.string ()); } ostream& operator<< (ostream& os, const dir_path& d) { - if (relative (os)) - os << diag_relative (d); + if (stream_verb (os) < 2) + os << diag_relative (d); // Adds trailing '/'. else { const string& s (d.string ()); diff --git a/build2/prerequisite.cxx b/build2/prerequisite.cxx index 2192abe..fb432eb 100644 --- a/build2/prerequisite.cxx +++ b/build2/prerequisite.cxx @@ -22,16 +22,26 @@ namespace build2 { if (pk.proj != nullptr) os << *pk.proj << '%'; - - // Don't print scope if we are project-qualified or the - // prerequisite's directory is absolute. In both these - // cases the scope is not used to resolve it to target. + // + // Don't print scope if we are project-qualified or the prerequisite's + // directory is absolute. In both these cases the scope is not used to + // resolve it to target. // else if (!pk.tk.dir->absolute ()) { - string s (diag_relative (pk.scope->out_path (), false)); + // Avoid printing './' in './:...', similar to what we do for the + // directory in target_key. + // + const dir_path& s (pk.scope->out_path ()); + + if (stream_verb (os) < 2) + { + const string& r (diag_relative (s, false)); - if (!s.empty ()) + if (!r.empty ()) + os << r << ':'; + } + else os << s << ':'; } diff --git a/build2/spec.cxx b/build2/spec.cxx index 89ed9c8..3f02d87 100644 --- a/build2/spec.cxx +++ b/build2/spec.cxx @@ -18,10 +18,18 @@ namespace build2 { if (!s.src_base.empty ()) { - string d (diag_relative (s.src_base, false)); - - if (!d.empty ()) - os << d << '@'; + // Avoid printing './' in './@...', similar to what we do for the + // {target,prerequisite}_key. + // + if (stream_verb (os) < 2) + { + const string& r (diag_relative (s.src_base, false)); + + if (!r.empty ()) + os << r << '@'; + } + else + os << s.src_base << '@'; } os << s.name; diff --git a/build2/target.cxx b/build2/target.cxx index 0dcf959..43c6a41 100644 --- a/build2/target.cxx +++ b/build2/target.cxx @@ -254,10 +254,16 @@ namespace build2 // inside {}, e.g., dir{bar/}, not bar/dir{}. // bool n (!k.name->empty ()); - string d (diag_relative (*k.dir, false)); if (n) - os << d; + { + // Avoid printing './' in './{...}' + // + if (stream_verb (os) < 2) + os << diag_relative (*k.dir, false); + else + os << *k.dir; + } os << k.type->name << '{'; @@ -269,7 +275,7 @@ namespace build2 os << '.' << *k.ext; } else - os << d; + os << *k.dir; os << '}'; diff --git a/build2/types b/build2/types index 407a3b5..35aaa58 100644 --- a/build2/types +++ b/build2/types @@ -5,11 +5,13 @@ #ifndef BUILD2_TYPES #define BUILD2_TYPES +#include // ostream #include #include #include // pair #include // unique_ptr, shared_ptr #include // size_t +#include // uint{8,16,32,64}_t #include // reference_wrapper #include @@ -21,8 +23,13 @@ namespace build2 { // Commonly-used types. // - using std::pair; + using std::uint8_t; + using std::uint16_t; + using std::uint32_t; + using std::uint64_t; using std::size_t; + + using std::pair; using std::string; using std::unique_ptr; using std::shared_ptr; @@ -32,6 +39,10 @@ namespace build2 using strings = vector; using cstrings = vector; + // + // + using std::ostream; + // // using butl::path; -- cgit v1.1