diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2022-06-24 10:29:09 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2022-06-24 10:29:09 +0200 |
commit | f1c981a22365411794806ed0744b857ef0804e35 (patch) | |
tree | af453c932bfa597a04dc7af17d5f5720f863d081 | |
parent | 1c12242aa7cd00e35a9be43b664e5486b2adc846 (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.
-rw-r--r-- | libbuild2/adhoc-rule-buildscript.cxx | 35 | ||||
-rw-r--r-- | libbuild2/adhoc-rule-buildscript.hxx | 4 | ||||
-rw-r--r-- | libbuild2/algorithm.hxx | 5 | ||||
-rw-r--r-- | libbuild2/build/script/parser.cxx | 9 | ||||
-rw-r--r-- | libbuild2/cc/common.cxx | 4 | ||||
-rw-r--r-- | libbuild2/cc/common.hxx | 5 | ||||
-rw-r--r-- | libbuild2/cc/link-rule.cxx | 18 | ||||
-rw-r--r-- | libbuild2/dyndep.cxx | 53 | ||||
-rw-r--r-- | libbuild2/dyndep.hxx | 7 | ||||
-rw-r--r-- | libbuild2/target.hxx | 33 |
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>; |