aboutsummaryrefslogtreecommitdiff
path: root/libbuild2
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2022-06-24 10:29:09 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2022-06-24 10:29:09 +0200
commitf1c981a22365411794806ed0744b857ef0804e35 (patch)
treeaf453c932bfa597a04dc7af17d5f5720f863d081 /libbuild2
parent1c12242aa7cd00e35a9be43b664e5486b2adc846 (diff)
Allow ad hoc rules not to list targets that are updated during match
For example, this allows a Qt moc rule not to list generated headers from libQtCore since they are pre-generated by the library.
Diffstat (limited to 'libbuild2')
-rw-r--r--libbuild2/adhoc-rule-buildscript.cxx35
-rw-r--r--libbuild2/adhoc-rule-buildscript.hxx4
-rw-r--r--libbuild2/algorithm.hxx5
-rw-r--r--libbuild2/build/script/parser.cxx9
-rw-r--r--libbuild2/cc/common.cxx4
-rw-r--r--libbuild2/cc/common.hxx5
-rw-r--r--libbuild2/cc/link-rule.cxx18
-rw-r--r--libbuild2/dyndep.cxx53
-rw-r--r--libbuild2/dyndep.hxx7
-rw-r--r--libbuild2/target.hxx33
10 files changed, 136 insertions, 37 deletions
diff --git a/libbuild2/adhoc-rule-buildscript.cxx b/libbuild2/adhoc-rule-buildscript.cxx
index 03e810d..9a38a31 100644
--- a/libbuild2/adhoc-rule-buildscript.cxx
+++ b/libbuild2/adhoc-rule-buildscript.cxx
@@ -350,8 +350,8 @@ namespace build2
include_type pi (
include (a, xt, p, a.operation () == update_id ? &l : nullptr));
- // Use bit 2 of prerequisite_target::include to signal update during
- // match and bit 3 -- unmatch.
+ // Use prerequisite_target::include to signal update during match or
+ // unmatch.
//
uintptr_t mask (0);
if (l)
@@ -361,12 +361,12 @@ namespace build2
if (v == "match")
{
if (a == perform_update_id)
- mask = 2;
+ mask = prerequisite_target::include_udm;
}
else if (v == "unmatch")
{
if (a == perform_update_id)
- mask = 4;
+ mask = include_unmatch;
}
else if (v != "false" && v != "true" && v != "execute")
{
@@ -422,7 +422,9 @@ namespace build2
// Handle update=unmatch.
//
- unmatch um ((pt.include & 4) != 0 ? unmatch::safe : unmatch::none);
+ unmatch um ((pt.include & include_unmatch) != 0
+ ? unmatch::safe
+ : unmatch::none);
pair<bool, target_state> mr (match_complete (a, *pt.target, um));
@@ -444,7 +446,7 @@ namespace build2
if (mr.first)
pt.target = nullptr;
else
- pt.include |= 1;
+ pt.include |= prerequisite_target::include_adhoc;
}
}
}
@@ -464,7 +466,7 @@ namespace build2
// prerequisite_target::data.
//
if (a == perform_update_id)
- update_during_match_prerequisites (trace, a, xt, 2 /* mask */);
+ update_during_match_prerequisites (trace, a, xt);
// See if this is not update or not on a file-based target.
//
@@ -531,6 +533,10 @@ namespace build2
// them to the auxiliary data member in prerequisite_target (see
// execute_update_prerequisites() for details).
//
+ // @@ This actually messes up with updated_during_match() check. Could
+ // we not redo this so that we always keep p.target intact? Can't
+ // we just omit p.adhoc() targets from $<?
+ //
for (prerequisite_target& p: pts)
{
// Note that fsdir{} injected above is adhoc.
@@ -565,7 +571,7 @@ namespace build2
p.adhoc () ? reinterpret_cast<target*> (p.data) :
nullptr))
{
- if ((p.include & 4) != 0) // Skip update=unmatch.
+ if ((p.include & include_unmatch) != 0) // Skip update=unmatch.
continue;
hash_prerequisite_target (prq_cs, exe_cs, env_cs, *pt, storage);
@@ -665,7 +671,8 @@ namespace build2
// update on encountering any non-existent files in depbd, we may
// actually incorrectly "validate" some number of depdb entires while
// having an out-of-date main source file. We could probably avoid the
- // update if we are already updating.
+ // update if we are already updating (or not: there is pre-generation
+ // to consider; see inject_existing_file() for details).
//
{
build::script::parser p (ctx);
@@ -701,7 +708,7 @@ namespace build2
size_t& skip_count (mdb->skip_count);
auto add = [&trace, what,
- a, &bs, &t,
+ a, &bs, &t, pts_n = mdb->pts_n,
&byp, &map_ext,
&skip_count, mt] (path fp) -> optional<bool>
{
@@ -716,7 +723,7 @@ namespace build2
//
if (optional<bool> u = dyndep::inject_existing_file (
trace, what,
- a, t,
+ a, t, pts_n,
*ft, mt,
false /* fail */,
false /* adhoc */,
@@ -961,7 +968,7 @@ namespace build2
// @@ Currently we will issue an imprecise diagnostics if this is
// a static prerequisite that was not updated (see above).
//
- dyndep::verify_existing_file (trace, what, a, t, *ft);
+ dyndep::verify_existing_file (trace, what, a, t, pts_n, *ft);
}
dd.write (fp);
@@ -1156,7 +1163,7 @@ namespace build2
p.adhoc () ? reinterpret_cast<target*> (p.data)
: nullptr))
{
- if ((p.include & 4) != 0) // Skip update=unmatch.
+ if ((p.include & include_unmatch) != 0) // Skip update=unmatch.
continue;
hash_prerequisite_target (prq_cs, exe_cs, env_cs, *pt, storage);
@@ -1396,7 +1403,7 @@ namespace build2
// Compare our timestamp to this prerequisite's skipping
// update=unmatch.
//
- if (!e && (p.include & 4) == 0)
+ if (!e && (p.include & include_unmatch) == 0)
{
// If this is an mtime-based target, then compare timestamps.
//
diff --git a/libbuild2/adhoc-rule-buildscript.hxx b/libbuild2/adhoc-rule-buildscript.hxx
index e7b18e2..2334cdd 100644
--- a/libbuild2/adhoc-rule-buildscript.hxx
+++ b/libbuild2/adhoc-rule-buildscript.hxx
@@ -82,6 +82,10 @@ namespace build2
public:
using script_type = build::script::script;
+ // The prerequisite_target::include bit that indicates update=unmatch.
+ //
+ static const uintptr_t include_unmatch = 0x100;
+
script_type script;
string checksum; // Script text hash.
const target_type* ttype; // First target/pattern type.
diff --git a/libbuild2/algorithm.hxx b/libbuild2/algorithm.hxx
index db3e93d..300016f 100644
--- a/libbuild2/algorithm.hxx
+++ b/libbuild2/algorithm.hxx
@@ -637,7 +637,10 @@ namespace build2
// for temporary storage). But it resets data to 0 once done.
//
LIBBUILD2_SYMEXPORT bool
- update_during_match_prerequisites (tracer&, action, target&, uintptr_t mask);
+ update_during_match_prerequisites (
+ tracer&,
+ action, target&,
+ uintptr_t mask = prerequisite_target::include_udm);
// The default prerequisite execute implementation. Call execute_async() on
// each non-ignored (non-NULL) prerequisite target in a loop and then wait
diff --git a/libbuild2/build/script/parser.cxx b/libbuild2/build/script/parser.cxx
index 9f04102..795dc72 100644
--- a/libbuild2/build/script/parser.cxx
+++ b/libbuild2/build/script/parser.cxx
@@ -15,6 +15,8 @@
#include <libbuild2/algorithm.hxx>
#include <libbuild2/make-parser.hxx>
+#include <libbuild2/adhoc-rule-buildscript.hxx>
+
#include <libbuild2/script/run.hxx>
#include <libbuild2/build/script/lexer.hxx>
@@ -1632,7 +1634,7 @@ namespace build2
// So there is a nuanced interaction between update=match and
// --update-*.
//
- if ((p.include & 4) != 0)
+ if ((p.include & adhoc_buildscript_rule::include_unmatch) != 0)
{
l6 ([&]{trace << "skipping unmatched " << *pt;});
continue;
@@ -1705,6 +1707,11 @@ namespace build2
update = dyndep::update (
trace, a, *pt, update ? timestamp_unknown : mt) || update;
+ // While implicit, it is for a static prerequisite, so marking it
+ // feels correct.
+ //
+ p.include |= prerequisite_target::include_udm;
+
// Mark as updated (see execute_update_prerequisites() for
// details.
//
diff --git a/libbuild2/cc/common.cxx b/libbuild2/cc/common.cxx
index 976127f..a320626 100644
--- a/libbuild2/cc/common.cxx
+++ b/libbuild2/cc/common.cxx
@@ -397,7 +397,9 @@ namespace build2
{
// See link_rule for details.
//
- const target* g ((pt.include & 4) != 0 ? f->group : nullptr);
+ const target* g ((pt.include & include_group) != 0
+ ? f->group
+ : nullptr);
if (sysd == nullptr) find_sysd ();
if (!li) find_linfo ();
diff --git a/libbuild2/cc/common.hxx b/libbuild2/cc/common.hxx
index a5a4859..2aaa0d0 100644
--- a/libbuild2/cc/common.hxx
+++ b/libbuild2/cc/common.hxx
@@ -305,6 +305,11 @@ namespace build2
using library_cache = small_vector<library_cache_entry, 32>;
+ // The prerequisite_target::include bit that indicates a library
+ // member has been picked from the group.
+ //
+ static const uintptr_t include_group = 0x100;
+
void
process_libraries (
action,
diff --git a/libbuild2/cc/link-rule.cxx b/libbuild2/cc/link-rule.cxx
index 0081fe2..d0a1f4a 100644
--- a/libbuild2/cc/link-rule.cxx
+++ b/libbuild2/cc/link-rule.cxx
@@ -1003,7 +1003,7 @@ namespace build2
if (*um)
{
pto.target = &p.search (t); // mark 0
- pto.include |= 2;
+ pto.include |= prerequisite_target::include_udm;
update_match = true;
}
}
@@ -1144,7 +1144,7 @@ namespace build2
if (const libx* l = pt->is_a<libx> ())
{
pt = link_member (*l, a, li);
- pto.include |= 4;
+ pto.include |= include_group;
}
}
else
@@ -1231,7 +1231,7 @@ namespace build2
<< "not supported by this rule";
m = 0;
- pto.include |= 2;
+ pto.include |= prerequisite_target::include_udm;
update_match = true;
}
}
@@ -1245,10 +1245,14 @@ namespace build2
// of the libraries (for example, if generation requires some of the
// metadata; think poptions needed by Qt moc).
//
- match_members (a, t, pts, start, {2 /* mask */, 0 /* value */});
+ {
+ auto mask (prerequisite_target::include_udm);
- if (update_match)
- match_members (a, t, pts, start, {2, 2});
+ match_members (a, t, pts, start, {mask, 0});
+
+ if (update_match)
+ match_members (a, t, pts, start, {mask, mask});
+ }
// Check if we have any binful utility libraries.
//
@@ -1383,7 +1387,7 @@ namespace build2
// prerequisite_target::data.
//
if (update_match)
- update_during_match_prerequisites (trace, a, t, 2 /* mask */);
+ update_during_match_prerequisites (trace, a, t);
// Now that we know for sure whether we are binless, derive file name(s)
// and add ad hoc group members. Note that for binless we still need the
diff --git a/libbuild2/dyndep.cxx b/libbuild2/dyndep.cxx
index 61fa8cb..bc307f6 100644
--- a/libbuild2/dyndep.cxx
+++ b/libbuild2/dyndep.cxx
@@ -56,9 +56,43 @@ namespace build2
return r;
}
+ // Check if the specified prerequisite is updated during match by any other
+ // prerequisites of the specified target, recursively.
+ //
+ static bool
+ updated_during_match (action a, const target& t, size_t pts_n,
+ const target& pt)
+ {
+ const auto& pts (t.prerequisite_targets[a]);
+
+ for (size_t i (0); i != pts_n; ++i)
+ {
+ const prerequisite_target& p (pts[i]);
+
+ // @@ This currently doesn't cover adhoc targets if matched with
+ // buildscript (it stores them in p.data). Probably need to redo
+ // things there (see adhoc_buildscript_rule::apply()).
+ //
+ if (p.target != nullptr)
+ {
+ if (p.target == &pt &&
+ (p.include & prerequisite_target::include_udm) != 0)
+ return true;
+
+ if (size_t n = p.target->prerequisite_targets[a].size ())
+ {
+ if (updated_during_match (a, *p.target, n, pt))
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
optional<bool> dyndep_rule::
inject_existing_file (tracer& trace, const char* what,
- action a, target& t,
+ action a, target& t, size_t pts_n,
const file& pt,
timestamp mt,
bool f,
@@ -81,8 +115,11 @@ namespace build2
recipe_function* const* rf (pt[a].recipe.target<recipe_function*> ());
if (rf == nullptr || *rf != &noop_action)
{
- fail << what << ' ' << pt << " has non-noop recipe" <<
- info << "consider listing it as static prerequisite of " << t;
+ if (!updated_during_match (a, t, pts_n, pt))
+ {
+ fail << what << ' ' << pt << " has non-noop recipe" <<
+ info << "consider listing it as static prerequisite of " << t;
+ }
}
bool r (update (trace, a, pt, mt));
@@ -96,7 +133,7 @@ namespace build2
void dyndep_rule::
verify_existing_file (tracer&, const char* what,
- action a, const target& t,
+ action a, const target& t, size_t pts_n,
const file& pt)
{
diag_record dr;
@@ -106,11 +143,17 @@ namespace build2
recipe_function* const* rf (pt[a].recipe.target<recipe_function*> ());
if (rf == nullptr || *rf != &noop_action)
{
- dr << fail << what << ' ' << pt << " has non-noop recipe";
+ if (!updated_during_match (a, t, pts_n, pt))
+ {
+ dr << fail << what << ' ' << pt << " has non-noop recipe";
+ }
}
}
else if (pt.decl == target_decl::real)
{
+ // Note that this target could not possibly be updated during match
+ // since it's not matched.
+ //
dr << fail << what << ' ' << pt << " is explicitly declared as "
<< "target and may have non-noop recipe";
}
diff --git a/libbuild2/dyndep.hxx b/libbuild2/dyndep.hxx
index 6632eb6..0789e78 100644
--- a/libbuild2/dyndep.hxx
+++ b/libbuild2/dyndep.hxx
@@ -65,9 +65,12 @@ namespace build2
// byproduct of recipe execution (and thus must have all the generated
// prerequisites specified statically).
//
+ // Note that this function expects all the static prerequisites of the
+ // target to already be matched and their number passed in pts_n.
+ //
static optional<bool>
inject_existing_file (tracer&, const char* what,
- action, target&,
+ action, target&, size_t pts_n,
const file& prerequiste,
timestamp,
bool fail,
@@ -82,7 +85,7 @@ namespace build2
//
static void
verify_existing_file (tracer&, const char* what,
- action, const target&,
+ action, const target&, size_t pts_n,
const file& prerequiste);
// Reverse-lookup target type(s) from file name/extension.
diff --git a/libbuild2/target.hxx b/libbuild2/target.hxx
index 411025c..1a7abfc 100644
--- a/libbuild2/target.hxx
+++ b/libbuild2/target.hxx
@@ -75,27 +75,48 @@ namespace build2
//
// The include member normally just indicates (in the first bit) whether
// this prerequisite is ad hoc. But it can also carry additional information
- // (for example, from operation-specific override) in other bits.
+ // (for example, from operation-specific override) in other bits (see below
+ // for details).
//
struct prerequisite_target
{
using target_type = build2::target;
prerequisite_target (const target_type* t, bool a = false, uintptr_t d = 0)
- : target (t), include (a ? 1 : 0), data (d) {}
+ : target (t), include (a ? include_adhoc : 0), data (d) {}
prerequisite_target (const target_type* t, include_type a, uintptr_t d = 0)
: prerequisite_target (t, a == include_type::adhoc, d) {}
+ const target_type* target;
+
operator const target_type*& () {return target;}
operator const target_type* () const {return target;}
const target_type* operator-> () const {return target;}
- bool adhoc () const {return (include & 1) != 0;}
+ // The first 8 bits are reserved with the first two having the following
+ // semantics:
+ //
+ // adhoc
+ //
+ // This prerequisite is ad hoc.
+ //
+ // udm
+ //
+ // This prerequisite is updated during match. Note that only static
+ // prerequisites that are updated during match should have this bit set
+ // (see dyndep_rule::*_existing_file() for details).
+ //
+ static const uintptr_t include_adhoc = 0x01;
+ static const uintptr_t include_udm = 0x02;
- const target_type* target;
- uintptr_t include; // First bit is 1 if include=adhoc.
- uintptr_t data;
+ uintptr_t include;
+
+ bool adhoc () const {return (include & include_adhoc) != 0;}
+
+ // Auxiliary data.
+ //
+ uintptr_t data;
};
using prerequisite_targets = vector<prerequisite_target>;