From 54e24990203f5e123396a44297ea4656ed3b6101 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Sun, 26 Apr 2015 09:53:41 +0200 Subject: Rework resolved prerequisite targets storage --- build/algorithm | 29 +++----- build/algorithm.cxx | 197 ++++++++++++++++++++++++++++++++++++++++---------- build/algorithm.ixx | 14 ---- build/algorithm.txx | 146 ++------------------------------------ build/cxx/rule.cxx | 201 +++++++++++++++------------------------------------- build/dump | 6 +- build/dump.cxx | 48 +++++++++---- build/operation.cxx | 4 +- build/prerequisite | 2 +- build/rule.cxx | 12 ++-- build/scope | 2 +- build/search.cxx | 8 +-- build/target | 64 ++++++++--------- build/target.ixx | 4 +- 14 files changed, 317 insertions(+), 420 deletions(-) diff --git a/build/algorithm b/build/algorithm index 7d82285..161baa2 100644 --- a/build/algorithm +++ b/build/algorithm @@ -14,7 +14,7 @@ namespace build class prerequisite; class prerequisite_key; - // The default prerequsite search implementation. It first calls the + // The default prerequisite search implementation. It first calls the // target-type-specific search function. If that doesn't yeld anything, // it creates a new target. // @@ -41,23 +41,19 @@ namespace build void search_and_match (action, target&); - // As above but ignores (does not match) prerequsites that are not + // As above but ignores (does not match) prerequisites that are not // in the same or a subdirectory of dir. // void search_and_match (action, target&, const dir_path& dir); - // Inject dependency on the parent directory fsdir{}, unless it is - // the project's out_root (or is outside of any project; think, for - // example, install directories). + // Inject dependency on the parent directory's fsdir{}, unless it is + // the project's out_root (or is outside of any project; say, for + // example, an install directory). // void inject_parent_fsdir (action, target&); - // Executor function type. - // - using executor_function = target_state (action, target&); - // Execute the action on target, assuming a rule has been matched // and the recipe for this action has been set. This is the default // executor implementation. @@ -66,27 +62,24 @@ namespace build execute (action, target&); // The default prerequisite execute implementation. It calls execute() - // on each non-ignored (non-NULL target) prerequisite in a loop. If this + // on each non-ignored (non-NULL) prerequisite target in a loop. If this // target is a member of a group, then it first does this to the group's // prerequisites. Returns target_state::changed if any of them were // changed and target_state::unchanged otherwise. It treats targets // with postponed execution the same as ignored. Note that this // function can be used as a recipe. // - template target_state execute_prerequisites (action, target&); // As above but iterates over the prerequisites in reverse. // - template target_state reverse_execute_prerequisites (action, target&); // A version of the above that also determines whether the action // needs to be executed on the target based on the passed timestamp. // - template bool execute_prerequisites (action, target&, const timestamp&); @@ -95,9 +88,9 @@ namespace build // the target based on the passed timestamp and, if so, finds a // prerequisite of the specified type (e.g., a source file). If // there are multiple prerequisites of this type, then the last - // one returned. + // is returned. // - template + template T* execute_find_prerequisites (action, target&, const timestamp&); @@ -106,17 +99,15 @@ namespace build target_state noop_action (action, target&); - // Default action implementation which forwards to the prerequsites. - // Use default_recipe for the standard executor. + // Default action implementation which forwards to the prerequisites. + // Use default_recipe instead of using this function directly. // - template target_state default_action (action, target&); // Standard perform(clean) action implementation for the file target // or derived. // - template target_state perform_clean (action, target&); } diff --git a/build/algorithm.cxx b/build/algorithm.cxx index 9f428b3..e6a71df 100644 --- a/build/algorithm.cxx +++ b/build/algorithm.cxx @@ -5,6 +5,7 @@ #include #include // unique_ptr +#include // size_t #include // move #include @@ -14,6 +15,7 @@ #include #include #include +#include #include #include @@ -33,6 +35,12 @@ namespace build void match_impl (action a, target& t) { + // Clear the resolved targets list before calling match(). The rule + // is free to, say, resize() this list in match() (provided that it + // matches) in order to, for example, prepare it for apply(). + // + t.prerequisite_targets.clear (); + for (auto tt (&t.type ()); tt != nullptr && !t.recipe (a); tt = tt->base) @@ -148,30 +156,30 @@ namespace build void search_and_match (action a, target& t) { - if (t.group != nullptr) - search_and_match (a, *t.group); + group_prerequisites gp (t); + t.prerequisite_targets.resize (gp.size ()); - for (prerequisite_target& p: t.prerequisites) + size_t i (0); + for (prerequisite& p: gp) { - p.target = &search (p); - match (a, *p.target); + target& pt (search (p)); + match (a, pt); + t.prerequisite_targets[i++] = &pt; } } void search_and_match (action a, target& t, const dir_path& d) { - if (t.group != nullptr) - search_and_match (a, *t.group, d); - - for (prerequisite_target& p: t.prerequisites) + for (prerequisite& p: group_prerequisites (t)) { - p.target = &search (p); + target& pt (search (p)); if (p.target->dir.sub (d)) - match (a, *p.target); - else - p.target = nullptr; // Ignore. + { + match (a, pt); + t.prerequisite_targets.push_back (&pt); + } } } @@ -181,36 +189,41 @@ namespace build tracer trace ("inject_parent_fsdir"); scope& s (t.base_scope ()); + scope* rs (s.root_scope ()); - if (scope* rs = s.root_scope ()) // Could be outside any project. - { - const dir_path& out_root (rs->path ()); - - // If t is a directory (name is empty), say foo/bar/, then - // t is bar and its parent directory is foo/. - // - const dir_path& d (t.name.empty () ? t.dir.directory () : t.dir); - - if (d.sub (out_root) && d != out_root) - { - level5 ([&]{trace << "injecting prerequisite for " << t;}); - - prerequisite& p ( - s.prerequisites.insert ( - fsdir::static_type, - d, - string (), - nullptr, - s, - trace).first); + if (rs == nullptr) // Could be outside any project. + return; - target& pt (search (p)); + const dir_path& out_root (rs->path ()); - t.prerequisites.emplace_back (p, pt); + // If t is a directory (name is empty), say foo/bar/, then + // t is bar and its parent directory is foo/. + // + const dir_path& d (t.name.empty () ? t.dir.directory () : t.dir); + + if (!d.sub (out_root) || d == out_root) + return; + + prerequisite& p ( + s.prerequisites.insert ( + fsdir::static_type, + d, + string (), + nullptr, + s, + trace).first); + + // This function is normally called from match() which means + // it can be called several times if we are performing several + // operations (e.g., clean update). Since it is a fairly common + // pattern to add this prerequisite at the end, do a quick check + // if the last prerequisite is already what we are about to add. + // + if (!t.prerequisites.empty () && &t.prerequisites.back ().get () == &p) + return; - match (a, pt); - } - } + level5 ([&]{trace << "injecting prerequisite for " << t;}); + t.prerequisites.emplace_back (p); } target_state @@ -250,9 +263,115 @@ namespace build } target_state + execute_prerequisites (action a, target& t) + { + target_state ts (target_state::unchanged); + + for (target* pt: t.prerequisite_targets) + { + if (pt == nullptr) // Skipped. + continue; + + if (execute (a, *pt) == target_state::changed) + ts = target_state::changed; + } + + return ts; + } + + target_state + reverse_execute_prerequisites (action a, target& t) + { + target_state ts (target_state::unchanged); + + for (target* pt: reverse_iterate (t.prerequisite_targets)) + { + if (pt == nullptr) // Skipped. + continue; + + if (execute (a, *pt) == target_state::changed) + ts = target_state::changed; + } + + return ts; + } + + bool + execute_prerequisites (action a, target& t, const timestamp& mt) + { + bool e (mt == timestamp_nonexistent); + + for (target* pt: t.prerequisite_targets) + { + if (pt == nullptr) // Skipped. + continue; + + target_state ts (execute (a, *pt)); + + if (!e) + { + // If this is an mtime-based target, then compare timestamps. + // + if (auto mpt = dynamic_cast (pt)) + { + timestamp mp (mpt->mtime ()); + + // What do we do if timestamps are equal? This can happen, for + // example, on filesystems that don't have subsecond resolution. + // There is not much we can do here except detect the case where + // the prerequisite was changed in this run which means the + // action must be executed on the target as well. + // + if (mt < mp || (mt == mp && ts == target_state::changed)) + e = true; + } + else + { + // Otherwise we assume the prerequisite is newer if it was changed. + // + if (ts == target_state::changed) + e = true; + } + } + } + + return e; + } + + target_state noop_action (action, target&) { assert (false); // We shouldn't be called, see target::recipe(). return target_state::unchanged; } + + target_state + default_action (action a, target& t) + { + return current_mode == execution_mode::first + ? execute_prerequisites (a, t) + : reverse_execute_prerequisites (a, t); + } + + target_state + perform_clean (action a, target& t) + { + // The reverse order of update: first delete the file, then clean + // prerequisites. + // + file& ft (dynamic_cast (t)); + + bool r (rmfile (ft.path (), ft)); + + // Update timestamp in case there are operations after us that + // could use the information. + // + ft.mtime (timestamp_nonexistent); + + // Clean prerequisites. + // + target_state ts (reverse_execute_prerequisites (a, t)); + + return r ? target_state::changed : ts; + } } diff --git a/build/algorithm.ixx b/build/algorithm.ixx index 76f9a0f..ee63f90 100644 --- a/build/algorithm.ixx +++ b/build/algorithm.ixx @@ -52,18 +52,4 @@ namespace build } } } - - template - T* - execute_find_prerequisites (action, target&, const timestamp&, bool&); - - template - inline T* - execute_find_prerequisites (action a, target& t, const timestamp& mt) - { - bool e (mt == timestamp_nonexistent); - T* r (execute_find_prerequisites (a, t, mt, e)); - assert (r != nullptr); - return e ? r : nullptr; - } } diff --git a/build/algorithm.txx b/build/algorithm.txx index 4f5f9f3..a62603d 100644 --- a/build/algorithm.txx +++ b/build/algorithm.txx @@ -2,126 +2,25 @@ // copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC // license : MIT; see accompanying LICENSE file -#include -#include // reverse_iterate - namespace build { - template - target_state - execute_prerequisites (action a, target& t) - { - target_state ts (target_state::unchanged); - - if (t.group != nullptr) - ts = execute_prerequisites (a, *t.group); - - for (target* pt: t.prerequisites) - { - if (pt == nullptr) // Skip ignored. - continue; - - if (E (a, *pt) == target_state::changed) - ts = target_state::changed; - } - - return ts; - } - - template - target_state - reverse_execute_prerequisites (action a, target& t) - { - target_state ts (target_state::unchanged); - - for (target* pt: reverse_iterate (t.prerequisites)) - { - if (pt == nullptr) // Skip ignored. - continue; - - if (E (a, *pt) == target_state::changed) - ts = target_state::changed; - } - - if (t.group != nullptr) - { - if (reverse_execute_prerequisites (a, *t.group) == - target_state::changed) - ts = target_state::changed; - } - - return ts; - } - - template - bool - execute_prerequisites (action a, target& t, const timestamp& mt) - { - bool e (mt == timestamp_nonexistent); - - if (t.group != nullptr) - { - if (execute_prerequisites (a, *t.group, mt)) - e = true; - } - - for (target* pt: t.prerequisites) - { - if (pt == nullptr) // Skip ignored. - continue; - - target_state ts (E (a, *pt)); - - if (!e) - { - // If this is an mtime-based target, then compare timestamps. - // - if (auto mpt = dynamic_cast (pt)) - { - timestamp mp (mpt->mtime ()); - - // What do we do if timestamps are equal? This can happen, for - // example, on filesystems that don't have subsecond resolution. - // There is not much we can do here except detect the case where - // the prerequisite was changed in this run which means the - // action must be executed on the target as well. - // - if (mt < mp || (mt == mp && ts == target_state::changed)) - e = true; - } - else - { - // Otherwise we assume the prerequisite is newer if it was changed. - // - if (ts == target_state::changed) - e = true; - } - } - } - - return e; - } - - template + template T* - execute_find_prerequisites ( - action a, target& t, const timestamp& mt, bool& e) + execute_prerequisites (action a, target& t, const timestamp& mt) { //@@ Can factor the bulk of it into a non-template code. Can // either do a function template that will do dynamic_cast check // or can scan the target type info myself. I think latter. // + bool e (mt == timestamp_nonexistent); T* r (nullptr); - if (t.group != nullptr) - r = execute_find_prerequisites (a, *t.group, mt, e); - - for (target* pt: t.prerequisites) + for (target* pt: t.prerequisite_targets) { if (pt == nullptr) // Skip ignored. continue; - target_state ts (E (a, *pt)); + target_state ts (execute (a, *pt)); if (!e) { @@ -153,38 +52,7 @@ namespace build r = tmp; } - return r; - } - - template - target_state - default_action (action a, target& t) - { - return current_mode == execution_mode::first - ? execute_prerequisites (a, t) - : reverse_execute_prerequisites (a, t); - } - - template - target_state - perform_clean (action a, target& t) - { - // The reverse order of update: first delete the file, then clean - // prerequisites. - // - file& ft (dynamic_cast (t)); - - bool r (rmfile (ft.path (), ft)); - - // Update timestamp in case there are operations after us that - // could use the information. - // - ft.mtime (timestamp_nonexistent); - - // Clean prerequisites. - // - target_state ts (reverse_execute_prerequisites (a, t)); - - return r ? target_state::changed : ts; + assert (r != nullptr); + return e ? r : nullptr; } } diff --git a/build/cxx/rule.cxx b/build/cxx/rule.cxx index e59c5b9..7f9ea02 100644 --- a/build/cxx/rule.cxx +++ b/build/cxx/rule.cxx @@ -83,10 +83,10 @@ namespace build // a source file specified for an obj member overrides the one // specified for the group. // - for (prerequisite_target& pe: reverse_iterate (group_prerequisites (t))) + for (prerequisite& p: reverse_iterate (group_prerequisites (t))) { - if (pe.prereq->type.id == typeid (cxx)) - return &pe; + if (p.type.id == typeid (cxx)) + return &p; } level3 ([&]{trace << "no c++ source file for target " << t;}); @@ -108,6 +108,10 @@ namespace build t.path (t.derived_path ("o", nullptr, "-so")); } + // Inject dependency on the output directory. + // + inject_parent_fsdir (a, t); + // Search and match all the existing prerequisites. The injection // code (below) takes care of the ones it is adding. // @@ -127,17 +131,17 @@ namespace build // if (a.operation () == update_id || a.operation () == default_id) { - prerequisite_target& spe (*static_cast (v)); - cxx& st (dynamic_cast (*spe.target)); + // The cached prerequisite target (sp.target) should be the + // same as what is in t.prerequisite_targets since we used + // standard search_and_match() above. + // + prerequisite& sp (*static_cast (v)); + cxx& st (dynamic_cast (*sp.target)); if (st.mtime () != timestamp_nonexistent) - inject_prerequisites (a, t, st, spe.prereq->scope); + inject_prerequisites (a, t, st, sp.scope); } - // Inject dependency on the output directory. - // - inject_parent_fsdir (a, t); - switch (a) { case perform_update_id: return &perform_update; @@ -299,13 +303,13 @@ namespace build ds.prerequisites.insert ( hxx::static_type, move (d), move (n), e, ds, trace).first); - // Resolve to target. + // Add to our prerequisites list. // - path_target& pt (dynamic_cast (search (p))); + t.prerequisites.emplace_back (p); - // Add to prerequisites list. + // Resolve to target. // - t.prerequisites.emplace_back (p, pt); + path_target& pt (dynamic_cast (search (p))); // Assign path. // @@ -315,6 +319,10 @@ namespace build // Match to a rule. // build::match (a, pt); + + // Add to our resolved target list. + // + t.prerequisite_targets.push_back (&pt); } } @@ -342,7 +350,7 @@ namespace build perform_update (action a, target& xt) { path_target& t (static_cast (xt)); - cxx* s (execute_find_prerequisites (a, t, t.mtime ())); + cxx* s (execute_prerequisites (a, t, t.mtime ())); if (s == nullptr) return target_state::unchanged; @@ -484,83 +492,6 @@ namespace build return seen_cxx || seen_c || seen_obj || seen_lib ? &t : nullptr; } - static inline target_state - select_obja_liba (action a, target& t) - { - target* r; - if (obj* o = t.is_a ()) - r = o->a; - else if (lib* l = t.is_a ()) - r = l->a; - else - r = &t; - - return execute (a, *r); - } - - static inline target_state - select_obja_libso (action a, target& t) - { - target* r; - if (obj* o = t.is_a ()) - r = o->a; - else if (lib* l = t.is_a ()) - r = l->so; - else - r = &t; - - return execute (a, *r); - } - - static inline target_state - select_objso_liba (action a, target& t) - { - target* r; - if (obj* o = t.is_a ()) - r = o->so; - else if (lib* l = t.is_a ()) - r = l->a; - else - r = &t; - - return execute (a, *r); - } - - static inline target_state - select_objso_libso (action a, target& t) - { - target* r; - if (obj* o = t.is_a ()) - r = o->so; - else if (lib* l = t.is_a ()) - r = l->so; - else - r = &t; - - return execute (a, *r); - } - - static executor_function* clean_table[2][2] = - { - {&perform_clean, &perform_clean}, - {&perform_clean, &perform_clean} - }; - - static executor_function* default_table[2][2] = - { - {&default_action, &default_action}, - {&default_action, &default_action} - }; - - static bool (*execute_table[2][2]) (action, target&, const timestamp&) = - { - {&execute_prerequisites, - &execute_prerequisites}, - - {&execute_prerequisites, - &execute_prerequisites} - }; - recipe link:: apply (action a, target& xt, void*) const { @@ -596,6 +527,10 @@ namespace build } } + // Inject dependency on the output directory. + // + inject_parent_fsdir (a, t); + // We may need the project roots for rule chaining (see below). // We will resolve them lazily only if needed. // @@ -605,29 +540,29 @@ namespace build // Process prerequisites: do rule chaining for C and C++ source // files as well as search and match. // - for (prerequisite_target& pe: group_prerequisites (t)) + group_prerequisites gp (t); + t.prerequisite_targets.reserve (gp.size ()); + + for (prerequisite_ref& pr: gp) { - bool group (!pe.belongs (t)); // Target group's prerequisite. - prerequisite& p (pe); + bool group (!pr.belongs (t)); // Target group's prerequisite. + + prerequisite& p (pr); + target* pt (nullptr); if (!p.is_a () && !p.is_a ()) { // The same basic logic as in search_and_match(). // - pe.target = &search (p); + pt = &search (p); - if (a.operation () == clean_id && !pe.target->dir.sub (t.dir)) - { - pe.target = nullptr; // Ignore. - continue; - } + if (a.operation () == clean_id && !pt->dir.sub (t.dir)) + continue; // Skip. // If this is the obj{} or lib{} target group, then pick the // appropriate member and make sure it is searched and matched. // - target* pt; - - if (obj* o = pe.target->is_a ()) + if (obj* o = pt->is_a ()) { pt = so ? static_cast (o->so) : o->a; @@ -639,20 +574,8 @@ namespace build pt = &search ( prerequisite_key {&type, &p.dir, &p.name, &p.ext, &p.scope}); } - - // This is a bit tricky: if this is a group's prerequisite, - // then we have to keep pe.target pointing to the obj{} group - // since there could be another match that uses a different - // member of this prerequisite. In this case we specify the - // "executor" (see below) which will pick the right member - // for one of common algorithms. However, if this is our own - // prerequisite, then we are free to hard-wire the member - // we need directly in pe.target. - // - if (!group) - pe.target = pt; } - else if (lib* l = pe.target->is_a ()) + else if (lib* l = pt->is_a ()) { // Make sure the library build that we need is available. // @@ -672,16 +595,10 @@ namespace build pt = &search ( prerequisite_key {&type, &p.dir, &p.name, &p.ext, &p.scope}); } - - // Same logic as for obj{} - // - if (!group) - pe.target = pt; } - else - pt = pe.target; build::match (a, *pt); + t.prerequisite_targets.push_back (pt); continue; } @@ -750,13 +667,12 @@ namespace build // If we shouldn't clean obj{}, then it is fair to assume // we shouldn't clean cxx{} either (generated source will // be in the same directory as obj{} and if not, well, go - // and find yourself another build system). + // find yourself another build system). // - pe.target = nullptr; // Skip. - continue; + continue; // Skip. } - pe.target = ot; + pt = ot; // If we have created the obj{} target group, pick one of its // members; the rest would be primarily concerned with it. @@ -808,7 +724,7 @@ namespace build fail << "synthesized target for prerequisite " << cp << " would be incompatible with existing target " << *ot << - info << "unknown existing prerequsite type " << p << + info << "unknown existing prerequisite type " << p << info << "specify corresponding obj{} target explicitly"; } @@ -820,33 +736,31 @@ namespace build if (cp.target != cp1->target) fail << "synthesized target for prerequisite " << cp << " would be incompatible with existing target " << *ot << - info << "existing prerequsite " << *cp1 << " does not " + info << "existing prerequisite " << *cp1 << " does not " << "match " << cp << info << "specify corresponding " << o_type.name << "{} " << "target explicitly"; } else { - // Note: add the source to the group, not member. + // Note: add the source to the group, not the member. // - pe.target->prerequisites.emplace_back (cp); + pt->prerequisites.emplace_back (cp); build::match (a, *ot); } - // Change the exe{} target's prerequsite ref from cxx{} to obj*{}. + // Change the exe{} target's prerequisite from cxx{} to obj*{}. // - pe.prereq = &op; - } + pr = op; - // Inject dependency on the output directory. - // - inject_parent_fsdir (a, t); + t.prerequisite_targets.push_back (ot); + } switch (a) { case perform_update_id: return &perform_update; - case perform_clean_id: return clean_table[so][lso]; - default: return default_table[so][lso]; // Forward to prerequisites. + case perform_clean_id: return &perform_clean; + default: return default_recipe; // Forward to prerequisites. } } @@ -871,7 +785,7 @@ namespace build case type::libso: lso = true; break; } - if (!execute_table[so][lso] (a, t, t.mtime ())) + if (!execute_prerequisites (a, t, t.mtime ())) return target_state::unchanged; // Translate paths to relative (to working directory) ones. This @@ -909,11 +823,8 @@ namespace build append_options (args, t, "cxx.loptions"); } - for (target* pt: group_prerequisites (t)) + for (target* pt: t.prerequisite_targets) { - if (pt == nullptr) - continue; // Skipped. - path_target* ppt; if (obj* o = pt->is_a ()) diff --git a/build/dump b/build/dump index 48da079..3830b69 100644 --- a/build/dump +++ b/build/dump @@ -5,10 +5,14 @@ #ifndef BUILD_DUMP #define BUILD_DUMP +#include + namespace build { + // Dump the state pertaining to the specified action. + // void - dump (); + dump (action); } #endif // BUILD_DUMP diff --git a/build/dump.cxx b/build/dump.cxx index 9299a86..3f8eb15 100644 --- a/build/dump.cxx +++ b/build/dump.cxx @@ -19,25 +19,47 @@ using namespace std; namespace build { static void - dump_target (ostream& os, const target& t) + dump_target (ostream& os, action a, const target& t) { - //@@ Need to print group info somehow. + os << t; - os << t << ':'; + if (t.group != nullptr) + os << "->" << *t.group; - for (const prerequisite_target& pe: t.prerequisites) + os << ':'; + + // If the target has been matched to a rule, print resolved + // prerequisite targets. + // + if (t.recipe (a)) { - os << ' '; + for (const target* pt: t.prerequisite_targets) + { + if (pt == nullptr) // Skipped. + continue; - if (pe.target != nullptr) - os << *pe.target; - else - os << *pe.prereq; + os << ' ' << *pt; + } + } + else + { + for (const prerequisite& p: t.prerequisites) + { + os << ' '; + + // Print it as target if one has been cached. + // + if (p.target != nullptr) + os << *p.target; + else + os << p; + } } } static void dump_scope (ostream& os, + action a, scope& p, scope_map::iterator& i, string& ind, @@ -94,7 +116,7 @@ namespace build os << endl; scope& s (i->second); - dump_scope (os, s, ++i, ind, rts); + dump_scope (os, a, s, ++i, ind, rts); sb = true; } @@ -141,7 +163,7 @@ namespace build os << endl << ind; - dump_target (os, t); + dump_target (os, a, t); } ind.resize (ind.size () - 2); @@ -152,7 +174,7 @@ namespace build } void - dump () + dump (action a) { auto i (scopes.begin ()); scope& g (i->second); // Global scope. @@ -162,7 +184,7 @@ namespace build set rts; ostream& os (*diag_stream); - dump_scope (os, g, ++i, ind, rts); + dump_scope (os, a, g, ++i, ind, rts); os << endl; } } diff --git a/build/operation.cxx b/build/operation.cxx index cabf73b..39de5f3 100644 --- a/build/operation.cxx +++ b/build/operation.cxx @@ -74,13 +74,13 @@ namespace build target& t (**i); if (verb >= 5) - dump (); + dump (a); level4 ([&]{trace << "matching " << t;}); match (a, t); if (verb >= 5) - dump (); + dump (a); ts.push_back (&t); } diff --git a/build/prerequisite b/build/prerequisite index 8a601fb..f46cb98 100644 --- a/build/prerequisite +++ b/build/prerequisite @@ -23,7 +23,7 @@ namespace build class scope; class target; - // Light-weight (by being shallow-pointing) prerequsite key, similar + // Light-weight (by being shallow-pointing) prerequisite key, similar // to (and based on) target key. // class prerequisite_key diff --git a/build/rule.cxx b/build/rule.cxx index e912b2e..475d812 100644 --- a/build/rule.cxx +++ b/build/rule.cxx @@ -102,10 +102,8 @@ namespace build // timestamp mt (dynamic_cast (t).mtime ()); - for (target* pt: t.prerequisites) + for (target* pt: t.prerequisite_targets) { - assert (pt != nullptr); // We don't skip anything. - target_state ts (execute (a, *pt)); // If this is an mtime-based target, then compare timestamps. @@ -170,6 +168,10 @@ namespace build recipe fsdir_rule:: apply (action a, target& t, void*) const { + // Inject dependency on the parent directory. + // + inject_parent_fsdir (a, t); + switch (a.operation ()) { // For default, we don't do anything other than letting our @@ -191,10 +193,6 @@ namespace build assert (false); } - // Inject dependency on the parent directory. - // - inject_parent_fsdir (a, t); - switch (a) { case perform_update_id: return &perform_update; diff --git a/build/scope b/build/scope index 2824aeb..1608be7 100644 --- a/build/scope +++ b/build/scope @@ -142,7 +142,7 @@ namespace build // scope in order not to change the variables in the current scope. // Such a scope is not entered in to the scope map. As a result it // can only be used as a temporary set of variables. In particular, - // defining targets/prerequsites directly in such a scope will surely + // defining targets/prerequisites directly in such a scope will surely // end up badly. Defining any nested scopes will be as if defining // such a scope in the parent (since path() returns parent's path). // diff --git a/build/search.cxx b/build/search.cxx index b1d5ec6..bdc5917 100644 --- a/build/search.cxx +++ b/build/search.cxx @@ -48,7 +48,7 @@ namespace build target& t (**i); - level4 ([&]{trace << "existing target " << t << " for prerequsite " + level4 ([&]{trace << "existing target " << t << " for prerequisite " << pk;}); return &t; @@ -84,7 +84,7 @@ namespace build if (mt == timestamp_nonexistent) continue; - level4 ([&]{trace << "found existing file " << f << " for prerequsite " + level4 ([&]{trace << "found existing file " << f << " for prerequisite " << pk;}); // Find or insert. @@ -98,7 +98,7 @@ namespace build path_target& t (dynamic_cast (r.first)); level4 ([&]{trace << (r.second ? "new" : "existing") << " target " - << t << " for prerequsite " << pk;}); + << t << " for prerequisite " << pk;}); if (t.path ().empty ()) t.path (move (f)); @@ -140,7 +140,7 @@ namespace build target& t (r.first); - level4 ([&]{trace << "new target " << t << " for prerequsite " << pk;}); + level4 ([&]{trace << "new target " << t << " for prerequisite " << pk;}); return t; } diff --git a/build/target b/build/target index cca8f8d..0aa0227 100644 --- a/build/target +++ b/build/target @@ -72,46 +72,24 @@ namespace build target_state noop_action (action, target&); // Defined in - // Prerequisite target. It consist of a reference to the prerequisite - // plus a pointer to target to which it resolves in this context. + // Prerequisite references as used in the target::prerequisites list + // below. // - struct prerequisite_target + struct prerequisite_ref: std::reference_wrapper { - typedef build::target target_type; + typedef std::reference_wrapper base; - prerequisite* prereq; // Must not be NULL but can be reset. + using base::base; - operator prerequisite& () const {return *prereq;} - - // Target to which this prerequisite resolves in this context. - // Note that unlike prerequisite::target, this can be a group - // member target. Depending on the stage, NULL means either - // the target is not yet resolved or it should be skipped. - // - // Note also that it is possible the target can vary from - // action to action, just like recipes. We don't need to keep - // track of the action here since the target will be updates - // if the recipe is updated, as part of rule::apply(). - // - target_type* target {nullptr}; - - operator target_type* () const {return target;} - - explicit - prerequisite_target (prerequisite& p): prereq (&p) {} - - prerequisite_target (prerequisite& p, target_type& t) - : prereq (&p), target (&t) {} - - // Return true if this object belongs to the target's prerequisite + // Return true if this reference belongs to the target's prerequisite // list. Note that this test only works if you use references to // the container elements and the container hasn't been resized // since such a reference was obtained. Normally this function is - // used when iterating over combined prerequisites range (see + // used when iterating over a combined prerequisites range (see // group_prerequisites below). // bool - belongs (const target_type&) const; + belongs (const target&) const; }; // Target. @@ -155,9 +133,22 @@ namespace build // Prerequisites. // public: - typedef std::vector prerequisites_type; + typedef std::vector prerequisites_type; prerequisites_type prerequisites; + // Targets to which prerequisites resolve for this recipe. Note + // that unlike prerequisite::target, these can be resolved to + // group members. NULL means the target should be skipped (or + // the rule may simply not add such a target to the list). + // + // Note also that it is possible the target can vary from + // action to action, just like recipes. We don't need to keep + // track of the action here since the targets will be updated + // if the recipe is updated, normally as part of rule::apply(). + // + typedef std::vector prerequisite_targets_type; + prerequisite_targets_type prerequisite_targets; + // Check if there are any prerequisites, taking into account // group prerequisites. // @@ -284,9 +275,9 @@ namespace build // come first followed by the member's. If you need to see them // in the other direction, iterate in reverse, for example: // - // for (prerequisite_target& pe: group_prerequisites (t)) + // for (prerequisite_ref& pr: group_prerequisites (t)) // - // for (prerequisite_target& pe: reverse_iterate (group_prerequisites (t)) + // for (prerequisite_ref& pr: reverse_iterate (group_prerequisites (t)) // // Note that in this case the individual elements of each list will // also be traversed in reverse, but that's what you usually want, @@ -385,6 +376,13 @@ namespace build reverse_iterator rend () const {return reverse_iterator (begin ());} + std::size_t + size () const + { + return t_.prerequisites.size () + + (t_.group != nullptr ? t_.group->prerequisites.size () : 0); + } + private: target& t_; }; diff --git a/build/target.ixx b/build/target.ixx index 02bdc69..45a8c69 100644 --- a/build/target.ixx +++ b/build/target.ixx @@ -4,8 +4,8 @@ namespace build { - inline bool prerequisite_target:: - belongs (const target_type& t) const + inline bool prerequisite_ref:: + belongs (const target& t) const { const auto& p (t.prerequisites); return !(p.empty () || this < &p.front () || this > &p.back ()); -- cgit v1.1