// file : build2/algorithm.cxx -*- C++ -*- // copyright : Copyright (c) 2014-2016 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file #include #include // unique_ptr #include // size_t #include // move #include #include // reverse_iterate #include #include #include #include #include // import() #include #include #include #include using namespace std; using namespace butl; namespace build2 { target& search (const prerequisite_key& pk) { // If this is a project-qualified prerequisite, then this // is import's business. // if (pk.proj != nullptr) return import (pk); if (target* t = pk.tk.type->search (pk)) return *t; return create_new_target (pk); } target& search (name n, scope& s) { const string* e; const target_type* tt (s.find_target_type (n, e)); if (tt == nullptr) fail << "unknown target type " << n.type << " in name " << n; n.dir.normalize (); return search (*tt, move (n.dir), move (n.value), e, &s); } pair match_impl (action a, target& t, bool apply) { pair r; // By default, clear the resolved targets list before calling // match(). The rule is free to modify this list in match() // (provided that it matches) in order to, for example, prepare // it for apply(). // t.reset (a); // If this is a nested operation, first try the outer operation. // This allows a rule to implement a "precise match", that is, // both inner and outer operations match. // for (operation_id oo (a.outer_operation ()), io (a.operation ()), o (oo != 0 ? oo : io); o != 0; o = (oo != 0 ? io : 0)) { // Adjust action for recipe: on the first iteration we want it // {inner, outer} (which is the same as 'a') while on the second // -- {inner, 0}. Note that {inner, 0} is the same or "stronger" // (i.e., overrides; see action::operator<()) than 'a'. This // allows "unconditional inner" to override "inner for outer" // recipes. // action ra (a.meta_operation (), io, o != oo ? 0 : oo); scope& bs (t.base_scope ()); for (auto tt (&t.type ()); tt != nullptr; tt = tt->base) { // Search scopes outwards, stopping at the project root. // for (const scope* s (&bs); s != nullptr; s = s->root () ? global_scope : s->parent_scope ()) { const operation_rule_map* om (s->rules[a.meta_operation ()]); if (om == nullptr) continue; // No entry for this meta-operation id. // First try the map for the actual operation. If that // doesn't yeld anything, try the wildcard map. // for (size_t oi (o), oip (o); oip != 0; oip = oi, oi = 0) { const target_type_rule_map* ttm ((*om)[oi]); if (ttm == nullptr) continue; // No entry for this operation id. if (ttm->empty ()) continue; // Empty map for this operation id. auto i (ttm->find (tt)); if (i == ttm->end () || i->second.empty ()) continue; // No rules registered for this target type. const auto& rules (i->second); // Hint map. // @@ TODO // // Different rules can be used for different operations (update // vs test is a good example). So, at some point, we will probably // have to support a list of hints or even an operation-hint map // (e.g., 'hint=cxx test=foo' if cxx supports the test operation // but we want the foo rule instead). This is also the place where // the '{build clean}=cxx' construct (which we currently do not // support) can come handy. // // Also, ignore the hint (that is most likely ment for a different // operation) if this is a unique match. // string hint; auto rs (rules.size () == 1 ? make_pair (rules.begin (), rules.end ()) : rules.find_prefix (hint)); for (auto i (rs.first); i != rs.second; ++i) { const string& n (i->first); const rule& ru (i->second); match_result m; { auto g ( make_exception_guard ( [ra, &t, &n]() { info << "while matching rule " << n << " to " << diag_do (ra, t); })); if (!(m = ru.match (ra, t, hint))) continue; if (!m.recipe_action.valid ()) m.recipe_action = ra; // Default, if not set. } // Do the ambiguity test. // bool ambig (false); diag_record dr; for (++i; i != rs.second; ++i) { const string& n1 (i->first); const rule& ru1 (i->second); { auto g ( make_exception_guard ( [ra, &t, &n1]() { info << "while matching rule " << n1 << " to " << diag_do (ra, t); })); if (!ru1.match (ra, t, hint)) continue; } if (!ambig) { dr << fail << "multiple rules matching " << diag_doing (ra, t) << info << "rule " << n << " matches"; ambig = true; } dr << info << "rule " << n1 << " also matches"; } if (!ambig) { ra = m.recipe_action; // Use custom, if set. if (apply) { auto g ( make_exception_guard ( [ra, &t, &n]() { info << "while applying rule " << n << " to " << diag_do (ra, t); })); // @@ We could also allow the rule to change the recipe // action in apply(). Could be useful with delegates. // t.recipe (ra, ru.apply (ra, t, m)); } else { r.first = &ru; r.second = move (m); } return r; } else dr << info << "use rule hint to disambiguate this match"; } } } } } diag_record dr; dr << fail << "no rule to " << diag_do (a, t); if (verb < 4) dr << info << "re-run with --verbose 4 for more information"; return r; } group_view resolve_group_members_impl (action a, target& g) { group_view r; // Unless we already have a recipe, try matching the target to // the rule. // if (!g.recipe (a)) { auto rp (match_impl (a, g, false)); r = g.group_members (a); if (r.members != nullptr) return r; // That didn't help, so apply the rule and go to the building // phase. // const match_result& mr (rp.second); g.recipe (mr.recipe_action, rp.first->apply (mr.recipe_action, g, mr)); } // Note that we use execute_direct() rather than execute() here to // sidestep the dependents count logic. In this context, this is by // definition the first attempt to execute this rule (otherwise we // would have already known the members list) and we really do need // to execute it now. // execute_direct (a, g); r = g.group_members (a); return r; // Might still be unresolved. } void search_and_match_prerequisites (action a, target& t, const dir_path& d) { const bool e (d.empty ()); for (prerequisite p: group_prerequisites (t)) { target& pt (search (p)); if (e || pt.dir.sub (d)) { match (a, pt); t.prerequisite_targets.push_back (&pt); } } } void search_and_match_prerequisite_members (action a, target& t, const dir_path& d) { const bool e (d.empty ()); for (prerequisite_member p: group_prerequisite_members (a, t)) { target& pt (p.search ()); if (e || pt.dir.sub (d)) { match (a, pt); t.prerequisite_targets.push_back (&pt); } } } void inject_parent_fsdir (action a, target& t) { tracer trace ("inject_parent_fsdir"); scope& s (t.base_scope ()); scope* rs (s.root_scope ()); if (rs == nullptr) // Could be outside any project. return; const dir_path& out_root (rs->out_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) return; level6 ([&]{trace << "for " << t;}); fsdir& dt (search (d, string (), nullptr, &s)); match (a, dt); t.prerequisite_targets.emplace_back (&dt); } target_state execute_impl (action a, target& t) { // Implementation with some multi-threading ideas in mind. // switch (t.raw_state) { case target_state::group: // Means group's state is unknown. case target_state::unknown: case target_state::postponed: { auto g ( make_exception_guard ( [a, &t]() { t.raw_state = target_state::failed; info << "while " << diag_doing (a, t); })); target_state ts (t.recipe (a) (a, t)); assert (ts != target_state::unknown && ts != target_state::failed); // Set the target's state unless it should be the group's state. // if (t.raw_state != target_state::group) t.raw_state = ts; return ts; } case target_state::unchanged: case target_state::changed: // Should have been handled by inline execute(). assert (false); case target_state::failed: break; } throw failed (); } target_state execute_prerequisites (action a, target& t) { target_state r (target_state::unchanged); for (target* pt: t.prerequisite_targets) { if (pt == nullptr) // Skipped. continue; r |= execute (a, *pt); } return r; } target_state reverse_execute_prerequisites (action a, target& t) { target_state r (target_state::unchanged); for (target* pt: reverse_iterate (t.prerequisite_targets)) { if (pt == nullptr) // Skipped. continue; r |= execute (a, *pt); } return r; } 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 group_action (action a, target& t) { target_state r (execute (a, *t.group)); // Indicate to the standard execute() logic that this target's // state comes from the group. // t.raw_state = target_state::group; return r; } 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)); target_state r (rmfile (ft.path (), ft) ? target_state::changed : target_state::unchanged); // Update timestamp in case there are operations after us that // could use the information. // ft.mtime (timestamp_nonexistent); // Clean prerequisites. // r |= reverse_execute_prerequisites (a, t); return r; } }