diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2023-05-02 13:05:27 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2023-05-03 05:03:20 +0200 |
commit | 7a458f210f296cb3cc1551a4606f0cf025003f3a (patch) | |
tree | 71b20a6c67fb9b6801916406391c34e6710c3c2e | |
parent | f66848dbd677b1027bade5728e04954c313231af (diff) |
Add --dump-scope and --dump-target options to limit --dump output
-rw-r--r-- | build2/b.cxx | 133 | ||||
-rw-r--r-- | doc/manual.cli | 6 | ||||
-rw-r--r-- | libbuild2/b-options.cxx | 51 | ||||
-rw-r--r-- | libbuild2/b-options.hxx | 30 | ||||
-rw-r--r-- | libbuild2/b-options.ixx | 30 | ||||
-rw-r--r-- | libbuild2/b.cli | 28 | ||||
-rw-r--r-- | libbuild2/dump.cxx | 40 | ||||
-rw-r--r-- | libbuild2/dump.hxx | 9 | ||||
-rw-r--r-- | libbuild2/parser.cxx | 4 | ||||
-rw-r--r-- | libbuild2/types-parsers.cxx | 67 | ||||
-rw-r--r-- | 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<action> a) + { + const dir_paths& scopes (ops.dump_scope ()); + const vector<pair<name, optional<name>>>& 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<name, optional<name>>& 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<string>>::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<pair<name, optional<name>>>>::merge ( + this->dump_target_, a.dump_target_); + this->dump_target_specified_ = true; + } + if (a.trace_match_specified_) { - ::build2::build::cli::parser< std::vector<name>>::merge ( + ::build2::build::cli::parser< vector<name>>::merge ( this->trace_match_, a.trace_match_); this->trace_match_specified_ = true; } if (a.trace_execute_specified_) { - ::build2::build::cli::parser< std::vector<name>>::merge ( + ::build2::build::cli::parser< vector<name>>::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<string>, &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<pair<name, optional<name>>>, &b_options::dump_target_, + &b_options::dump_target_specified_ >; _cli_b_options_map_["--trace-match"] = - &::build2::build::cli::thunk< b_options, std::vector<name>, &b_options::trace_match_, + &::build2::build::cli::thunk< b_options, vector<name>, &b_options::trace_match_, &b_options::trace_match_specified_ >; _cli_b_options_map_["--trace-execute"] = - &::build2::build::cli::thunk< b_options, std::vector<name>, &b_options::trace_execute_, + &::build2::build::cli::thunk< b_options, vector<name>, &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 <set> - #include <libbuild2/common-options.hxx> namespace build2 @@ -167,19 +165,31 @@ namespace build2 const bool& no_mtime_check () const; - const std::set<string>& + const strings& dump () const; bool dump_specified () const; - const std::vector<name>& + const dir_paths& + dump_scope () const; + + bool + dump_scope_specified () const; + + const vector<pair<name, optional<name>>>& + dump_target () const; + + bool + dump_target_specified () const; + + const vector<name>& trace_match () const; bool trace_match_specified () const; - const std::vector<name>& + const vector<name>& trace_execute () const; bool @@ -293,11 +303,15 @@ namespace build2 bool structured_result_specified_; bool mtime_check_; bool no_mtime_check_; - std::set<string> dump_; + strings dump_; bool dump_specified_; - std::vector<name> trace_match_; + dir_paths dump_scope_; + bool dump_scope_specified_; + vector<pair<name, optional<name>>> dump_target_; + bool dump_target_specified_; + vector<name> trace_match_; bool trace_match_specified_; - std::vector<name> trace_execute_; + vector<name> 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<string>& 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<name>& 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<pair<name, optional<name>>>& 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<name>& b_options:: trace_match () const { return this->trace_match_; @@ -230,7 +254,7 @@ namespace build2 return this->trace_match_specified_; } - inline const std::vector<name>& b_options:: + inline const vector<name>& 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 <set>; - include <libbuild2/common.cli>; "\section=1" @@ -774,23 +772,39 @@ namespace build2 \cb{--mtime-check} for details." } - std::set<string> --dump + strings --dump { "<phase>", "Dump the build system state after the specified phase. Valid <phase> 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 + { + "<dir>", + "Dump the build system state for the specified scope only. Repeat this + option to dump the state of multiple scopes." + } + + vector<pair<name, optional<name>>> --dump-target + { + "<target>", + "Dump the build system state for the specified target only. Repeat this + option to dump the state of multiple targets." } - std::vector<name> --trace-match + vector<name> --trace-match { "<target>", "Trace rule matching for the specified target. This is primarily useful during troubleshooting. Repeat this option to trace multiple targets." } - std::vector<name> --trace-execute + vector<name> --trace-execute { "<target>", "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<action> 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 << "<no known scope to dump>"; + os << endl; } void - dump (const target& t, const char* cind) + dump (const target* t, optional<action> 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 << "<no known target to dump>"; + 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<action> = nullopt); + dump (const context&, optional<action>); LIBBUILD2_SYMEXPORT void - dump (const scope&, const char* ind = ""); + dump (const scope*, optional<action>, const char* ind = ""); LIBBUILD2_SYMEXPORT void - dump (const target&, const char* ind = ""); + dump (const target*, optional<action>, 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 << " <no current scope>" << 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 << " <no target " << n; diff --git a/libbuild2/types-parsers.cxx b/libbuild2/types-parsers.cxx index d220541..9c3dc52 100644 --- a/libbuild2/types-parsers.cxx +++ b/libbuild2/types-parsers.cxx @@ -52,6 +52,24 @@ namespace build2 parse_path (x, s); } + static names + parse_names (const char* o, const char* v) + { + 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? Also below. + // + path_name in (o); + lexer l (is, in, 1 /* line */, "\'\"\\$("); // Effective. + parser p (nullptr); + return p.parse_names (l, nullptr, parser::pattern_mode::preserve); + } + void parser<name>:: 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<pair<name, optional<name>>>:: + parse (pair<name, optional<name>>& 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<structured_result_format>:: 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<pair<name, optional<name>>> + { + static void + parse (pair<name, optional<name>>&, bool&, scanner&); + + static void + merge (pair<name, optional<name>>& b, + const pair<name, optional<name>>& a) {b = a;} + }; + + template <> struct parser<structured_result_format> { static void |