aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2020-09-24 13:10:25 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2020-09-24 13:10:25 +0200
commitb6c61ea9afd2d738711770e44748e48be009154d (patch)
tree9f4e2ab650e8359e3fe0a9c5eed45dc2f2f4f160
parentc94f066bbd47520cf52937fc4ad08a699abda28a (diff)
Give hints for common causes of "no rule to update ..." error
-rw-r--r--libbuild2/algorithm.cxx51
-rw-r--r--libbuild2/bash/rule.cxx2
-rw-r--r--libbuild2/cc/common.txx2
-rw-r--r--libbuild2/cc/compile-rule.cxx4
-rw-r--r--libbuild2/cc/pkgconfig.cxx2
-rw-r--r--libbuild2/config/operation.cxx2
-rw-r--r--libbuild2/file.cxx2
-rw-r--r--libbuild2/parser.cxx27
-rw-r--r--libbuild2/search.cxx12
-rw-r--r--libbuild2/target.cxx22
-rw-r--r--libbuild2/target.hxx43
-rw-r--r--libbuild2/target.txx2
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<file> ())
+ 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<bash> ());
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<dir_path> (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<file&> (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<file&> (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<file> ())
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<target*> ( // Ok (serial execution).
+ const_cast<target*> ( // 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<string> 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<target&, ulock> (*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<target&, ulock> (*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<uint8_t> (l) > static_cast<uint8_t> (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<string> ext,
- bool implied,
+ target_decl,
tracer&);
pair<target&, bool>
@@ -1358,7 +1379,7 @@ namespace build2
dir_path out,
string name,
optional<string> 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<target&, bool> (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<T> ();
}
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;