aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libbuild2/action.hxx4
-rw-r--r--libbuild2/adhoc-rule-buildscript.cxx16
-rw-r--r--libbuild2/algorithm.cxx68
-rw-r--r--libbuild2/algorithm.hxx14
-rw-r--r--libbuild2/bash/rule.cxx2
-rw-r--r--libbuild2/build/script/parser.cxx12
-rw-r--r--libbuild2/build/script/script.cxx2
-rw-r--r--libbuild2/cc/common.cxx2
-rw-r--r--libbuild2/cc/link-rule.cxx104
-rw-r--r--libbuild2/cc/windows-rpath.cxx4
-rw-r--r--libbuild2/context.cxx24
-rw-r--r--libbuild2/context.hxx36
-rw-r--r--libbuild2/dist/operation.cxx7
-rw-r--r--libbuild2/dyndep.cxx56
-rw-r--r--libbuild2/dyndep.hxx9
-rw-r--r--libbuild2/install/operation.cxx3
-rw-r--r--libbuild2/install/rule.cxx36
-rw-r--r--libbuild2/operation.cxx3
-rw-r--r--libbuild2/operation.hxx15
-rw-r--r--libbuild2/target.cxx73
-rw-r--r--libbuild2/target.hxx31
-rw-r--r--libbuild2/target.ixx18
-rw-r--r--libbuild2/test/operation.cxx2
-rw-r--r--libbuild2/variable.cxx2
24 files changed, 395 insertions, 148 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<uintptr_t> (p.target);
p.target = nullptr;
@@ -426,7 +426,7 @@ namespace build2
{
if (const target* pt =
(p.target != nullptr ? p.target :
- p.adhoc ? reinterpret_cast<target*> (p.data) :
+ p.adhoc () ? reinterpret_cast<target*> (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<target*> (p.data) :
+ p.adhoc () ? reinterpret_cast<target*> (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<target*> (p.data)
+ p.adhoc () ? reinterpret_cast<target*> (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<target*> (p.data) : nullptr))
+ p.adhoc () ? reinterpret_cast<target*> (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<target*> (p.data) : nullptr))
+ p.adhoc () ? reinterpret_cast<target*> (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<uintptr_t> (p.target);
p.target = nullptr;
diff --git a/libbuild2/algorithm.cxx b/libbuild2/algorithm.cxx
index 9b6fd4e..cbf0495 100644
--- a/libbuild2/algorithm.cxx
+++ b/libbuild2/algorithm.cxx
@@ -2057,6 +2057,68 @@ namespace build2
return t.executed_state (a);
}
+ bool
+ update_during_match (tracer& trace, action a, const target& t, timestamp ts)
+ {
+ assert (a == perform_update_id);
+
+ // Note: this function is used to make sure header dependencies are up to
+ // date (and which is where it originated).
+ //
+ // There would normally be a lot of headers for every source file (think
+ // all the system headers) and just calling execute_direct() on all of
+ // them can get expensive. At the same time, most of these headers are
+ // existing files that we will never be updating (again, system headers,
+ // for example) and the rule that will match them is the fallback
+ // file_rule. That rule has an optimization: it returns noop_recipe (which
+ // causes the target state to be automatically set to unchanged) if the
+ // file is known to be up to date. So we do the update "smartly".
+ //
+ const path_target* pt (t.is_a<path_target> ());
+
+ if (pt == nullptr)
+ ts = timestamp_unknown;
+
+ target_state os (t.matched_state (a));
+
+ if (os == target_state::unchanged)
+ {
+ if (ts == timestamp_unknown)
+ return false;
+ else
+ {
+ // We expect the timestamp to be known (i.e., existing file).
+ //
+ timestamp mt (pt->mtime ());
+ assert (mt != timestamp_unknown);
+ return mt > ts;
+ }
+ }
+ else
+ {
+ // We only want to return true if our call to execute() actually caused
+ // an update. In particular, the target could already have been in
+ // target_state::changed because of the dynamic dependency extraction
+ // run for some other target.
+ //
+ // @@ MT perf: so we are going to switch the phase and execute for
+ // any generated header.
+ //
+ phase_switch ps (t.ctx, run_phase::execute);
+ target_state ns (execute_direct (a, t));
+
+ if (ns != os && ns != target_state::unchanged)
+ {
+ l6 ([&]{trace << "updated " << t
+ << "; old state " << os
+ << "; new state " << ns;});
+ return true;
+ }
+ else
+ return ts != timestamp_unknown ? pt->newer (ts, ns) : false;
+ }
+ }
+
static inline void
blank_adhoc_member (const target*&)
{
@@ -2065,7 +2127,7 @@ namespace build2
static inline void
blank_adhoc_member (prerequisite_target& pt)
{
- if (pt.adhoc)
+ if (pt.adhoc ())
pt.target = nullptr;
}
@@ -2254,7 +2316,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 +2334,7 @@ namespace build2
}
}
- if (p.adhoc)
+ if (p.adhoc ())
p.target = nullptr; // Blank out.
else
{
diff --git a/libbuild2/algorithm.hxx b/libbuild2/algorithm.hxx
index 8a6eb65..c30680a 100644
--- a/libbuild2/algorithm.hxx
+++ b/libbuild2/algorithm.hxx
@@ -595,6 +595,20 @@ namespace build2
LIBBUILD2_SYMEXPORT target_state
execute_direct (action, const target&);
+ // Update the target during the match phase (by switching the phase and
+ // calling execute_direct()). Return true if the target has changed or, if
+ // the passed timestamp is not timestamp_unknown, it is older than the
+ // target.
+ //
+ // Note that such a target must still be updated normally during the execute
+ // phase in order to keep the dependency counts straight (at which point the
+ // target state/timestamp will be re-incorporated into the result).
+ //
+ LIBBUILD2_SYMEXPORT bool
+ update_during_match (tracer&,
+ action, const target&,
+ timestamp = timestamp_unknown);
+
// The default prerequisite execute implementation. Call execute_async() on
// each non-ignored (non-NULL) prerequisite target in a loop and then wait
// for their completion. Return target_state::changed if any of them were
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<bash> ())
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<target*> (p.data)
+ p.adhoc () ? reinterpret_cast<target*> (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<target*> (p.data) :
+ p.adhoc () ? reinterpret_cast<target*> (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<uintptr_t> (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..c993df6 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 (since we may not match). 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,68 @@ namespace build2
return a.operation () == clean_id && !pt.dir.sub (rs.out_path ());
};
+ bool update_match (false); // Have update during match.
+
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));
+ // Note that we have to recognize update=match for *(update), not just
+ // perform(update). But only actually update for perform(update).
+ //
+ lookup l; // The `update` variable value, if any.
+ include_type pi (
+ include (a, t, p, a.operation () == 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 ());
+
+ // 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<string> (l));
+
+ if (v == "match")
+ {
+ if (a == perform_update_id)
+ {
+ 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 on this pass.
+ // 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 +1167,45 @@ 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).
+ //
+ // Note also that we ignore the result and whether it renders us out of
+ // date, leaving it to the common execute logic in perform_update().
+ //
+ if (update_match)
+ {
+ for (prerequisite_target& pto: pts)
+ {
+ if ((pto.include & 2) != 0)
+ update_during_match (trace, a, *pto.target);
+ }
+ }
+
// Check if we have any binful utility libraries.
//
if (binless)
@@ -1432,6 +1510,7 @@ namespace build2
continue;
// New mark:
+ // 0 - already matched
// 1 - completion
// 2 - verification
//
@@ -1619,7 +1698,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 +1748,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 +1763,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<string> ("extension", v_t);
- var_clean = &vp.insert<bool> ("clean", v_t);
- var_backlink = &vp.insert<string> ("backlink", v_t);
- var_include = &vp.insert<string> ("include", v_q);
+ var_update = &vp.insert<string> ("update", v_q);
+ var_clean = &vp.insert<bool> ("clean", v_t);
+ var_backlink = &vp.insert<string> ("backlink", v_t);
+ var_include = &vp.insert<string> ("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..20098dc 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).
//
@@ -473,14 +488,19 @@ namespace build2
// Sometimes it may be desirable to apply exclusions only to specific
// operations. The initial idea was to extend this value to allow
// specifying the operation (e.g., clean@false). However, later we
- // realized that we could reuse the "operation variables" (clean, install,
- // test) with a more natural-looking result. Note that currently we only
- // recognize the built-in clean variable (for other variables we will need
- // some kind of registration in an operation-to-variable map, probably in
- // root scope). See also install::file_rule::filter().
- //
- // To query this value in rule implementations use the include() helpers
- // from <libbuild2/prerequisites.hxx>.
+ // realized that we could reuse the "operation-specific variables"
+ // (update, clean, install, test; see context::current_ovar) with a more
+ // natural-looking and composable result. Plus, this allows for
+ // operation-specific "modifiers", for example, "unmatch" and "update
+ // during match" logic for update (see var_update for details) or
+ // requiring explicit install=true to install exe{} prerequisites (see
+ // install::file_rule::filter()).
+ //
+ // To query this value and its operation-specific override if any, the
+ // rule implementations use the include() helper.
+ //
+ // Note that there are also related (but quite different) for_<operation>
+ // variables for operations that act as outer (e.g., test, install).
//
// [string] prereq visibility
//
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/dyndep.cxx b/libbuild2/dyndep.cxx
index f1fc5ec..47c6396 100644
--- a/libbuild2/dyndep.cxx
+++ b/libbuild2/dyndep.cxx
@@ -18,61 +18,7 @@ namespace build2
bool dyndep_rule::
update (tracer& trace, action a, const target& t, timestamp ts)
{
- // In particular, this function is used to make sure header dependencies
- // are up to date.
- //
- // There would normally be a lot of headers for every source file (think
- // all the system headers) and just calling execute_direct() on all of
- // them can get expensive. At the same time, most of these headers are
- // existing files that we will never be updating (again, system headers,
- // for example) and the rule that will match them is the fallback
- // file_rule. That rule has an optimization: it returns noop_recipe (which
- // causes the target state to be automatically set to unchanged) if the
- // file is known to be up to date. So we do the update "smartly".
- //
- const path_target* pt (t.is_a<path_target> ());
-
- if (pt == nullptr)
- ts = timestamp_unknown;
-
- target_state os (t.matched_state (a));
-
- if (os == target_state::unchanged)
- {
- if (ts == timestamp_unknown)
- return false;
- else
- {
- // We expect the timestamp to be known (i.e., existing file).
- //
- timestamp mt (pt->mtime ());
- assert (mt != timestamp_unknown);
- return mt > ts;
- }
- }
- else
- {
- // We only want to return true if our call to execute() actually caused
- // an update. In particular, the target could already have been in
- // target_state::changed because of the dynamic dependency extraction
- // run for some other target.
- //
- // @@ MT perf: so we are going to switch the phase and execute for
- // any generated header.
- //
- phase_switch ps (t.ctx, run_phase::execute);
- target_state ns (execute_direct (a, t));
-
- if (ns != os && ns != target_state::unchanged)
- {
- l6 ([&]{trace << "updated " << t
- << "; old state " << os
- << "; new state " << ns;});
- return true;
- }
- else
- return ts != timestamp_unknown ? pt->newer (ts, ns) : false;
- }
+ return update_during_match (trace, a, t, ts);
}
optional<bool> dyndep_rule::
diff --git a/libbuild2/dyndep.hxx b/libbuild2/dyndep.hxx
index 257ef7b..6632eb6 100644
--- a/libbuild2/dyndep.hxx
+++ b/libbuild2/dyndep.hxx
@@ -22,9 +22,12 @@ namespace build2
class LIBBUILD2_SYMEXPORT dyndep_rule
{
public:
- // Update the target during the match phase. Return true if it has changed
- // or if the passed timestamp is not timestamp_unknown and is older than
- // the target.
+ // Update the target during the match phase. Return true if the target has
+ // changed or, if the passed timestamp is not timestamp_unknown, it is
+ // older than the target.
+ //
+ // Note that such a target must still be updated during the execute phase
+ // in order to keep the dependency counts straight.
//
static bool
update (tracer&, action, const target&, timestamp);
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..db9c64a 100644
--- a/libbuild2/install/rule.cxx
+++ b/libbuild2/install/rule.cxx
@@ -74,6 +74,8 @@ namespace build2
{
tracer trace ("install::alias_rule::apply");
+ context& ctx (t.ctx);
+
// Pass-through to our installable prerequisites.
//
// @@ Shouldn't we do match in parallel (here and below)?
@@ -125,7 +127,7 @@ namespace build2
//
// Note: not the same as lookup_install() above.
//
- auto l ((*pt)["install"]);
+ auto l ((*pt)[ctx.current_ovar]); // "install"
if (l && cast<path> (l).string () == "false")
{
l5 ([&]{trace << "ignoring " << *pt << " (not installable)";});
@@ -207,6 +209,8 @@ namespace build2
{
tracer trace ("install::group_rule::apply");
+ context& ctx (t.ctx);
+
// Resolve group members.
//
// Remember that we are called twice: first during update for install
@@ -245,7 +249,7 @@ namespace build2
//
// Note: not the same as lookup_install() above.
//
- auto l ((*mt)["install"]);
+ auto l ((*mt)[ctx.current_ovar]); // "install"
if (l && cast<path> (l).string () == "false")
{
l5 ([&]{trace << "ignoring " << *mt << " (not installable)";});
@@ -290,11 +294,13 @@ namespace build2
{
if (p.is_a<exe> ())
{
- // Feels like one day this should be unified with include (see
- // context::var_include).
+ // Note that while include() checks for install=false, here we need to
+ // check for explicit install=true. We could have re-used the lookup
+ // performed by include(), but then we would have had to drag it
+ // through and also diagnose any invalid values.
//
if (p.vars.empty () ||
- cast_empty<path> (p.vars["install"]).string () != "true")
+ cast_empty<path> (p.vars[t.ctx.current_ovar]).string () != "true")
return nullptr;
}
@@ -314,6 +320,8 @@ namespace build2
{
tracer trace ("install::file_rule::apply");
+ context& ctx (t.ctx);
+
// Note that we are called both as the outer part during the update-for-
// un/install pre-operation and as the inner part during the un/install
// operation itself.
@@ -381,7 +389,7 @@ namespace build2
//
// Note: not the same as lookup_install() above.
//
- auto l ((*pt)["install"]);
+ auto l ((*pt)[ctx.current_ovar]); // "install"
if (l && cast<path> (l).string () == "false")
{
l5 ([&]{trace << "ignoring " << *pt << " (not installable)";});
@@ -936,6 +944,8 @@ namespace build2
target_state file_rule::
perform_install (action a, const target& xt) const
{
+ context& ctx (xt.ctx);
+
const file& t (xt.as<file> ());
const path& tp (t.path ());
@@ -1033,7 +1043,7 @@ namespace build2
//
if (!tp.empty ())
{
- install_target (t, cast<path> (t["install"]), 1);
+ install_target (t, cast<path> (t[ctx.current_ovar]), 1); // "install"
r |= target_state::changed;
}
@@ -1160,6 +1170,8 @@ namespace build2
const path& name,
uint16_t verbosity)
{
+ context& ctx (rs.ctx);
+
assert (t != nullptr || !name.empty ());
path f (chroot_path (rs, base.dir) /
(name.empty () ? t->path ().leaf () : name));
@@ -1196,7 +1208,7 @@ namespace build2
if (verb >= verbosity && verb >= 2)
text << "rm " << relf;
- if (!rs.ctx.dry_run)
+ if (!ctx.dry_run)
{
try
{
@@ -1225,7 +1237,7 @@ namespace build2
if (verb >= verbosity && verb >= 2)
print_process (args);
- if (!rs.ctx.dry_run)
+ if (!ctx.dry_run)
run (pp, args);
}
@@ -1235,6 +1247,8 @@ namespace build2
target_state file_rule::
perform_uninstall (action a, const target& xt) const
{
+ context& ctx (xt.ctx);
+
const file& t (xt.as<file> ());
const path& tp (t.path ());
@@ -1298,7 +1312,9 @@ namespace build2
target_state r (target_state::unchanged);
if (!tp.empty ())
- r |= uninstall_target (t, cast<path> (t["install"]), 1);
+ r |= uninstall_target (t,
+ cast<path> (t[ctx.current_ovar]), // "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,
"<default>",
+ 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 <typename T>
+ template <typename T, size_t N>
struct sparse_vector
{
- using base_type = vector<T*>;
+ using base_type = small_vector<T*, N>;
using size_type = typename base_type::size_type;
void
@@ -348,8 +351,8 @@ namespace build2
base_type v_;
};
- using meta_operations = sparse_vector<const meta_operation_info>;
- using operations = sparse_vector<const operation_info>;
+ using meta_operations = sparse_vector<const meta_operation_info, 8>;
+ using operations = sparse_vector<const operation_info, 10>;
}
namespace butl
diff --git a/libbuild2/target.cxx b/libbuild2/target.cxx
index bc5dbba..f829cca 100644
--- a/libbuild2/target.cxx
+++ b/libbuild2/target.cxx
@@ -541,40 +541,87 @@ namespace build2
// include()
//
+ // See var_include documentation for details on what's going on here.
+ //
include_type
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.
- //
- lookup l;
- if (a.operation () == clean_id && (l = p.vars[ctx.var_clean]))
- {
- r = cast<bool> (l) ? include_type::normal : include_type::excluded;
- }
- else if (const string* v = cast_null<string> (p.vars[ctx.var_include]))
+ if (const string* v = cast_null<string> (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<bool> 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. Plus
+ // will complicate the diagnostics below.
+ //
+ 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 && !*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..efc3291 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<const target*> (which is what we
+ // needed, it can be treated as just vector<const target*> (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 override) 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<prerequisite_target>;
@@ -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 (note: 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<bool>::
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);