aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2022-02-24 10:03:43 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2022-02-24 10:03:43 +0200
commit294a883863fa7713edc5278adddf8f60a28c8a9f (patch)
treeaec212cacc941e217ac60ae906f1c92209c3e86b
parentc397402cba2f8bccaa9b8e63f7ae95f3540f54cd (diff)
-rw-r--r--libbuild2/action.hxx4
-rw-r--r--libbuild2/adhoc-rule-buildscript.cxx16
-rw-r--r--libbuild2/adhoc-rule-regex-pattern.cxx2
-rw-r--r--libbuild2/algorithm.cxx6
-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.cxx92
-rw-r--r--libbuild2/cc/windows-rpath.cxx4
-rw-r--r--libbuild2/context.cxx24
-rw-r--r--libbuild2/context.hxx15
-rw-r--r--libbuild2/dist/operation.cxx7
-rw-r--r--libbuild2/install/operation.cxx3
-rw-r--r--libbuild2/install/rule.cxx12
-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
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<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/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<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..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<string> (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<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..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<path> (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<path> (l).string () == "false")
{
l5 ([&]{trace << "ignoring " << *mt << " (not installable)";});
@@ -291,7 +291,7 @@ namespace build2
if (p.is_a<exe> ())
{
// 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<path> (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<path> (l).string () == "false")
{
l5 ([&]{trace << "ignoring " << *pt << " (not installable)";});
@@ -1033,7 +1033,7 @@ namespace build2
//
if (!tp.empty ())
{
- install_target (t, cast<path> (t["install"]), 1);
+ install_target (t, cast<path> (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<path> (t["install"]), 1);
+ r |= uninstall_target (t, cast<path> (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,
"<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..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<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.
+ //
+ 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<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 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<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 (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);