diff options
Diffstat (limited to 'libbuild2/algorithm.hxx')
-rw-r--r-- | libbuild2/algorithm.hxx | 328 |
1 files changed, 260 insertions, 68 deletions
diff --git a/libbuild2/algorithm.hxx b/libbuild2/algorithm.hxx index db3e93d..a4feaea 100644 --- a/libbuild2/algorithm.hxx +++ b/libbuild2/algorithm.hxx @@ -78,6 +78,9 @@ 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&); @@ -161,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); - // Note: returns 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&); @@ -188,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; @@ -206,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 @@ -250,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 @@ -269,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); @@ -302,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. // @@ -356,18 +385,34 @@ namespace build2 // 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_sync (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_sync (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_sync (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 // asynchronous operation has been started and target_state::busy if the @@ -379,28 +424,60 @@ namespace build2 // 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&, bool fail = true); + 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); + 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 @@ -409,7 +486,10 @@ namespace build2 // 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. // @@ -417,13 +497,43 @@ namespace build2 match_inc_dependents (action, const target&); // Match (synchronously) a rule for the inner operation from withing the - // outer rule's apply() function. See also the companion execute_inner(). + // 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 @@ -453,6 +563,19 @@ 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 are cleaning, this function doesn't go into group @@ -468,24 +591,34 @@ 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. // LIBBUILD2_SYMEXPORT void - match_members (action, target&, const target* const*, size_t); + 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); } @@ -495,9 +628,9 @@ namespace build2 // ((prerequisite_target::include & mask) == value) condition. // LIBBUILD2_SYMEXPORT void - match_members (action a, - target& t, - prerequisite_targets& ts, + match_members (action, + const target&, + prerequisite_targets&, size_t start = 0, pair<uintptr_t, uintptr_t> include = {0, 0}); @@ -539,17 +672,35 @@ 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. // - // 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. + // 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. + // + // 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); + + // As above, but match the injected fsdir{} target directly (that is, + // without incrementing the dependency counts). + // + LIBBUILD2_SYMEXPORT const fsdir* + inject_fsdir_direct (action, target&, 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 @@ -591,7 +742,8 @@ namespace build2 // 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). + // 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. @@ -608,7 +760,7 @@ namespace build2 // translates target_state::failed to the failed exception. // target_state - execute_direct_sync (action, const target&); + execute_direct_sync (action, const target&, bool fail = true); target_state execute_direct_async (action, const target&, @@ -622,7 +774,8 @@ namespace build2 // // 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). + // target state/timestamp will be re-incorporated into the result). Unless + // it was matched direct. // LIBBUILD2_SYMEXPORT bool update_during_match (tracer&, @@ -631,13 +784,35 @@ namespace build2 // 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. + // 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); + 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 @@ -787,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 @@ -828,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&); @@ -839,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&); @@ -851,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 @@ -884,16 +1061,25 @@ 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)); + return perform_clean_extra (a, f, clean_extras (e), {}, show_adhoc_members); } // Similar to perform_clean_group() but with extras similar to @@ -913,6 +1099,8 @@ namespace build2 // 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. @@ -935,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, @@ -942,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, |