aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build/algorithm13
-rw-r--r--build/algorithm.cxx12
-rw-r--r--build/algorithm.ixx9
-rw-r--r--build/bin/rule8
-rw-r--r--build/bin/rule.cxx10
-rw-r--r--build/cli/rule4
-rw-r--r--build/cli/rule.cxx18
-rw-r--r--build/cxx/rule8
-rw-r--r--build/cxx/rule.cxx241
-rw-r--r--build/prerequisite8
-rw-r--r--build/prerequisite.cxx2
-rw-r--r--build/rule44
-rw-r--r--build/rule.cxx18
-rw-r--r--build/search.cxx4
-rw-r--r--build/target225
-rw-r--r--build/target-key7
-rw-r--r--build/target.cxx12
-rw-r--r--build/target.ixx61
-rw-r--r--tests/cli/lib/libtest/test/buildfile6
-rw-r--r--tests/cli/lib/test/buildfile4
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<const rule*, void*>
+ pair<const rule*, match_result>
match_impl (action a, target& t, bool apply)
{
- pair<const rule*, void*> r (nullptr, nullptr);
+ pair<const rule*, match_result> 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<T&> (search (T::static_type, dir, name, ext, scope));
}
- std::pair<const rule*, void*>
+ std::pair<const rule*, match_result>
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<lib&> (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<cli> ())
{
//@@ 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<cli> ()) // @@ Need to check that stems match.
{
g = &targets.insert<cli_cxx> (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<cli_cxx> ())
{
@@ -182,7 +182,7 @@ namespace build
}
else
{
- cli_cxx& g (*static_cast<cli_cxx*> (vp));
+ cli_cxx& g (*static_cast<cli_cxx*> (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<lib> () || t->is_a<liba> () || t->is_a<libso> ())
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<cxx> ())
+ 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<path_target&> (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<target_group> ())
+ {
+ if (!p.is_a<lib> ())
+ 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<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);
}
@@ -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<prerequisite*> (v));
- cxx& st (dynamic_cast<cxx&> (*sp.target));
-
- inject_prerequisites (a, t, st, sp.scope);
+ // @@ Ugly.
+ //
+ cxx& st (
+ dynamic_cast<cxx&> (
+ 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<const char*> 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<const char*> 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<cxx> ())
{
seen_cxx = seen_cxx || true;
}
- else if (p.type.id == typeid (c))
+ else if (p.is_a<c> ())
{
seen_c = seen_c || true;
}
- else if (p.type.id == typeid (obja))
+ else if (p.is_a<obja> ())
{
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<objso> () ||
+ p.is_a<obj> ())
{
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<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.type.id != typeid (fsdir))
+ else if (p.is_a<h> () ||
+ p.is_a<hxx> () ||
+ p.is_a<ixx> () ||
+ 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;});
+ 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<target_group> ())
+ {
+ if (!p.is_a<lib> () &&
+ !p.is_a<obj> ())
+ continue;
+ }
+
+ bool group (!p.prerequisite.belongs (t)); // Group's prerequisite.
- prerequisite& p (pr);
target* pt (nullptr);
if (!p.is_a<c> () && !p.is_a<cxx> ())
{
// 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<obj> ())
{
@@ -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<lib> ())
{
@@ -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<fsdir> () ||
- p.is_a<h> () ||
- (cp.is_a<cxx> () && (p.is_a<hxx> () ||
- p.is_a<ixx> () ||
- p.is_a<txx> ())) ||
- p.is_a<lib> () ||
- p.is_a<liba> () ||
- p.is_a<libso> ())
- continue;
+ prerequisite_member p1 (*i);
- if (p.is_a<cxx> ())
+ // See through the group unless it is one that we recognize.
+ //
+ if (p1.is_a<target_group> ())
{
- cp1 = &p;
- continue; // Check the rest of the prerequisites.
+ if (!p1.is_a<lib> ())
+ 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<fsdir> () ||
+ p1.is_a<h> () ||
+ (p.is_a<cxx> () && (p1.is_a<hxx> () ||
+ p1.is_a<ixx> () ||
+ p1.is_a<txx> ())) ||
+ p1.is_a<lib> () ||
+ p1.is_a<liba> () ||
+ p1.is_a<libso> ())
+ {
+ i.skip_group (); // Skip going inside lib{}.
+ continue;
+ }
- if (cp.target != cp1->target)
+ if (!p1.is_a<cxx> ())
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 <typename T>
bool
- is_a () const {return type.id == typeid (T);}
+ is_a () const {return type.is_a<T> ();}
};
inline bool
@@ -107,6 +107,12 @@ namespace build
const std::string* ext,
scope&,
tracer&);
+
+ std::pair<prerequisite&, bool>
+ 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 <string>
+#include <cstddef> // nullptr_t
#include <typeindex>
#include <functional> // reference_wrapper
#include <unordered_map>
@@ -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 <map>
#include <string>
#include <vector>
-#include <memory> // unique_ptr
-#include <cstddef> // size_t
-#include <functional> // function, reference_wrapper
+#include <memory> // unique_ptr
+#include <cstddef> // size_t
+#include <functional> // function, reference_wrapper
#include <ostream>
#include <cassert>
-#include <utility> // move()
+#include <utility> // move(), forward(), declval()
#include <iterator>
+#include <type_traits>
-#include <butl/utility> // compare_c_string
+#include <butl/utility> // compare_c_string, reverse_iterate()
#include <butl/multi-index> // map_iterator_adapter
#include <build/types>
@@ -31,6 +32,9 @@ namespace build
class target;
class target_group;
+ target&
+ search (prerequisite&); // From <build/algorithm>.
+
// 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<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 <typename T>
+ bool
+ is_a () const
+ {
+ return target != nullptr
+ ? target->is_a<T> () != nullptr
+ : prerequisite.get ().is_a<T> ();
+ }
+
+ 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 <typename T>
+ class prerequisite_members_range;
+
+ template <typename T>
+ inline prerequisite_members_range<T>
+ prerequisite_members (action a, T&& x)
+ {
+ return prerequisite_members_range<T> (a, std::forward<T> (x));
+ }
+
+ template <typename T>
+ class prerequisite_members_range
+ {
+ public:
+ prerequisite_members_range (action a, T&& r)
+ : a_ (a), r_ (std::forward<T> (r)) {}
+
+ 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& 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<prerequisite_member>::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<sizeof (prerequisite_member),
+ alignof (prerequisite_member)>::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 <typename T>
+ 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 <build/scope>
+
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&); // <build/algorithm>
+
+ template <typename T>
+ inline auto prerequisite_members_range<T>::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<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_;
+ }
+
+ 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