aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/algorithm.hxx
diff options
context:
space:
mode:
Diffstat (limited to 'libbuild2/algorithm.hxx')
-rw-r--r--libbuild2/algorithm.hxx573
1 files changed, 444 insertions, 129 deletions
diff --git a/libbuild2/algorithm.hxx b/libbuild2/algorithm.hxx
index 984b786..a4feaea 100644
--- a/libbuild2/algorithm.hxx
+++ b/libbuild2/algorithm.hxx
@@ -17,7 +17,7 @@
namespace build2
{
// The default prerequisite search implementation. It first calls the
- // prerequisite-type-specific search function. If that doesn't yeld
+ // prerequisite-type-specific search function. If that doesn't yield
// anything, it creates a new target.
//
LIBBUILD2_SYMEXPORT const target&
@@ -45,20 +45,32 @@ namespace build2
LIBBUILD2_SYMEXPORT pair<target&, ulock>
search_locked (const target&, const prerequisite_key&);
- // Note that unlike the above version, this one can be called during the
- // load and execute phases.
+ // As above but this one can be called during the load and execute phases.
//
LIBBUILD2_SYMEXPORT const target*
search_existing (context&, const prerequisite_key&);
+ // First search for an existing target and if that doesn't yield anything,
+ // creates a new target, bypassing any prerequisite-type-specific search.
+ // Can be called during the load and match phases but only on project-
+ // unqualified prerequisites. This version is suitable for cases where you
+ // know the target is in out and cannot be possibly found in src.
+ //
+ LIBBUILD2_SYMEXPORT const target&
+ search_new (context&, const prerequisite_key&);
+
+ // As above but return the lock if the target was newly created.
+ //
+ LIBBUILD2_SYMEXPORT pair<target&, ulock>
+ search_new_locked (context&, const prerequisite_key&);
+
// Uniform search interface for prerequisite/prerequisite_member.
//
inline const target&
search (const target& t, const prerequisite_member& p) {return p.search (t);}
- // As above but override the target type. Useful for searching for
- // target group members where we need to search for a different
- // target type.
+ // As above but override the target type. Useful for searching for target
+ // group members where we need to search for a different target type.
//
const target&
search (const target&, const target_type&, const prerequisite_key&);
@@ -66,6 +78,15 @@ namespace build2
pair<target&, ulock>
search_locked (const target&, const target_type&, const prerequisite_key&);
+ const target*
+ search_existing (context&, const target_type&, const prerequisite_key&);
+
+ const target&
+ search_new (context&, const target_type&, const prerequisite_key&);
+
+ pair<target&, ulock>
+ search_new_locked (context&, const target_type&, const prerequisite_key&);
+
// As above but specify the prerequisite to search as individual key
// components. Scope can be NULL if the directory is absolute.
//
@@ -85,8 +106,8 @@ namespace build2
const dir_path& dir,
const dir_path& out,
const string& name,
- const string* ext = nullptr, // NULL means unspecified.
- const scope* = nullptr); // NULL means dir is absolute.
+ const string* ext = nullptr,
+ const scope* = nullptr);
const target*
search_existing (context&,
@@ -98,6 +119,24 @@ namespace build2
const scope* = nullptr,
const optional<project_name>& proj = nullopt);
+ const target&
+ search_new (context&,
+ const target_type&,
+ const dir_path& dir,
+ const dir_path& out,
+ const string& name,
+ const string* ext = nullptr,
+ const scope* = nullptr);
+
+ pair<target&, ulock>
+ search_new_locked (context&,
+ const target_type&,
+ const dir_path& dir,
+ const dir_path& out,
+ const string& name,
+ const string* ext = nullptr,
+ const scope* = nullptr);
+
// As above but specify the target type as template argument.
//
template <typename T>
@@ -109,6 +148,15 @@ namespace build2
const string* ext = nullptr,
const scope* = nullptr);
+ template <typename T>
+ const T*
+ search_existing (context&,
+ const dir_path& dir,
+ const dir_path& out,
+ const string& name,
+ const string* ext = nullptr,
+ const scope* = nullptr);
+
// Search for a target identified by the name. The semantics is "as if" we
// first created a prerequisite based on this name in exactly the same way
// as the parser would and then searched based on this prerequisite. If the
@@ -116,15 +164,13 @@ namespace build2
// argument.
//
LIBBUILD2_SYMEXPORT const target&
- search (const target&, name, const scope&, const target_type* = nullptr);
+ search (const target&, name&&, const scope&, const target_type* = nullptr);
- // Return NULL for unknown target types. Note that unlike the above version,
- // these ones can be called during the load and execute phases.
+ // Note: returns NULL for unknown target types. Note also that unlike the
+ // above version, these can be called during the load and execute phases.
//
LIBBUILD2_SYMEXPORT const target*
- search_existing (const name&,
- const scope&,
- const dir_path& out = dir_path ());
+ search_existing (const name&, const scope&, const dir_path& out = dir_path ());
LIBBUILD2_SYMEXPORT const target*
search_existing (const names&, const scope&);
@@ -143,17 +189,20 @@ namespace build2
action_type action;
target_type* target = nullptr;
size_t offset = 0;
+ bool first;
explicit operator bool () const {return target != nullptr;}
+ // Note: achieved offset is preserved.
+ //
void
unlock ();
// Movable-only type with move-assignment only to NULL lock.
//
target_lock () = default;
- target_lock (target_lock&&);
- target_lock& operator= (target_lock&&);
+ target_lock (target_lock&&) noexcept;
+ target_lock& operator= (target_lock&&) noexcept;
target_lock (const target_lock&) = delete;
target_lock& operator= (const target_lock&) = delete;
@@ -161,13 +210,14 @@ namespace build2
// Implementation details.
//
~target_lock ();
- target_lock (action_type, target_type*, size_t);
+ target_lock (action_type, target_type*, size_t, bool);
struct data
{
action_type action;
target_type* target;
size_t offset;
+ bool first;
};
data
@@ -205,10 +255,10 @@ namespace build2
// If the target is already applied (for this action) or executed, then no
// lock is acquired. Otherwise, unless matched is true, the target must not
- // be matched but not yet applied for this action (and if that's the case
- // and matched is true, then you get a locked target that you should
- // probably check for consistency, for exmaple, by comparing the matched
- // rule).
+ // be in the matched but not yet applied state for this action (and if
+ // that's the case and matched is true, then you get a locked target that
+ // you should probably check for consistency, for example, by comparing the
+ // matched rule).
//
// @@ MT fuzzy: what if it is already in the desired state, why assert?
// Currently we only use it with match_recipe/rule() and if it is matched
@@ -224,21 +274,27 @@ namespace build2
//
// Note that here and in find_adhoc_member() below (as well as in
// perform_clean_extra()) we use target type (as opposed to, say, type and
- // name) as the member's identity. This fits our current needs where every
+ // name) as the member's identity. This fits common needs where every
// (rule-managed) ad hoc member has a unique target type and we have no need
// for multiple members of the same type. This also allows us to support
// things like changing the ad hoc member name by declaring it in a
- // buildfile.
+ // buildfile. However, if this semantics is not appropriate, use the
+ // add_adhoc_member_identity() version below.
+ //
+ // Note that the current implementation asserts if the member target already
+ // exists but is not already a member.
//
LIBBUILD2_SYMEXPORT target&
add_adhoc_member (target&,
const target_type&,
dir_path dir,
dir_path out,
- string name);
+ string name,
+ optional<string> ext);
// If the extension is specified then it is added to the member's target
- // name.
+ // name as a second-level extension (the first-level extension, if any,
+ // comes from the target type).
//
target&
add_adhoc_member (target&, const target_type&, const char* ext = nullptr);
@@ -257,6 +313,24 @@ namespace build2
return add_adhoc_member<T> (g, T::static_type, e);
}
+ // Add an ad hoc member using the member identity (as opposed to only its
+ // type as in add_adhoc_member() above) to suppress diplicates. See also
+ // dyndep::inject_adhoc_group_member().
+ //
+ // Return the member target as well as an indication of whether it was added
+ // or was already a member. Fail if the member target already exists but is
+ // not a member since it's not possible to make it a member in an MT-safe
+ // manner.
+ //
+ LIBBUILD2_SYMEXPORT pair<target&, bool>
+ add_adhoc_member_identity (target&,
+ const target_type&,
+ dir_path dir,
+ dir_path out,
+ string name,
+ optional<string> ext,
+ const location& = location ());
+
// Find an ad hoc member of the specified target type returning NULL if not
// found.
//
@@ -295,78 +369,171 @@ namespace build2
}
// Match and apply a rule to the action/target with ambiguity detection.
- // Increment the target's dependents count, which means that you should call
- // this function with the intent to also call execute(). Return the target
- // state translating target_state::failed to the failed exception unless
- // instructed otherwise.
- //
- // The try_match() version doesn't issue diagnostics if there is no rule
- // match (but fails as match() for all other errors, like rule ambiguity,
- // inability to apply, etc). The first half of the result indicated whether
- // there was a rule match.
- //
- // The unmatch argument allows optimizations that avoid calling execute().
+ // This is the synchrounous match implementation that waits for completion
+ // if the target is already being matched. Increment the target's dependents
+ // count, which means that you should call this function with the intent to
+ // also call execute*(). Translating target_state::failed to the failed
+ // exception unless instructed otherwise.
+ //
+ // The try_match_sync() version doesn't issue diagnostics if there is no
+ // rule match (but fails as match_sync() for all other errors, like rule
+ // ambiguity, inability to apply, etc). The first half of the result
+ // indicated whether there was a rule match.
+ //
+ // The unmatch argument allows optimizations that avoid calling execute*().
// If it is unmatch::unchanged then only unmatch the target if it is known
// to be unchanged after match. If it is unmatch::safe, then unmatch the
// target if it is safe (this includes unchanged or if we know that someone
// else will execute this target). Return true in first half of the pair if
- // unmatch succeeded. Always throw if failed.
+ // unmatch succeeded. Always throw if failed. Note that unmatching may not
+ // play well with options -- if unmatch succeeds, the options that have been
+ // passed to match will not be cleared.
//
enum class unmatch {none, unchanged, safe};
target_state
- match (action, const target&, bool fail = true);
+ match_sync (action, const target&,
+ uint64_t options = match_extra::all_options,
+ bool fail = true);
pair<bool, target_state>
- try_match (action, const target&, bool fail = true);
+ try_match_sync (action, const target&,
+ uint64_t options = match_extra::all_options,
+ bool fail = true);
pair<bool, target_state>
- match (action, const target&, unmatch);
+ match_sync (action, const target&,
+ unmatch,
+ uint64_t options = match_extra::all_options);
+
+ // As above but only match the target (unless already matched) without
+ // applying the match (which is normally done with match_sync()). You will
+ // most likely regret using this function.
+ //
+ LIBBUILD2_SYMEXPORT void
+ match_only_sync (action, const target&,
+ uint64_t options = match_extra::all_options);
// Start asynchronous match. Return target_state::postponed if the
- // asynchrounous operation has been started and target_state::busy if the
- // target has already been busy. Regardless of the result, match() must be
- // called in order to complete the operation (except target_state::failed).
+ // asynchronous operation has been started and target_state::busy if the
+ // target has already been busy. Regardless of the result, match_complete()
+ // must be called in order to complete the operation (except if the result
+ // is target_state::failed), which has the result semantics of match_sync().
//
// If fail is false, then return target_state::failed if the target match
// failed. Otherwise, throw the failed exception if keep_going is false and
// return target_state::failed otherwise.
//
+ // Note: same options must be passed to match_async() and match_complete().
+ //
target_state
match_async (action, const target&,
size_t start_count, atomic_count& task_count,
+ uint64_t options = match_extra::all_options,
bool fail = true);
+ target_state
+ match_complete (action, const target&,
+ uint64_t options = match_extra::all_options,
+ bool fail = true);
+
+ pair<bool, target_state>
+ match_complete (action, const target&,
+ unmatch,
+ uint64_t options = match_extra::all_options);
+
+ // As above but without incrementing the target's dependents count. Should
+ // be executed with execute_direct_*().
+ //
+ // For async, call match_async() followed by match_direct_complete().
+ //
+ target_state
+ match_direct_sync (action, const target&,
+ uint64_t options = match_extra::all_options,
+ bool fail = true);
+
+ target_state
+ match_direct_complete (action, const target&,
+ uint64_t options = match_extra::all_options,
+ bool fail = true);
+
// Apply the specified recipe directly and without incrementing the
- // dependency counts. The target must be locked.
+ // dependency counts. The target must be locked (and it remains locked
+ // after this function returns).
+ //
+ // Note that there will be no way to rematch on options change (since there
+ // is no rule), so passing anything other than all_options is most likely a
+ // bad idea. Passing 0 for options is illegal.
//
void
- match_recipe (target_lock&, recipe);
+ match_recipe (target_lock&,
+ recipe,
+ uint64_t options = match_extra::all_options);
// Match (but do not apply) the specified rule directly and without
- // incrementing the dependency counts. The target must be locked.
+ // incrementing the dependency counts. The target must be locked (and it
+ // remains locked after this function returns).
//
void
- match_rule (target_lock&, const rule_match&);
+ match_rule (target_lock&,
+ const rule_match&,
+ uint64_t options = match_extra::all_options);
// Match a "delegate rule" from withing another rules' apply() function
// avoiding recursive matches (thus the third argument). Unless try_match is
// true, fail if no rule is found. Otherwise return empty recipe. Note that
- // unlike match(), this function does not increment the dependents count and
- // the two rules must coordinate who is using the target's data pad and/or
- // prerequisite_targets. See also the companion execute_delegate().
+ // unlike match(), this function does not increment the dependents count.
+ // See also the companion execute_delegate().
//
recipe
- match_delegate (action, target&, const rule&, bool try_match = false);
+ match_delegate (action, target&,
+ const rule&,
+ uint64_t options = match_extra::all_options,
+ bool try_match = false);
+
+ // Incrementing the dependency counts of the specified target.
+ //
+ void
+ match_inc_dependents (action, const target&);
- // Match a rule for the inner operation from withing the outer rule's
- // apply() function. See also the companion execute_inner().
+ // Match (synchronously) a rule for the inner operation from withing the
+ // outer rule's apply() function. See also the companion execute_inner()
+ // and inner_recipe.
//
target_state
- match_inner (action, const target&);
+ match_inner (action, const target&,
+ uint64_t options = match_extra::all_options);
pair<bool, target_state>
- match_inner (action, const target&, unmatch);
+ match_inner (action, const target&,
+ unmatch,
+ uint64_t options = match_extra::all_options);
+
+ // Re-match with new options a target that has already been matched with one
+ // of the match_*() functions. Note that natually you cannot rematch a
+ // target that you have unmatched.
+ //
+ // Note also that there is no way to check if the rematch is unnecessary
+ // (i.e., because the target is already matched with this option) because
+ // that would require MT-safety considerations (since there could be a
+ // concurrent rematch). Instead, you should rematch unconditionally and if
+ // the option is already present, it will be a cheap noop.
+ //
+ target_state
+ rematch_sync (action, const target&,
+ uint64_t options,
+ bool fail = true);
+
+ target_state
+ rematch_async (action, const target&,
+ size_t start_count, atomic_count& task_count,
+ uint64_t options,
+ bool fail = true);
+
+ target_state
+ rematch_complete (action, const target&,
+ uint64_t options,
+ bool fail = true);
// The standard prerequisite search and match implementations. They call
// search() (unless a custom is provided) and then match() (unless custom
@@ -375,6 +542,18 @@ namespace build2
// the target is a member of a group, then first do this to the group's
// prerequisites.
//
+ // Regarding clean, it may seem more natural to only clean prerequisites
+ // that are in the same base rather than root scope. While it's often true
+ // for simple projects, in more complex cases it's not unusual to have
+ // common intermediate build results (object files, utility libraries, etc)
+ // reside in the parent and/or sibling directories. With such arrangements,
+ // cleaning only in base (even from the project root) may leave such
+ // intermediate build results laying around (since there is no reason to
+ // list them as prerequisites of any directory aliases). So we clean in the
+ // root scope by default but any target-prerequisite relationship can be
+ // marked not to trigger a clean with the clean=false prerequisite-specific
+ // value (see the include variable for details).
+ //
using match_search = function<
prerequisite_target (action,
const target&,
@@ -384,10 +563,23 @@ namespace build2
void
match_prerequisites (action, target&, const match_search& = nullptr);
+ // As above but only do search. The match part can be performed later, for
+ // example, with the match_members() function below. The typical call
+ // sequence would be:
+ //
+ // inject_fsdir (a, t, false /* match */);
+ // search_prerequisite_members (a, t); // Potentially with filter.
+ // pattern->apply_prerequisites (a, t, bs, me); // If ad hoc pattern.
+ // <dependency synthesis> // Optional.
+ // match_members (a, t, t.prerequisite_targets[a]);
+ //
+ void
+ search_prerequisites (action, target&, const match_search& = nullptr);
+
// As above but go into group members.
//
- // Note that if we cleaning, this function doesn't go into group members, as
- // an optimization (the group should clean everything up).
+ // Note that if we are cleaning, this function doesn't go into group
+ // members, as an optimization (the group should clean everything up).
//
using match_search_member = function<
prerequisite_target (action,
@@ -399,39 +591,48 @@ namespace build2
match_prerequisite_members (action, target&,
const match_search_member& = nullptr);
+ void
+ search_prerequisite_members (action, target&,
+ const match_search_member& = nullptr);
+
// As above but omit prerequisites that are not in the specified scope.
//
void
match_prerequisites (action, target&, const scope&);
void
+ search_prerequisites (action, target&, const scope&);
+
+ void
match_prerequisite_members (action, target&, const scope&);
+ void
+ search_prerequisite_members (action, target&, const scope&);
+
// Match (already searched) members of a group or similar prerequisite-like
// dependencies. Similar in semantics to match_prerequisites(). Any marked
// target pointers are skipped.
//
- // T can only be const target* or prerequisite_target.
- //
- template <typename T>
- void
- match_members (action, target&, T const*, size_t);
+ LIBBUILD2_SYMEXPORT void
+ match_members (action, const target&, const target* const*, size_t);
template <size_t N>
inline void
- match_members (action a, target& t, const target* (&ts)[N])
+ match_members (action a, const target& t, const target* (&ts)[N])
{
match_members (a, t, ts, N);
}
- inline void
- match_members (action a,
- target& t,
- prerequisite_targets& ts,
- size_t start = 0)
- {
- match_members (a, t, ts.data () + start, ts.size () - start);
- }
+ // As above plus if the include mask (first) and value (second) are
+ // specified, then only match prerequisites that satisfy the
+ // ((prerequisite_target::include & mask) == value) condition.
+ //
+ LIBBUILD2_SYMEXPORT void
+ match_members (action,
+ const target&,
+ prerequisite_targets&,
+ size_t start = 0,
+ pair<uintptr_t, uintptr_t> include = {0, 0});
// Unless already known, match, and, if necessary, execute the group in
// order to resolve its members list. Note that even after that the member's
@@ -461,8 +662,9 @@ namespace build2
resolve_group (action, const target&);
// Inject a target as a "prerequisite target" (note: not a prerequisite) of
- // another target. Specifically, first match the prerequisite target and
- // then add it to the back of the dependent target's prerequisite_targets.
+ // another target. Specifically, match (synchronously) the prerequisite
+ // target and then add it to the back of the dependent target's
+ // prerequisite_targets.
//
void
inject (action, target&, const target& prereq);
@@ -470,60 +672,78 @@ namespace build2
// Inject dependency on the target's directory fsdir{}, unless it is in the
// src tree or is outside of any project (say, for example, an installation
// directory). If the parent argument is true, then inject the parent
- // directory of a target that is itself a directory (name is empty). Return
- // the injected target or NULL. Normally this function is called from the
- // rule's apply() function.
+ // directory of a target that is itself a directory (name is empty). Match
+ // unless match is false and return the injected target or NULL. Normally
+ // this function is called from the rule's apply() function.
+ //
+ // The match=false semantics is useful when you wish to first collect all
+ // the prerequisites targets and then match them all as a separate step, for
+ // example, with match_members().
+ //
+ // As an extension, unless prereq is false, this function will also search
+ // for an existing fsdir{} prerequisite for the directory and if one exists,
+ // return that (even if the target is in the src tree). In this case, the
+ // injected fsdir{} (if any) must be the first prerequisite in this target's
+ // prerequisite_targets, which is relied upon by the match_prerequisite*()
+ // family of functons to suppress the duplicate addition.
//
- // As an extension, this function will also search for an existing fsdir{}
- // prerequisite for the directory and if one exists, return that (even if
- // the target is in src tree). This can be used, for example, to place
- // output into an otherwise non-existent directory.
+ // Note that the explicit fsdir{} prerquiste is used to place output into an
+ // otherwise non-existent (in src) directory.
//
LIBBUILD2_SYMEXPORT const fsdir*
- inject_fsdir (action, target&, bool parent = true);
+ inject_fsdir (action, target&,
+ bool match = true,
+ bool prereq = true,
+ bool parent = true);
- // Execute the action on target, assuming a rule has been matched and the
- // recipe for this action has been set. This is the synchrounous executor
- // implementation (but may still return target_state::busy if the target
- // is already being executed). Decrements the dependents count.
+ // As above, but match the injected fsdir{} target directly (that is,
+ // without incrementing the dependency counts).
//
- // Note: does not translate target_state::failed to the failed exception.
- //
- target_state
- execute (action, const target&);
+ LIBBUILD2_SYMEXPORT const fsdir*
+ inject_fsdir_direct (action, target&, bool prereq = true, bool parent = true);
- // As above but wait for completion if the target is busy and translate
- // target_state::failed to the failed exception.
+ // Execute the action on target, assuming a rule has been matched and the
+ // recipe for this action has been set. This is the synchrounous executor
+ // implementation that waits for completion if the target is already being
+ // executed. Translate target_state::failed to the failed exception unless
+ // fail is false.
//
target_state
- execute_wait (action, const target&);
+ execute_sync (action, const target&, bool fail = true);
// As above but start asynchronous execution. Return target_state::unknown
// if the asynchrounous execution has been started and target_state::busy if
// the target has already been busy.
//
- // If fail is false, then return target_state::failed if the target match
- // failed. Otherwise, throw the failed exception if keep_going is false and
- // return target_state::failed otherwise.
+ // If fail is false, then return target_state::failed if the target
+ // execution failed. Otherwise, throw the failed exception if keep_going is
+ // false and return target_state::failed otherwise. Regardless of the
+ // result, execute_complete() must be called in order to complete the
+ // operation (except if the result is target_state::failed), which has the
+ // result semantics of execute_sync().
//
target_state
execute_async (action, const target&,
size_t start_count, atomic_count& task_count,
bool fail = true);
- // Execute the recipe obtained with match_delegate(). Note that the target's
- // state is neither checked nor updated by this function. In other words,
- // the appropriate usage is to call this function from another recipe and to
- // factor the obtained state into the one returned.
+ target_state
+ execute_complete (action, const target&);
+
+ // Execute (synchronously) the recipe obtained with match_delegate(). Note
+ // that the target's state is neither checked nor updated by this function.
+ // In other words, the appropriate usage is to call this function from
+ // another recipe and to factor the obtained state into the one returned.
//
target_state
execute_delegate (const recipe&, action, const target&);
- // Execute the inner operation matched with match_inner(). Note that the
- // returned target state is for the inner operation. The appropriate usage
- // is to call this function from the outer operation's recipe and to factor
- // the obtained state into the one returned (similar to how we do it for
- // prerequisites).
+ // Execute (synchronously) the inner operation matched with match_inner().
+ // Note that the returned target state is for the inner operation. The
+ // appropriate usage is to call this function from the outer operation's
+ // recipe and to factor the obtained state into the one returned (similar to
+ // how we do it for prerequisites). Or, if factoring is not needed, simply
+ // return inner_recipe as outer recipe.
//
// Note: waits for the completion if the target is busy and translates
// target_state::failed to the failed exception.
@@ -536,11 +756,63 @@ namespace build2
// relationship (so no dependents count is decremented) and execution order
// (so this function never returns the postponed target state).
//
- // Note: waits for the completion if the target is busy and translates
- // target_state::failed to the failed exception.
+ // The first version waits for the completion if the target is busy and
+ // translates target_state::failed to the failed exception.
//
- LIBBUILD2_SYMEXPORT target_state
- execute_direct (action, const target&);
+ target_state
+ execute_direct_sync (action, const target&, bool fail = true);
+
+ target_state
+ execute_direct_async (action, const target&,
+ size_t start_count, atomic_count& task_count,
+ bool fail = true);
+
+ // 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). Unless
+ // it was matched direct.
+ //
+ LIBBUILD2_SYMEXPORT bool
+ update_during_match (tracer&,
+ action, const target&,
+ timestamp = timestamp_unknown);
+
+ // As above, but update all the targets in prerequisite_targets that have
+ // the specified mask in prerequisite_target::include. Return true if any of
+ // them have changed. If mask is 0, then update all the targets.
+ //
+ // Note that this function spoils prerequisite_target::data (which is used
+ // for temporary storage). But it resets data to 0 once done.
+ //
+ LIBBUILD2_SYMEXPORT bool
+ update_during_match_prerequisites (
+ tracer&,
+ action, target&,
+ uintptr_t mask = prerequisite_target::include_udm);
+
+ // Equivalent functions for clean. Note that if possible you should leave
+ // cleaning to normal execute and these functions should only be used in
+ // special cases where this is not possible.
+ //
+ // Note also that neither function should be called on fsdir{} since it's
+ // hard to guarantee such an execution won't be too early (see the
+ // implementation for details). If you do need to clean fsdir{} during
+ // match, use fsdir_rule::perform_clean_direct() instead.
+ //
+ LIBBUILD2_SYMEXPORT bool
+ clean_during_match (tracer&,
+ action, const target&);
+
+ LIBBUILD2_SYMEXPORT bool
+ clean_during_match_prerequisites (
+ tracer&,
+ action, target&,
+ uintptr_t mask = prerequisite_target::include_udm);
// The default prerequisite execute implementation. Call execute_async() on
// each non-ignored (non-NULL) prerequisite target in a loop and then wait
@@ -603,8 +875,8 @@ namespace build2
// case if they are up to something tricky (like recursively linking liba{}
// prerequisites).
//
- // Note that because we use mtime, this function should normally only be
- // used in the perform_update action (which is straight).
+ // Note that because we use mtime, this function can only be used for the
+ // perform_update action.
//
using execute_filter = function<bool (const target&, size_t pos)>;
@@ -614,6 +886,18 @@ namespace build2
const execute_filter& = nullptr,
size_t count = 0);
+ // As above, but execute prerequisites in reverse.
+ //
+ // Sometime it may be advantageous to execute prerequisites in reverse, for
+ // example, to have more immediate incremental compilation or more accurate
+ // progress. See cc::link_rule for background.
+ //
+ optional<target_state>
+ reverse_execute_prerequisites (action, const target&,
+ const timestamp&,
+ const execute_filter& = nullptr,
+ size_t count = 0);
+
// Another version of the above that does two extra things for the caller:
// it determines whether the action needs to be executed on the target based
// on the passed timestamp and finds a prerequisite of the specified type
@@ -678,8 +962,9 @@ namespace build2
// Call straight or reverse depending on the current mode.
//
+ template <typename T>
target_state
- execute_members (action, const target&, const target*[], size_t);
+ execute_members (action, const target&, T[], size_t);
template <size_t N>
inline target_state
@@ -719,8 +1004,8 @@ namespace build2
LIBBUILD2_SYMEXPORT target_state
group_action (action, const target&);
- // Standard perform(clean) action implementation for the file target
- // (or derived).
+ // Standard perform(clean) action implementation for the file target (or
+ // derived). Note: also cleans ad hoc group members, if any.
//
LIBBUILD2_SYMEXPORT target_state
perform_clean (action, const target&);
@@ -730,8 +1015,8 @@ namespace build2
LIBBUILD2_SYMEXPORT target_state
perform_clean_depdb (action, const target&);
- // As above but clean the target group. The group should be an mtime_target
- // and members should be files.
+ // As above but clean the (non-ad hoc) target group. The group should be an
+ // mtime_target and members should be files.
//
LIBBUILD2_SYMEXPORT target_state
perform_clean_group (action, const target&);
@@ -742,21 +1027,22 @@ namespace build2
LIBBUILD2_SYMEXPORT target_state
perform_clean_group_depdb (action, const target&);
- // Helper for custom perform(clean) implementations that cleans extra files
- // and directories (recursively) specified as a list of either absolute
- // paths or "path derivation directives". The directive string can be NULL,
- // or empty in which case it is ignored. If the last character in a
- // directive is '/', then the resulting path is treated as a directory
- // rather than a file. The directive can start with zero or more '-'
- // characters which indicate the number of extensions that should be
- // stripped before the new extension (if any) is added (so if you want to
- // strip the extension, specify just "-"). For example:
+ // Helpers for custom perform(clean) implementations that, besides the
+ // target and group members, can also clean extra files and directories
+ // (recursively) specified as a list of either absolute paths or "path
+ // derivation directives". The directive string can be NULL, or empty in
+ // which case it is ignored. If the last character in a directive is '/',
+ // then the resulting path is treated as a directory rather than a file. The
+ // directive can start with zero or more '-' characters which indicate the
+ // number of extensions that should be stripped before the new extension (if
+ // any) is added (so if you want to strip the extension, specify just
+ // "-"). For example:
//
// perform_clean_extra (a, t, {".d", ".dlls/", "-.dll"});
//
// The extra files/directories are removed first in the specified order
- // followed by the ad hoc group member, then target itself, and, finally,
- // the prerequisites in the reverse order.
+ // followed by the group member, then target itself, and, finally, the
+ // prerequisites in the reverse order.
//
// You can also clean extra files derived from ad hoc group members that are
// "indexed" using their target types (see add/find_adhoc_member() for
@@ -775,21 +1061,46 @@ namespace build2
using clean_adhoc_extras = small_vector<clean_adhoc_extra, 2>;
+ // If show_adhoc_members is true, then print the entire ad hoc group instead
+ // of just the primary member at verbosity level 1 (see print_diag() for
+ // details). Note that the default is false because normally a rule
+ // implemented in C++ would only use an ad hoc group for subordiate members
+ // (.pdb, etc) and would use a dedicate target group type if the members
+ // are equal.
+ //
LIBBUILD2_SYMEXPORT target_state
perform_clean_extra (action, const file&,
const clean_extras&,
- const clean_adhoc_extras& = {});
+ const clean_adhoc_extras& = {},
+ bool show_adhoc_members = false);
inline target_state
perform_clean_extra (action a, const file& f,
- initializer_list<const char*> e)
+ initializer_list<const char*> e,
+ bool show_adhoc_members = false)
+ {
+ return perform_clean_extra (a, f, clean_extras (e), {}, show_adhoc_members);
+ }
+
+ // Similar to perform_clean_group() but with extras similar to
+ // perform_clean_extra(). Note that the extras are derived from the group
+ // "path" (g.dir / g.name).
+ //
+ LIBBUILD2_SYMEXPORT target_state
+ perform_clean_group_extra (action, const mtime_target&, const clean_extras&);
+
+ inline target_state
+ perform_clean_group_extra (action a, const mtime_target& g,
+ initializer_list<const char*> e)
{
- return perform_clean_extra (a, f, clean_extras (e));
+ return perform_clean_group_extra (a, g, clean_extras (e));
}
// Update/clean a backlink issuing appropriate diagnostics at appropriate
// levels depending on the overload and the changed argument.
//
+ // Note that these functions assume (target.leaf() == link.leaf ()).
+ //
enum class backlink_mode
{
link, // Make a symbolic link if possible, hard otherwise.
@@ -812,6 +1123,8 @@ namespace build2
bool changed,
backlink_mode = backlink_mode::link);
+ // Note: verbosity should be 2 or greater.
+ //
LIBBUILD2_SYMEXPORT void
update_backlink (context&,
const path& target,
@@ -819,6 +1132,8 @@ namespace build2
backlink_mode = backlink_mode::link,
uint16_t verbosity = 3);
+ // Note: verbosity should be 2 or greater.
+ //
LIBBUILD2_SYMEXPORT void
clean_backlink (context&,
const path& link,