aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libbuild2/algorithm.cxx373
-rw-r--r--libbuild2/algorithm.hxx4
-rw-r--r--libbuild2/cc/link-rule.cxx29
-rw-r--r--libbuild2/context.cxx8
-rw-r--r--libbuild2/context.hxx17
-rw-r--r--libbuild2/diagnostics.cxx195
-rw-r--r--libbuild2/diagnostics.hxx15
-rw-r--r--libbuild2/target.cxx3
8 files changed, 469 insertions, 175 deletions
diff --git a/libbuild2/algorithm.cxx b/libbuild2/algorithm.cxx
index 0326839..cc48a38 100644
--- a/libbuild2/algorithm.cxx
+++ b/libbuild2/algorithm.cxx
@@ -1613,11 +1613,26 @@ namespace build2
return ts;
}
- void
- update_backlink (const file& f, const path& l, bool changed, backlink_mode m)
+ static inline const char*
+ update_backlink_name (backlink_mode m, bool to_dir)
{
using mode = backlink_mode;
+ const char* r (nullptr);
+ switch (m)
+ {
+ case mode::link:
+ case mode::symbolic: r = verb >= 3 ? "ln -sf" : verb >= 2 ? "ln -s" : "ln"; break;
+ case mode::hard: r = verb >= 3 ? "ln -f" : "ln"; break;
+ case mode::copy:
+ case mode::overwrite: r = to_dir ? "cp -r" : "cp"; break;
+ }
+ return r;
+ }
+
+ void
+ update_backlink (const file& f, const path& l, bool changed, backlink_mode m)
+ {
const path& p (f.path ());
dir_path d (l.directory ());
@@ -1629,25 +1644,17 @@ namespace build2
// actually updated to signal to the user that the updated out target is
// now available in src.
//
- if (verb <= 2)
+ if (verb == 1 || verb == 2)
{
if (changed || !butl::entry_exists (l,
false /* follow_symlinks */,
true /* ignore_errors */))
{
- const char* c (nullptr);
- switch (m)
- {
- case mode::link:
- case mode::symbolic: c = verb >= 2 ? "ln -s" : "ln"; break;
- case mode::hard: c = "ln"; break;
- case mode::copy:
- case mode::overwrite: c = l.to_directory () ? "cp -r" : "cp"; break;
- }
+ const char* c (update_backlink_name (m, l.to_directory ()));
// Note: 'ln foo/ bar/' means a different thing (and below).
//
- if (verb >= 2)
+ if (verb == 2)
text << c << ' ' << p.string () << ' ' << l.string ();
else
print_diag (c, f, d);
@@ -1670,25 +1677,15 @@ namespace build2
{
// As above but with a slightly different diagnostics.
- using mode = backlink_mode;
-
dir_path d (l.directory ());
- if (verb <= 2)
+ if (verb == 1 || verb == 2)
{
if (changed || !butl::entry_exists (l,
false /* follow_symlinks */,
true /* ignore_errors */))
{
- const char* c (nullptr);
- switch (m)
- {
- case mode::link:
- case mode::symbolic: c = verb >= 2 ? "ln -s" : "ln"; break;
- case mode::hard: c = "ln"; break;
- case mode::copy:
- case mode::overwrite: c = l.to_directory () ? "cp -r" : "cp"; break;
- }
+ const char* c (update_backlink_name (m, l.to_directory ()));
// Note: 'ln foo/ bar/' means a different thing (and above) so strip
// trailing directory separator (but keep as path for relative).
@@ -1761,17 +1758,8 @@ namespace build2
{
if (verb >= verbosity)
{
- const char* c (nullptr);
- switch (m)
- {
- case mode::link:
- case mode::symbolic: c = "ln -sf"; break;
- case mode::hard: c = "ln -f"; break;
- case mode::copy:
- case mode::overwrite: c = d ? "cp -r" : "cp"; break;
- }
-
- text << c << ' ' << p.string () << ' ' << l.string ();
+ text << update_backlink_name (m, d) << ' ' << p.string () << ' '
+ << l.string ();
}
};
@@ -1926,9 +1914,15 @@ namespace build2
struct backlink: auto_rm<path>
{
using path_type = build2::path;
+ using target_type = build2::target;
reference_wrapper<const path_type> target;
- backlink_mode mode;
+ backlink_mode mode;
+
+ // Ad hoc group-specific information for diagnostics (see below).
+ //
+ const target_type* member = nullptr;
+ bool print = true;
backlink (const path_type& t, path_type&& l, backlink_mode m, bool active)
: auto_rm<path_type> (move (l), active), target (t), mode (m)
@@ -1950,33 +1944,65 @@ namespace build2
};
// Normally (i.e., on sane platforms that don't have things like PDBs, etc)
- // there will be just one backlink so optimize for that.
+ // there will be just one or two backlinks so optimize for that.
//
- using backlinks = small_vector<backlink, 1>;
+ using backlinks = small_vector<backlink, 2>;
- static optional<backlink_mode>
- backlink_test (const target& t, const lookup& l)
+ static optional<pair<backlink_mode, bool>>
+ backlink_test (const target& t, const lookup& l, optional<backlink_mode> gm)
{
using mode = backlink_mode;
- optional<mode> r;
- const string& v (cast<string> (l));
+ const names& ns (cast<names> (l));
- if (v == "true") r = mode::link;
- else if (v == "symbolic") r = mode::symbolic;
- else if (v == "hard") r = mode::hard;
- else if (v == "copy") r = mode::copy;
- else if (v == "overwrite") r = mode::overwrite;
- else if (v != "false")
- fail << "invalid backlink variable value '" << v << "' "
+ if (ns.size () != 1 && ns.size () != 2)
+ {
+ fail << "invalid backlink variable value '" << ns << "' "
<< "specified for target " << t;
+ }
- return r;
+ optional<mode> m;
+ for (;;) // Breakout loop.
+ {
+ const name& n (ns.front ());
+
+ if (n.simple ())
+ {
+ const string& v (n.value);
+
+ if (v == "true") {m = mode::link; break;}
+ else if (v == "symbolic") {m = mode::symbolic; break;}
+ else if (v == "hard") {m = mode::hard; break;}
+ else if (v == "copy") {m = mode::copy; break;}
+ else if (v == "overwrite") {m = mode::overwrite; break;}
+ else if (v == "false") { break;}
+ else if (v == "group") {if ((m = gm)) break;}
+ }
+
+ fail << "invalid backlink variable value mode component '" << n << "' "
+ << "specified for target " << t << endf;
+ }
+
+ bool np (false); // "not print"
+ if (ns.size () == 2)
+ {
+ const name& n (ns.back ());
+
+ if (n.simple () && (n.value == "true" || (np = (n.value == "false"))))
+ ;
+ else
+ fail << "invalid backlink variable value print component '" << n
+ << "' specified for target " << t;
+ }
+
+ return m ? optional<pair<mode, bool>> (make_pair (*m, !np)) : nullopt;
}
static optional<backlink_mode>
backlink_test (action a, target& t)
{
+ using mode = backlink_mode;
+
context& ctx (t.ctx);
// Note: the order of these checks is from the least to most expensive.
@@ -1986,9 +2012,20 @@ namespace build2
if (a.outer () || (a != perform_update_id && a != perform_clean_id))
return nullopt;
- // Only file-based targets in the out tree can be backlinked.
+ // Only targets in the out tree can be backlinked.
//
- if (!t.out.empty () || !t.is_a<file> ())
+ if (!t.out.empty ())
+ return nullopt;
+
+ // Only file-based targets or groups containing file-based targets can be
+ // backlinked. Note that we don't do the "file-based" check of the latter
+ // case here since they can still be execluded. So instead we are prepared
+ // to handle the empty backlinks list.
+ //
+ // @@ Potentially members could only be resolved in execute. I guess we
+ // don't support backlink for such groups at the moment.
+ //
+ if (!t.is_a<file> () && t.group_members (a).members == nullptr)
return nullopt;
// Neither an out-of-project nor in-src configuration can be forwarded.
@@ -2012,7 +2049,13 @@ namespace build2
if (!l.defined ())
l = ctx.global_scope.lookup (*ctx.var_backlink, t.key ());
- return l ? backlink_test (t, l) : nullopt;
+ optional<pair<mode, bool>> r (l ? backlink_test (t, l, nullopt) : nullopt);
+
+ if (r && !r->second)
+ fail << "backlink variable value print component cannot be false "
+ << "for primary target " << t;
+
+ return r ? optional<mode> (r->first) : nullopt;
}
static backlinks
@@ -2024,56 +2067,100 @@ namespace build2
const scope& s (t.base_scope ());
backlinks bls;
- auto add = [&bls, &s] (const path& p, mode m)
+ auto add = [&bls, &s] (const path& p,
+ mode m,
+ const target* mt = nullptr,
+ bool print = true)
{
bls.emplace_back (p,
s.src_path () / p.leaf (s.out_path ()),
m,
!s.ctx.dry_run /* active */);
+
+ if (mt != nullptr)
+ {
+ backlink& bl (bls.back ());
+ bl.member = mt;
+ bl.print = print;
+ }
};
- // First the target itself.
+ // Check for a custom backlink mode for this member. If none, then
+ // inherit the one from the group (so if the user asked to copy
+ // .exe, we will also copy .pdb).
//
- add (t.as<file> ().path (), m);
-
- // Then ad hoc group file/fsdir members, if any.
+ // Note that we want to avoid group or tt/patter-spec lookup. And
+ // since this is an ad hoc member (which means it was either declared
+ // in the buildfile or added by the rule), we assume that the value,
+ // if any, will be set as a target or rule-specific variable.
//
- for (const target* mt (t.adhoc_member);
- mt != nullptr;
- mt = mt->adhoc_member)
+ auto member_mode = [a, m, &ctx] (const target& mt)
+ -> optional<pair<mode, bool>>
{
- const path* p (nullptr);
+ lookup l (mt.state[a].vars[ctx.var_backlink]);
- if (const file* f = mt->is_a<file> ())
- {
- p = &f->path ();
+ if (!l)
+ l = mt.vars[ctx.var_backlink];
- if (p->empty ()) // The "trust me, it's somewhere" case.
- p = nullptr;
- }
- else if (const fsdir* d = mt->is_a<fsdir> ())
- p = &d->dir;
+ return l ? backlink_test (mt, l, m) : make_pair (m, true);
+ };
+
+ // @@ Currently we don't handle the following cases:
+ //
+ // 1. File-based explicit groups.
+ //
+ // 2. Ad hoc subgroups in explicit groups.
+ //
+ // Note: see also the corresponding code in backlink_update_post().
+ //
+ if (file* f = t.is_a<file> ())
+ {
+ // First the target itself.
+ //
+ add (f->path (), m, f, true); // Note: always printed.
- if (p != nullptr)
+ // Then ad hoc group file/fsdir members, if any.
+ //
+ for (const target* mt (t.adhoc_member);
+ mt != nullptr;
+ mt = mt->adhoc_member)
{
- // Check for a custom backlink mode for this member. If none, then
- // inherit the one from the group (so if the user asked to copy .exe,
- // we will also copy .pdb).
- //
- // Note that we want to avoid group or tt/patter-spec lookup. And
- // since this is an ad hoc member (which means it was either declared
- // in the buildfile or added by the rule), we assume that the value,
- // if any, will be set as a target or rule-specific variable.
- //
- lookup l (mt->state[a].vars[ctx.var_backlink]);
+ const path* p (nullptr);
+
+ if (const file* f = mt->is_a<file> ())
+ {
+ p = &f->path ();
- if (!l)
- l = mt->vars[ctx.var_backlink];
+ if (p->empty ()) // The "trust me, it's somewhere" case.
+ p = nullptr;
+ }
+ else if (const fsdir* d = mt->is_a<fsdir> ())
+ p = &d->dir;
- optional<mode> bm (l ? backlink_test (*mt, l) : m);
+ if (p != nullptr)
+ {
+ if (optional<pair<mode, bool>> m = member_mode (*mt))
+ add (*p, m->first, mt, m->second);
+ }
+ }
+ }
+ else
+ {
+ // Explicit group.
+ //
+ group_view gv (t.group_members (a));
+ assert (gv.members != nullptr);
- if (bm)
- add (*p, *bm);
+ for (size_t i (0); i != gv.count; ++i)
+ {
+ if (const target* mt = gv.members[i])
+ {
+ if (const file* f = mt->is_a<file> ())
+ {
+ if (optional<pair<mode, bool>> m = member_mode (*mt))
+ add (f->path (), m->first);
+ }
+ }
}
}
@@ -2087,29 +2174,89 @@ namespace build2
}
static void
- backlink_update_post (target& t, target_state ts, backlinks& bls)
+ backlink_update_post (target& t, target_state ts,
+ backlink_mode m, backlinks& bls)
{
if (ts == target_state::failed)
return; // Let auto rm clean things up.
- // Make backlinks.
- //
- for (auto b (bls.begin ()), i (b); i != bls.end (); ++i)
+ context& ctx (t.ctx);
+
+ file* ft (t.is_a<file> ());
+
+ if (ft != nullptr && bls.size () == 1)
{
- const backlink& bl (*i);
+ // Single file-based target.
+ //
+ const backlink& bl (bls.front ());
- if (i == b)
- update_backlink (t.as<file> (),
- bl.path,
- ts == target_state::changed,
- bl.mode);
- else
- update_backlink (t.ctx, bl.target, bl.path, bl.mode);
+ update_backlink (*ft,
+ bl.path,
+ ts == target_state::changed,
+ bl.mode);
+ }
+ else
+ {
+ // Explicit or ad hoc group.
+ //
+ // What we have below is a custom variant of update_backlink(file).
+ //
+ dir_path d (bls.front ().path.directory ());
+
+ // First print the verbosity level 1 diagnostics. Level 2 and higher are
+ // handled by the update_backlink() calls below.
+ //
+ if (verb == 1)
+ {
+ bool changed (ts == target_state::changed);
+
+ if (!changed)
+ {
+ for (const backlink& bl: bls)
+ {
+ changed = !butl::entry_exists (bl.path,
+ false /* follow_symlinks */,
+ true /* ignore_errors */);
+ if (changed)
+ break;
+ }
+ }
+
+ if (changed)
+ {
+ const char* c (update_backlink_name (m, false /* to_dir */));
+
+ // For explicit groups we only print the group target. For ad hoc
+ // groups we print all the members except those explicitly excluded.
+ //
+ if (ft == nullptr)
+ print_diag (c, t, d);
+ else
+ {
+ vector<target_key> tks;
+ tks.reserve (bls.size ());
+
+ for (const backlink& bl: bls)
+ if (bl.print)
+ tks.push_back (bl.member->key ());
+
+ print_diag (c, move (tks), d);
+ }
+ }
+ }
+
+ if (!exists (d))
+ mkdir_p (d, 2 /* verbosity */);
+
+ // Make backlinks.
+ //
+ for (const backlink& bl: bls)
+ update_backlink (ctx, bl.target, bl.path, bl.mode, 2 /* verbosity */);
}
// Cancel removal.
//
- if (!t.ctx.dry_run)
+ if (!ctx.dry_run)
{
for (backlink& bl: bls)
bl.cancel ();
@@ -2150,15 +2297,27 @@ namespace build2
// which is ok since such targets are probably not interesting for
// backlinking.
//
+ // Note also that for group members (both ad hoc and non) backlinking
+ // is handled when updating/cleaning the group.
+ //
backlinks bls;
- optional<backlink_mode> blm (backlink_test (a, t));
+ optional<backlink_mode> blm;
- if (blm)
+ if (t.group == nullptr) // Matched so must be already resolved.
{
- if (a == perform_update_id)
- bls = backlink_update_pre (a, t, *blm);
- else
- backlink_clean_pre (a, t, *blm);
+ blm = backlink_test (a, t);
+
+ if (blm)
+ {
+ if (a == perform_update_id)
+ {
+ bls = backlink_update_pre (a, t, *blm);
+ if (bls.empty ())
+ blm = nullopt;
+ }
+ else
+ backlink_clean_pre (a, t, *blm);
+ }
}
// Note: see similar code in set_rule_trace() for match.
@@ -2196,7 +2355,7 @@ namespace build2
if (blm)
{
if (a == perform_update_id)
- backlink_update_post (t, ts, bls);
+ backlink_update_post (t, ts, *blm, bls);
}
}
catch (const failed&)
diff --git a/libbuild2/algorithm.hxx b/libbuild2/algorithm.hxx
index aae59ca..9a6a56b 100644
--- a/libbuild2/algorithm.hxx
+++ b/libbuild2/algorithm.hxx
@@ -964,7 +964,7 @@ namespace build2
bool changed,
backlink_mode = backlink_mode::link);
- // Note: verbosite should be 2 or greater.
+ // Note: verbosity should be 2 or greater.
//
LIBBUILD2_SYMEXPORT void
update_backlink (context&,
@@ -973,7 +973,7 @@ namespace build2
backlink_mode = backlink_mode::link,
uint16_t verbosity = 3);
- // Note: verbosite should be 2 or greater.
+ // Note: verbosity should be 2 or greater.
//
LIBBUILD2_SYMEXPORT void
clean_backlink (context&,
diff --git a/libbuild2/cc/link-rule.cxx b/libbuild2/cc/link-rule.cxx
index 20c6c62..39f6f54 100644
--- a/libbuild2/cc/link-rule.cxx
+++ b/libbuild2/cc/link-rule.cxx
@@ -1537,6 +1537,11 @@ namespace build2
if (wasm.path ().empty ())
wasm.derive_path ();
+ // We don't want to print this member at level 1 diagnostics.
+ //
+ wasm.state[a].assign (ctx.var_backlink) = names {
+ name ("group"), name ("false")};
+
// If we have -pthread then we get additional .worker.js file
// which is used for thread startup. In a somewhat hackish way we
// represent it as an exe{} member to make sure it gets installed
@@ -1560,6 +1565,11 @@ namespace build2
if (worker.path ().empty ())
worker.derive_path ();
+
+ // We don't want to print this member at level 1 diagnostics.
+ //
+ worker.state[a].assign (ctx.var_backlink) = names {
+ name ("group"), name ("false")};
}
}
@@ -1587,6 +1597,11 @@ namespace build2
//
if (pdb.path ().empty ())
pdb.derive_path (t.path ());
+
+ // We don't want to print this member at level 1 diagnostics.
+ //
+ pdb.state[a].assign (ctx.var_backlink) = names {
+ name ("group"), name ("false")};
}
}
}
@@ -1666,14 +1681,12 @@ namespace build2
// exists (windows_rpath_assembly() does take care to clean it up
// if not used).
//
-#ifdef _WIN32
- target& dir =
-#endif
+ target& dir (
add_adhoc_member (t,
fsdir::static_type,
path_cast<dir_path> (t.path () + ".dlls"),
t.out,
- string () /* name */);
+ string () /* name */));
// By default our backlinking logic will try to symlink the
// directory and it can even be done on Windows using junctions.
@@ -1687,9 +1700,15 @@ namespace build2
// Wine. So we only resort to copy-link'ing if we are running on
// Windows.
//
+ // We also don't want to print this member at level 1 diagnostics.
+ //
+ dir.state[a].assign (ctx.var_backlink) = names {
#ifdef _WIN32
- dir.state[a].assign (ctx.var_backlink) = "copy";
+ name ("copy"), name ("false")
+#else
+ name ("group"), name ("false")
#endif
+ };
}
}
}
diff --git a/libbuild2/context.cxx b/libbuild2/context.cxx
index 4858c4c..1da6fd3 100644
--- a/libbuild2/context.cxx
+++ b/libbuild2/context.cxx
@@ -614,13 +614,15 @@ namespace build2
var_extension = &vp.insert<string> ("extension", v_t);
var_update = &vp.insert<string> ("update", v_q);
var_clean = &vp.insert<bool> ("clean", v_t);
- var_backlink = &vp.insert<string> ("backlink", v_t);
+ var_backlink = &vp.insert ("backlink", v_t); // Untyped.
var_include = &vp.insert<string> ("include", v_q);
// Backlink executables and (generated) documentation by default.
//
- gs.target_vars[exe::static_type]["*"].assign (var_backlink) = "true";
- gs.target_vars[doc::static_type]["*"].assign (var_backlink) = "true";
+ gs.target_vars[exe::static_type]["*"].assign (var_backlink) =
+ names {name ("true")};
+ gs.target_vars[doc::static_type]["*"].assign (var_backlink) =
+ names {name ("true")};
// Register builtin rules.
//
diff --git a/libbuild2/context.hxx b/libbuild2/context.hxx
index c16181f..b71a068 100644
--- a/libbuild2/context.hxx
+++ b/libbuild2/context.hxx
@@ -505,7 +505,12 @@ namespace build2
//
const variable* var_clean;
- // Forwarded configuration backlink mode. Valid values are:
+ // Forwarded configuration backlink mode. The value has two components
+ // in the form:
+ //
+ // <mode> [<print>]
+ //
+ // Valid <mode> values are:
//
// false - no link.
// true - make a link using appropriate mechanism.
@@ -513,8 +518,14 @@ namespace build2
// hard - make a hard link.
// copy - make a copy.
// overwrite - copy over but don't remove on clean.
+ // group - inherit the group mode (only valid for group members).
//
- // Note that it can be set by a matching rule as a rule-specific variable.
+ // While the <print> component should be either true or false and can be
+ // used to suppress printing of specific ad hoc group members at verbosity
+ // level 1. Note that it cannot be false for the primary member.
+ //
+ // Note that this value can be set by a matching rule as a rule-specific
+ // variable.
//
// Note also that the overwrite mode was originally meant for handling
// pregenerated source code. But in the end this did not pan out for
@@ -538,7 +549,7 @@ namespace build2
// just expose a mechanism to delegate to a different rule, which we
// already have).
//
- // [string] target visibility
+ // [names] target visibility
//
const variable* var_backlink;
diff --git a/libbuild2/diagnostics.cxx b/libbuild2/diagnostics.cxx
index bc74db3..d91150b 100644
--- a/libbuild2/diagnostics.cxx
+++ b/libbuild2/diagnostics.cxx
@@ -82,39 +82,26 @@ namespace build2
dr << r;
}
- template <typename L> // L can be target_key, path, or string.
- static void
- print_diag_impl (const char* p,
- const L* l, bool lempty,
- vector<target_key>&& rs,
- const char* c)
- {
- assert (rs.size () > 1);
- // The overall plan is as follows:
- //
- // 1. Collect the printed names for all the group members.
- //
- // Note if the printed representation is irregular (see
- // to_stream(target_key) for details). We will print such members each
- // on a separate line.
- //
- // 2. Move the names so that we end up with contiguous partitions of
- // targets with the same name.
- //
- // 3. Print the partitions, one per line.
- //
- vector<pair<optional<string>, const target_key*>> ns;
- ns.reserve (rs.size ());
+ static inline bool
+ print_diag_cmp (const pair<optional<string>, const target_key*>& x,
+ const pair<optional<string>, const target_key*>& y)
+ {
+ return (x.second->dir->compare (*y.second->dir) == 0 &&
+ x.first->compare (*y.first) == 0);
+ }
- // Use the diag_record's ostringstream so that we get the appropriate
- // stream verbosity, etc.
- //
- diag_record dr (text);
- ostringstream& os (dr.os);
- stream_verbosity sv (stream_verb (os));
+ // Return true if we have multiple partitions (see below for details).
+ //
+ static bool
+ print_diag_collect (const vector<target_key>& tks,
+ ostringstream& os,
+ stream_verbosity sv,
+ vector<pair<optional<string>, const target_key*>>& ns)
+ {
+ ns.reserve (tks.size ());
- for (const target_key& k: rs)
+ for (const target_key& k: tks)
{
bool r;
if (auto p = k.type->print)
@@ -130,16 +117,9 @@ namespace build2
// Partition.
//
- auto cmp = [] (const pair<optional<string>, const target_key*>& x,
- const pair<optional<string>, const target_key*>& y)
- {
- return (x.second->dir->compare (*y.second->dir) == 0 &&
- x.first->compare (*y.first) == 0);
- };
-
// While at it also determine whether we have multiple partitions.
//
- optional<string> ml;
+ bool ml (false);
for (auto b (ns.begin ()), e (ns.end ()); b != e; )
{
const pair<optional<string>, const target_key*>& x (*b++);
@@ -149,25 +129,24 @@ namespace build2
//
b = stable_partition (
b, e,
- [&cmp, &x] (const pair<optional<string>, const target_key*>& y)
+ [&x] (const pair<optional<string>, const target_key*>& y)
{
- return (x.first && y.first && cmp (x, y));
+ return (x.first && y.first && print_diag_cmp (x, y));
});
if (!ml && b != e)
- ml = string ();
+ ml = true;
}
- // Print.
- //
- os << p << ' ';
-
- if (l != nullptr)
- os << *l << (lempty ? "" : " ") << (c == nullptr ? "->" : c) << ' ';
-
- if (ml)
- ml = string (os.str ().size (), ' '); // Indentation.
+ return ml;
+ }
+ static void
+ print_diag_print (const vector<pair<optional<string>, const target_key*>>& ns,
+ ostringstream& os,
+ stream_verbosity sv,
+ const optional<string>& ml)
+ {
for (auto b (ns.begin ()), i (b), e (ns.end ()); i != e; )
{
if (i != b)
@@ -185,7 +164,7 @@ namespace build2
// Calculate the number of members in this partition.
//
size_t n (1);
- for (auto j (i + 1); j != e && j->first && cmp (*i, *j); ++j)
+ for (auto j (i + 1); j != e && j->first && print_diag_cmp (*i, *j); ++j)
++n;
// Similar code to to_stream(target_key).
@@ -232,6 +211,95 @@ namespace build2
}
}
+ template <typename L> // L can be target_key, path, or string.
+ static void
+ print_diag_impl (const char* p,
+ const L* l, bool lempty,
+ vector<target_key>&& rs,
+ const char* c)
+ {
+ assert (rs.size () > 1);
+
+ // The overall plan is as follows:
+ //
+ // 1. Collect the printed names for all the group members.
+ //
+ // Note if the printed representation is irregular (see
+ // to_stream(target_key) for details). We will print such members each
+ // on a separate line.
+ //
+ // 2. Move the names around so that we end up with contiguous partitions
+ // of targets with the same name.
+ //
+ // 3. Print the partitions, one per line.
+ //
+ // The steps 1-2 are performed by print_diag_impl_common() above.
+ //
+ vector<pair<optional<string>, const target_key*>> ns;
+
+ // Use the diag_record's ostringstream so that we get the appropriate
+ // stream verbosity, etc.
+ //
+ diag_record dr (text);
+ ostringstream& os (dr.os);
+ stream_verbosity sv (stream_verb (os));
+
+ optional<string> ml;
+ if (print_diag_collect (rs, os, sv, ns))
+ ml = string ();
+
+ // Print.
+ //
+ os << p << ' ';
+
+ if (l != nullptr)
+ os << *l << (lempty ? "" : " ") << (c == nullptr ? "->" : c) << ' ';
+
+ if (ml)
+ ml = string (os.str ().size (), ' '); // Indentation.
+
+ print_diag_print (ns, os, sv, ml);
+ }
+
+ template <typename R> // R can be target_key, path, or string.
+ static void
+ print_diag_impl (const char* p,
+ vector<target_key>&& ls, const R& r,
+ const char* c)
+ {
+ assert (ls.size () > 1);
+
+ // As above but for the group on the LHS.
+ //
+ vector<pair<optional<string>, const target_key*>> ns;
+
+ diag_record dr (text);
+ ostringstream& os (dr.os);
+ stream_verbosity sv (stream_verb (os));
+
+ optional<string> ml;
+ if (print_diag_collect (ls, os, sv, ns))
+ ml = string ();
+
+ // Print.
+ //
+ os << p << ' ';
+
+ if (ml)
+ ml = string (os.str ().size (), ' '); // Indentation.
+
+ print_diag_print (ns, os, sv, ml);
+
+ // @@ TODO: make sure `->` is aligned with longest line printed by
+ // print_diag_print(). Currently it can look like this:
+ //
+ // ln /tmp/hello-gcc/hello/hello/{hxx cxx}{hello-types}
+ // /tmp/hello-gcc/hello/hello/{hxx cxx}{hello-stubs}
+ // /tmp/hello-gcc/hello/hello/cxx{hello-ext} -> ./
+ //
+ os << ' ' << (c == nullptr ? "->" : c) << ' ' << r;
+ }
+
void
print_diag_impl (const char* p,
target_key* l, vector<target_key>&& rs,
@@ -361,7 +429,9 @@ namespace build2
}
void
- print_diag (const char* p, const target& l, const dir_path& r, const char* c)
+ print_diag (const char* p,
+ const target& l, const path_name_view& r,
+ const char* c)
{
// @@ TODO: out qualification stripping: only do if p.out is subdir of t
// (also below)?
@@ -370,11 +440,28 @@ namespace build2
}
void
+ print_diag (const char* p, const target& l, const dir_path& r, const char* c)
+ {
+ print_diag (p, l.key (), r, c);
+ }
+
+ void
+ print_diag (const char* p, target_key&& l, const dir_path& r, const char* c)
+ {
+ text << p << ' ' << l << ' ' << (c == nullptr ? "->" : c) << ' ' << r;
+ }
+
+ void
print_diag (const char* p,
- const target& l, const path_name_view& r,
+ vector<target_key>&& ls, const dir_path& r,
const char* c)
{
- text << p << ' ' << l << ' ' << (c == nullptr ? "->" : c) << ' ' << r;
+ assert (!ls.empty ());
+
+ if (ls.size () == 1)
+ print_diag (p, move (ls.front ()), r, c);
+ else
+ print_diag_impl<dir_path> (p, move (ls), r, c);
}
void
diff --git a/libbuild2/diagnostics.hxx b/libbuild2/diagnostics.hxx
index 397d3c0..c048d5b 100644
--- a/libbuild2/diagnostics.hxx
+++ b/libbuild2/diagnostics.hxx
@@ -69,6 +69,9 @@ namespace build2
// {hxx cxx}{foo-stubs} -> {hxx cxx}{foo-insts}
// {hxx cxx}{foo-impls}
//
+ // Currently we only support this for the `group -> dir_path` form (used
+ // by the backlink machinery).
+ //
// See also the `diag` Buildscript pseudo-builtin which is reduced to one of
// the print_diag() calls (adhoc_buildscript_rule::print_custom_diag()). In
// particular, if you are adding a new overload, also consider if/how it
@@ -196,6 +199,18 @@ namespace build2
const target& l, const path_name_view& r,
const char* comb = nullptr);
+ LIBBUILD2_SYMEXPORT void
+ print_diag (const char* prog,
+ target_key&& l, const dir_path& r,
+ const char* comb = nullptr);
+
+ // prog group -> dir_path
+ //
+ LIBBUILD2_SYMEXPORT void
+ print_diag (const char* prog,
+ vector<target_key>&& l, const dir_path& r,
+ const char* comb = nullptr);
+
// prog path -> path
//
void
diff --git a/libbuild2/target.cxx b/libbuild2/target.cxx
index 34b131f..9f26eee 100644
--- a/libbuild2/target.cxx
+++ b/libbuild2/target.cxx
@@ -109,7 +109,8 @@ namespace build2
group_view target::
group_members (action) const
{
- assert (false); // Not a group or doesn't expose its members.
+ // Not a group or doesn't expose its members.
+ //
return group_view {nullptr, 0};
}