aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-04-26 09:53:41 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-04-26 09:53:41 +0200
commit54e24990203f5e123396a44297ea4656ed3b6101 (patch)
treed6581a15fa0fea6a5de7c7c7e5147f3d7c865cc9
parent478c521c09f1962e9d82a3fc4297e5cafb8c0758 (diff)
Rework resolved prerequisite targets storage
-rw-r--r--build/algorithm29
-rw-r--r--build/algorithm.cxx197
-rw-r--r--build/algorithm.ixx14
-rw-r--r--build/algorithm.txx146
-rw-r--r--build/cxx/rule.cxx201
-rw-r--r--build/dump6
-rw-r--r--build/dump.cxx48
-rw-r--r--build/operation.cxx4
-rw-r--r--build/prerequisite2
-rw-r--r--build/rule.cxx12
-rw-r--r--build/scope2
-rw-r--r--build/search.cxx8
-rw-r--r--build/target64
-rw-r--r--build/target.ixx4
14 files changed, 317 insertions, 420 deletions
diff --git a/build/algorithm b/build/algorithm
index 7d82285..161baa2 100644
--- a/build/algorithm
+++ b/build/algorithm
@@ -14,7 +14,7 @@ namespace build
class prerequisite;
class prerequisite_key;
- // The default prerequsite search implementation. It first calls the
+ // The default prerequisite search implementation. It first calls the
// target-type-specific search function. If that doesn't yeld anything,
// it creates a new target.
//
@@ -41,23 +41,19 @@ namespace build
void
search_and_match (action, target&);
- // As above but ignores (does not match) prerequsites that are not
+ // 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& dir);
- // Inject dependency on the parent directory fsdir{}, unless it is
- // the project's out_root (or is outside of any project; think, for
- // example, install directories).
+ // Inject dependency on the parent directory's fsdir{}, unless it is
+ // the project's out_root (or is outside of any project; say, for
+ // example, an install directory).
//
void
inject_parent_fsdir (action, target&);
- // Executor function type.
- //
- using executor_function = target_state (action, target&);
-
// Execute the action on target, assuming a rule has been matched
// and the recipe for this action has been set. This is the default
// executor implementation.
@@ -66,27 +62,24 @@ namespace build
execute (action, target&);
// The default prerequisite execute implementation. It calls execute()
- // on each non-ignored (non-NULL target) prerequisite in a loop. If this
+ // on each non-ignored (non-NULL) prerequisite target in a loop. If this
// target is a member of a group, then it first does this to the group's
// prerequisites. Returns target_state::changed if any of them were
// changed and target_state::unchanged otherwise. It treats targets
// with postponed execution the same as ignored. Note that this
// function can be used as a recipe.
//
- template <executor_function* E = execute>
target_state
execute_prerequisites (action, target&);
// As above but iterates over the prerequisites in reverse.
//
- template <executor_function* E = execute>
target_state
reverse_execute_prerequisites (action, target&);
// A version of the above that also determines whether the action
// needs to be executed on the target based on the passed timestamp.
//
- template <executor_function* E = execute>
bool
execute_prerequisites (action, target&, const timestamp&);
@@ -95,9 +88,9 @@ namespace build
// the target based on the passed timestamp and, if so, finds a
// prerequisite of the specified type (e.g., a source file). If
// there are multiple prerequisites of this type, then the last
- // one returned.
+ // is returned.
//
- template <typename T, executor_function* E = execute>
+ template <typename T>
T*
execute_find_prerequisites (action, target&, const timestamp&);
@@ -106,17 +99,15 @@ namespace build
target_state
noop_action (action, target&);
- // Default action implementation which forwards to the prerequsites.
- // Use default_recipe for the standard executor.
+ // Default action implementation which forwards to the prerequisites.
+ // Use default_recipe instead of using this function directly.
//
- template <executor_function* E = execute>
target_state
default_action (action, target&);
// Standard perform(clean) action implementation for the file target
// or derived.
//
- template <executor_function* E = execute>
target_state
perform_clean (action, target&);
}
diff --git a/build/algorithm.cxx b/build/algorithm.cxx
index 9f428b3..e6a71df 100644
--- a/build/algorithm.cxx
+++ b/build/algorithm.cxx
@@ -5,6 +5,7 @@
#include <build/algorithm>
#include <memory> // unique_ptr
+#include <cstddef> // size_t
#include <utility> // move
#include <cassert>
@@ -14,6 +15,7 @@
#include <build/prerequisite>
#include <build/rule>
#include <build/search>
+#include <build/context>
#include <build/utility>
#include <build/diagnostics>
@@ -33,6 +35,12 @@ namespace build
void
match_impl (action a, target& t)
{
+ // Clear the resolved targets list before calling match(). The rule
+ // is free to, say, resize() this list in match() (provided that it
+ // matches) in order to, for example, prepare it for apply().
+ //
+ t.prerequisite_targets.clear ();
+
for (auto tt (&t.type ());
tt != nullptr && !t.recipe (a);
tt = tt->base)
@@ -148,30 +156,30 @@ namespace build
void
search_and_match (action a, target& t)
{
- if (t.group != nullptr)
- search_and_match (a, *t.group);
+ group_prerequisites gp (t);
+ t.prerequisite_targets.resize (gp.size ());
- for (prerequisite_target& p: t.prerequisites)
+ size_t i (0);
+ for (prerequisite& p: gp)
{
- p.target = &search (p);
- match (a, *p.target);
+ target& pt (search (p));
+ match (a, pt);
+ t.prerequisite_targets[i++] = &pt;
}
}
void
search_and_match (action a, target& t, const dir_path& d)
{
- if (t.group != nullptr)
- search_and_match (a, *t.group, d);
-
- for (prerequisite_target& p: t.prerequisites)
+ for (prerequisite& p: group_prerequisites (t))
{
- p.target = &search (p);
+ target& pt (search (p));
if (p.target->dir.sub (d))
- match (a, *p.target);
- else
- p.target = nullptr; // Ignore.
+ {
+ match (a, pt);
+ t.prerequisite_targets.push_back (&pt);
+ }
}
}
@@ -181,36 +189,41 @@ namespace build
tracer trace ("inject_parent_fsdir");
scope& s (t.base_scope ());
+ scope* rs (s.root_scope ());
- if (scope* rs = s.root_scope ()) // Could be outside any project.
- {
- const dir_path& out_root (rs->path ());
-
- // If t is a directory (name is empty), say foo/bar/, then
- // t is bar and its parent directory is foo/.
- //
- const dir_path& d (t.name.empty () ? t.dir.directory () : t.dir);
-
- if (d.sub (out_root) && d != out_root)
- {
- level5 ([&]{trace << "injecting prerequisite for " << t;});
-
- prerequisite& p (
- s.prerequisites.insert (
- fsdir::static_type,
- d,
- string (),
- nullptr,
- s,
- trace).first);
+ if (rs == nullptr) // Could be outside any project.
+ return;
- target& pt (search (p));
+ const dir_path& out_root (rs->path ());
- t.prerequisites.emplace_back (p, pt);
+ // If t is a directory (name is empty), say foo/bar/, then
+ // t is bar and its parent directory is foo/.
+ //
+ const dir_path& d (t.name.empty () ? t.dir.directory () : t.dir);
+
+ if (!d.sub (out_root) || d == out_root)
+ return;
+
+ prerequisite& p (
+ s.prerequisites.insert (
+ fsdir::static_type,
+ d,
+ string (),
+ nullptr,
+ s,
+ trace).first);
+
+ // This function is normally called from match() which means
+ // it can be called several times if we are performing several
+ // operations (e.g., clean update). Since it is a fairly common
+ // pattern to add this prerequisite at the end, do a quick check
+ // if the last prerequisite is already what we are about to add.
+ //
+ if (!t.prerequisites.empty () && &t.prerequisites.back ().get () == &p)
+ return;
- match (a, pt);
- }
- }
+ level5 ([&]{trace << "injecting prerequisite for " << t;});
+ t.prerequisites.emplace_back (p);
}
target_state
@@ -250,9 +263,115 @@ namespace build
}
target_state
+ execute_prerequisites (action a, target& t)
+ {
+ target_state ts (target_state::unchanged);
+
+ for (target* pt: t.prerequisite_targets)
+ {
+ if (pt == nullptr) // Skipped.
+ continue;
+
+ if (execute (a, *pt) == target_state::changed)
+ ts = target_state::changed;
+ }
+
+ return ts;
+ }
+
+ target_state
+ reverse_execute_prerequisites (action a, target& t)
+ {
+ target_state ts (target_state::unchanged);
+
+ for (target* pt: reverse_iterate (t.prerequisite_targets))
+ {
+ if (pt == nullptr) // Skipped.
+ continue;
+
+ if (execute (a, *pt) == target_state::changed)
+ ts = target_state::changed;
+ }
+
+ return ts;
+ }
+
+ bool
+ execute_prerequisites (action a, target& t, const timestamp& mt)
+ {
+ bool e (mt == timestamp_nonexistent);
+
+ for (target* pt: t.prerequisite_targets)
+ {
+ if (pt == nullptr) // Skipped.
+ continue;
+
+ target_state ts (execute (a, *pt));
+
+ if (!e)
+ {
+ // If this is an mtime-based target, then compare timestamps.
+ //
+ if (auto mpt = dynamic_cast<const mtime_target*> (pt))
+ {
+ timestamp mp (mpt->mtime ());
+
+ // What do we do if timestamps are equal? This can happen, for
+ // example, on filesystems that don't have subsecond resolution.
+ // There is not much we can do here except detect the case where
+ // the prerequisite was changed in this run which means the
+ // action must be executed on the target as well.
+ //
+ if (mt < mp || (mt == mp && ts == target_state::changed))
+ e = true;
+ }
+ else
+ {
+ // Otherwise we assume the prerequisite is newer if it was changed.
+ //
+ if (ts == target_state::changed)
+ e = true;
+ }
+ }
+ }
+
+ return e;
+ }
+
+ target_state
noop_action (action, target&)
{
assert (false); // We shouldn't be called, see target::recipe().
return target_state::unchanged;
}
+
+ target_state
+ default_action (action a, target& t)
+ {
+ return current_mode == execution_mode::first
+ ? execute_prerequisites (a, t)
+ : reverse_execute_prerequisites (a, t);
+ }
+
+ target_state
+ perform_clean (action a, target& t)
+ {
+ // The reverse order of update: first delete the file, then clean
+ // prerequisites.
+ //
+ file& ft (dynamic_cast<file&> (t));
+
+ bool r (rmfile (ft.path (), ft));
+
+ // Update timestamp in case there are operations after us that
+ // could use the information.
+ //
+ ft.mtime (timestamp_nonexistent);
+
+ // Clean prerequisites.
+ //
+ target_state ts (reverse_execute_prerequisites (a, t));
+
+ return r ? target_state::changed : ts;
+ }
}
diff --git a/build/algorithm.ixx b/build/algorithm.ixx
index 76f9a0f..ee63f90 100644
--- a/build/algorithm.ixx
+++ b/build/algorithm.ixx
@@ -52,18 +52,4 @@ namespace build
}
}
}
-
- template <typename T, executor_function* E>
- T*
- execute_find_prerequisites (action, target&, const timestamp&, bool&);
-
- template <typename T, executor_function* E>
- inline T*
- execute_find_prerequisites (action a, target& t, const timestamp& mt)
- {
- bool e (mt == timestamp_nonexistent);
- T* r (execute_find_prerequisites<T, E> (a, t, mt, e));
- assert (r != nullptr);
- return e ? r : nullptr;
- }
}
diff --git a/build/algorithm.txx b/build/algorithm.txx
index 4f5f9f3..a62603d 100644
--- a/build/algorithm.txx
+++ b/build/algorithm.txx
@@ -2,126 +2,25 @@
// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
// license : MIT; see accompanying LICENSE file
-#include <build/context>
-#include <build/utility> // reverse_iterate
-
namespace build
{
- template <executor_function* E>
- target_state
- execute_prerequisites (action a, target& t)
- {
- target_state ts (target_state::unchanged);
-
- if (t.group != nullptr)
- ts = execute_prerequisites<E> (a, *t.group);
-
- for (target* pt: t.prerequisites)
- {
- if (pt == nullptr) // Skip ignored.
- continue;
-
- if (E (a, *pt) == target_state::changed)
- ts = target_state::changed;
- }
-
- return ts;
- }
-
- template <executor_function* E>
- target_state
- reverse_execute_prerequisites (action a, target& t)
- {
- target_state ts (target_state::unchanged);
-
- for (target* pt: reverse_iterate (t.prerequisites))
- {
- if (pt == nullptr) // Skip ignored.
- continue;
-
- if (E (a, *pt) == target_state::changed)
- ts = target_state::changed;
- }
-
- if (t.group != nullptr)
- {
- if (reverse_execute_prerequisites<E> (a, *t.group) ==
- target_state::changed)
- ts = target_state::changed;
- }
-
- return ts;
- }
-
- template <executor_function* E>
- bool
- execute_prerequisites (action a, target& t, const timestamp& mt)
- {
- bool e (mt == timestamp_nonexistent);
-
- if (t.group != nullptr)
- {
- if (execute_prerequisites<E> (a, *t.group, mt))
- e = true;
- }
-
- for (target* pt: t.prerequisites)
- {
- if (pt == nullptr) // Skip ignored.
- continue;
-
- target_state ts (E (a, *pt));
-
- if (!e)
- {
- // If this is an mtime-based target, then compare timestamps.
- //
- if (auto mpt = dynamic_cast<const mtime_target*> (pt))
- {
- timestamp mp (mpt->mtime ());
-
- // What do we do if timestamps are equal? This can happen, for
- // example, on filesystems that don't have subsecond resolution.
- // There is not much we can do here except detect the case where
- // the prerequisite was changed in this run which means the
- // action must be executed on the target as well.
- //
- if (mt < mp || (mt == mp && ts == target_state::changed))
- e = true;
- }
- else
- {
- // Otherwise we assume the prerequisite is newer if it was changed.
- //
- if (ts == target_state::changed)
- e = true;
- }
- }
- }
-
- return e;
- }
-
- template <typename T, executor_function* E>
+ template <typename T>
T*
- execute_find_prerequisites (
- action a, target& t, const timestamp& mt, bool& e)
+ execute_prerequisites (action a, target& t, const timestamp& mt)
{
//@@ Can factor the bulk of it into a non-template code. Can
// either do a function template that will do dynamic_cast check
// or can scan the target type info myself. I think latter.
//
+ bool e (mt == timestamp_nonexistent);
T* r (nullptr);
- if (t.group != nullptr)
- r = execute_find_prerequisites<T, E> (a, *t.group, mt, e);
-
- for (target* pt: t.prerequisites)
+ for (target* pt: t.prerequisite_targets)
{
if (pt == nullptr) // Skip ignored.
continue;
- target_state ts (E (a, *pt));
+ target_state ts (execute (a, *pt));
if (!e)
{
@@ -153,38 +52,7 @@ namespace build
r = tmp;
}
- return r;
- }
-
- template <executor_function* E = execute>
- target_state
- default_action (action a, target& t)
- {
- return current_mode == execution_mode::first
- ? execute_prerequisites<E> (a, t)
- : reverse_execute_prerequisites<E> (a, t);
- }
-
- template <executor_function* E>
- target_state
- perform_clean (action a, target& t)
- {
- // The reverse order of update: first delete the file, then clean
- // prerequisites.
- //
- file& ft (dynamic_cast<file&> (t));
-
- bool r (rmfile (ft.path (), ft));
-
- // Update timestamp in case there are operations after us that
- // could use the information.
- //
- ft.mtime (timestamp_nonexistent);
-
- // Clean prerequisites.
- //
- target_state ts (reverse_execute_prerequisites<E> (a, t));
-
- return r ? target_state::changed : ts;
+ assert (r != nullptr);
+ return e ? r : nullptr;
}
}
diff --git a/build/cxx/rule.cxx b/build/cxx/rule.cxx
index e59c5b9..7f9ea02 100644
--- a/build/cxx/rule.cxx
+++ b/build/cxx/rule.cxx
@@ -83,10 +83,10 @@ namespace build
// a source file specified for an obj member overrides the one
// specified for the group.
//
- for (prerequisite_target& pe: reverse_iterate (group_prerequisites (t)))
+ for (prerequisite& p: reverse_iterate (group_prerequisites (t)))
{
- if (pe.prereq->type.id == typeid (cxx))
- return &pe;
+ if (p.type.id == typeid (cxx))
+ return &p;
}
level3 ([&]{trace << "no c++ source file for target " << t;});
@@ -108,6 +108,10 @@ namespace build
t.path (t.derived_path ("o", nullptr, "-so"));
}
+ // Inject dependency on the output directory.
+ //
+ inject_parent_fsdir (a, t);
+
// Search and match all the existing prerequisites. The injection
// code (below) takes care of the ones it is adding.
//
@@ -127,17 +131,17 @@ namespace build
//
if (a.operation () == update_id || a.operation () == default_id)
{
- prerequisite_target& spe (*static_cast<prerequisite_target*> (v));
- cxx& st (dynamic_cast<cxx&> (*spe.target));
+ // 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.
+ //
+ prerequisite& sp (*static_cast<prerequisite*> (v));
+ cxx& st (dynamic_cast<cxx&> (*sp.target));
if (st.mtime () != timestamp_nonexistent)
- inject_prerequisites (a, t, st, spe.prereq->scope);
+ inject_prerequisites (a, t, st, sp.scope);
}
- // Inject dependency on the output directory.
- //
- inject_parent_fsdir (a, t);
-
switch (a)
{
case perform_update_id: return &perform_update;
@@ -299,13 +303,13 @@ namespace build
ds.prerequisites.insert (
hxx::static_type, move (d), move (n), e, ds, trace).first);
- // Resolve to target.
+ // Add to our prerequisites list.
//
- path_target& pt (dynamic_cast<path_target&> (search (p)));
+ t.prerequisites.emplace_back (p);
- // Add to prerequisites list.
+ // Resolve to target.
//
- t.prerequisites.emplace_back (p, pt);
+ path_target& pt (dynamic_cast<path_target&> (search (p)));
// Assign path.
//
@@ -315,6 +319,10 @@ namespace build
// Match to a rule.
//
build::match (a, pt);
+
+ // Add to our resolved target list.
+ //
+ t.prerequisite_targets.push_back (&pt);
}
}
@@ -342,7 +350,7 @@ namespace build
perform_update (action a, target& xt)
{
path_target& t (static_cast<path_target&> (xt));
- cxx* s (execute_find_prerequisites<cxx> (a, t, t.mtime ()));
+ cxx* s (execute_prerequisites<cxx> (a, t, t.mtime ()));
if (s == nullptr)
return target_state::unchanged;
@@ -484,83 +492,6 @@ namespace build
return seen_cxx || seen_c || seen_obj || seen_lib ? &t : nullptr;
}
- static inline target_state
- select_obja_liba (action a, target& t)
- {
- target* r;
- if (obj* o = t.is_a<obj> ())
- r = o->a;
- else if (lib* l = t.is_a<lib> ())
- r = l->a;
- else
- r = &t;
-
- return execute (a, *r);
- }
-
- static inline target_state
- select_obja_libso (action a, target& t)
- {
- target* r;
- if (obj* o = t.is_a<obj> ())
- r = o->a;
- else if (lib* l = t.is_a<lib> ())
- r = l->so;
- else
- r = &t;
-
- return execute (a, *r);
- }
-
- static inline target_state
- select_objso_liba (action a, target& t)
- {
- target* r;
- if (obj* o = t.is_a<obj> ())
- r = o->so;
- else if (lib* l = t.is_a<lib> ())
- r = l->a;
- else
- r = &t;
-
- return execute (a, *r);
- }
-
- static inline target_state
- select_objso_libso (action a, target& t)
- {
- target* r;
- if (obj* o = t.is_a<obj> ())
- r = o->so;
- else if (lib* l = t.is_a<lib> ())
- r = l->so;
- else
- r = &t;
-
- return execute (a, *r);
- }
-
- static executor_function* clean_table[2][2] =
- {
- {&perform_clean<select_obja_liba>, &perform_clean<select_obja_libso>},
- {&perform_clean<select_objso_liba>, &perform_clean<select_objso_libso>}
- };
-
- static executor_function* default_table[2][2] =
- {
- {&default_action<select_obja_liba>, &default_action<select_obja_libso>},
- {&default_action<select_objso_liba>, &default_action<select_objso_libso>}
- };
-
- static bool (*execute_table[2][2]) (action, target&, const timestamp&) =
- {
- {&execute_prerequisites<select_obja_liba>,
- &execute_prerequisites<select_obja_libso>},
-
- {&execute_prerequisites<select_objso_liba>,
- &execute_prerequisites<select_objso_libso>}
- };
-
recipe link::
apply (action a, target& xt, void*) const
{
@@ -596,6 +527,10 @@ namespace build
}
}
+ // Inject dependency on the output directory.
+ //
+ inject_parent_fsdir (a, t);
+
// We may need the project roots for rule chaining (see below).
// We will resolve them lazily only if needed.
//
@@ -605,29 +540,29 @@ namespace build
// Process prerequisites: do rule chaining for C and C++ source
// files as well as search and match.
//
- for (prerequisite_target& pe: group_prerequisites (t))
+ group_prerequisites gp (t);
+ t.prerequisite_targets.reserve (gp.size ());
+
+ for (prerequisite_ref& pr: gp)
{
- bool group (!pe.belongs (t)); // Target group's prerequisite.
- prerequisite& p (pe);
+ bool group (!pr.belongs (t)); // Target 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().
//
- pe.target = &search (p);
+ pt = &search (p);
- if (a.operation () == clean_id && !pe.target->dir.sub (t.dir))
- {
- pe.target = nullptr; // Ignore.
- continue;
- }
+ 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.
//
- target* pt;
-
- if (obj* o = pe.target->is_a<obj> ())
+ if (obj* o = pt->is_a<obj> ())
{
pt = so ? static_cast<target*> (o->so) : o->a;
@@ -639,20 +574,8 @@ namespace build
pt = &search (
prerequisite_key {&type, &p.dir, &p.name, &p.ext, &p.scope});
}
-
- // This is a bit tricky: if this is a group's prerequisite,
- // then we have to keep pe.target pointing to the obj{} group
- // since there could be another match that uses a different
- // member of this prerequisite. In this case we specify the
- // "executor" (see below) which will pick the right member
- // for one of common algorithms. However, if this is our own
- // prerequisite, then we are free to hard-wire the member
- // we need directly in pe.target.
- //
- if (!group)
- pe.target = pt;
}
- else if (lib* l = pe.target->is_a<lib> ())
+ else if (lib* l = pt->is_a<lib> ())
{
// Make sure the library build that we need is available.
//
@@ -672,16 +595,10 @@ namespace build
pt = &search (
prerequisite_key {&type, &p.dir, &p.name, &p.ext, &p.scope});
}
-
- // Same logic as for obj{}
- //
- if (!group)
- pe.target = pt;
}
- else
- pt = pe.target;
build::match (a, *pt);
+ t.prerequisite_targets.push_back (pt);
continue;
}
@@ -750,13 +667,12 @@ namespace build
// If we shouldn't clean obj{}, then it is fair to assume
// we shouldn't clean cxx{} either (generated source will
// be in the same directory as obj{} and if not, well, go
- // and find yourself another build system).
+ // find yourself another build system).
//
- pe.target = nullptr; // Skip.
- continue;
+ continue; // Skip.
}
- pe.target = ot;
+ pt = ot;
// If we have created the obj{} target group, pick one of its
// members; the rest would be primarily concerned with it.
@@ -808,7 +724,7 @@ namespace build
fail << "synthesized target for prerequisite " << cp
<< " would be incompatible with existing target " << *ot <<
- info << "unknown existing prerequsite type " << p <<
+ info << "unknown existing prerequisite type " << p <<
info << "specify corresponding obj{} target explicitly";
}
@@ -820,33 +736,31 @@ namespace build
if (cp.target != cp1->target)
fail << "synthesized target for prerequisite " << cp
<< " would be incompatible with existing target " << *ot <<
- info << "existing prerequsite " << *cp1 << " does not "
+ info << "existing prerequisite " << *cp1 << " does not "
<< "match " << cp <<
info << "specify corresponding " << o_type.name << "{} "
<< "target explicitly";
}
else
{
- // Note: add the source to the group, not member.
+ // Note: add the source to the group, not the member.
//
- pe.target->prerequisites.emplace_back (cp);
+ pt->prerequisites.emplace_back (cp);
build::match (a, *ot);
}
- // Change the exe{} target's prerequsite ref from cxx{} to obj*{}.
+ // Change the exe{} target's prerequisite from cxx{} to obj*{}.
//
- pe.prereq = &op;
- }
+ pr = op;
- // Inject dependency on the output directory.
- //
- inject_parent_fsdir (a, t);
+ t.prerequisite_targets.push_back (ot);
+ }
switch (a)
{
case perform_update_id: return &perform_update;
- case perform_clean_id: return clean_table[so][lso];
- default: return default_table[so][lso]; // Forward to prerequisites.
+ case perform_clean_id: return &perform_clean;
+ default: return default_recipe; // Forward to prerequisites.
}
}
@@ -871,7 +785,7 @@ namespace build
case type::libso: lso = true; break;
}
- if (!execute_table[so][lso] (a, t, t.mtime ()))
+ if (!execute_prerequisites (a, t, t.mtime ()))
return target_state::unchanged;
// Translate paths to relative (to working directory) ones. This
@@ -909,11 +823,8 @@ namespace build
append_options (args, t, "cxx.loptions");
}
- for (target* pt: group_prerequisites (t))
+ for (target* pt: t.prerequisite_targets)
{
- if (pt == nullptr)
- continue; // Skipped.
-
path_target* ppt;
if (obj* o = pt->is_a<obj> ())
diff --git a/build/dump b/build/dump
index 48da079..3830b69 100644
--- a/build/dump
+++ b/build/dump
@@ -5,10 +5,14 @@
#ifndef BUILD_DUMP
#define BUILD_DUMP
+#include <build/operation>
+
namespace build
{
+ // Dump the state pertaining to the specified action.
+ //
void
- dump ();
+ dump (action);
}
#endif // BUILD_DUMP
diff --git a/build/dump.cxx b/build/dump.cxx
index 9299a86..3f8eb15 100644
--- a/build/dump.cxx
+++ b/build/dump.cxx
@@ -19,25 +19,47 @@ using namespace std;
namespace build
{
static void
- dump_target (ostream& os, const target& t)
+ dump_target (ostream& os, action a, const target& t)
{
- //@@ Need to print group info somehow.
+ os << t;
- os << t << ':';
+ if (t.group != nullptr)
+ os << "->" << *t.group;
- for (const prerequisite_target& pe: t.prerequisites)
+ os << ':';
+
+ // If the target has been matched to a rule, print resolved
+ // prerequisite targets.
+ //
+ if (t.recipe (a))
{
- os << ' ';
+ for (const target* pt: t.prerequisite_targets)
+ {
+ if (pt == nullptr) // Skipped.
+ continue;
- if (pe.target != nullptr)
- os << *pe.target;
- else
- os << *pe.prereq;
+ os << ' ' << *pt;
+ }
+ }
+ else
+ {
+ for (const prerequisite& p: t.prerequisites)
+ {
+ os << ' ';
+
+ // Print it as target if one has been cached.
+ //
+ if (p.target != nullptr)
+ os << *p.target;
+ else
+ os << p;
+ }
}
}
static void
dump_scope (ostream& os,
+ action a,
scope& p,
scope_map::iterator& i,
string& ind,
@@ -94,7 +116,7 @@ namespace build
os << endl;
scope& s (i->second);
- dump_scope (os, s, ++i, ind, rts);
+ dump_scope (os, a, s, ++i, ind, rts);
sb = true;
}
@@ -141,7 +163,7 @@ namespace build
os << endl
<< ind;
- dump_target (os, t);
+ dump_target (os, a, t);
}
ind.resize (ind.size () - 2);
@@ -152,7 +174,7 @@ namespace build
}
void
- dump ()
+ dump (action a)
{
auto i (scopes.begin ());
scope& g (i->second); // Global scope.
@@ -162,7 +184,7 @@ namespace build
set<const target*> rts;
ostream& os (*diag_stream);
- dump_scope (os, g, ++i, ind, rts);
+ dump_scope (os, a, g, ++i, ind, rts);
os << endl;
}
}
diff --git a/build/operation.cxx b/build/operation.cxx
index cabf73b..39de5f3 100644
--- a/build/operation.cxx
+++ b/build/operation.cxx
@@ -74,13 +74,13 @@ namespace build
target& t (**i);
if (verb >= 5)
- dump ();
+ dump (a);
level4 ([&]{trace << "matching " << t;});
match (a, t);
if (verb >= 5)
- dump ();
+ dump (a);
ts.push_back (&t);
}
diff --git a/build/prerequisite b/build/prerequisite
index 8a601fb..f46cb98 100644
--- a/build/prerequisite
+++ b/build/prerequisite
@@ -23,7 +23,7 @@ namespace build
class scope;
class target;
- // Light-weight (by being shallow-pointing) prerequsite key, similar
+ // Light-weight (by being shallow-pointing) prerequisite key, similar
// to (and based on) target key.
//
class prerequisite_key
diff --git a/build/rule.cxx b/build/rule.cxx
index e912b2e..475d812 100644
--- a/build/rule.cxx
+++ b/build/rule.cxx
@@ -102,10 +102,8 @@ namespace build
//
timestamp mt (dynamic_cast<path_target&> (t).mtime ());
- for (target* pt: t.prerequisites)
+ for (target* pt: t.prerequisite_targets)
{
- assert (pt != nullptr); // We don't skip anything.
-
target_state ts (execute (a, *pt));
// If this is an mtime-based target, then compare timestamps.
@@ -170,6 +168,10 @@ namespace build
recipe fsdir_rule::
apply (action a, target& t, void*) const
{
+ // Inject dependency on the parent directory.
+ //
+ inject_parent_fsdir (a, t);
+
switch (a.operation ())
{
// For default, we don't do anything other than letting our
@@ -191,10 +193,6 @@ namespace build
assert (false);
}
- // Inject dependency on the parent directory.
- //
- inject_parent_fsdir (a, t);
-
switch (a)
{
case perform_update_id: return &perform_update;
diff --git a/build/scope b/build/scope
index 2824aeb..1608be7 100644
--- a/build/scope
+++ b/build/scope
@@ -142,7 +142,7 @@ namespace build
// scope in order not to change the variables in the current scope.
// Such a scope is not entered in to the scope map. As a result it
// can only be used as a temporary set of variables. In particular,
- // defining targets/prerequsites directly in such a scope will surely
+ // defining targets/prerequisites directly in such a scope will surely
// end up badly. Defining any nested scopes will be as if defining
// such a scope in the parent (since path() returns parent's path).
//
diff --git a/build/search.cxx b/build/search.cxx
index b1d5ec6..bdc5917 100644
--- a/build/search.cxx
+++ b/build/search.cxx
@@ -48,7 +48,7 @@ namespace build
target& t (**i);
- level4 ([&]{trace << "existing target " << t << " for prerequsite "
+ level4 ([&]{trace << "existing target " << t << " for prerequisite "
<< pk;});
return &t;
@@ -84,7 +84,7 @@ namespace build
if (mt == timestamp_nonexistent)
continue;
- level4 ([&]{trace << "found existing file " << f << " for prerequsite "
+ level4 ([&]{trace << "found existing file " << f << " for prerequisite "
<< pk;});
// Find or insert.
@@ -98,7 +98,7 @@ namespace build
path_target& t (dynamic_cast<path_target&> (r.first));
level4 ([&]{trace << (r.second ? "new" : "existing") << " target "
- << t << " for prerequsite " << pk;});
+ << t << " for prerequisite " << pk;});
if (t.path ().empty ())
t.path (move (f));
@@ -140,7 +140,7 @@ namespace build
target& t (r.first);
- level4 ([&]{trace << "new target " << t << " for prerequsite " << pk;});
+ level4 ([&]{trace << "new target " << t << " for prerequisite " << pk;});
return t;
}
diff --git a/build/target b/build/target
index cca8f8d..0aa0227 100644
--- a/build/target
+++ b/build/target
@@ -72,46 +72,24 @@ namespace build
target_state
noop_action (action, target&); // Defined in <algorithm>
- // Prerequisite target. It consist of a reference to the prerequisite
- // plus a pointer to target to which it resolves in this context.
+ // Prerequisite references as used in the target::prerequisites list
+ // below.
//
- struct prerequisite_target
+ struct prerequisite_ref: std::reference_wrapper<prerequisite>
{
- typedef build::target target_type;
+ typedef std::reference_wrapper<prerequisite> base;
- prerequisite* prereq; // Must not be NULL but can be reset.
+ using base::base;
- operator prerequisite& () const {return *prereq;}
-
- // Target to which this prerequisite resolves in this context.
- // Note that unlike prerequisite::target, this can be a group
- // member target. Depending on the stage, NULL means either
- // the target is not yet resolved or it should be skipped.
- //
- // Note also that it is possible the target can vary from
- // action to action, just like recipes. We don't need to keep
- // track of the action here since the target will be updates
- // if the recipe is updated, as part of rule::apply().
- //
- target_type* target {nullptr};
-
- operator target_type* () const {return target;}
-
- explicit
- prerequisite_target (prerequisite& p): prereq (&p) {}
-
- prerequisite_target (prerequisite& p, target_type& t)
- : prereq (&p), target (&t) {}
-
- // Return true if this object belongs to the target's prerequisite
+ // Return true if this reference belongs to the target's prerequisite
// list. Note that this test only works if you use references to
// the container elements and the container hasn't been resized
// since such a reference was obtained. Normally this function is
- // used when iterating over combined prerequisites range (see
+ // used when iterating over a combined prerequisites range (see
// group_prerequisites below).
//
bool
- belongs (const target_type&) const;
+ belongs (const target&) const;
};
// Target.
@@ -155,9 +133,22 @@ namespace build
// Prerequisites.
//
public:
- typedef std::vector<prerequisite_target> prerequisites_type;
+ typedef std::vector<prerequisite_ref> prerequisites_type;
prerequisites_type prerequisites;
+ // Targets to which prerequisites resolve for this recipe. Note
+ // that unlike prerequisite::target, these can be resolved to
+ // group members. NULL means the target should be skipped (or
+ // the rule may simply not add such a target to the list).
+ //
+ // Note also that it is possible the target can vary from
+ // action to action, just like recipes. We don't need to keep
+ // track of the action here since the targets will be updated
+ // if the recipe is updated, normally as part of rule::apply().
+ //
+ typedef std::vector<target*> prerequisite_targets_type;
+ prerequisite_targets_type prerequisite_targets;
+
// Check if there are any prerequisites, taking into account
// group prerequisites.
//
@@ -284,9 +275,9 @@ namespace build
// come first followed by the member's. If you need to see them
// in the other direction, iterate in reverse, for example:
//
- // for (prerequisite_target& pe: group_prerequisites (t))
+ // for (prerequisite_ref& pr: group_prerequisites (t))
//
- // for (prerequisite_target& pe: reverse_iterate (group_prerequisites (t))
+ // for (prerequisite_ref& pr: reverse_iterate (group_prerequisites (t))
//
// Note that in this case the individual elements of each list will
// also be traversed in reverse, but that's what you usually want,
@@ -385,6 +376,13 @@ namespace build
reverse_iterator
rend () const {return reverse_iterator (begin ());}
+ std::size_t
+ size () const
+ {
+ return t_.prerequisites.size () +
+ (t_.group != nullptr ? t_.group->prerequisites.size () : 0);
+ }
+
private:
target& t_;
};
diff --git a/build/target.ixx b/build/target.ixx
index 02bdc69..45a8c69 100644
--- a/build/target.ixx
+++ b/build/target.ixx
@@ -4,8 +4,8 @@
namespace build
{
- inline bool prerequisite_target::
- belongs (const target_type& t) const
+ inline bool prerequisite_ref::
+ belongs (const target& t) const
{
const auto& p (t.prerequisites);
return !(p.empty () || this < &p.front () || this > &p.back ());