aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/dump.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'libbuild2/dump.cxx')
-rw-r--r--libbuild2/dump.cxx491
1 files changed, 491 insertions, 0 deletions
diff --git a/libbuild2/dump.cxx b/libbuild2/dump.cxx
new file mode 100644
index 0000000..a866fe3
--- /dev/null
+++ b/libbuild2/dump.cxx
@@ -0,0 +1,491 @@
+// file : libbuild2/dump.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <libbuild2/dump.hxx>
+
+#include <libbuild2/scope.hxx>
+#include <libbuild2/target.hxx>
+#include <libbuild2/variable.hxx>
+#include <libbuild2/context.hxx>
+#include <libbuild2/diagnostics.hxx>
+
+using namespace std;
+
+namespace build2
+{
+ // If type is false, don't print the value's type (e.g., because it is the
+ // same as variable's).
+ //
+ static void
+ dump_value (ostream& os, const value& v, bool type)
+ {
+ // First print attributes if any.
+ //
+ bool a (!v || (type && v.type != nullptr));
+
+ if (a)
+ os << '[';
+
+ const char* s ("");
+
+ if (type && v.type != nullptr)
+ {
+ os << s << v.type->name;
+ s = " ";
+ }
+
+ if (!v)
+ {
+ os << s << "null";
+ s = " ";
+ }
+
+ if (a)
+ os << ']';
+
+ // Now the value if there is one.
+ //
+ if (v)
+ {
+ names storage;
+ os << (a ? " " : "") << reverse (v, storage);
+ }
+ }
+
+ enum class variable_kind {scope, tt_pat, target, rule, prerequisite};
+
+ static void
+ dump_variable (ostream& os,
+ const variable_map& vm,
+ const variable_map::const_iterator& vi,
+ const scope& s,
+ variable_kind k)
+ {
+ // Target type/pattern-specific prepends/appends are kept untyped and not
+ // overriden.
+ //
+ if (k == variable_kind::tt_pat && vi.extra () != 0)
+ {
+ // @@ Might be useful to dump the cache.
+ //
+ const auto& p (vi.untyped ());
+ const variable& var (p.first);
+ const value& v (p.second);
+ assert (v.type == nullptr);
+
+ os << var << (v.extra == 1 ? " =+ " : " += ");
+ dump_value (os, v, false);
+ }
+ else
+ {
+ const auto& p (*vi);
+ const variable& var (p.first);
+ const value& v (p.second);
+
+ if (var.type != nullptr)
+ os << '[' << var.type->name << "] ";
+
+ os << var << " = ";
+
+ // If this variable is overriden, print both the override and the
+ // original values.
+ //
+ // @@ The override semantics for prerequisite-specific variables
+ // is still fuzzy/unimplemented, so ignore it for now.
+ //
+ if (k != variable_kind::prerequisite)
+ {
+ if (var.overrides != nullptr && !var.override ())
+ {
+ lookup org (v, var, vm);
+
+ // The original is always from this scope/target, so depth is 1.
+ //
+ lookup l (
+ s.find_override (
+ var,
+ make_pair (org, 1),
+ k == variable_kind::target || k == variable_kind::rule,
+ k == variable_kind::rule).first);
+
+ assert (l.defined ()); // We at least have the original.
+
+ if (org != l)
+ {
+ dump_value (os, *l, l->type != var.type);
+ os << " # original: ";
+ }
+ }
+ }
+
+ dump_value (os, v, v.type != var.type);
+ }
+ }
+
+ static void
+ dump_variables (ostream& os,
+ string& ind,
+ const variable_map& vars,
+ const scope& s,
+ variable_kind k)
+ {
+ for (auto i (vars.begin ()), e (vars.end ()); i != e; ++i)
+ {
+ os << endl
+ << ind;
+
+ dump_variable (os, vars, i, s, k);
+ }
+ }
+
+ // Dump target type/pattern-specific variables.
+ //
+ static void
+ dump_variables (ostream& os,
+ string& ind,
+ const variable_type_map& vtm,
+ const scope& s)
+ {
+ for (const auto& vt: vtm)
+ {
+ const target_type& t (vt.first);
+ const variable_pattern_map& vpm (vt.second);
+
+ for (const auto& vp: vpm)
+ {
+ const string p (vp.first);
+ const variable_map& vars (vp.second);
+
+ os << endl
+ << ind;
+
+ if (t != target::static_type)
+ os << t.name << '{';
+
+ os << p;
+
+ if (t != target::static_type)
+ os << '}';
+
+ os << ':';
+
+ if (vars.size () == 1)
+ {
+ os << ' ';
+ dump_variable (os, vars, vars.begin (), s, variable_kind::tt_pat);
+ }
+ else
+ {
+ os << endl
+ << ind << '{';
+ ind += " ";
+ dump_variables (os, ind, vars, s, variable_kind::tt_pat);
+ ind.resize (ind.size () - 2);
+ os << endl
+ << ind << '}';
+ }
+ }
+ }
+ }
+
+ static void
+ dump_target (optional<action> a,
+ ostream& os,
+ string& ind,
+ const target& t,
+ const scope& s,
+ bool rel)
+ {
+ // If requested, print the target and its prerequisites relative to the
+ // scope. To achieve this we are going to temporarily lower the stream
+ // path verbosity to level 0.
+ //
+ stream_verbosity osv, nsv;
+ if (rel)
+ {
+ osv = nsv = stream_verb (os);
+ nsv.path = 0;
+ stream_verb (os, nsv);
+ }
+
+ if (t.group != nullptr)
+ os << ind << t << " -> " << *t.group << endl;
+
+ os << ind << t << ':';
+
+ // First print target/rule-specific variables, if any.
+ //
+ {
+ bool tv (!t.vars.empty ());
+ bool rv (a && !t.state[*a].vars.empty ());
+
+ if (tv || rv)
+ {
+ if (rel)
+ stream_verb (os, osv); // We want variable values in full.
+
+ os << endl
+ << ind << '{';
+ ind += " ";
+
+ if (tv)
+ dump_variables (os, ind, t.vars, s, variable_kind::target);
+
+ if (rv)
+ {
+ // To distinguish target and rule-specific variables, we put the
+ // latter into a nested block.
+ //
+ // @@ Maybe if we also print the rule name, then we could make
+ // the block associated with that?
+
+ if (tv)
+ os << endl;
+
+ os << endl
+ << ind << '{';
+ ind += " ";
+ dump_variables (os, ind, t.state[*a].vars, s, variable_kind::rule);
+ ind.resize (ind.size () - 2);
+ os << endl
+ << ind << '}';
+ }
+
+ ind.resize (ind.size () - 2);
+ os << endl
+ << ind << '}';
+
+ if (rel)
+ stream_verb (os, nsv);
+
+ os << endl
+ << ind << t << ':';
+ }
+ }
+
+ bool used (false); // Target header has been used to display prerequisites.
+
+ // If the target has been matched to a rule, first print resolved
+ // prerequisite targets.
+ //
+ // Note: running serial and task_count is 0 before any operation has
+ // started.
+ //
+ action inner; // @@ Only for the inner part of the action currently.
+
+ if (size_t c = t[inner].task_count.load (memory_order_relaxed))
+ {
+ if (c == target::count_applied () || c == target::count_executed ())
+ {
+ bool f (false);
+ for (const target* pt: t.prerequisite_targets[inner])
+ {
+ if (pt == nullptr) // Skipped.
+ continue;
+
+ os << ' ' << *pt;
+ f = true;
+ }
+
+ // Only omit '|' if we have no prerequisites nor targets.
+ //
+ if (f || !t.prerequisites ().empty ())
+ {
+ os << " |";
+ used = true;
+ }
+ }
+ }
+
+ // Print prerequisites. Those that have prerequisite-specific variables
+ // have to be printed as a separate dependency.
+ //
+ const prerequisites& ps (t.prerequisites ());
+ for (auto i (ps.begin ()), e (ps.end ()); i != e; )
+ {
+ const prerequisite& p (*i++);
+ bool ps (!p.vars.empty ()); // Has prerequisite-specific vars.
+
+ if (ps && used) // If it has been used, get a new header.
+ os << endl
+ << ind << t << ':';
+
+ // Print it as a target if one has been cached.
+ //
+ if (const target* t = p.target.load (memory_order_relaxed)) // Serial.
+ os << ' ' << *t;
+ else
+ os << ' ' << p;
+
+ if (ps)
+ {
+ if (rel)
+ stream_verb (os, osv); // We want variable values in full.
+
+ os << ':' << endl
+ << ind << '{';
+ ind += " ";
+ dump_variables (os, ind, p.vars, s, variable_kind::prerequisite);
+ ind.resize (ind.size () - 2);
+ os << endl
+ << ind << '}';
+
+ if (rel)
+ stream_verb (os, nsv);
+
+ if (i != e) // If we have another, get a new header.
+ os << endl
+ << ind << t << ':';
+ }
+
+ used = !ps;
+ }
+
+ if (rel)
+ stream_verb (os, osv);
+ }
+
+ static void
+ dump_scope (optional<action> a,
+ ostream& os,
+ string& ind,
+ scope_map::const_iterator& i,
+ bool rel)
+ {
+ const scope& p (i->second);
+ const dir_path& d (i->first);
+ ++i;
+
+ // We don't want the extra notations (e.g., ~/) provided by diag_relative()
+ // since we want the path to be relative to the outer scope. Print the root
+ // scope path (represented by an empty one) as a platform-dependent path
+ // separator.
+ //
+ if (d.empty ())
+ os << ind << dir_path::traits_type::directory_separator;
+ else
+ {
+ const dir_path& rd (rel ? relative (d) : d);
+ os << ind << (rd.empty () ? dir_path (".") : rd);
+ }
+
+ os << endl
+ << ind << '{';
+
+ const dir_path* orb (relative_base);
+ relative_base = &d;
+
+ ind += " ";
+
+ bool vb (false), sb (false), tb (false); // Variable/scope/target block.
+
+ // Target type/pattern-sepcific variables.
+ //
+ if (!p.target_vars.empty ())
+ {
+ dump_variables (os, ind, p.target_vars, p);
+ vb = true;
+ }
+
+ // Scope variables.
+ //
+ if (!p.vars.empty ())
+ {
+ if (vb)
+ os << endl;
+
+ dump_variables (os, ind, p.vars, p, variable_kind::scope);
+ vb = true;
+ }
+
+ // Nested scopes of which we are an immediate parent.
+ //
+ for (auto e (scopes.end ()); i != e && i->second.parent_scope () == &p;)
+ {
+ if (vb)
+ {
+ os << endl;
+ vb = false;
+ }
+
+ if (sb)
+ os << endl; // Extra newline between scope blocks.
+
+ os << endl;
+ dump_scope (a, os, ind, i, true /* relative */);
+ sb = true;
+ }
+
+ // Targets.
+ //
+ // Since targets can occupy multiple lines, we separate them with a
+ // blank line.
+ //
+ for (const auto& pt: targets)
+ {
+ const target& t (*pt);
+
+ if (&p != &t.base_scope ())
+ continue;
+
+ if (vb || sb || tb)
+ {
+ os << endl;
+ vb = sb = false;
+ }
+
+ os << endl;
+ dump_target (a, os, ind, t, p, true /* relative */);
+ tb = true;
+ }
+
+ ind.resize (ind.size () - 2);
+ relative_base = orb;
+
+ os << endl
+ << ind << '}';
+ }
+
+ void
+ dump (optional<action> a)
+ {
+ auto i (scopes.cbegin ());
+ assert (&i->second == global_scope);
+
+ // We don't lock diag_stream here as dump() is supposed to be called from
+ // the main thread prior/after to any other threads being spawned.
+ //
+ string ind;
+ ostream& os (*diag_stream);
+ dump_scope (a, os, ind, i, false /* relative */);
+ 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 (nullopt /* action */, os, ind, i, false /* relative */);
+ os << endl;
+ }
+
+ void
+ dump (const target& t, const char* cind)
+ {
+ string ind (cind);
+ ostream& os (*diag_stream);
+ dump_target (nullopt /* action */,
+ os,
+ ind,
+ t,
+ t.base_scope (),
+ false /* relative */);
+ os << endl;
+ }
+}