aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2023-05-02 13:05:27 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2023-05-03 05:03:20 +0200
commit7a458f210f296cb3cc1551a4606f0cf025003f3a (patch)
tree71b20a6c67fb9b6801916406391c34e6710c3c2e
parentf66848dbd677b1027bade5728e04954c313231af (diff)
Add --dump-scope and --dump-target options to limit --dump output
-rw-r--r--build2/b.cxx133
-rw-r--r--doc/manual.cli6
-rw-r--r--libbuild2/b-options.cxx51
-rw-r--r--libbuild2/b-options.hxx30
-rw-r--r--libbuild2/b-options.ixx30
-rw-r--r--libbuild2/b.cli28
-rw-r--r--libbuild2/dump.cxx40
-rw-r--r--libbuild2/dump.hxx9
-rw-r--r--libbuild2/parser.cxx4
-rw-r--r--libbuild2/types-parsers.cxx67
-rw-r--r--libbuild2/types-parsers.hxx11
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