From 9964a9aca03b38c2959994e0fdc91014da252cb8 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 20 Nov 2017 13:11:59 +0200 Subject: Implement dump directive It can be used to print (to stderr) a human-readable representation of the current scope or a list of targets. For example: dump # Dump current scope. dump lib{foo} details/exe{bar} # Dump two targets. This is primarily useful for debugging as well as to write build system tests. --- build2/dump.cxx | 61 ++++++++++++++++++++++++------ build2/dump.hxx | 13 +++++-- build2/parser.cxx | 109 +++++++++++++++++++++++++++++++++++++++++++++++++----- build2/parser.hxx | 3 ++ 4 files changed, 162 insertions(+), 24 deletions(-) diff --git a/build2/dump.cxx b/build2/dump.cxx index ab2e73f..7e2676d 100644 --- a/build2/dump.cxx +++ b/build2/dump.cxx @@ -184,16 +184,27 @@ namespace build2 } static void - dump_target (ostream& os, string& ind, const target& t, const scope& s) + dump_target (ostream& os, + string& ind, + const target& t, + const scope& s, + bool relative) { - // Print the target and its prerequisites relative to the scope. To achieve - // this we are going to temporarily lower the stream verbosity to level 1. - // The drawback of doing this is that we also lower the verbosity of - // extension printing (it wouldn't have been bad at all to get 'foo.?' for - // unassigned and 'foo.' for empty). + // If requested, print the target and its prerequisites relative to the + // scope. To achieve this we are going to temporarily lower the stream + // verbosity to level 1. The drawback of doing this is that we also lower + // the verbosity of extension printing (it wouldn't have been bad at all + // to get 'foo.?' for unassigned and 'foo.' for empty). // - uint16_t sv (stream_verb (os)); - stream_verb (os, 1); + // @@ Actually, all those foo.? look rather hairy... + // @@ Can't we change level to a bit mask? + // + uint16_t sv; + if (relative) + { + sv = stream_verb (os); + stream_verb (os, 1); + } os << ind << t; @@ -234,7 +245,8 @@ namespace build2 } } - stream_verb (os, sv); // We want variable values in full. + if (relative) + stream_verb (os, sv); // We want variable values in full. // Print target-specific variables. // @@ -265,7 +277,10 @@ namespace build2 if (d.empty ()) os << ind << dir_path::traits::directory_separator; else - os << ind << relative (d); + { + dir_path rd (relative (d)); + os << ind << (rd.empty () ? dir_path (".") : rd); + } os << ":" << endl << ind << '{'; @@ -329,7 +344,7 @@ namespace build2 } os << endl; - dump_target (os, ind, t, p); + dump_target (os, ind, t, p, true /* relative */); } ind.resize (ind.size () - 2); @@ -346,11 +361,33 @@ namespace build2 assert (&i->second == global_scope); // We don't lock diag_stream here as dump() is supposed to be called from - // the main thread prior to any other threads being spawned. + // the main thread prior/after to any other threads being spawned. // string ind; ostream& os (*diag_stream); dump_scope (os, ind, i); os << endl; } + + void + dump (const scope& s, const char* cind) + { + const scope_map_base& m (scopes); // Iterator interface. + auto i (m.find (s.out_path ())); + assert (i != m.end () && &i->second == &s); + + string ind (cind); + ostream& os (*diag_stream); + dump_scope (os, ind, i); + os << endl; + } + + void + dump (const target& t, const char* cind) + { + string ind (cind); + ostream& os (*diag_stream); + dump_target (os, ind, t, t.base_scope (), false /* relative */); + os << endl; + } } diff --git a/build2/dump.hxx b/build2/dump.hxx index e456540..8330708 100644 --- a/build2/dump.hxx +++ b/build2/dump.hxx @@ -8,14 +8,21 @@ #include #include -#include - namespace build2 { - // Dump the state pertaining to the current action. + class scope; + class target; + + // Dump the build state to diag_stream. // void dump (); + + void + dump (const scope&, const char* ind = ""); + + void + dump (const target&, const char* ind = ""); } #endif // BUILD2_DUMP_HXX diff --git a/build2/parser.cxx b/build2/parser.cxx index 1863f67..7c8695a 100644 --- a/build2/parser.cxx +++ b/build2/parser.cxx @@ -9,6 +9,7 @@ #include // path_search(), path_match() +#include #include #include #include @@ -112,6 +113,40 @@ namespace build2 tracer& tr) : p_ (&p), t_ (p.target_) { + // Find or insert. + // + auto r (process_target (p, n, o, loc)); + p.target_ = &targets.insert (*r.first, // target type + move (n.dir), + move (o.dir), + move (n.value), + move (r.second), // extension + implied, + tr).first; + } + + static const target* + find_target (parser& p, + name& n, // If n.pair, then o is out dir. + name& o, + const location& loc, + tracer& tr) + { + auto r (process_target (p, n, o, loc)); + return targets.find (*r.first, // target type + n.dir, + o.dir, + n.value, + r.second, // extension + tr); + } + + static pair> + process_target (parser& p, + name& n, // If n.pair, then o is out dir. + name& o, + const location& loc) + { optional e; const target_type* ti (p.scope_->find_target_type (n, e)); @@ -148,16 +183,9 @@ namespace build2 out = o.dir.relative () ? od / o.dir : move (o.dir); out.normalize (); } + o.dir = move (out); // Result. - // Find or insert. - // - p.target_ = &targets.insert (*ti, - move (d), - move (out), - move (n.value), - move (e), - implied, - tr).first; + return make_pair (ti, move (e)); } ~enter_target () @@ -348,6 +376,10 @@ namespace build2 { f = &parser::parse_diag; } + else if (n == "dump") + { + f = &parser::parse_dump; + } else if (n == "source") { f = &parser::parse_source; @@ -1823,6 +1855,65 @@ namespace build2 next (t, tt); // Swallow newline. } + void parser:: + parse_dump (token& t, type& tt) + { + // dump [...] + // + // If there are no targets, then we dump the current scope. + // + tracer trace ("parser::parse_dump", &path_); + + const location l (get_location (t)); + next (t, tt); + names ns (tt != type::newline && tt != type::eos + ? parse_names (t, tt, pattern_mode::ignore) + : names ()); + + text (l) << "dump:"; + + // Dump directly into diag_stream. + // + ostream& os (*diag_stream); + + // Print directories as absolute. + // + const dir_path* orb (relative_base); + relative_base = &empty_dir_path; + + if (ns.empty ()) + { + if (scope_ != nullptr) + dump (*scope_, " "); // Indent two spaces. + else + os << " " << endl; + } + else + { + for (auto i (ns.begin ()), e (ns.end ()); i != e; ++i) + { + name& n (*i); + name o (n.pair ? move (*++i) : name ()); + + const target* t (enter_target::find_target (*this, n, o, l, trace)); + + if (t != nullptr) + dump (*t, " "); // Indent two spaces. + else + { + os << " ' << endl; + } + } + } + + relative_base = orb; + + if (tt != type::eos) + next (t, tt); // Swallow newline. + } + string parser:: parse_variable_name (names&& ns, const location& l) { diff --git a/build2/parser.hxx b/build2/parser.hxx index ce9ad2f..3c51801 100644 --- a/build2/parser.hxx +++ b/build2/parser.hxx @@ -85,6 +85,9 @@ namespace build2 parse_diag (token&, token_type&); void + parse_dump (token&, token_type&); + + void parse_source (token&, token_type&); void -- cgit v1.1