From 294a883863fa7713edc5278adddf8f60a28c8a9f Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 24 Feb 2022 10:03:43 +0200 Subject: WIP --- libbuild2/action.hxx | 4 ++ libbuild2/adhoc-rule-buildscript.cxx | 16 +++--- libbuild2/adhoc-rule-regex-pattern.cxx | 2 +- libbuild2/algorithm.cxx | 6 +-- libbuild2/bash/rule.cxx | 2 +- libbuild2/build/script/parser.cxx | 12 ++--- libbuild2/build/script/script.cxx | 2 +- libbuild2/cc/common.cxx | 2 +- libbuild2/cc/link-rule.cxx | 92 ++++++++++++++++++++++++++++++---- libbuild2/cc/windows-rpath.cxx | 4 +- libbuild2/context.cxx | 24 +++++++-- libbuild2/context.hxx | 15 ++++++ libbuild2/dist/operation.cxx | 7 ++- libbuild2/install/operation.cxx | 3 ++ libbuild2/install/rule.cxx | 12 ++--- libbuild2/operation.cxx | 3 ++ libbuild2/operation.hxx | 15 +++--- libbuild2/target.cxx | 73 ++++++++++++++++++++++----- libbuild2/target.hxx | 31 +++++++----- libbuild2/target.ixx | 18 +++++-- libbuild2/test/operation.cxx | 2 + libbuild2/variable.cxx | 2 +- 22 files changed, 269 insertions(+), 78 deletions(-) diff --git a/libbuild2/action.hxx b/libbuild2/action.hxx index e149574..dce3a1a 100644 --- a/libbuild2/action.hxx +++ b/libbuild2/action.hxx @@ -150,6 +150,7 @@ namespace build2 // Id constants for build-in and pre-defined meta/operations. // // Note: currently max 15 (see above). + // Note: update small_vector in meta_operations if adding more. // const meta_operation_id noop_id = 1; // nomop? const meta_operation_id perform_id = 2; @@ -164,6 +165,7 @@ namespace build2 // something here remember to update the man page. // // Note: currently max 15 (see above). + // Note: update small_vector in operations if adding more. // const operation_id default_id = 1; // Shall be first. const operation_id update_id = 2; // Shall be second. @@ -176,6 +178,8 @@ namespace build2 const operation_id uninstall_id = 7; const operation_id update_for_install_id = 8; // update(for install) alias. + // Commonly-used action ids. + // const action_id perform_update_id = (perform_id << 4) | update_id; const action_id perform_clean_id = (perform_id << 4) | clean_id; const action_id perform_test_id = (perform_id << 4) | test_id; diff --git a/libbuild2/adhoc-rule-buildscript.cxx b/libbuild2/adhoc-rule-buildscript.cxx index 7174296..e9bd2c6 100644 --- a/libbuild2/adhoc-rule-buildscript.cxx +++ b/libbuild2/adhoc-rule-buildscript.cxx @@ -399,7 +399,7 @@ namespace build2 { // Note that fsdir{} injected above is adhoc. // - if (p.target != nullptr && p.adhoc) + if (p.target != nullptr && p.adhoc ()) { p.data = reinterpret_cast (p.target); p.target = nullptr; @@ -426,7 +426,7 @@ namespace build2 { if (const target* pt = (p.target != nullptr ? p.target : - p.adhoc ? reinterpret_cast (p.data) : + p.adhoc () ? reinterpret_cast (p.data) : nullptr)) { hash_prerequisite_target (prq_cs, exe_cs, env_cs, *pt, storage); @@ -812,10 +812,10 @@ namespace build2 if (const target* pt = (p.target != nullptr ? p.target : - p.adhoc ? reinterpret_cast (p.data) : + p.adhoc () ? reinterpret_cast (p.data) : nullptr)) { - if (ft == pt && (p.adhoc || p.data == 1)) + if (ft == pt && (p.adhoc () || p.data == 1)) return; } } @@ -1036,7 +1036,7 @@ namespace build2 { if (const target* pt = (p.target != nullptr ? p.target : - p.adhoc ? reinterpret_cast (p.data) + p.adhoc () ? reinterpret_cast (p.data) : nullptr)) { hash_prerequisite_target (prq_cs, exe_cs, env_cs, *pt, storage); @@ -1251,7 +1251,7 @@ namespace build2 { if (const target* pt = (p.target != nullptr ? p.target : - p.adhoc ? reinterpret_cast (p.data) : nullptr)) + p.adhoc () ? reinterpret_cast (p.data) : nullptr)) { target_state s (execute_async (a, *pt, busy, t[a].task_count)); assert (s != target_state::postponed); @@ -1265,7 +1265,7 @@ namespace build2 { if (const target* pt = (p.target != nullptr ? p.target : - p.adhoc ? reinterpret_cast (p.data) : nullptr)) + p.adhoc () ? reinterpret_cast (p.data) : nullptr)) { ctx.sched.wait (exec, (*pt)[a].task_count, scheduler::work_none); @@ -1297,7 +1297,7 @@ namespace build2 // Blank out adhoc. // - if (p.adhoc) + if (p.adhoc ()) { p.data = reinterpret_cast (p.target); p.target = nullptr; diff --git a/libbuild2/adhoc-rule-regex-pattern.cxx b/libbuild2/adhoc-rule-regex-pattern.cxx index b0de827..b892676 100644 --- a/libbuild2/adhoc-rule-regex-pattern.cxx +++ b/libbuild2/adhoc-rule-regex-pattern.cxx @@ -162,7 +162,7 @@ namespace build2 // for (prerequisite_member p: group_prerequisite_members (a, t)) { - if (include (a, t, p) == include_type::normal && p.is_a (tt)) + if (include (a, t, p) == include_type::normal && p.is_a (tt)) // @@ return p.key ().tk; } return nullopt; diff --git a/libbuild2/algorithm.cxx b/libbuild2/algorithm.cxx index 9b6fd4e..06ffd5e 100644 --- a/libbuild2/algorithm.cxx +++ b/libbuild2/algorithm.cxx @@ -2065,7 +2065,7 @@ namespace build2 static inline void blank_adhoc_member (prerequisite_target& pt) { - if (pt.adhoc) + if (pt.adhoc ()) pt.target = nullptr; } @@ -2254,7 +2254,7 @@ namespace build2 // Should we compare the timestamp to this target's? // - if (!e && (p.adhoc || !ef || ef (pt, i))) + if (!e && (p.adhoc () || !ef || ef (pt, i))) { // If this is an mtime-based target, then compare timestamps. // @@ -2272,7 +2272,7 @@ namespace build2 } } - if (p.adhoc) + if (p.adhoc ()) p.target = nullptr; // Blank out. else { diff --git a/libbuild2/bash/rule.cxx b/libbuild2/bash/rule.cxx index 5ba298c..ec24226 100644 --- a/libbuild2/bash/rule.cxx +++ b/libbuild2/bash/rule.cxx @@ -267,7 +267,7 @@ namespace build2 const path* ap (nullptr); for (const prerequisite_target& pt: t.prerequisite_targets[a]) { - if (pt.adhoc || pt.target == nullptr) + if (pt.target == nullptr || pt.adhoc ()) continue; if (const bash* b = pt.target->is_a ()) diff --git a/libbuild2/build/script/parser.cxx b/libbuild2/build/script/parser.cxx index cba4b88..e9c8d9c 100644 --- a/libbuild2/build/script/parser.cxx +++ b/libbuild2/build/script/parser.cxx @@ -1622,12 +1622,12 @@ namespace build2 { if (const target* pt = (p.target != nullptr ? p.target : - p.adhoc ? reinterpret_cast (p.data) + p.adhoc () ? reinterpret_cast (p.data) : nullptr)) { // Apply the --update-* filter. // - if (!p.adhoc && !filters.empty ()) + if (!p.adhoc () && !filters.empty ()) { // Compute and cache "effective" name that we will be pattern- // matching (similar code to variable_type_map::find()). @@ -1695,7 +1695,7 @@ namespace build2 // Mark as updated (see execute_update_prerequisites() for // details. // - if (!p.adhoc) + if (!p.adhoc ()) p.data = 1; } } @@ -1923,10 +1923,10 @@ namespace build2 if (const target* pt = (p.target != nullptr ? p.target : - p.adhoc ? reinterpret_cast (p.data) : + p.adhoc () ? reinterpret_cast (p.data) : nullptr)) { - if (ft == pt && (p.adhoc || p.data == 1)) + if (ft == pt && (p.adhoc () || p.data == 1)) return false; } } @@ -1968,7 +1968,7 @@ namespace build2 { prerequisite_target& pt (pts.back ()); - if (pt.adhoc) + if (pt.adhoc ()) { pt.data = reinterpret_cast (pt.target); pt.target = nullptr; diff --git a/libbuild2/build/script/script.cxx b/libbuild2/build/script/script.cxx index 480903e..b230ca5 100644 --- a/libbuild2/build/script/script.cxx +++ b/libbuild2/build/script/script.cxx @@ -78,7 +78,7 @@ namespace build2 { // See adhoc_buildscript_rule::execute_update_prerequisites(). // - if (pt.target != nullptr && !pt.adhoc) + if (pt.target != nullptr && !pt.adhoc ()) pt.target->as_name (ns); } diff --git a/libbuild2/cc/common.cxx b/libbuild2/cc/common.cxx index ccb678c..97ac6b8 100644 --- a/libbuild2/cc/common.cxx +++ b/libbuild2/cc/common.cxx @@ -317,7 +317,7 @@ namespace build2 // Note: adhoc prerequisites are not part of the library metadata // protocol (and we should check for adhoc first to avoid races). // - if (pt.adhoc || pt == nullptr) + if (pt == nullptr || pt.adhoc ()) continue; if (marked (pt)) diff --git a/libbuild2/cc/link-rule.cxx b/libbuild2/cc/link-rule.cxx index 28bd54f..f39da6a 100644 --- a/libbuild2/cc/link-rule.cxx +++ b/libbuild2/cc/link-rule.cxx @@ -282,7 +282,11 @@ namespace build2 { // If excluded or ad hoc, then don't factor it into our tests. // - if (include (a, t, p) != include_type::normal) + // Note that here we don't validate the update operation override + // value (in the end we may not much). Instead we do this in apply(). + // + lookup l; + if (include (a, t, p, &l) != include_type::normal) continue; if (p.is_a (x_src) || @@ -914,26 +918,62 @@ namespace build2 return a.operation () == clean_id && !pt.dir.sub (rs.out_path ()); }; + bool update_match (false); + auto& pts (t.prerequisite_targets[a]); size_t start (pts.size ()); for (prerequisite_member p: group_prerequisite_members (a, t)) { - include_type pi (include (a, t, p)); + lookup l; // The `update` variable value, if any. + include_type pi ( + include (a, t, p, a == perform_update_id ? &l : nullptr)); // We pre-allocate a NULL slot for each (potential; see clean) // prerequisite target. // pts.push_back (prerequisite_target (nullptr, pi)); - const target*& pt (pts.back ()); + auto& pto (pts.back ()); - // Skip excluded and ad hoc on this pass. + // Use bit 2 of prerequisite_target::include to signal update during + // match. + // + // Not that for now we only allow updating during match ad hoc and + // mark 3 (headers, etc; see below) prerequisites. + // + bool um (false); + + if (l) + { + const string& v (cast (l)); + + if (v == "match") + { + pto.include |= 2; + update_match = um = true; + } + else if (v != "false" && v != "true") + { + fail << "unrecognized update variable value '" << v + << "' specified for prerequisite " << p.prerequisite; + } + } + + // Skip excluded and ad hoc (unless updated during match) on this + // pass. // if (pi != include_type::normal) + { + if (pi == include_type::adhoc && um) + pto.target = &p.search (t); // mark 0 + continue; + } + + const target*& pt (pto); - // Mark: - // 0 - lib + // Mark (2 bits): + // 0 - lib or update during match // 1 - src // 2 - mod // 3 - obj/bmi and also lib not to be cleaned (and other stuff) @@ -1121,13 +1161,39 @@ namespace build2 if (user_binless && !binless) fail << t << " cannot be binless due to " << p << " prerequisite"; + // Upgrade update during match prerequisites to mark 0 (see above for + // details). + // + if (um) + { + if (m != 3) + fail << "unable to update during match prerequisite " << p << + info << "updating this type of prerequisites during match is " + << "not supported by this rule"; + + m = 0; + } + mark (pt, m); } - // Match lib{} (the only unmarked) in parallel and wait for completion. + // Match lib{} and update during match (the only unmarked) in parallel + // and wait for completion. // match_members (a, t, pts, start); + // If we have any update during match prerequisites, now is the time to + // update them. Note that we have to do it before any further matches + // since they may rely on these prerequisites already being updated (for + // example, object file matches may need the headers to be already + // updated). + // + // @@ TODO: test adhoc + // + if (update_match) + { + } + // Check if we have any binful utility libraries. // if (binless) @@ -1432,6 +1498,7 @@ namespace build2 continue; // New mark: + // 0 - already matched // 1 - completion // 2 - verification // @@ -1619,7 +1686,7 @@ namespace build2 m = 2; // Needs verification. } } - else // lib*{} + else // lib*{} or update during match { // If this is a static library, see if we need to link it whole. // Note that we have to do it after match since we rely on the @@ -1669,7 +1736,7 @@ namespace build2 i = start; for (prerequisite_member p: group_prerequisite_members (a, t)) { - bool adhoc (pts[i].adhoc); + bool adhoc (pts[i].adhoc ()); const target*& pt (pts[i++]); uint8_t m; @@ -1684,8 +1751,13 @@ namespace build2 pt = &p.search (t); m = 1; // Mark for completion. } - else if ((m = unmark (pt)) != 0) + else { + m = unmark (pt); + + if (m == 0) + continue; // Already matched. + // If this is a library not to be cleaned, we can finally blank it // out. // diff --git a/libbuild2/cc/windows-rpath.cxx b/libbuild2/cc/windows-rpath.cxx index 2d90ace..e205924 100644 --- a/libbuild2/cc/windows-rpath.cxx +++ b/libbuild2/cc/windows-rpath.cxx @@ -128,7 +128,7 @@ namespace build2 library_cache lib_cache; for (const prerequisite_target& pt: t.prerequisite_targets[a]) { - if (pt.adhoc || pt == nullptr) + if (pt == nullptr || pt.adhoc ()) continue; bool la; @@ -253,7 +253,7 @@ namespace build2 library_cache lib_cache; for (const prerequisite_target& pt: t.prerequisite_targets[a]) { - if (pt.adhoc || pt == nullptr) + if (pt == nullptr || pt.adhoc ()) continue; bool la; diff --git a/libbuild2/context.cxx b/libbuild2/context.cxx index d78efba..d0b24e0 100644 --- a/libbuild2/context.cxx +++ b/libbuild2/context.cxx @@ -586,9 +586,10 @@ namespace build2 var_export_metadata = &vp.insert ("export.metadata", v_t); // Untyped. var_extension = &vp.insert ("extension", v_t); - var_clean = &vp.insert ("clean", v_t); - var_backlink = &vp.insert ("backlink", v_t); - var_include = &vp.insert ("include", v_q); + var_update = &vp.insert ("update", v_q); + var_clean = &vp.insert ("clean", v_t); + var_backlink = &vp.insert ("backlink", v_t); + var_include = &vp.insert ("include", v_q); // Backlink executables and (generated) documentation by default. // @@ -696,13 +697,28 @@ namespace build2 const operation_info* outer_oif, bool diag_noise) { - current_oname = (outer_oif == nullptr ? inner_oif : *outer_oif).name; + const auto& oif (outer_oif == nullptr ? inner_oif : *outer_oif); + + current_oname = oif.name; current_inner_oif = &inner_oif; current_outer_oif = outer_oif; current_on++; current_mode = inner_oif.mode; current_diag_noise = diag_noise; + if (oif.var_name != nullptr) + { + current_ovar = var_pool.find (oif.var_name); + + // The operation variable should have prerequisite or target visibility. + // + assert (current_ovar != nullptr && + (current_ovar->visibility == variable_visibility::prereq || + current_ovar->visibility == variable_visibility::target)); + } + else + current_ovar = nullptr; + // Reset counters (serial execution). // dependency_count.store (0, memory_order_relaxed); diff --git a/libbuild2/context.hxx b/libbuild2/context.hxx index 3563c16..9bcf583 100644 --- a/libbuild2/context.hxx +++ b/libbuild2/context.hxx @@ -283,6 +283,8 @@ namespace build2 const operation_info* current_inner_oif; const operation_info* current_outer_oif; + const variable* current_ovar; // Current (outer) operation variable. + action current_action () const { @@ -428,6 +430,19 @@ namespace build2 // const variable* var_extension; + // This variable can only be specified as prerequisite-specific (see the + // `include` variable for details). + // + // [string] prerequisite visibility + // + // Valid values are `true` and `false`. Additionally, some rules (and + // potentially only for certain types of prerequisites) may support the + // `unmatch` (match but do not update, if possible) and `match` (update + // during match) values. Note that if unmatch is impossible, then the + // prerequisite is treated as ad hoc. + // + const variable* var_update; + // Note that this variable can also be specified as prerequisite-specific // (see the `include` variable for details). // diff --git a/libbuild2/dist/operation.cxx b/libbuild2/dist/operation.cxx index f3db8ad..150a5a1 100644 --- a/libbuild2/dist/operation.cxx +++ b/libbuild2/dist/operation.cxx @@ -1001,7 +1001,8 @@ namespace build2 dist_include (action, const target&, const prerequisite_member& p, - include_type i) + include_type i, + lookup& l) { tracer trace ("dist::dist_include"); @@ -1016,6 +1017,10 @@ namespace build2 i = include_type::adhoc; } + // Also clear any operation-specific overrides. + // + l = lookup (); + return i; } diff --git a/libbuild2/install/operation.cxx b/libbuild2/install/operation.cxx index 52e8c94..6ae2819 100644 --- a/libbuild2/install/operation.cxx +++ b/libbuild2/install/operation.cxx @@ -37,6 +37,7 @@ namespace build2 0, "install", "install", + "install", "installing", "installed", "has nothing to install", // We cannot "be installed". @@ -61,6 +62,7 @@ namespace build2 uninstall_id, 0, "uninstall", + "install", "uninstall", "uninstalling", "uninstalled", @@ -79,6 +81,7 @@ namespace build2 update_id, // Note: not update_for_install_id. install_id, op_update.name, + nullptr, // Outer operation variable is always used. op_update.name_do, op_update.name_doing, op_update.name_did, diff --git a/libbuild2/install/rule.cxx b/libbuild2/install/rule.cxx index b8d716d..77da466 100644 --- a/libbuild2/install/rule.cxx +++ b/libbuild2/install/rule.cxx @@ -125,7 +125,7 @@ namespace build2 // // Note: not the same as lookup_install() above. // - auto l ((*pt)["install"]); + auto l ((*pt)["install"]); //@@??? if (l && cast (l).string () == "false") { l5 ([&]{trace << "ignoring " << *pt << " (not installable)";}); @@ -245,7 +245,7 @@ namespace build2 // // Note: not the same as lookup_install() above. // - auto l ((*mt)["install"]); + auto l ((*mt)["install"]); //@@ if (l && cast (l).string () == "false") { l5 ([&]{trace << "ignoring " << *mt << " (not installable)";}); @@ -291,7 +291,7 @@ namespace build2 if (p.is_a ()) { // Feels like one day this should be unified with include (see - // context::var_include). + // context::var_include). @@ Isn't it now? // if (p.vars.empty () || cast_empty (p.vars["install"]).string () != "true") @@ -381,7 +381,7 @@ namespace build2 // // Note: not the same as lookup_install() above. // - auto l ((*pt)["install"]); + auto l ((*pt)["install"]); //@@ if (l && cast (l).string () == "false") { l5 ([&]{trace << "ignoring " << *pt << " (not installable)";}); @@ -1033,7 +1033,7 @@ namespace build2 // if (!tp.empty ()) { - install_target (t, cast (t["install"]), 1); + install_target (t, cast (t["install"]), 1); //@@ r |= target_state::changed; } @@ -1298,7 +1298,7 @@ namespace build2 target_state r (target_state::unchanged); if (!tp.empty ()) - r |= uninstall_target (t, cast (t["install"]), 1); + r |= uninstall_target (t, cast (t["install"]), 1); //@@ // Then installable ad hoc group members, if any. To be anally precise, // we would have to do it in reverse, but that's not easy (it's a diff --git a/libbuild2/operation.cxx b/libbuild2/operation.cxx index 0f30c4a..68666cb 100644 --- a/libbuild2/operation.cxx +++ b/libbuild2/operation.cxx @@ -737,6 +737,7 @@ namespace build2 default_id, 0, "", + nullptr, "", "", "", @@ -764,6 +765,7 @@ namespace build2 0, "update", "update", + "update", "updating", "updated", "is up to date", @@ -780,6 +782,7 @@ namespace build2 0, "clean", "clean", + "clean", "cleaning", "cleaned", "is clean", diff --git a/libbuild2/operation.hxx b/libbuild2/operation.hxx index 2f88e88..ce3cd79 100644 --- a/libbuild2/operation.hxx +++ b/libbuild2/operation.hxx @@ -125,12 +125,14 @@ namespace build2 void (*meta_operation_post) (context&, const values&); // Optional prerequisite exclusion override callback. See include() for - // details. Note that it's not called for include_type::normal; + // details. Note that it's not called for include_type::normal without + // operation-specific override. // include_type (*include) (action, const target&, const prerequisite_member&, - include_type); + include_type, + lookup&); }; // Built-in meta-operations. @@ -194,6 +196,7 @@ namespace build2 const operation_id id; const operation_id outer_id; const char* name; + const char* var_name; // Operation variable or NULL if not used. // Name derivatives for diagnostics. Note that unlike meta-operations, // these can only be empty for the default operation (id 1), And @@ -309,10 +312,10 @@ namespace build2 // are represented as NULL pointers. Also, lookup out of bounds // is treated as a hole. // - template + template struct sparse_vector { - using base_type = vector; + using base_type = small_vector; using size_type = typename base_type::size_type; void @@ -348,8 +351,8 @@ namespace build2 base_type v_; }; - using meta_operations = sparse_vector; - using operations = sparse_vector; + using meta_operations = sparse_vector; + using operations = sparse_vector; } namespace butl diff --git a/libbuild2/target.cxx b/libbuild2/target.cxx index bc5dbba..7c2ee9a 100644 --- a/libbuild2/target.cxx +++ b/libbuild2/target.cxx @@ -545,36 +545,85 @@ namespace build2 include_impl (action a, const target& t, const prerequisite& p, - const target* m) + const target* m, + lookup* rl) { context& ctx (t.ctx); include_type r (include_type::normal); - // If var_clean is defined, then it takes precedence over include for - // the clean operation. + // @@ TODO doc. // - lookup l; - if (a.operation () == clean_id && (l = p.vars[ctx.var_clean])) - { - r = cast (l) ? include_type::normal : include_type::excluded; - } - else if (const string* v = cast_null (p.vars[ctx.var_include])) + if (const string* v = cast_null (p.vars[ctx.var_include])) { if (*v == "false") r = include_type::excluded; else if (*v == "adhoc") r = include_type::adhoc; else if (*v == "true") r = include_type::normal; else - fail << "invalid " << ctx.var_include->name << " variable value " + fail << "invalid " << *ctx.var_include << " variable value " << "'" << *v << "' specified for prerequisite " << p; } + // Handle operation-specific override. + // + lookup l; + optional r1; // Absent means something other than true|false. + + names storage; + names_view ns; + + if (r != include_type::excluded && ctx.current_ovar != nullptr) + { + if ((l = p.vars[*ctx.current_ovar])) + { + // Maybe we should optimize this for the common cases (bool, path, + // name)? But then again we don't expect many such overrides. + // + ns = reverse (*l, storage); + + if (ns.size () == 1) + { + const name& n (ns[0]); + + if (n.simple ()) + { + const string& v (n.value); + + if (v == "false") + r1 = false; + else if (v == "true") + r1 = true; + } + } + + if (r1) + { + if (!*r1) + r = include_type::excluded; + } + } + } + // Call the meta-operation override, if any (currently used by dist). // - if (r != include_type::normal) + if (r != include_type::normal || l) { if (auto f = ctx.current_mif->include) - r = f (a, t, prerequisite_member {p, m}, r); + r = f (a, t, prerequisite_member {p, m}, r, l); + } + + if (l) + { + if (rl != nullptr) + *rl = l; + else if (!r1) + { + // Note: we have to delay this until the meta-operation callback above + // had a chance to override it. + // + fail << "unrecognized " << *ctx.current_ovar << " variable value " + << "'" << ns << "' specified for prerequisite " << p; + } } return r; diff --git a/libbuild2/target.hxx b/libbuild2/target.hxx index 5eed0a5..32352c8 100644 --- a/libbuild2/target.hxx +++ b/libbuild2/target.hxx @@ -70,15 +70,19 @@ namespace build2 }; // List of prerequisites resolved to targets. Unless additional storage is - // needed, it can be used as just vector (which is what we + // needed, it can be treated as just vector (which is what we // used to have initially). // + // 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 variable) in other bits. + // struct prerequisite_target { using target_type = build2::target; prerequisite_target (const target_type* t, bool a = false, uintptr_t d = 0) - : target (t), adhoc (a), data (d) {} + : target (t), include (a ? 1 : 0), data (d) {} prerequisite_target (const target_type* t, include_type a, uintptr_t d = 0) : prerequisite_target (t, a == include_type::adhoc, d) {} @@ -87,8 +91,10 @@ namespace build2 operator const target_type* () const {return target;} const target_type* operator-> () const {return target;} + bool adhoc () const {return (include & 1) != 0;} + const target_type* target; - bool adhoc; // True if include=adhoc. + uintptr_t include; // First bit is 1 if include=adhoc. uintptr_t data; }; using prerequisite_targets = vector; @@ -892,13 +898,15 @@ namespace build2 // Helper for dealing with the prerequisite inclusion/exclusion (see // var_include in context.hxx). // + // If the lookup argument is not NULL, then it will be set to the operation- + // specific override, if present. Note that in this case the caller is + // expected to validate that the override value is valid (use the same + // diagnostics as in include() for consistency). + // // Note that the include(prerequisite_member) overload is also provided. // include_type - include (action, - const target&, - const prerequisite&, - const target* = nullptr); + include (action, const target&, const prerequisite&, lookup* = nullptr); // A "range" that presents the prerequisites of a group and one of // its members as one continuous sequence, or, in other words, as @@ -1111,11 +1119,10 @@ namespace build2 return os << pm.key (); } - inline include_type - include (action a, const target& t, const prerequisite_member& pm) - { - return include (a, t, pm.prerequisite, pm.member); - } + include_type + include (action, const target&, + const prerequisite_member&, + lookup* = nullptr); // A "range" that presents a sequence of prerequisites (e.g., from // group_prerequisites()) as a sequence of prerequisite_member's. For each diff --git a/libbuild2/target.ixx b/libbuild2/target.ixx index 05f9698..a5ad32b 100644 --- a/libbuild2/target.ixx +++ b/libbuild2/target.ixx @@ -338,15 +338,27 @@ namespace build2 // include() // LIBBUILD2_SYMEXPORT include_type - include_impl (action, const target&, const prerequisite&, const target*); + include_impl (action, const target&, + const prerequisite&, const target*, + lookup*); inline include_type - include (action a, const target& t, const prerequisite& p, const target* m) + include (action a, const target& t, const prerequisite& p, lookup* l) { // Most of the time no prerequisite-specific variables will be specified, // so let's optimize for that. // - return p.vars.empty () ? include_type (true) : include_impl (a, t, p, m); + return p.vars.empty () + ? include_type (true) + : include_impl (a, t, p, nullptr, l); + } + + inline include_type + include (action a, const target& t, const prerequisite_member& pm, lookup* l) + { + return pm.prerequisite.vars.empty () + ? include_type (true) + : include_impl (a, t, pm.prerequisite, pm.member, l); } // group_prerequisites diff --git a/libbuild2/test/operation.cxx b/libbuild2/test/operation.cxx index 841abb5..ff841a6 100644 --- a/libbuild2/test/operation.cxx +++ b/libbuild2/test/operation.cxx @@ -65,6 +65,7 @@ namespace build2 0, "test", "test", + "test", "testing", "tested", "has nothing to test", // We cannot "be tested". @@ -82,6 +83,7 @@ namespace build2 update_id, // Note: not update_for_test_id. test_id, op_update.name, + nullptr, // Outer operation variable is always used. op_update.name_do, op_update.name_doing, op_update.name_did, diff --git a/libbuild2/variable.cxx b/libbuild2/variable.cxx index 8ed9605..8a063f7 100644 --- a/libbuild2/variable.cxx +++ b/libbuild2/variable.cxx @@ -470,7 +470,7 @@ namespace build2 bool value_traits:: convert (const name& n, const name* r) { - if (r == nullptr && !n.pattern && n.simple () ) + if (r == nullptr && !n.pattern && n.simple ()) { const string& s (n.value); -- cgit v1.1