diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2015-07-01 09:11:31 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2015-07-01 09:11:31 +0200 |
commit | 17b3a78696f0b1fd6f0f60d53ec568cf3b9e32b4 (patch) | |
tree | f54f92e5066e1a97a070af248931316cd76b61c3 | |
parent | 70af0087d8efb3f2f7dc9ffdf2568419913f16da (diff) |
Cleanup group "see through" design
-rw-r--r-- | build/algorithm | 29 | ||||
-rw-r--r-- | build/algorithm.cxx | 44 | ||||
-rw-r--r-- | build/algorithm.ixx | 26 | ||||
-rw-r--r-- | build/bin/rule.cxx | 2 | ||||
-rw-r--r-- | build/bin/target.cxx | 21 | ||||
-rw-r--r-- | build/cli/rule.cxx | 10 | ||||
-rw-r--r-- | build/cli/target | 6 | ||||
-rw-r--r-- | build/cli/target.cxx | 10 | ||||
-rw-r--r-- | build/cxx/rule.cxx | 60 | ||||
-rw-r--r-- | build/cxx/target.cxx | 18 | ||||
-rw-r--r-- | build/rule.cxx | 39 | ||||
-rw-r--r-- | build/target | 313 | ||||
-rw-r--r-- | build/target-key | 1 | ||||
-rw-r--r-- | build/target.cxx | 34 | ||||
-rw-r--r-- | build/target.ixx | 53 |
15 files changed, 350 insertions, 316 deletions
diff --git a/build/algorithm b/build/algorithm index 2547466..1cfd15b 100644 --- a/build/algorithm +++ b/build/algorithm @@ -60,25 +60,38 @@ namespace build void match (action, target&); - // The default prerequisite search and match implementation. It calls + // The standard prerequisite search and match implementations. They call + // search_and_match_*() versions below passing non-empty directory for + // the clean operation. + // + void + search_and_match_prerequisites (action, target&); + + // If we are cleaning, this function doesn't go into group members, + // as an optimization (the group should clean everything up). + // + void + search_and_match_prerequisite_members (action, target&); + + // The actual prerequisite search and match implementations. They call // search() and then match() for each prerequisite in a loop. If this - // target is a member of a group, then it first does this to the group's + // target is a member of a group, then they first do this to the group's // prerequisites. // + // If the directory argument is not empty, then they ignore (do not + // match) prerequisites that are not in the same or its subdirectory. + // void - search_and_match (action, target&); + search_and_match_prerequisites (action, target&, const dir_path&); - // 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&); + search_and_match_prerequisite_members (action, target&, const dir_path&); // Unless already available, match, and, if necessary, execute // the group in order to obtain its members list. // group_view - resolve_group_members (action, target_group&); + resolve_group_members (action, target&); // Inject dependency on the parent directory's fsdir{}, unless it is // the project's out_root (or is outside of any project; say, for diff --git a/build/algorithm.cxx b/build/algorithm.cxx index 5c7805f..40d4b0b 100644 --- a/build/algorithm.cxx +++ b/build/algorithm.cxx @@ -175,7 +175,7 @@ namespace build } group_view - resolve_group_members_impl (action a, target_group& g) + resolve_group_members_impl (action a, target& g) { group_view r; @@ -186,7 +186,7 @@ namespace build { auto p (match_impl (a, g, false)); - r = g.members (a); + r = g.group_members (a); if (r.members != nullptr) return r; @@ -196,42 +196,48 @@ namespace build g.recipe (a, p.first->apply (a, g, p.second)); } - // 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. + // 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.members (a); + r = g.group_members (a); assert (r.members != nullptr); // What "next step" did the group expect? return r; } void - search_and_match (action a, target& t) + search_and_match_prerequisites (action a, target& t, const dir_path& d) { - group_prerequisites gp (t); - size_t i (t.prerequisite_targets.size ()); - t.prerequisite_targets.resize (gp.size () + i); + const bool e (d.empty ()); - for (prerequisite& p: gp) + for (prerequisite p: group_prerequisites (t)) { target& pt (search (p)); - match (a, pt); - t.prerequisite_targets[i++] = &pt; + + if (e || pt.dir.sub (d)) + { + match (a, pt); + t.prerequisite_targets.push_back (&pt); + } } } void - search_and_match (action a, target& t, const dir_path& d) + search_and_match_prerequisite_members (action a, + target& t, + const dir_path& d) { - for (prerequisite& p: group_prerequisites (t)) + const bool e (d.empty ()); + + for (prerequisite_member p: group_prerequisite_members (a, t)) { - target& pt (search (p)); + target& pt (p.search ()); - if (pt.dir.sub (d)) + if (e || pt.dir.sub (d)) { match (a, pt); t.prerequisite_targets.push_back (&pt); diff --git a/build/algorithm.ixx b/build/algorithm.ixx index bd1f9c7..ddd63f6 100644 --- a/build/algorithm.ixx +++ b/build/algorithm.ixx @@ -58,15 +58,35 @@ namespace build } group_view - resolve_group_members_impl (action, target_group&); + resolve_group_members_impl (action, target&); inline group_view - resolve_group_members (action a, target_group& g) + resolve_group_members (action a, target& g) { - group_view r (g.members (a)); + group_view r (g.group_members (a)); return r.members != nullptr ? r : resolve_group_members_impl (a, g); } + inline void + search_and_match_prerequisites (action a, target& t) + { + search_and_match_prerequisites ( + a, t, a.operation () != clean_id ? dir_path () : t.dir); + } + + inline void + search_and_match_prerequisite_members (action a, target& t) + { + if (a.operation () != clean_id) + search_and_match_prerequisite_members (a, t, dir_path ()); + else + // Note that here we don't iterate over members even for see + // through groups since the group target should clean eveything + // up. A bit of an optimization. + // + search_and_match_prerequisites (a, t, t.dir); + } + target_state execute_impl (action, target&); diff --git a/build/bin/rule.cxx b/build/bin/rule.cxx index d2928ca..0fc0e40 100644 --- a/build/bin/rule.cxx +++ b/build/bin/rule.cxx @@ -34,7 +34,7 @@ namespace build // lib // // The whole logic is pretty much as if we had our two group - // members as prerequisites. + // members as our prerequisites. // match_result lib_rule:: match (action, target& t, const std::string&) const diff --git a/build/bin/target.cxx b/build/bin/target.cxx index 66eadb7..1190bfc 100644 --- a/build/bin/target.cxx +++ b/build/bin/target.cxx @@ -29,7 +29,8 @@ namespace build &file::static_type, &obja_factory, nullptr, - &search_file + &search_file, + false }; static target* @@ -51,7 +52,8 @@ namespace build &file::static_type, &objso_factory, nullptr, - &search_file + &search_file, + false }; static target* @@ -77,7 +79,8 @@ namespace build &target::static_type, &obj_factory, nullptr, - &search_target + &search_target, + false }; const target_type exe::static_type @@ -87,7 +90,8 @@ namespace build &file::static_type, &target_factory<exe>, nullptr, - &search_file + &search_file, + false }; static target* @@ -109,7 +113,8 @@ namespace build &file::static_type, &liba_factory, nullptr, - &search_file + &search_file, + false }; static target* @@ -131,7 +136,8 @@ namespace build &file::static_type, &libso_factory, nullptr, - &search_file + &search_file, + false }; static target* @@ -157,7 +163,8 @@ namespace build &target::static_type, &lib_factory, nullptr, - &search_target + &search_target, + false }; } } diff --git a/build/cli/rule.cxx b/build/cli/rule.cxx index 09ef4f3..2d001c4 100644 --- a/build/cli/rule.cxx +++ b/build/cli/rule.cxx @@ -163,15 +163,9 @@ namespace build // inject_parent_fsdir (a, t); - // Search and match prerequisites. + // Search and match prerequisite members. // - switch (a.operation ()) - { - case default_id: - case update_id: search_and_match (a, t); break; - case clean_id: search_and_match (a, t, t.dir); break; - default: assert (false); // We didn't register for this. - } + search_and_match_prerequisite_members (a, t); switch (a) { diff --git a/build/cli/target b/build/cli/target index e5bf16d..e72d4e0 100644 --- a/build/cli/target +++ b/build/cli/target @@ -23,10 +23,10 @@ namespace build static const target_type static_type; }; - class cli_cxx: public target_group + class cli_cxx: public target { public: - using target_group::target_group; + using target::target; target* m[3] {nullptr, nullptr, nullptr}; @@ -39,7 +39,7 @@ namespace build void i (cxx::ixx& t) {m[2] = &t;} virtual group_view - members (action) const; + group_members (action) const; public: virtual const target_type& type () const {return static_type;} diff --git a/build/cli/target.cxx b/build/cli/target.cxx index 2854818..aded1ff 100644 --- a/build/cli/target.cxx +++ b/build/cli/target.cxx @@ -20,13 +20,14 @@ namespace build &file::static_type, &target_factory<cli>, &target_extension_fix<cli_ext>, - &search_file + &search_file, + false }; // cli.cxx // group_view cli_cxx:: - members (action) const + group_members (action) const { return m[0] != nullptr ? group_view {m, (m[2] != nullptr ? 3U : 2U)} @@ -37,10 +38,11 @@ namespace build { typeid (cli_cxx), "cli.cxx", - &target_group::static_type, + &target::static_type, &target_factory<cli_cxx>, nullptr, - &search_target + &search_target, + true // See through default semantics. }; } } diff --git a/build/cxx/rule.cxx b/build/cxx/rule.cxx index 84e2f91..0812922 100644 --- a/build/cxx/rule.cxx +++ b/build/cxx/rule.cxx @@ -128,19 +128,8 @@ namespace build // When cleaning, ignore prerequisites that are not in the same // or a subdirectory of ours. // - const auto& ps (group_prerequisite_members (a, t)); - for (auto i (ps.begin ()); i != ps.end (); ++i) + for (prerequisite_member p: group_prerequisite_members (a, t)) { - prerequisite_member p (*i); - - // See through the group unless it is one that we recognize. - // - if (p.is_a<target_group> ()) - { - if (!p.is_a<lib> ()) - continue; - } - target& pt (p.search ()); if (a.operation () == clean_id && !pt.dir.sub (t.dir)) @@ -155,10 +144,7 @@ namespace build // populated; see append_lib_options() above. // if (pt.is_a<lib> () || pt.is_a<liba> () || pt.is_a<libso> ()) - { - i.skip_group (); // Don't go inside the lib{} group. continue; - } t.prerequisite_targets.push_back (&pt); } @@ -855,11 +841,8 @@ namespace build bool seen_cxx (false), seen_c (false), seen_obj (false), seen_lib (false); - const auto& ps (group_prerequisite_members (a, t)); - for (auto i (ps.begin ()); i != ps.end (); ++i) + for (prerequisite_member p: group_prerequisite_members (a, t)) { - prerequisite_member p (*i); - if (p.is_a<cxx> ()) { seen_cxx = seen_cxx || true; @@ -880,14 +863,12 @@ namespace build p.is_a<obj> ()) { seen_obj = seen_obj || true; - i.skip_group (); // Don't go inside the obj{} group. } else if (p.is_a<liba> () || p.is_a<libso> () || p.is_a<lib> ()) { seen_lib = seen_lib || true; - i.skip_group (); // Don't go inside the lib{} group. } else if (p.is_a<h> () || p.is_a<hxx> () || @@ -895,8 +876,6 @@ namespace build p.is_a<txx> () || p.is_a<fsdir> ()) ; - else if (p.is_a<target_group> ()) - ; // See through. else { level3 ([&]{trace << "unexpected prerequisite type " << p.type ();}); @@ -953,22 +932,9 @@ namespace build // Process prerequisites: do rule chaining for C and C++ source // files as well as search and match. // - const auto& ps (group_prerequisite_members (a, t)); - for (auto i (ps.begin ()); i != ps.end (); ++i) + for (prerequisite_member p: group_prerequisite_members (a, t)) { - prerequisite_member p (*i); - - // See through the group unless it is one that we recognize. - // - if (p.is_a<target_group> ()) - { - if (!p.is_a<lib> () && - !p.is_a<obj> ()) - continue; - } - bool group (!p.prerequisite.belongs (t)); // Group's prerequisite. - target* pt (nullptr); if (!p.is_a<c> () && !p.is_a<cxx> ()) @@ -982,7 +948,6 @@ namespace build // 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<obj> ()) { @@ -991,8 +956,6 @@ namespace build if (pt == nullptr) pt = &search (so ? objso::static_type : obja::static_type, p.key ()); - - i.skip_group (); } else if (lib* l = pt->is_a<lib> ()) { @@ -1028,8 +991,6 @@ namespace build if (pt == nullptr) pt = &search (lso ? libso::static_type : liba::static_type, p.key ()); - - i.skip_group (); } build::match (a, *pt); @@ -1120,19 +1081,9 @@ namespace build // searching and matching speculatively doesn't really hurt. // bool found (false); - const auto& ps (reverse_group_prerequisite_members (a, *pt)); - for (auto i (ps.begin ()); i != ps.end (); ++i) + for (prerequisite_member p1: + reverse_group_prerequisite_members (a, *pt)) { - prerequisite_member p1 (*i); - - // See through the group unless it is one that we recognize. - // - if (p1.is_a<target_group> ()) - { - if (!p1.is_a<lib> ()) - continue; - } - // Ignore some known target types (fsdir, headers, libraries). // if (p1.is_a<fsdir> () || @@ -1144,7 +1095,6 @@ namespace build p1.is_a<liba> () || p1.is_a<libso> ()) { - i.skip_group (); // Skip going inside lib{}. continue; } diff --git a/build/cxx/target.cxx b/build/cxx/target.cxx index 9fd5487..c23fdd0 100644 --- a/build/cxx/target.cxx +++ b/build/cxx/target.cxx @@ -18,7 +18,8 @@ namespace build &file::static_type, &target_factory<hxx>, &target_extension_var<hxx_ext_var>, - &search_file + &search_file, + false }; constexpr const char ixx_ext_var[] = "ixx.ext"; @@ -29,7 +30,8 @@ namespace build &file::static_type, &target_factory<ixx>, &target_extension_var<ixx_ext_var>, - &search_file + &search_file, + false }; constexpr const char txx_ext_var[] = "txx.ext"; @@ -40,7 +42,8 @@ namespace build &file::static_type, &target_factory<txx>, &target_extension_var<txx_ext_var>, - &search_file + &search_file, + false }; constexpr const char cxx_ext_var[] = "cxx.ext"; @@ -51,7 +54,8 @@ namespace build &file::static_type, &target_factory<cxx>, &target_extension_var<cxx_ext_var>, - &search_file + &search_file, + false }; constexpr const char h_ext_var[] = "h.ext"; @@ -62,7 +66,8 @@ namespace build &file::static_type, &target_factory<h>, &target_extension_var<h_ext_var>, - &search_file + &search_file, + false }; constexpr const char c_ext_var[] = "c.ext"; @@ -73,7 +78,8 @@ namespace build &file::static_type, &target_factory<c>, &target_extension_var<c_ext_var>, - &search_file + &search_file, + false }; } } diff --git a/build/rule.cxx b/build/rule.cxx index e834426..ce8ef01 100644 --- a/build/rule.cxx +++ b/build/rule.cxx @@ -80,7 +80,7 @@ namespace build // Search and match all the prerequisites. // - search_and_match (a, t); + search_and_match_prerequisites (a, t); return a == perform_update_id ? &perform_update @@ -134,18 +134,7 @@ namespace build recipe dir_rule:: 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 - // other than letting our prerequisites do their thing. - // - switch (a.operation ()) - { - case default_id: - case update_id: search_and_match (a, t); break; - case clean_id: search_and_match (a, t, t.dir); break; - default: assert (false); - } - + search_and_match_prerequisites (a, t); return default_recipe; } @@ -162,26 +151,30 @@ namespace build { switch (a.operation ()) { - // For default, we don't do anything other than letting our - // prerequisites do their thing. - // case default_id: case update_id: + // For default, we don't do anything other than letting our + // prerequisites do their thing. + // + // Inject dependency on the parent directory. Note that we // don't do it for clean since we shouldn't be removing it. // inject_parent_fsdir (a, t); - search_and_match (a, t); + search_and_match_prerequisites (a, t, dir_path ()); break; - // For clean, ignore prerequisites that are not in the same or a - // subdirectory of ours (if t.dir is foo/bar/, then "we" are bar - // and our directory is foo/). Just meditate on it a bit and you - // will see the light. - // + case clean_id: - search_and_match (a, t, t.dir.root () ? t.dir : t.dir.directory ()); + // For clean, ignore prerequisites that are not in the same or a + // subdirectory of ours (if t.dir is foo/bar/, then "we" are bar + // and our directory is foo/). Just meditate on it a bit and you + // will see the light. + // + search_and_match_prerequisites ( + a, t, t.dir.root () ? t.dir : t.dir.directory ()); break; + default: assert (false); } diff --git a/build/target b/build/target index b86ea94..9587c6a 100644 --- a/build/target +++ b/build/target @@ -30,7 +30,6 @@ namespace build { class scope; class target; - class target_group; target& search (prerequisite&); // From <build/algorithm>. @@ -99,6 +98,14 @@ namespace build belongs (const target&) const; }; + // A view of target group members. + // + struct group_view + { + target* const* members; // NULL means not yet known. + std::size_t count; + }; + // Target. // class target @@ -118,13 +125,52 @@ namespace build const std::string* ext; // Extension, NULL means unspecified, // empty means no extension. - //@@ Make target_group. - target* group {nullptr}; // Target group to which this target belongs, - // if any. Note that we assume that the group - // and all its members are in the same scope - // (see, for example, variable lookup). - // We also currently assume that there are - // no multi-level groups. + // Target group to which this target belongs, if any. Note that + // we assume that the group and all its members are in the same + // scope (for example, in variable lookup). We also don't support + // nested groups. + // + // The semantics of the interaction between the group and its + // members and what it means to, say, update the group, is + // unspecified and determined by the group's type. In particular, + // a group can be created out of member types that have no idea + // they are part of this group (e.g., cli.cxx{}). + // + // Normally, however, there are two kinds of groups: "alternatives" + // and "combination". In an alternatives group, normally one of the + // members is selected when the group is mentioned as a prerequisite + // with, perhaps, an exception for special rules, like aliases, where + // it makes more sense to treat the group as a whole. In this case we + // say that the rule "semantically recognizes" the group and picks + // some of its members. + // + // Updating an alternative group as a whole can mean updating some + // subset of its members (e.g., lib{}). Or the group may not support + // this at all (e.g., obj{}). + // + // In a combination group, when a group is updated, normally all + // members are updates (and usually with a single command), though + // there could be some members that are omitted, depending on the + // configuration (e.g., an inline file not/being generated). When + // a combination group is mentioned as a prerequisite, the rule + // is usually interested in the individual members rather than + // the whole group. For example, a C++ compile rule would like to + // "see" the ?xx{} members when it gets a cli.cxx{} group. + // + // Which brings us to the group iteration mode. The target type + // contains a member called see_through that indicates whether the + // default iteration mode for the group should be "see through"; + // that is, whether we see the members or the group itself. For + // the iteration support itself, see the *_prerequisite_members() + // machinery below. + // + target* group {nullptr}; + + // You should not call this function directly; rather use + // resolve_group_members() from <build/algorithm>. + // + virtual group_view + group_members (action) const; target_key key () const {return target_key {&type (), &dir, &name, &ext};} @@ -400,110 +446,6 @@ namespace build target& t_; }; - // - // - struct target_set - { - typedef std::map<target_key, std::unique_ptr<target>> map; - typedef butl::map_iterator_adapter<map::const_iterator> iterator; - - iterator - find (const target_key& k, tracer& trace) const; - - iterator - find (const target_type& type, - const dir_path& dir, - const std::string& name, - const std::string* ext, - tracer& trace) const - { - return find (target_key {&type, &dir, &name, &ext}, trace); - } - - // As above but ignore the extension and return the target or - // nullptr instead of the iterator. - // - template <typename T> - T* - find (const dir_path& dir, const std::string& name) const - { - const std::string* e (nullptr); - auto i (map_.find (target_key {&T::static_type, &dir, &name, &e})); - return i != map_.end () ? static_cast<T*> (i->second.get ()) : nullptr; - } - - iterator begin () const {return map_.begin ();} - iterator end () const {return map_.end ();} - - std::pair<target&, bool> - insert (const target_type&, - dir_path dir, - std::string name, - const std::string* ext, - tracer&); - - template <typename T> - T& - insert (const dir_path& dir, const std::string& name, tracer& t) - { - return static_cast<T&> ( - insert (T::static_type, dir, name, nullptr, t).first); - } - - void - clear () {map_.clear ();} - - private: - map map_; - }; - - extern target_set targets; - - using target_type_map_base = std::map< - const char*, - std::reference_wrapper<const target_type>, - butl::compare_c_string>; - - class target_type_map: public target_type_map_base - { - public: - void - insert (const target_type& tt) {emplace (tt.name, tt);} - - using target_type_map_base::find; - - // Given a name, figure out its type, taking into account extensions, - // special names (e.g., '.' and '..'), or anything else that might be - // relevant. Also process the name (in place) by extracting the - // extension, adjusting dir/value, etc (note that the dir is not - // necessarily normalized). Return NULL if not found. - // - const target_type* - find (name&, const std::string*& ext) const; - }; - - extern target_type_map target_types; - - // Target group. - // - struct group_view - { - target* const* members; // NULL means not yet known. - std::size_t count; - }; - - class target_group: public target - { - public: - using target::target; - - virtual group_view - members (action) const = 0; - - public: - 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. @@ -557,10 +499,11 @@ namespace build // 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: + // each group prerequisite you will "see" either the prerequisite + // itself or all its members, depending on the default iteration + // mode of the target group type. You can skip the rest of the + // group members with leave_group() and you can force iteration + // over the members with enter_group(). Usage: // // for (prerequisite_member pm: prerequisite_members (a, ...)) // @@ -593,31 +536,35 @@ namespace build { public: prerequisite_members_range (action a, T&& r) - : a_ (a), r_ (std::forward<T> (r)) {} + : a_ (a), r_ (std::forward<T> (r)), e_ (r_.end ()) {} + + using base_iterator = decltype (std::declval<T> ().begin ()); struct iterator { - using base_iterator = decltype (std::declval<T> ().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 (): r_ (nullptr) {} + iterator (const prerequisite_members_range* r, const base_iterator& i) + : r_ (r), i_ (i), g_ {nullptr, 0} + { + if (i_ != r_->e_ && i_->get ().type.see_through) + switch_members (); + } 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. - // + // Skip iterating over the rest of this group's members, if any. + // Note that the only valid operation after this call is to + // increment the iterator. // void - skip_group () + leave_group () { // Pretend we are on the last member of some group. // @@ -625,18 +572,19 @@ namespace build g_.count = 1; } - /* - reference operator* () const + // Iterate over this group's members. Similar to leave_group(), + // you should increment the iterator after calling this function. + // + void + enter_group () { - m_.prerequisite = *i; - m_.target = g_.count != 0 ? g_.members[j_] : nullptr; - return m_; + switch_members (); + --j_; // Compensate for the increment that will follow. } - */ value_type operator* () const { - return value_type {*i_, g_.count != 0 ? g_.members[j_] : nullptr}; + return value_type {*i_, g_.count != 0 ? g_.members[j_ - 1] : nullptr}; } pointer operator-> () const @@ -646,7 +594,7 @@ namespace build "prerequisite_member is not trivially destructible"); return new (&m_) - value_type {*i_, g_.count != 0 ? g_.members[j_] : nullptr}; + value_type {*i_, g_.count != 0 ? g_.members[j_ - 1] : nullptr}; } friend bool @@ -661,23 +609,28 @@ namespace build operator!= (const iterator& x, const iterator& y) {return !(x == y);} private: - action a_; + void + switch_members (); + + private: + const prerequisite_members_range* r_; base_iterator i_; group_view g_; - std::size_t j_; + std::size_t j_; // 1-based index, to support enter_group(). mutable std::aligned_storage<sizeof (prerequisite_member), alignof (prerequisite_member)>::type m_; }; iterator - begin () const {return iterator (a_, r_.begin ());} + begin () const {return iterator (this, r_.begin ());} iterator - end () const {return iterator (a_, r_.end ());} + end () const {return iterator (this, e_);} private: action a_; T r_; + base_iterator e_; }; // prerequisite_members(t.prerequisites) @@ -713,6 +666,90 @@ namespace build a, butl::reverse_iterate (group_prerequisites (t))); } + // + // + struct target_set + { + typedef std::map<target_key, std::unique_ptr<target>> map; + typedef butl::map_iterator_adapter<map::const_iterator> iterator; + + iterator + find (const target_key& k, tracer& trace) const; + + iterator + find (const target_type& type, + const dir_path& dir, + const std::string& name, + const std::string* ext, + tracer& trace) const + { + return find (target_key {&type, &dir, &name, &ext}, trace); + } + + // As above but ignore the extension and return the target or + // nullptr instead of the iterator. + // + template <typename T> + T* + find (const dir_path& dir, const std::string& name) const + { + const std::string* e (nullptr); + auto i (map_.find (target_key {&T::static_type, &dir, &name, &e})); + return i != map_.end () ? static_cast<T*> (i->second.get ()) : nullptr; + } + + iterator begin () const {return map_.begin ();} + iterator end () const {return map_.end ();} + + std::pair<target&, bool> + insert (const target_type&, + dir_path dir, + std::string name, + const std::string* ext, + tracer&); + + template <typename T> + T& + insert (const dir_path& dir, const std::string& name, tracer& t) + { + return static_cast<T&> ( + insert (T::static_type, dir, name, nullptr, t).first); + } + + void + clear () {map_.clear ();} + + private: + map map_; + }; + + extern target_set targets; + + using target_type_map_base = std::map< + const char*, + std::reference_wrapper<const target_type>, + butl::compare_c_string>; + + class target_type_map: public target_type_map_base + { + public: + void + insert (const target_type& tt) {emplace (tt.name, tt);} + + using target_type_map_base::find; + + // Given a name, figure out its type, taking into account extensions, + // special names (e.g., '.' and '..'), or anything else that might be + // relevant. Also process the name (in place) by extracting the + // extension, adjusting dir/value, etc (note that the dir is not + // necessarily normalized). Return NULL if not found. + // + const target_type* + find (name&, const std::string*& ext) const; + }; + + extern target_type_map target_types; + // Modification time-based target. // class mtime_target: public target diff --git a/build/target-key b/build/target-key index d791d61..54813b0 100644 --- a/build/target-key +++ b/build/target-key @@ -28,6 +28,7 @@ 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 see_through; // A group with the default "see through" semantics. bool is_a (const std::type_index&) const; // Defined in target.cxx diff --git a/build/target.cxx b/build/target.cxx index ec96779..7958a85 100644 --- a/build/target.cxx +++ b/build/target.cxx @@ -48,6 +48,14 @@ namespace build // target // + + group_view target:: + group_members (action) const + { + assert (false); // Not a group or doesn't expose its members. + return group_view {nullptr, 0}; + } + scope& target:: base_scope () const { @@ -388,16 +396,7 @@ namespace build nullptr, nullptr, &search_target, - }; - - const target_type target_group::static_type - { - typeid (target_group), - "target_group", - &target::static_type, - nullptr, - nullptr, - &search_target + false }; const target_type mtime_target::static_type @@ -407,7 +406,8 @@ namespace build &target::static_type, nullptr, nullptr, - &search_target + &search_target, + false }; const target_type path_target::static_type @@ -417,7 +417,8 @@ namespace build &mtime_target::static_type, nullptr, nullptr, - &search_target + &search_target, + false }; static target* @@ -439,7 +440,8 @@ namespace build &path_target::static_type, &file_factory, nullptr, // Factory always assigns an extension. - &search_file + &search_file, + false }; const target_type dir::static_type @@ -449,7 +451,8 @@ namespace build &target::static_type, &target_factory<dir>, nullptr, // Should never need. - &search_alias + &search_alias, + false }; const target_type fsdir::static_type @@ -459,6 +462,7 @@ namespace build &target::static_type, &target_factory<fsdir>, nullptr, // Should never need. - &search_target + &search_target, + false }; } diff --git a/build/target.ixx b/build/target.ixx index b6bf509..16616b5 100644 --- a/build/target.ixx +++ b/build/target.ixx @@ -32,7 +32,7 @@ namespace build // prerequisite_members // group_view - resolve_group_members (action, target_group&); // <build/algorithm> + resolve_group_members (action, target&); // <build/algorithm> template <typename T> inline auto prerequisite_members_range<T>::iterator:: @@ -40,35 +40,36 @@ namespace build { if (g_.count != 0) { - // Member iteration. + if (++j_ <= g_.count) + return *this; + + // Switch back to prerequisite iteration mode. // - if (++j_ == g_.count) - { - // Switch back to prerequisite iteration. - // - g_.count = 0; - ++i_; - } + g_.count = 0; } - else - { - // Prerequisite iteration. - // - if (i_->get ().template is_a<target_group> ()) - { - // Switch to member iteration. - // - target_group& g (static_cast<target_group&> (search (*i_))); - j_ = 0; - g_ = resolve_group_members (a_, g); - if (g_.count == 0) - ++i_; // Empty group. - } - else - ++i_; - } + ++i_; + + // Switch to member iteration mode. + // + if (i_ != r_->e_ && i_->get ().type.see_through) + switch_members (); return *this; } + + template <typename T> + inline void prerequisite_members_range<T>::iterator:: + switch_members () + { + j_ = 1; + + do + { + g_ = resolve_group_members (r_->a_, search (*i_)); + } + while (g_.count == 0 && // Skip empty groups. + ++i_ != r_->e_ && + i_->get ().type.see_through); + } } |