From 70af0087d8efb3f2f7dc9ffdf2568419913f16da Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 30 Jun 2015 15:07:03 +0200 Subject: Group "see through" iteration, take 1 --- build/algorithm | 13 +- build/algorithm.cxx | 12 +- build/algorithm.ixx | 9 +- build/bin/rule | 8 +- build/bin/rule.cxx | 10 +- build/cli/rule | 4 +- build/cli/rule.cxx | 18 +-- build/cxx/rule | 8 +- build/cxx/rule.cxx | 241 ++++++++++++++++++++++------------- build/prerequisite | 8 +- build/prerequisite.cxx | 2 + build/rule | 44 +++++-- build/rule.cxx | 18 +-- build/search.cxx | 4 + build/target | 225 +++++++++++++++++++++++++++++++- build/target-key | 7 + build/target.cxx | 12 ++ build/target.ixx | 61 +++++++++ tests/cli/lib/libtest/test/buildfile | 6 +- tests/cli/lib/test/buildfile | 4 +- 20 files changed, 562 insertions(+), 152 deletions(-) diff --git a/build/algorithm b/build/algorithm index ad53b95..2547466 100644 --- a/build/algorithm +++ b/build/algorithm @@ -29,9 +29,15 @@ namespace build target& search (const prerequisite_key&); + // As above but override the target type. Useful for searching for + // target group members where we need to search for a different + // target type. + // + target& + search (const target_type&, const prerequisite_key&); + // As above but specify the prerequisite to search as individual - // key components. Useful for searching for target group members - // where we need to search for a different target type. + // key components. // target& search (const target_type& type, @@ -69,8 +75,7 @@ namespace build search_and_match (action, target&, const dir_path&); // Unless already available, match, and, if necessary, execute - // (not yet implemented) the group in order to obtain its members - // list. + // the group in order to obtain its members list. // group_view resolve_group_members (action, target_group&); diff --git a/build/algorithm.cxx b/build/algorithm.cxx index 4f30944..5c7805f 100644 --- a/build/algorithm.cxx +++ b/build/algorithm.cxx @@ -34,10 +34,10 @@ namespace build return create_new_target (pk); } - pair + pair match_impl (action a, target& t, bool apply) { - pair r (nullptr, nullptr); + pair r (nullptr, nullptr); // Clear the resolved targets list before calling match(). The rule // is free to, say, resize() this list in match() (provided that it @@ -79,7 +79,7 @@ namespace build const string& n (i->first); const rule& ru (i->second); - void* m (nullptr); + match_result m; { auto g ( make_exception_guard ( @@ -93,7 +93,7 @@ namespace build m = ru.match (a, t, hint); } - if (m != nullptr) + if (m) { // Do the ambiguity test. // @@ -106,7 +106,7 @@ namespace build const string& n1 (i->first); const rule& ru1 (i->second); - void* m1; + match_result m1; { auto g ( make_exception_guard ( @@ -120,7 +120,7 @@ namespace build m1 = ru1.match (a, t, hint); } - if (m1 != nullptr) + if (m1) { if (!ambig) { diff --git a/build/algorithm.ixx b/build/algorithm.ixx index 7300848..bd1f9c7 100644 --- a/build/algorithm.ixx +++ b/build/algorithm.ixx @@ -19,6 +19,13 @@ namespace build } inline target& + search (const target_type& t, const prerequisite_key& k) + { + return search ( + prerequisite_key {{&t, k.tk.dir, k.tk.name, k.tk.ext}, k.scope}); + } + + inline target& search (const target_type& type, const dir_path& dir, const std::string& name, @@ -38,7 +45,7 @@ namespace build return static_cast (search (T::static_type, dir, name, ext, scope)); } - std::pair + std::pair match_impl (action, target&, bool apply); inline void diff --git a/build/bin/rule b/build/bin/rule index b8b9873..743b1ca 100644 --- a/build/bin/rule +++ b/build/bin/rule @@ -14,21 +14,21 @@ namespace build class obj_rule: public rule { public: - virtual void* + virtual match_result match (action, target&, const std::string& hint) const; virtual recipe - apply (action, target&, void*) const; + apply (action, target&, const match_result&) const; }; class lib_rule: public rule { public: - virtual void* + virtual match_result match (action, target&, const std::string& hint) const; virtual recipe - apply (action, target&, void*) const; + apply (action, target&, const match_result&) const; static target_state perform (action, target&); diff --git a/build/bin/rule.cxx b/build/bin/rule.cxx index 1834f7a..d2928ca 100644 --- a/build/bin/rule.cxx +++ b/build/bin/rule.cxx @@ -19,7 +19,7 @@ namespace build { // obj // - void* obj_rule:: + match_result obj_rule:: match (action a, target& t, const std::string&) const { fail << diag_doing (a, t) << " target group" << @@ -29,21 +29,21 @@ namespace build } recipe obj_rule:: - apply (action, target&, void*) const {return empty_recipe;} + apply (action, target&, const match_result&) const {return empty_recipe;} // lib // // The whole logic is pretty much as if we had our two group // members as prerequisites. // - void* lib_rule:: + match_result lib_rule:: match (action, target& t, const std::string&) const { - return &t; + return t; } recipe lib_rule:: - apply (action a, target& xt, void*) const + apply (action a, target& xt, const match_result&) const { lib& t (static_cast (xt)); diff --git a/build/cli/rule b/build/cli/rule index 0f38381..d52d0e0 100644 --- a/build/cli/rule +++ b/build/cli/rule @@ -14,11 +14,11 @@ namespace build class compile: public rule { public: - virtual void* + virtual match_result match (action, target&, const std::string& hint) const; virtual recipe - apply (action, target&, void*) const; + apply (action, target&, const match_result&) const; static target_state perform_update (action, target&); diff --git a/build/cli/rule.cxx b/build/cli/rule.cxx index 6f8b648..09ef4f3 100644 --- a/build/cli/rule.cxx +++ b/build/cli/rule.cxx @@ -25,7 +25,7 @@ namespace build { using config::append_options; - void* compile:: + match_result compile:: match (action a, target& xt, const std::string&) const { tracer trace ("cli::compile::match"); @@ -36,18 +36,18 @@ namespace build // See if we have a .cli source file. // - prerequisite* r (nullptr); - for (prerequisite& p: group_prerequisites (t)) + match_result r; + for (prerequisite_member p: group_prerequisite_members (a, t)) { if (p.is_a ()) { //@@ Need to verify input and output stems match. - r = &p; + r = p; break; } } - if (r == nullptr) + if (!r) { level3 ([&]{trace << "no .cli source file for target " << t;}); return nullptr; @@ -112,12 +112,12 @@ namespace build // if (g == nullptr) { - for (prerequisite& p: group_prerequisites (t)) + for (prerequisite_member p: group_prerequisite_members (a, t)) { if (p.is_a ()) // @@ Need to check that stems match. { g = &targets.insert (t.dir, t.name, trace); - g->prerequisites.emplace_back (p); + g->prerequisites.emplace_back (p.as_prerequisite (trace)); break; } } @@ -146,7 +146,7 @@ namespace build } recipe compile:: - apply (action a, target& xt, void* vp) const + apply (action a, target& xt, const match_result& mr) const { if (cli_cxx* pt = xt.is_a ()) { @@ -182,7 +182,7 @@ namespace build } else { - cli_cxx& g (*static_cast (vp)); + cli_cxx& g (*static_cast (mr.target)); build::match (a, g); return &delegate; } diff --git a/build/cxx/rule b/build/cxx/rule index 8b42fc2..2bd344d 100644 --- a/build/cxx/rule +++ b/build/cxx/rule @@ -22,11 +22,11 @@ namespace build class compile: public rule { public: - virtual void* + virtual match_result match (action, target&, const std::string& hint) const; virtual recipe - apply (action, target&, void*) const; + apply (action, target&, const match_result&) const; static target_state perform_update (action, target&); @@ -35,11 +35,11 @@ namespace build class link: public rule { public: - virtual void* + virtual match_result match (action, target&, const std::string& hint) const; virtual recipe - apply (action, target&, void*) const; + apply (action, target&, const match_result&) const; static target_state perform_update (action, target&); diff --git a/build/cxx/rule.cxx b/build/cxx/rule.cxx index 8d91e1c..84e2f91 100644 --- a/build/cxx/rule.cxx +++ b/build/cxx/rule.cxx @@ -70,9 +70,6 @@ namespace build { for (target* t: l.prerequisite_targets) { - if (t == nullptr) - continue; - if (t->is_a () || t->is_a () || t->is_a ()) append_lib_options (args, *t, var); } @@ -82,7 +79,7 @@ namespace build // compile // - void* compile:: + match_result compile:: match (action a, target& t, const string&) const { tracer trace ("cxx::compile::match"); @@ -95,13 +92,13 @@ namespace build // // See if we have a C++ source file. Iterate in reverse so that - // a source file specified for an obj member overrides the one - // specified for the group. + // a source file specified for an obj*{} member overrides the one + // specified for the group. Also "see through" groups. // - for (prerequisite& p: reverse_iterate (group_prerequisites (t))) + for (prerequisite_member p: reverse_group_prerequisite_members (a, t)) { - if (p.type.id == typeid (cxx)) - return &p; + if (p.is_a ()) + return p; } level3 ([&]{trace << "no c++ source file for target " << t;}); @@ -112,7 +109,7 @@ namespace build inject_prerequisites (action, target&, cxx&, scope&); recipe compile:: - apply (action a, target& xt, void* v) const + apply (action a, target& xt, const match_result& mr) const { path_target& t (static_cast (xt)); @@ -131,9 +128,20 @@ namespace build // When cleaning, ignore prerequisites that are not in the same // or a subdirectory of ours. // - for (prerequisite& p: group_prerequisites (t)) + const auto& ps (group_prerequisite_members (a, t)); + for (auto i (ps.begin ()); i != ps.end (); ++i) { - target& pt (search (p)); + prerequisite_member p (*i); + + // See through the group unless it is one that we recognize. + // + if (p.is_a ()) + { + if (!p.is_a ()) + continue; + } + + target& pt (p.search ()); if (a.operation () == clean_id && !pt.dir.sub (t.dir)) continue; @@ -147,7 +155,10 @@ namespace build // populated; see append_lib_options() above. // if (pt.is_a () || pt.is_a () || pt.is_a ()) + { + i.skip_group (); // Don't go inside the lib{} group. continue; + } t.prerequisite_targets.push_back (&pt); } @@ -158,14 +169,16 @@ namespace build // if (a.operation () == update_id) { - // 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. + // The cached prerequisite 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)); - - inject_prerequisites (a, t, st, sp.scope); + // @@ Ugly. + // + cxx& st ( + dynamic_cast ( + mr.target != nullptr ? *mr.target : *mr.prerequisite->target)); + inject_prerequisites (a, t, st, mr.prerequisite->scope); } switch (a) @@ -313,8 +326,9 @@ namespace build { prefix_map m; - // First process the include directories from prerequsite - // libraries. + // First process the include directories from prerequisite + // libraries. Note that here we don't need to see group + // members (see apply()). // for (prerequisite& p: group_prerequisites (t)) { @@ -383,7 +397,8 @@ namespace build vector args {cxx.c_str ()}; - // Add cxx.export.poptions from prerequisite libraries. + // Add cxx.export.poptions from prerequisite libraries. Note + // that here we don't need to see group members (see apply()). // for (prerequisite& p: group_prerequisites (t)) { @@ -437,8 +452,8 @@ namespace build // end up with an error during compilation proper. // // One complication with this restart logic is that we will see - // a "prefix" of prerequsites that we have already processed - // (i.e., they are already in our prerequsite_targets list) and + // a "prefix" of prerequisites that we have already processed + // (i.e., they are already in our prerequisite_targets list) and // we don't want to keep redoing this over and over again. One // thing to note, however, is that the prefix that we have seen // on the previous run must appear exactly the same in the @@ -451,7 +466,7 @@ namespace build // of a reason why would someone do otherwise). And we have // already made sure that all those files are up to date. And // here is the way we are going to exploit this: we are going - // to keep track of how many prerequsites we have processed so + // to keep track of how many prerequisites we have processed so // far and on restart skip right to the next one. // // Also, before we do all that, make sure the source file itself @@ -726,7 +741,8 @@ namespace build vector args {cxx.c_str ()}; - // Add cxx.export.poptions from prerequisite libraries. + // Add cxx.export.poptions from prerequisite libraries. Note that + // here we don't need to see group members (see apply()). // for (prerequisite& p: group_prerequisites (t)) { @@ -814,7 +830,7 @@ namespace build : lv.size () > 1 && lv[1].value == "shared" ? order::a_so : order::a; } - void* link:: + match_result link:: match (action a, target& t, const string& hint) const { tracer trace ("cxx::link::match"); @@ -839,17 +855,20 @@ namespace build bool seen_cxx (false), seen_c (false), seen_obj (false), seen_lib (false); - for (prerequisite& p: group_prerequisites (t)) + const auto& ps (group_prerequisite_members (a, t)); + for (auto i (ps.begin ()); i != ps.end (); ++i) { - if (p.type.id == typeid (cxx)) // @@ Should use is_a (add to p.type). + prerequisite_member p (*i); + + if (p.is_a ()) { seen_cxx = seen_cxx || true; } - else if (p.type.id == typeid (c)) + else if (p.is_a ()) { seen_c = seen_c || true; } - else if (p.type.id == typeid (obja)) + else if (p.is_a ()) { if (so) fail << "shared library " << t << " prerequisite " << p @@ -857,19 +876,30 @@ namespace build seen_obj = seen_obj || true; } - else if (p.type.id == typeid (objso) || p.type.id == typeid (obj)) + else if (p.is_a () || + p.is_a ()) { seen_obj = seen_obj || true; + i.skip_group (); // Don't go inside the obj{} group. } - else if (p.type.id == typeid (liba) || - p.type.id == typeid (libso) || - p.type.id == typeid (lib)) + else if (p.is_a () || + p.is_a () || + p.is_a ()) { seen_lib = seen_lib || true; + i.skip_group (); // Don't go inside the lib{} group. } - else if (p.type.id != typeid (fsdir)) + else if (p.is_a () || + p.is_a () || + p.is_a () || + p.is_a () || + p.is_a ()) + ; + else if (p.is_a ()) + ; // See through. + else { - level3 ([&]{trace << "unexpected prerequisite type " << p.type;}); + level3 ([&]{trace << "unexpected prerequisite type " << p.type ();}); return nullptr; } } @@ -887,7 +917,7 @@ namespace build } recipe link:: - apply (action a, target& xt, void*) const + apply (action a, target& xt, const match_result&) const { tracer trace ("cxx::link::apply"); @@ -923,24 +953,36 @@ namespace build // Process prerequisites: do rule chaining for C and C++ source // files as well as search and match. // - for (prerequisite_ref& pr: group_prerequisites (t)) + const auto& ps (group_prerequisite_members (a, t)); + for (auto i (ps.begin ()); i != ps.end (); ++i) { - bool group (!pr.belongs (t)); // Target group's prerequisite. + prerequisite_member p (*i); + + // See through the group unless it is one that we recognize. + // + if (p.is_a ()) + { + if (!p.is_a () && + !p.is_a ()) + continue; + } + + bool group (!p.prerequisite.belongs (t)); // 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(). // - pt = &search (p); + pt = &p.search (); 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. + // In both cases, skip going over the group's members. // if (obj* o = pt->is_a ()) { @@ -948,7 +990,9 @@ namespace build if (pt == nullptr) pt = &search (so ? objso::static_type : obja::static_type, - p.dir, p.name, p.ext, &p.scope); + p.key ()); + + i.skip_group (); } else if (lib* l = pt->is_a ()) { @@ -983,7 +1027,9 @@ namespace build if (pt == nullptr) pt = &search (lso ? libso::static_type : liba::static_type, - p.dir, p.name, p.ext, &p.scope); + p.key ()); + + i.skip_group (); } build::match (a, *pt); @@ -1002,7 +1048,7 @@ namespace build src_root = &root->src_path (); } - prerequisite& cp (p); // c(xx){} prerequisite. + const prerequisite_key& cp (p.key ()); // c(xx){} prerequisite key. const target_type& o_type ( group ? obj::static_type @@ -1016,19 +1062,23 @@ namespace build // possible it is under out_root (e.g., generated source). // dir_path d; - if (cp.dir.relative () || cp.dir.sub (*out_root)) - d = cp.dir; - else { - if (!cp.dir.sub (*src_root)) - fail << "out of project prerequisite " << cp << - info << "specify corresponding " << o_type.name << "{} " - << "target explicitly"; + const dir_path& cpd (*cp.tk.dir); - d = *out_root / cp.dir.leaf (*src_root); + if (cpd.relative () || cpd.sub (*out_root)) + d = cpd; + else + { + if (!cpd.sub (*src_root)) + fail << "out of project prerequisite " << cp << + info << "specify corresponding " << o_type.name << "{} " + << "target explicitly"; + + d = *out_root / cpd.leaf (*src_root); + } } - target& ot (search (o_type, d, cp.name, nullptr, &cp.scope)); + target& ot (search (o_type, d, *cp.tk.name, nullptr, cp.scope)); // If we are cleaning, check that this target is in the same or // a subdirectory of ours. @@ -1069,54 +1119,69 @@ namespace build // target. If things don't work out, then we fail, in which case // searching and matching speculatively doesn't really hurt. // - prerequisite* cp1 (nullptr); - for (prerequisite& p: reverse_iterate (group_prerequisites (*pt))) + bool found (false); + const auto& ps (reverse_group_prerequisite_members (a, *pt)); + for (auto i (ps.begin ()); i != ps.end (); ++i) { - // Ignore some known target types (fsdir, headers, libraries). - // - if (p.is_a () || - p.is_a () || - (cp.is_a () && (p.is_a () || - p.is_a () || - p.is_a ())) || - p.is_a () || - p.is_a () || - p.is_a ()) - continue; + prerequisite_member p1 (*i); - if (p.is_a ()) + // See through the group unless it is one that we recognize. + // + if (p1.is_a ()) { - cp1 = &p; - continue; // Check the rest of the prerequisites. + if (!p1.is_a ()) + continue; } - fail << "synthesized target for prerequisite " << cp - << " would be incompatible with existing target " << *pt << - info << "unknown existing prerequisite type " << p << - info << "specify corresponding obj{} target explicitly"; - } - - if (cp1 != nullptr) - { - build::match (a, *pt); // Now cp1 should be resolved. - search (cp); // Our own prerequisite, so this is ok. + // Ignore some known target types (fsdir, headers, libraries). + // + if (p1.is_a () || + p1.is_a () || + (p.is_a () && (p1.is_a () || + p1.is_a () || + p1.is_a ())) || + p1.is_a () || + p1.is_a () || + p1.is_a ()) + { + i.skip_group (); // Skip going inside lib{}. + continue; + } - if (cp.target != cp1->target) + if (!p1.is_a ()) fail << "synthesized target for prerequisite " << cp << " would be incompatible with existing target " << *pt << - info << "existing prerequisite " << *cp1 << " does not " - << "match " << cp << - info << "specify corresponding " << o_type.name << "{} " - << "target explicitly"; + info << "unexpected existing prerequisite type " << p1 << + info << "specify corresponding obj{} target explicitly"; + + if (!found) + { + build::match (a, *pt); // Now p1 should be resolved. + + // Searching our own prerequisite is ok. + // + if (&p.search () != &p1.search ()) + fail << "synthesized target for prerequisite " << cp << " would " + << "be incompatible with existing target " << *pt << + info << "existing prerequisite " << p1 << " does not match " + << cp << + info << "specify corresponding " << o_type.name << "{} target " + << "explicitly"; + + found = true; + // Check the rest of the prerequisites. + } } - else + + if (!found) { // Note: add the source to the group, not the member. // - ot.prerequisites.emplace_back (cp); + ot.prerequisites.emplace_back (p.as_prerequisite (trace)); // Add our lib*{} prerequisites to the object file (see - // cxx.export.poptions above for details). + // cxx.export.poptions above for details). Note: no need + // to go into group members. // // Initially, we were only adding imported libraries, but // there is a problem with this approach: the non-imported diff --git a/build/prerequisite b/build/prerequisite index 529b225..e33feb6 100644 --- a/build/prerequisite +++ b/build/prerequisite @@ -81,7 +81,7 @@ namespace build // template bool - is_a () const {return type.id == typeid (T);} + is_a () const {return type.is_a ();} }; inline bool @@ -107,6 +107,12 @@ namespace build const std::string* ext, scope&, tracer&); + + std::pair + insert (const target_key& tk, scope& s, tracer& t) + { + return insert (*tk.type, *tk.dir, *tk.name, *tk.ext, s, t); + } }; } diff --git a/build/prerequisite.cxx b/build/prerequisite.cxx index 7ccf37d..d4c8a9c 100644 --- a/build/prerequisite.cxx +++ b/build/prerequisite.cxx @@ -15,6 +15,8 @@ using namespace std; namespace build { + // prerequisite_key + // ostream& operator<< (ostream& os, const prerequisite_key& pk) { diff --git a/build/rule b/build/rule index 7a4d4d7..8c7e4c1 100644 --- a/build/rule +++ b/build/rule @@ -6,6 +6,7 @@ #define BUILD_RULE #include +#include // nullptr_t #include #include // reference_wrapper #include @@ -18,14 +19,41 @@ namespace build { + class match_result + { + public: + typedef build::target target_type; + typedef build::prerequisite prerequisite_type; + + // Can contain neither (both are NULL), one of, or both. If both + // are NULL, then it is a "no match" indicator. + // + prerequisite_type* prerequisite; + target_type* target; + + match_result (std::nullptr_t v = nullptr): prerequisite (v), target (v) {} + match_result (prerequisite_type& p): prerequisite (&p), target (nullptr) {} + match_result (prerequisite_type* p): prerequisite (p), target (nullptr) {} + match_result (target_type& t): prerequisite (nullptr), target (&t) {} + match_result (target_type* t): prerequisite (nullptr), target (t) {} + match_result (const prerequisite_member& pm) + : prerequisite (&pm.prerequisite.get ()), target (pm.target) {} + + explicit + operator bool () const + { + return prerequisite != nullptr || target != nullptr; + } + }; + class rule { public: - virtual void* + virtual match_result match (action, target&, const std::string& hint) const = 0; virtual recipe - apply (action, target&, void*) const = 0; + apply (action, target&, const match_result&) const = 0; }; using target_rule_map = std::unordered_map< @@ -42,11 +70,11 @@ namespace build class path_rule: public rule { public: - virtual void* + virtual match_result match (action, target&, const std::string& hint) const; virtual recipe - apply (action, target&, void*) const; + apply (action, target&, const match_result&) const; static target_state perform_update (action, target&); @@ -55,21 +83,21 @@ namespace build class dir_rule: public rule { public: - virtual void* + virtual match_result match (action, target&, const std::string& hint) const; virtual recipe - apply (action, target&, void*) const; + apply (action, target&, const match_result&) const; }; class fsdir_rule: public rule { public: - virtual void* + virtual match_result match (action, target&, const std::string& hint) const; virtual recipe - apply (action, target&, void*) const; + apply (action, target&, const match_result&) const; static target_state perform_update (action, target&); diff --git a/build/rule.cxx b/build/rule.cxx index 34aae9d..e834426 100644 --- a/build/rule.cxx +++ b/build/rule.cxx @@ -30,7 +30,7 @@ namespace build // that normal implementations should follow. So you probably shouldn't // use it as a guide to implement your own, normal, rules. // - void* path_rule:: + match_result path_rule:: match (action a, target& t, const string&) const { // While strictly speaking we should check for the file's existence @@ -60,12 +60,12 @@ namespace build return pt.mtime () != timestamp_nonexistent ? &t : nullptr; } default: - return &t; + return t; } } recipe path_rule:: - apply (action a, target& t, void*) const + apply (action a, target& t, const match_result&) const { // Update triggers the update of this target's prerequisites // so it would seem natural that we should also trigger their @@ -125,14 +125,14 @@ namespace build // dir_rule // - void* dir_rule:: + match_result dir_rule:: match (action a, target& t, const string&) const { - return &t; + return t; } recipe dir_rule:: - apply (action a, target& t, void*) const + apply (action a, target& t, const match_result&) const { // When cleaning, ignore prerequisites that are not in the same // or a subdirectory of ours. For default, we don't do anything @@ -151,14 +151,14 @@ namespace build // fsdir_rule // - void* fsdir_rule:: + match_result fsdir_rule:: match (action a, target& t, const string&) const { - return &t; + return t; } recipe fsdir_rule:: - apply (action a, target& t, void*) const + apply (action a, target& t, const match_result&) const { switch (a.operation ()) { diff --git a/build/search.cxx b/build/search.cxx index dec86db..872a0d6 100644 --- a/build/search.cxx +++ b/build/search.cxx @@ -76,6 +76,7 @@ namespace build tk.ext = &ext; } else + { // What should we do here, fail or say we didn't find anything? // Current think is that if the target type didn't provide the // default extension, then it doesn't want us to search for an @@ -84,7 +85,10 @@ namespace build // think. // //fail << "no default extension for prerequisite " << pk; + level3 ([&]{trace << "no existing file found for prerequisite " + << pk;}); return nullptr; + } } // Go over paths looking for a file. diff --git a/build/target b/build/target index 8161438..b86ea94 100644 --- a/build/target +++ b/build/target @@ -8,15 +8,16 @@ #include #include #include -#include // unique_ptr -#include // size_t -#include // function, reference_wrapper +#include // unique_ptr +#include // size_t +#include // function, reference_wrapper #include #include -#include // move() +#include // move(), forward(), declval() #include +#include -#include // compare_c_string +#include // compare_c_string, reverse_iterate() #include // map_iterator_adapter #include @@ -31,6 +32,9 @@ namespace build class target; class target_group; + target& + search (prerequisite&); // From . + // Target state. // enum class target_state {unknown, postponed, unchanged, changed, failed}; @@ -359,7 +363,7 @@ namespace build private: target* t_ {nullptr}; prerequisites_type* c_ {nullptr}; - prerequisites_type::iterator i_; + base_iterator i_; }; typedef std::reverse_iterator reverse_iterator; @@ -500,6 +504,215 @@ namespace build static const target_type static_type; }; + // A member of a prerequisite. If 'target' is NULL, then this is the + // prerequisite itself. Otherwise, it is its member. In this case + // 'prerequisite' still refers to the prerequisite. + // + struct prerequisite_member + { + typedef build::target target_type; + typedef build::prerequisite prerequisite_type; + + prerequisite_ref& prerequisite; + target_type* target; + + template + bool + is_a () const + { + return target != nullptr + ? target->is_a () != nullptr + : prerequisite.get ().is_a (); + } + + prerequisite_key + key () const + { + return target != nullptr + ? prerequisite_key {target->key (), nullptr} + : prerequisite.get ().key (); + } + + const build::target_type& + type () const + { + return target != nullptr ? target->type () : prerequisite.get ().type; + } + + target_type& + search () const + { + return target != nullptr ? *target : build::search (prerequisite); + } + + prerequisite_type& + as_prerequisite (tracer&) const; + }; + + inline std::ostream& + operator<< (std::ostream& os, const prerequisite_member& pm) + { + return os << pm.key (); + } + + // A "range" that presents a sequence of prerequisites (e.g., from + // group_prerequisites()) as a sequence of prerequisite_member's. For + // each prerequisite you will first "see" the prerequisite itself + // followed by all its members, if it resolves to a target group. + // You can skip the group members with the skip_group() iterator + // function. Usage: + // + // for (prerequisite_member pm: prerequisite_members (a, ...)) + // + // Where ... can be: + // + // t.prerequisites + // reverse_iterate(t.prerequisites) + // group_prerequisites (t) + // reverse_iterate (group_prerequisites (t)) + // + // But use shortcuts instead: + // + // prerequisite_members (a, t) + // reverse_prerequisite_members (a, t) + // group_prerequisite_members (a, t) + // reverse_group_prerequisite_members (a, t) + // + template + class prerequisite_members_range; + + template + inline prerequisite_members_range + prerequisite_members (action a, T&& x) + { + return prerequisite_members_range (a, std::forward (x)); + } + + template + class prerequisite_members_range + { + public: + prerequisite_members_range (action a, T&& r) + : a_ (a), r_ (std::forward (r)) {} + + struct iterator + { + using base_iterator = decltype (std::declval ().begin ()); + + typedef prerequisite_member value_type; + typedef const value_type* pointer; + typedef const value_type& reference; + typedef typename base_iterator::difference_type difference_type; + typedef std::forward_iterator_tag iterator_category; + + iterator (): a_ (0, 0) {} + iterator (action a, base_iterator i): a_ (a), i_ (i), g_ {nullptr, 0} {} + + iterator& operator++ (); + iterator operator++ (int) {iterator r (*this); return ++r;} + + // Skip iterating over this group's members, if any. Note that + // the only valid operation after this call is to increment the + // iterator. + // + // + void + skip_group () + { + // Pretend we are on the last member of some group. + // + j_ = 0; + g_.count = 1; + } + + /* + reference operator* () const + { + m_.prerequisite = *i; + m_.target = g_.count != 0 ? g_.members[j_] : nullptr; + return m_; + } + */ + + value_type operator* () const + { + return value_type {*i_, g_.count != 0 ? g_.members[j_] : nullptr}; + } + + pointer operator-> () const + { + static_assert ( + std::is_trivially_destructible::value, + "prerequisite_member is not trivially destructible"); + + return new (&m_) + value_type {*i_, g_.count != 0 ? g_.members[j_] : nullptr}; + } + + friend bool + operator== (const iterator& x, const iterator& y) + { + return x.i_ == y.i_ && + x.g_.count == y.g_.count && + (x.g_.count == 0 || x.j_ == y.j_); + } + + friend bool + operator!= (const iterator& x, const iterator& y) {return !(x == y);} + + private: + action a_; + base_iterator i_; + group_view g_; + std::size_t j_; + mutable std::aligned_storage::type m_; + }; + + iterator + begin () const {return iterator (a_, r_.begin ());} + + iterator + end () const {return iterator (a_, r_.end ());} + + private: + action a_; + T r_; + }; + + // prerequisite_members(t.prerequisites) + // + inline auto + prerequisite_members (action a, target& t) + { + return prerequisite_members (a, t.prerequisites); + } + + // prerequisite_members(reverse_iterate(t.prerequisites)) + // + inline auto + reverse_prerequisite_members (action a, target& t) + { + return prerequisite_members (a, butl::reverse_iterate (t.prerequisites)); + } + + // prerequisite_members(group_prerequisites (t)) + // + inline auto + group_prerequisite_members (action a, target& t) + { + return prerequisite_members (a, group_prerequisites (t)); + } + + // prerequisite_members(reverse_iterate (group_prerequisites (t))) + // + inline auto + reverse_group_prerequisite_members (action a, target& t) + { + return prerequisite_members ( + a, butl::reverse_iterate (group_prerequisites (t))); + } + // Modification time-based target. // class mtime_target: public target diff --git a/build/target-key b/build/target-key index 3875a6a..d791d61 100644 --- a/build/target-key +++ b/build/target-key @@ -28,6 +28,13 @@ namespace build target* (*const factory) (dir_path, std::string, const std::string*); const std::string& (*const extension) (const target_key&, scope&); target* (*const search) (const prerequisite_key&); + + bool + is_a (const std::type_index&) const; // Defined in target.cxx + + template + bool + is_a () const {return is_a (typeid (T));} }; inline std::ostream& diff --git a/build/target.cxx b/build/target.cxx index 6cd1fc9..ec96779 100644 --- a/build/target.cxx +++ b/build/target.cxx @@ -17,6 +17,18 @@ using namespace std; namespace build { + // target_type + // + bool target_type:: + is_a (const type_index& id) const + { + for (const target_type* p (this); p != nullptr; p = p->base) + if (p->id == id) + return true; + + return false; + } + // target_state // static const char* target_state_[] = { diff --git a/build/target.ixx b/build/target.ixx index 837769e..b6bf509 100644 --- a/build/target.ixx +++ b/build/target.ixx @@ -2,12 +2,73 @@ // copyright : Copyright (c) 2014-2015 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file +#include + namespace build { + // prerequisite_ref + // inline bool prerequisite_ref:: belongs (const target& t) const { const auto& p (t.prerequisites); return !(p.empty () || this < &p.front () || this > &p.back ()); } + + // prerequisite_member + // + inline prerequisite& prerequisite_member:: + as_prerequisite (tracer& trace) const + { + if (target == nullptr) + return prerequisite; + + // The use of the group's prerequisite scope is debatable. + // + scope& s (prerequisite.get ().scope); + return s.prerequisites.insert (key ().tk, s, trace).first; + } + + // prerequisite_members + // + group_view + resolve_group_members (action, target_group&); // + + template + inline auto prerequisite_members_range::iterator:: + operator++ () -> iterator& + { + if (g_.count != 0) + { + // Member iteration. + // + if (++j_ == g_.count) + { + // Switch back to prerequisite iteration. + // + g_.count = 0; + ++i_; + } + } + else + { + // Prerequisite iteration. + // + if (i_->get ().template is_a ()) + { + // Switch to member iteration. + // + target_group& g (static_cast (search (*i_))); + j_ = 0; + g_ = resolve_group_members (a_, g); + + if (g_.count == 0) + ++i_; // Empty group. + } + else + ++i_; + } + + return *this; + } } diff --git a/tests/cli/lib/libtest/test/buildfile b/tests/cli/lib/libtest/test/buildfile index 61841e7..226e43d 100644 --- a/tests/cli/lib/libtest/test/buildfile +++ b/tests/cli/lib/libtest/test/buildfile @@ -1,6 +1,6 @@ -lib{test}: cxx{utility} cxx{test base} extra/cxx{test} -cxx{test} hxx{test}: cli{test} -cxx{base} hxx{base}: cli{base} +lib{test}: cxx{utility} cli.cxx{test base} extra/cxx{test} +cli.cxx{test}: cli{test} +cli.cxx{base}: cli{base} cli.options += -I $src_root --include-with-brackets diff --git a/tests/cli/lib/test/buildfile b/tests/cli/lib/test/buildfile index fc0e552..70a4dc1 100644 --- a/tests/cli/lib/test/buildfile +++ b/tests/cli/lib/test/buildfile @@ -1,6 +1,6 @@ import libs += cli-lib-libtest -exe{driver}: cxx{driver} cxx{test} $libs -cxx{test} hxx{test}: cli{test} +exe{driver}: cxx{driver} cli.cxx{test} $libs +cli.cxx{test}: cli{test} cxx.poptions = -I$out_root -- cgit v1.1