diff options
Diffstat (limited to 'libbuild2/algorithm.hxx')
-rw-r--r-- | libbuild2/algorithm.hxx | 356 |
1 files changed, 251 insertions, 105 deletions
diff --git a/libbuild2/algorithm.hxx b/libbuild2/algorithm.hxx index 73705d8..8bdf737 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_exsiting (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,10 +164,10 @@ 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 that unlike the above + // version, these ones can be called during the load and execute phases. // LIBBUILD2_SYMEXPORT const target* search_existing (const name&, @@ -143,17 +191,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 +212,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 @@ -295,17 +347,18 @@ 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 @@ -315,18 +368,25 @@ namespace build2 enum class unmatch {none, unchanged, safe}; target_state - match (action, const target&, bool fail = true); + match_sync (action, const target&, bool fail = true); pair<bool, target_state> - try_match (action, const target&, bool fail = true); + try_match_sync (action, const target&, bool fail = true); pair<bool, target_state> - match (action, const target&, unmatch); + match_sync (action, const target&, unmatch); + + // As above but without incrementing the target's dependents count. Should + // be executed with execute_direct_*(). + // + target_state + match_direct_sync (action, const target&, bool fail = true); // 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 @@ -337,6 +397,12 @@ namespace build2 size_t start_count, atomic_count& task_count, bool fail = true); + target_state + match_complete (action, const target&, bool fail = true); + + pair<bool, target_state> + match_complete (action, const target&, unmatch); + // Apply the specified recipe directly and without incrementing the // dependency counts. The target must be locked. // @@ -352,15 +418,20 @@ namespace build2 // 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 a rule for the inner operation from withing the outer rule's - // apply() function. See also the companion execute_inner(). + // Incrementing the dependency counts of the specified target. + // + void + 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() + // and inner_recipe. // target_state match_inner (action, const target&); @@ -423,27 +494,26 @@ namespace build2 // 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 @@ -473,8 +543,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); @@ -486,56 +557,56 @@ namespace build2 // 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. + // 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 src tree). This can be used, for + // example, to place output into an otherwise non-existent directory. // LIBBUILD2_SYMEXPORT const fsdir* - inject_fsdir (action, target&, bool parent = true); + inject_fsdir (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 - // implementation (but may still return target_state::busy if the target - // is already being executed). Decrements the dependents count. - // - // Note: does not translate target_state::failed to the failed exception. + // 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 (action, const target&); - - // As above but wait for completion if the target is busy and translate - // target_state::failed to the failed exception. - // - 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. @@ -548,11 +619,43 @@ 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). + // + 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. + // + // 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); // The default prerequisite execute implementation. Call execute_async() on // each non-ignored (non-NULL) prerequisite target in a loop and then wait @@ -615,8 +718,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)>; @@ -626,6 +729,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 @@ -690,8 +805,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 @@ -731,8 +847,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&); @@ -742,8 +858,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&); @@ -754,21 +870,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 @@ -787,21 +904,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)); + 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_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. @@ -824,6 +966,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, @@ -831,6 +975,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, |