aboutsummaryrefslogtreecommitdiff
path: root/build
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-07-01 09:11:31 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-07-01 09:11:31 +0200
commit17b3a78696f0b1fd6f0f60d53ec568cf3b9e32b4 (patch)
treef54f92e5066e1a97a070af248931316cd76b61c3 /build
parent70af0087d8efb3f2f7dc9ffdf2568419913f16da (diff)
Cleanup group "see through" design
Diffstat (limited to 'build')
-rw-r--r--build/algorithm29
-rw-r--r--build/algorithm.cxx44
-rw-r--r--build/algorithm.ixx26
-rw-r--r--build/bin/rule.cxx2
-rw-r--r--build/bin/target.cxx21
-rw-r--r--build/cli/rule.cxx10
-rw-r--r--build/cli/target6
-rw-r--r--build/cli/target.cxx10
-rw-r--r--build/cxx/rule.cxx60
-rw-r--r--build/cxx/target.cxx18
-rw-r--r--build/rule.cxx39
-rw-r--r--build/target313
-rw-r--r--build/target-key1
-rw-r--r--build/target.cxx34
-rw-r--r--build/target.ixx53
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);
+ }
}