From b6c61ea9afd2d738711770e44748e48be009154d Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 24 Sep 2020 13:10:25 +0200 Subject: Give hints for common causes of "no rule to update ..." error --- libbuild2/algorithm.cxx | 51 +++++++++++++++++++++++++++++++++++++++++- libbuild2/bash/rule.cxx | 2 +- libbuild2/cc/common.txx | 2 +- libbuild2/cc/compile-rule.cxx | 4 ++-- libbuild2/cc/pkgconfig.cxx | 2 +- libbuild2/config/operation.cxx | 2 +- libbuild2/file.cxx | 2 +- libbuild2/parser.cxx | 27 +++++++++++----------- libbuild2/search.cxx | 12 ++++++---- libbuild2/target.cxx | 22 ++++++++---------- libbuild2/target.hxx | 43 ++++++++++++++++++++++++++--------- libbuild2/target.txx | 2 +- 12 files changed, 121 insertions(+), 50 deletions(-) diff --git a/libbuild2/algorithm.cxx b/libbuild2/algorithm.cxx index 5b340c7..53aac4e 100644 --- a/libbuild2/algorithm.cxx +++ b/libbuild2/algorithm.cxx @@ -298,7 +298,7 @@ namespace build2 out, move (n), nullopt /* ext */, - true /* implied */, + target_decl::implied, trace)); assert (r.second.owns_lock ()); @@ -509,6 +509,55 @@ namespace build2 diag_record dr; dr << fail << "no rule to " << diag_do (a, t); + // Try to give some hints of the common causes. + // + switch (t.decl) + { + case target_decl::prereq_new: + { + dr << info << "target " << t << " is no declared in any buildfile"; + + if (t.is_a ()) + dr << info << "perhaps it is a missing source file?"; + + break; + } + case target_decl::prereq_file: + { + // It's an existing file so it's an unlikely case. + // + break; + } + case target_decl::implied: + { + // While the "in a buildfile" is not exactly accurate, we assume + // it's unlikely we will end up here in other cases. + // + dr << info << "target " << t << " is implicitly declared in a " + << "buildfile"; + + if (const scope* rs = bs.root_scope ()) + { + if (t.out.empty () && rs->src_path () != rs->out_path ()) + { + name n (t.as_name ()[0]); + n.dir.clear (); + dr << info << "perhaps it should be declared as being in the " + << "source tree: " << n << "@./ ?"; + } + } + + break; + } + case target_decl::real: + { + // If we had a location, maybe it would make sense to mention this + // case. + // + break; + } + } + if (verb < 4) dr << info << "re-run with --verbose=4 for more information"; } diff --git a/libbuild2/bash/rule.cxx b/libbuild2/bash/rule.cxx index 0df9792..148da95 100644 --- a/libbuild2/bash/rule.cxx +++ b/libbuild2/bash/rule.cxx @@ -161,7 +161,7 @@ namespace build2 dir_path () /* out */, p.name, ext, - true /* implied */, + target_decl::implied, trace)); bash& pt (rp.first.as ()); diff --git a/libbuild2/cc/common.txx b/libbuild2/cc/common.txx index bfbc52c..ce922cc 100644 --- a/libbuild2/cc/common.txx +++ b/libbuild2/cc/common.txx @@ -24,7 +24,7 @@ namespace build2 path_cast (out.effect), name, move (ext), - true, // Implied. + target_decl::implied, trace)); assert (!exist || !p.second.owns_lock ()); diff --git a/libbuild2/cc/compile-rule.cxx b/libbuild2/cc/compile-rule.cxx index 37daf76..d568883 100644 --- a/libbuild2/cc/compile-rule.cxx +++ b/libbuild2/cc/compile-rule.cxx @@ -5500,7 +5500,7 @@ namespace build2 dir_path (), // Always in the out tree. move (mf), nullopt, // Use default extension. - true, // Implied. + target_decl::implied, trace)); file& bt (static_cast (p.first)); @@ -5571,7 +5571,7 @@ namespace build2 dir_path (), // Always in the out tree. move (mf), nullopt, // Use default extension. - true, // Implied. + target_decl::implied, trace)); file& bt (static_cast (p.first)); diff --git a/libbuild2/cc/pkgconfig.cxx b/libbuild2/cc/pkgconfig.cxx index 7e74c24..516746b 100644 --- a/libbuild2/cc/pkgconfig.cxx +++ b/libbuild2/cc/pkgconfig.cxx @@ -1152,7 +1152,7 @@ namespace build2 dir_path (), mf.base ().string (), mf.extension (), - true, // Implied. + target_decl::implied, trace)); target& mt (tl.first); diff --git a/libbuild2/config/operation.cxx b/libbuild2/config/operation.cxx index 963a3e1..b9856be 100644 --- a/libbuild2/config/operation.cxx +++ b/libbuild2/config/operation.cxx @@ -1083,7 +1083,7 @@ namespace build2 dir_path (), // Out tree. "", nullopt, - true, // Implied. + target_decl::implied, trace).first); if (verb != 0 && diag >= 2) diff --git a/libbuild2/file.cxx b/libbuild2/file.cxx index 14b16a7..1bd5068 100644 --- a/libbuild2/file.cxx +++ b/libbuild2/file.cxx @@ -1660,7 +1660,7 @@ namespace build2 dir_path (), // No out (not in project). p.leaf ().base ().string (), p.extension (), // Always specified. - true /* implied */, + target_decl::implied, trace)); if (const file* f = r.first.is_a ()) diff --git a/libbuild2/parser.cxx b/libbuild2/parser.cxx index 8ae13a8..033395d 100644 --- a/libbuild2/parser.cxx +++ b/libbuild2/parser.cxx @@ -150,13 +150,14 @@ namespace build2 tracer& tr) { auto r (p.scope_->find_target_type (n, o, loc)); - return p.ctx.targets.insert (r.first, // target type - move (n.dir), - move (o.dir), - move (n.value), - move (r.second), // extension - implied, - tr).first; + return p.ctx.targets.insert ( + r.first, // target type + move (n.dir), + move (o.dir), + move (n.value), + move (r.second), // extension + implied ? target_decl::implied : target_decl::real, + tr).first; } // Only find. @@ -1076,10 +1077,10 @@ namespace build2 // If we have a recipe, the target is not implied. // - if (target_->implied) + if (target_->decl != target_decl::real) { for (target* m (target_); m != nullptr; m = m->adhoc_member) - m->implied = false; + m->decl = target_decl::real; if (default_target_ == nullptr) default_target_ = target_; @@ -6894,7 +6895,7 @@ namespace build2 target& dt (*default_target_); target* ct ( - const_cast ( // Ok (serial execution). + const_cast ( // Ok (serial execution). ctx.targets.find (dir::static_type, // Explicit current dir target. scope_->out_path (), dir_path (), // Out tree target. @@ -6914,13 +6915,13 @@ namespace build2 dir_path (), string (), nullopt, - false, + target_decl::real, trace).first; // Fall through. } - else if (ct->implied) + else if (ct->decl != target_decl::real) { - ct->implied = false; + ct->decl = target_decl::real; // Fall through. } else diff --git a/libbuild2/search.cxx b/libbuild2/search.cxx index 8f5410c..2b10e0b 100644 --- a/libbuild2/search.cxx +++ b/libbuild2/search.cxx @@ -183,9 +183,13 @@ namespace build2 // Find or insert. Note that we are using our updated extension. // - auto r ( - ctx.targets.insert ( - *tk.type, move (d), move (out), *tk.name, ext, true, trace)); + auto r (ctx.targets.insert (*tk.type, + move (d), + move (out), + *tk.name, + ext, + target_decl::prereq_file, + trace)); // Has to be a file_target. // @@ -231,7 +235,7 @@ namespace build2 *tk.out, *tk.name, tk.ext, - true /* implied */, + target_decl::prereq_new, trace)); const target& t (r.first); diff --git a/libbuild2/target.cxx b/libbuild2/target.cxx index 7327b1b..8d76f56 100644 --- a/libbuild2/target.cxx +++ b/libbuild2/target.cxx @@ -412,7 +412,7 @@ namespace build2 dir_path out, string name, optional ext, - bool implied, + target_decl decl, tracer& trace) { target_key tk {&tt, &dir, &out, &name, move (ext)}; @@ -447,7 +447,7 @@ namespace build2 if (p.second) { t->ext_ = &i->first.ext; - t->implied = implied; + t->decl = decl; t->state.inner.target_ = t; t->state.outer.target_ = t; return pair (*t, move (ul)); @@ -484,16 +484,12 @@ namespace build2 // Fall through (continue as if the first find() returned this target). } - if (!implied) + if (decl > t->decl) { - // The implied flag can only be cleared during the load phase. + // The decl value can only be adjusted during the load phase. // assert (ctx.phase == run_phase::load); - - // Clear the implied flag. - // - if (t->implied) - t->implied = false; + t->decl = decl; } return pair (*t, ulock ()); @@ -833,7 +829,7 @@ namespace build2 // const target* e (search_existing_target (t.ctx, pk)); - if (e == nullptr || e->implied) + if (e == nullptr || e->decl != target_decl::real) fail << "no explicit target for " << pk; return e; @@ -928,7 +924,7 @@ namespace build2 // const target* e (search_existing_target (t.ctx, pk)); - if (e != nullptr && !e->implied) + if (e != nullptr && e->decl == target_decl::real) return e; // If not found (or is implied), then try to load the corresponding @@ -977,7 +973,7 @@ namespace build2 if (e == nullptr) e = search_existing_target (t.ctx, pk); - if (e != nullptr && !e->implied) + if (e != nullptr && e->decl == target_decl::real) retest = true; else { @@ -1018,7 +1014,7 @@ namespace build2 if (e == nullptr) e = search_existing_target (t.ctx, pk); - if (e != nullptr && !e->implied) + if (e != nullptr && e->decl == target_decl::real) return e; } diff --git a/libbuild2/target.hxx b/libbuild2/target.hxx index 17a99d3..ec97950 100644 --- a/libbuild2/target.hxx +++ b/libbuild2/target.hxx @@ -99,6 +99,31 @@ namespace build2 // Target. // + + // A target can be entered for several reasons that are useful to + // distinguish for diagnostics, when considering as the default + // target, etc. + // + // Note that the order of the enumerators is arranged so that their + // integral values indicate whether one "overrides" the other. + // + // @@ We have cases (like pkg-config extraction) where it should probably be + // prereq_file rather than implied (also audit targets.insert<> calls). + // + enum class target_decl: uint8_t + { + prereq_new, // Created from prerequisite (create_new_target()). + prereq_file, // Created from prerequisite/file (search_existing_file ()). + implied, // Target-spec variable assignment, implicitly-entered, etc. + real // Real dependency declaration. + }; + + inline bool + operator> (target_decl l, target_decl r) + { + return static_cast (l) > static_cast (r); + } + class LIBBUILD2_SYMEXPORT target { public: @@ -131,14 +156,10 @@ namespace build2 const dir_path& out_dir () const {return out.empty () ? dir : out;} - // A target that is not (yet) entered as part of a real dependency - // declaration (for example, that is entered as part of a target-specific - // variable assignment, dependency extraction, etc) is called implied. - // - // The implied flag should only be cleared during the load phase via the - // MT-safe target_set::insert(). + // Note that the declaration should only be upgraded during the load phase + // via the MT-safe target_set::insert(). // - bool implied; + target_decl decl; // Target group to which this target belongs, if any. Note that we assume // that the group and all its members are in the same scope (for example, @@ -1349,7 +1370,7 @@ namespace build2 dir_path out, string name, optional ext, - bool implied, + target_decl, tracer&); pair @@ -1358,7 +1379,7 @@ namespace build2 dir_path out, string name, optional ext, - bool implied, + target_decl decl, tracer& t) { auto p (insert_locked (tt, @@ -1366,7 +1387,7 @@ namespace build2 move (out), move (name), move (ext), - implied, + decl, t)); return pair (p.first, p.second.owns_lock ()); @@ -1388,7 +1409,7 @@ namespace build2 move (out), move (name), move (ext), - true, + target_decl::implied, t).first.template as (); } diff --git a/libbuild2/target.txx b/libbuild2/target.txx index d304daa..fb9fe9c 100644 --- a/libbuild2/target.txx +++ b/libbuild2/target.txx @@ -176,7 +176,7 @@ namespace build2 dir_path (), string (), nullopt, - false, + target_decl::real, trace).first); t.prerequisites (move (ps)); return &t; -- cgit v1.1