From 7a458f210f296cb3cc1551a4606f0cf025003f3a Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 2 May 2023 13:05:27 +0200 Subject: Add --dump-scope and --dump-target options to limit --dump output --- build2/b.cxx | 133 ++++++++++++++++++++++++++++++++++++++++++-- doc/manual.cli | 6 +- libbuild2/b-options.cxx | 51 ++++++++++++++--- libbuild2/b-options.hxx | 30 +++++++--- libbuild2/b-options.ixx | 30 +++++++++- libbuild2/b.cli | 28 +++++++--- libbuild2/dump.cxx | 40 ++++++++----- libbuild2/dump.hxx | 9 ++- libbuild2/parser.cxx | 4 +- libbuild2/types-parsers.cxx | 67 +++++++++++++++++----- libbuild2/types-parsers.hxx | 11 ++++ 11 files changed, 348 insertions(+), 61 deletions(-) diff --git a/build2/b.cxx b/build2/b.cxx index 04f0677..add27d4 100644 --- a/build2/b.cxx +++ b/build2/b.cxx @@ -497,12 +497,137 @@ main (int argc, char* argv[]) bool dump_load (false); bool dump_match (false); - if (ops.dump_specified ()) + for (const string& p: ops.dump ()) { - dump_load = ops.dump ().find ("load") != ops.dump ().end (); - dump_match = ops.dump ().find ("match") != ops.dump ().end (); + if (p == "load") dump_load = true; + else if (p == "match") dump_match = true; + else fail << "unknown phase '" << p << "' specified with --dump"; } + auto dump = [&trace, &ops] (context& ctx, optional a) + { + const dir_paths& scopes (ops.dump_scope ()); + const vector>>& targets (ops.dump_target ()); + + if (scopes.empty () && targets.empty ()) + build2::dump (ctx, a); + else + { + auto comp_norm = [] (dir_path& d, const char* what) + { + try + { + if (d.relative ()) + d.complete (); + + d.normalize (); + } + catch (const invalid_path& e) + { + fail << "invalid path '" << e.path << "' specified with " << what; + } + }; + + // If exact is false then return any outer scope that contains this + // directory except for the global scope. + // + auto find_scope = [&ctx, &comp_norm] (dir_path& d, + bool exact, + const char* what) -> const scope* + { + comp_norm (d, what); + + // This is always the output directory (specifically, see the target + // case below). + // + const scope& s (ctx.scopes.find_out (d)); + + return ((exact ? s.out_path () == d : s != ctx.global_scope) + ? &s + : nullptr); + }; + + // Dump scopes. + // + for (dir_path d: scopes) + { + const scope* s (find_scope (d, true, "--dump-scope")); + + if (s == nullptr) + l5 ([&]{trace << "unknown target scope " << d + << " specified with --dump-scope";}); + + build2::dump (s, a); + } + + // Dump targets. + // + for (const pair>& p: targets) + { + const target* t (nullptr); + + // Find the innermost known scope that contains this target. This + // is where we are going to resolve its type. + // + dir_path d (p.second ? p.second->dir : p.first.dir); + + if (const scope* s = find_scope (d, false, "--dump-target")) + { + // Complete relative directories in names. + // + name n (p.first), o; + + if (p.second) + { + comp_norm (n.dir, "--dump-target"); + o.dir = move (d); + } + else + n.dir = move (d); + + // Similar logic to parser::enter_target::find_target() as used by + // the dump directive. Except here we treat unknown target type as + // unknown target. + // + auto r (s->find_target_type (n, location ())); + + if (r.first != nullptr) + { + t = ctx.targets.find (*r.first, // target type + n.dir, + o.dir, + n.value, + r.second, // extension + trace); + + if (t == nullptr) + l5 ([&] + { + // @@ TODO: default_extension? + // + target::combine_name (n.value, r.second, false); + names ns {move (n)}; + if (p.second) + ns.push_back (move (o)); + + trace << "unknown target " << ns + << " specified with --dump-target"; + }); + } + else + l5 ([&]{trace << "unknown target type '" << n.type << "' in " + << *s << " specified with --dump-target";}); + + } + else + l5 ([&]{trace << "unknown target scope " << d + << " specified with --dump-target";}); + + build2::dump (t, a); + } + } + }; + // If not NULL, then lifted points to the operation that has been "lifted" // to the meta-operaion (see the logic below for details). Skip is the // position of the next operation. @@ -1328,7 +1453,7 @@ main (int argc, char* argv[]) } // target if (dump_load) - dump (ctx); + dump (ctx, nullopt /* action */); // Finally, match the rules and perform the operation. // diff --git a/doc/manual.cli b/doc/manual.cli index 6f3def4..4583ca0 100644 --- a/doc/manual.cli +++ b/doc/manual.cli @@ -4493,7 +4493,8 @@ buildfile:5:1: dump: The output of \c{dump} might look familiar: in \l{#intro-dirs-scopes Output Directories and Scopes} we've used the \c{--dump} option to print the entire build state, which looks pretty similar. In fact, the \c{dump} directive uses -the same mechanism but allows us to print individual scopes and targets. +the same mechanism but allows us to print individual scopes and targets from +within a \c{buildfile}. There is, however, an important difference to keep in mind: \c{dump} prints the state of a target or scope at the point in the \c{buildfile} load phase @@ -4507,6 +4508,9 @@ a result, while the \c{dump} directive should be sufficient in most cases, sometimes you may need to use the \c{--dump} option to examine the build state just before rule execution. +\N|It is possible to limit the output of \c{--dump} to specific scopes and/or +targets with the \c{--dump-scope} and \c{--dump-target} options.| + Let's now move from state to behavior. As we already know, to see the underlying commands executed by the build system we use the \c{-v} options (which is equivalent to \c{--verbose\ 2}). Note, however, that these are diff --git a/libbuild2/b-options.cxx b/libbuild2/b-options.cxx index 9cdfd1b..1fbc5d9 100644 --- a/libbuild2/b-options.cxx +++ b/libbuild2/b-options.cxx @@ -361,6 +361,10 @@ namespace build2 no_mtime_check_ (), dump_ (), dump_specified_ (false), + dump_scope_ (), + dump_scope_specified_ (false), + dump_target_ (), + dump_target_specified_ (false), trace_match_ (), trace_match_specified_ (false), trace_execute_ (), @@ -609,21 +613,35 @@ namespace build2 if (a.dump_specified_) { - ::build2::build::cli::parser< std::set>::merge ( + ::build2::build::cli::parser< strings>::merge ( this->dump_, a.dump_); this->dump_specified_ = true; } + if (a.dump_scope_specified_) + { + ::build2::build::cli::parser< dir_paths>::merge ( + this->dump_scope_, a.dump_scope_); + this->dump_scope_specified_ = true; + } + + if (a.dump_target_specified_) + { + ::build2::build::cli::parser< vector>>>::merge ( + this->dump_target_, a.dump_target_); + this->dump_target_specified_ = true; + } + if (a.trace_match_specified_) { - ::build2::build::cli::parser< std::vector>::merge ( + ::build2::build::cli::parser< vector>::merge ( this->trace_match_, a.trace_match_); this->trace_match_specified_ = true; } if (a.trace_execute_specified_) { - ::build2::build::cli::parser< std::vector>::merge ( + ::build2::build::cli::parser< vector>::merge ( this->trace_execute_, a.trace_execute_); this->trace_execute_specified_ = true; } @@ -958,7 +976,20 @@ namespace build2 << "\033[1m--dump\033[0m \033[4mphase\033[0m Dump the build system state after the specified phase." << ::std::endl << " Valid \033[4mphase\033[0m values are \033[1mload\033[0m (after loading \033[1mbuildfiles\033[0m)" << ::std::endl << " and \033[1mmatch\033[0m (after matching rules to targets). Repeat" << ::std::endl - << " this option to dump the state after multiple phases." << ::std::endl; + << " this option to dump the state after multiple phases. By" << ::std::endl + << " default the entire build state is dumped but this" << ::std::endl + << " behavior can be altered with the --dump-scope\033[0m and" << ::std::endl + << " \033[1m--dump-target\033[0m options." << ::std::endl; + + os << std::endl + << "\033[1m--dump-scope\033[0m \033[4mdir\033[0m Dump the build system state for the specified scope" << ::std::endl + << " only. Repeat this option to dump the state of multiple" << ::std::endl + << " scopes." << ::std::endl; + + os << std::endl + << "\033[1m--dump-target\033[0m \033[4mtarget\033[0m Dump the build system state for the specified target" << ::std::endl + << " only. Repeat this option to dump the state of multiple" << ::std::endl + << " targets." << ::std::endl; os << std::endl << "\033[1m--trace-match\033[0m \033[4mtarget\033[0m Trace rule matching for the specified target. This is" << ::std::endl @@ -1135,13 +1166,19 @@ namespace build2 _cli_b_options_map_["--no-mtime-check"] = &::build2::build::cli::thunk< b_options, &b_options::no_mtime_check_ >; _cli_b_options_map_["--dump"] = - &::build2::build::cli::thunk< b_options, std::set, &b_options::dump_, + &::build2::build::cli::thunk< b_options, strings, &b_options::dump_, &b_options::dump_specified_ >; + _cli_b_options_map_["--dump-scope"] = + &::build2::build::cli::thunk< b_options, dir_paths, &b_options::dump_scope_, + &b_options::dump_scope_specified_ >; + _cli_b_options_map_["--dump-target"] = + &::build2::build::cli::thunk< b_options, vector>>, &b_options::dump_target_, + &b_options::dump_target_specified_ >; _cli_b_options_map_["--trace-match"] = - &::build2::build::cli::thunk< b_options, std::vector, &b_options::trace_match_, + &::build2::build::cli::thunk< b_options, vector, &b_options::trace_match_, &b_options::trace_match_specified_ >; _cli_b_options_map_["--trace-execute"] = - &::build2::build::cli::thunk< b_options, std::vector, &b_options::trace_execute_, + &::build2::build::cli::thunk< b_options, vector, &b_options::trace_execute_, &b_options::trace_execute_specified_ >; _cli_b_options_map_["--no-column"] = &::build2::build::cli::thunk< b_options, &b_options::no_column_ >; diff --git a/libbuild2/b-options.hxx b/libbuild2/b-options.hxx index d965ff6..9afef25 100644 --- a/libbuild2/b-options.hxx +++ b/libbuild2/b-options.hxx @@ -13,8 +13,6 @@ // // End prologue. -#include - #include namespace build2 @@ -167,19 +165,31 @@ namespace build2 const bool& no_mtime_check () const; - const std::set& + const strings& dump () const; bool dump_specified () const; - const std::vector& + const dir_paths& + dump_scope () const; + + bool + dump_scope_specified () const; + + const vector>>& + dump_target () const; + + bool + dump_target_specified () const; + + const vector& trace_match () const; bool trace_match_specified () const; - const std::vector& + const vector& trace_execute () const; bool @@ -293,11 +303,15 @@ namespace build2 bool structured_result_specified_; bool mtime_check_; bool no_mtime_check_; - std::set dump_; + strings dump_; bool dump_specified_; - std::vector trace_match_; + dir_paths dump_scope_; + bool dump_scope_specified_; + vector>> dump_target_; + bool dump_target_specified_; + vector trace_match_; bool trace_match_specified_; - std::vector trace_execute_; + vector trace_execute_; bool trace_execute_specified_; bool no_column_; bool no_line_; diff --git a/libbuild2/b-options.ixx b/libbuild2/b-options.ixx index cdbbc3a..d19edb8 100644 --- a/libbuild2/b-options.ixx +++ b/libbuild2/b-options.ixx @@ -206,7 +206,7 @@ namespace build2 return this->no_mtime_check_; } - inline const std::set& b_options:: + inline const strings& b_options:: dump () const { return this->dump_; @@ -218,7 +218,31 @@ namespace build2 return this->dump_specified_; } - inline const std::vector& b_options:: + inline const dir_paths& b_options:: + dump_scope () const + { + return this->dump_scope_; + } + + inline bool b_options:: + dump_scope_specified () const + { + return this->dump_scope_specified_; + } + + inline const vector>>& b_options:: + dump_target () const + { + return this->dump_target_; + } + + inline bool b_options:: + dump_target_specified () const + { + return this->dump_target_specified_; + } + + inline const vector& b_options:: trace_match () const { return this->trace_match_; @@ -230,7 +254,7 @@ namespace build2 return this->trace_match_specified_; } - inline const std::vector& b_options:: + inline const vector& b_options:: trace_execute () const { return this->trace_execute_; diff --git a/libbuild2/b.cli b/libbuild2/b.cli index 5d6ead2..768bcd0 100644 --- a/libbuild2/b.cli +++ b/libbuild2/b.cli @@ -1,8 +1,6 @@ // file : libbuild2/b.cli // license : MIT; see accompanying LICENSE file -include ; - include ; "\section=1" @@ -774,23 +772,39 @@ namespace build2 \cb{--mtime-check} for details." } - std::set --dump + strings --dump { "", "Dump the build system state after the specified phase. Valid values are \cb{load} (after loading \cb{buildfiles}) and \cb{match} - (after matching rules to targets). Repeat this option to dump the - state after multiple phases." + (after matching rules to targets). Repeat this option to dump the state + after multiple phases. By default the entire build state is dumped but + this behavior can be altered with the \c{--dump-scope} and + \cb{--dump-target} options." + } + + dir_paths --dump-scope + { + "", + "Dump the build system state for the specified scope only. Repeat this + option to dump the state of multiple scopes." + } + + vector>> --dump-target + { + "", + "Dump the build system state for the specified target only. Repeat this + option to dump the state of multiple targets." } - std::vector --trace-match + vector --trace-match { "", "Trace rule matching for the specified target. This is primarily useful during troubleshooting. Repeat this option to trace multiple targets." } - std::vector --trace-execute + vector --trace-execute { "", "Trace rule execution for the specified target. This is primarily useful diff --git a/libbuild2/dump.cxx b/libbuild2/dump.cxx index 4ee75ee..e00d1b9 100644 --- a/libbuild2/dump.cxx +++ b/libbuild2/dump.cxx @@ -651,29 +651,43 @@ namespace build2 } void - dump (const scope& s, const char* cind) + dump (const scope* s, optional a, const char* cind) { - const scope_map& m (s.ctx.scopes); - auto i (m.find_exact (s.out_path ())); - assert (i != m.end () && i->second.front () == &s); - string ind (cind); ostream& os (*diag_stream); - dump_scope (nullopt /* action */, os, ind, i, false /* relative */); + + if (s != nullptr) + { + const scope_map& m (s->ctx.scopes); + auto i (m.find_exact (s->out_path ())); + assert (i != m.end () && i->second.front () == s); + + dump_scope (a, os, ind, i, false /* relative */); + } + else + os << ind << ""; + os << endl; } void - dump (const target& t, const char* cind) + dump (const target* t, optional a, const char* cind) { string ind (cind); ostream& os (*diag_stream); - dump_target (nullopt /* action */, - os, - ind, - t, - t.base_scope (), - false /* relative */); + + if (t != nullptr) + { + dump_target (a, + os, + ind, + *t, + t->base_scope (), + false /* relative */); + } + else + os << ind << ""; + os << endl; } } diff --git a/libbuild2/dump.hxx b/libbuild2/dump.hxx index 6ec6944..4e08634 100644 --- a/libbuild2/dump.hxx +++ b/libbuild2/dump.hxx @@ -18,14 +18,17 @@ namespace build2 // rules have been matched for this action and dump action-specific // information (like rule-specific variables). // + // If scope or target is NULL, then assume not found and write a format- + // appropriate indication. + // LIBBUILD2_SYMEXPORT void - dump (const context&, optional = nullopt); + dump (const context&, optional); LIBBUILD2_SYMEXPORT void - dump (const scope&, const char* ind = ""); + dump (const scope*, optional, const char* ind = ""); LIBBUILD2_SYMEXPORT void - dump (const target&, const char* ind = ""); + dump (const target*, optional, const char* ind = ""); } #endif // LIBBUILD2_DUMP_HXX diff --git a/libbuild2/parser.cxx b/libbuild2/parser.cxx index 0b8fb42..6f212da 100644 --- a/libbuild2/parser.cxx +++ b/libbuild2/parser.cxx @@ -4703,7 +4703,7 @@ namespace build2 if (ns.empty ()) { if (scope_ != nullptr) - dump (*scope_, " "); // Indent two spaces. + dump (scope_, nullopt /* action */, " "); // Indent two spaces. else os << " " << endl; } @@ -4722,7 +4722,7 @@ namespace build2 const target* t (enter_target::find_target (*this, n, o, l, trace)); if (t != nullptr) - dump (*t, " "); // Indent two spaces. + dump (t, nullopt /* action */, " "); // Indent two spaces. else { os << " :: parse (name& x, bool& xs, scanner& s) { @@ -64,19 +82,7 @@ namespace build2 try { - using build2::parser; - using std::istringstream; - - istringstream is (v); - is.exceptions (istringstream::failbit | istringstream::badbit); - - // @@ TODO: currently this issues diagnostics to diag_stream. - // Perhaps we should redirect it? - // - path_name in (o); - lexer l (is, in, 1 /* line */, "\'\"\\$("); // Effective. - parser p (nullptr); - names r (p.parse_names (l, nullptr, parser::pattern_mode::preserve)); + names r (parse_names (o, v)); if (r.size () != 1) throw invalid_value (o, v); @@ -90,6 +96,41 @@ namespace build2 } } + void parser>>:: + parse (pair>& x, bool& xs, scanner& s) + { + const char* o (s.next ()); + + if (!s.more ()) + throw missing_value (o); + + const char* v (s.next ()); + + try + { + names r (parse_names (o, v)); + + if (r.size () == 1) + { + x.first = move (r.front ()); + x.second = nullopt; + } + else if (r.size () == 2 && r.front ().pair == '@') + { + x.first = move (r.front ()); + x.second = move (r.back ()); + } + else + throw invalid_value (o, v); + + xs = true; + } + catch (const failed&) + { + throw invalid_value (o, v); + } + } + void parser:: parse (structured_result_format& x, bool& xs, scanner& s) { diff --git a/libbuild2/types-parsers.hxx b/libbuild2/types-parsers.hxx index ebd2a02..42fc60d 100644 --- a/libbuild2/types-parsers.hxx +++ b/libbuild2/types-parsers.hxx @@ -54,6 +54,17 @@ namespace build2 }; template <> + struct parser>> + { + static void + parse (pair>&, bool&, scanner&); + + static void + merge (pair>& b, + const pair>& a) {b = a;} + }; + + template <> struct parser { static void -- cgit v1.1