aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-04-17 15:08:05 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-04-17 15:08:05 +0200
commit8f8ab1e8f6d85748547c0d0e9987eed4f3c3e17b (patch)
tree1ef9a9f271d688f1f6f2eb3fc5a8972574677433
parent6535bf6175af32e2514faf75d2742424751a783b (diff)
Add support for target groups, use to handle obj/obja/objso object targets
-rw-r--r--build/algorithm27
-rw-r--r--build/algorithm.cxx76
-rw-r--r--build/algorithm.ixx24
-rw-r--r--build/algorithm.txx23
-rw-r--r--build/b.cxx10
-rw-r--r--build/cxx/rule2
-rw-r--r--build/cxx/rule.cxx243
-rw-r--r--build/dump.cxx8
-rw-r--r--build/native29
-rw-r--r--build/native.cxx41
-rw-r--r--build/parser.cxx4
-rw-r--r--build/prerequisite55
-rw-r--r--build/prerequisite.cxx48
-rw-r--r--build/rule.cxx13
-rw-r--r--build/search12
-rw-r--r--build/search.cxx60
-rw-r--r--build/target144
-rw-r--r--build/target-key73
-rw-r--r--build/target.cxx30
19 files changed, 598 insertions, 324 deletions
diff --git a/build/algorithm b/build/algorithm
index 7d2c226..81ad8d8 100644
--- a/build/algorithm
+++ b/build/algorithm
@@ -12,6 +12,7 @@
namespace build
{
class prerequisite;
+ class prerequisite_key;
// The default prerequsite search implementation. It first calls the
// target-type-specific search function. If that doesn't yeld anything,
@@ -20,13 +21,22 @@ namespace build
target&
search (prerequisite&);
+ // As above but specify the prerequisite to search as a key. Useful
+ // for searching for target group members where we need to search
+ // for a different target type.
+ //
+ target&
+ search (const prerequisite_key&);
+
// Match a rule to the action/target with ambiguity detection.
//
void
match (action, target&);
// The default prerequisite search and match implementation. It calls
- // search() and then match() for each prerequisite in a loop.
+ // 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
+ // prerequisites.
//
void
search_and_match (action, target&);
@@ -51,11 +61,12 @@ namespace build
execute (action, target&);
// The default prerequisite execute implementation. It calls execute()
- // on each non-ignored (non-NULL target) prerequisite in a loop.
- // 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.
+ // on each non-ignored (non-NULL target) prerequisite 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.
//
target_state
execute_prerequisites (action, target&);
@@ -74,7 +85,9 @@ namespace build
// Another version of the above that does two extra things for the
// caller: it determines whether the action needs to be executed on
// the target based on the passed timestamp and, if so, finds a
- // prerequisite of the specified type (e.g., a source file).
+ // prerequisite of the specified type (e.g., a source file). If
+ // there are multiple prerequisites of this type, then the last
+ // one returned.
//
template <typename T>
T*
diff --git a/build/algorithm.cxx b/build/algorithm.cxx
index 7675b0c..82286f5 100644
--- a/build/algorithm.cxx
+++ b/build/algorithm.cxx
@@ -22,14 +22,12 @@ using namespace std;
namespace build
{
target&
- search_impl (prerequisite& p)
+ search (const prerequisite_key& pk)
{
- assert (p.target == nullptr);
-
- if (target* t = p.type.search (p))
+ if (target* t = pk.tk.type->search (pk))
return *t;
- return create_new_target (p);
+ return create_new_target (pk);
}
void
@@ -150,19 +148,28 @@ namespace build
void
search_and_match (action a, target& t)
{
- for (prerequisite& p: t.prerequisites)
- match (a, search (p));
+ if (t.group != nullptr)
+ search_and_match (a, *t.group);
+
+ for (prerequisite_target& p: t.prerequisites)
+ {
+ p.target = &search (p);
+ match (a, *p.target);
+ }
}
void
search_and_match (action a, target& t, const dir_path& d)
{
- for (prerequisite& p: t.prerequisites)
+ if (t.group != nullptr)
+ search_and_match (a, *t.group, d);
+
+ for (prerequisite_target& p: t.prerequisites)
{
- target& pt (search (p));
+ p.target = &search (p);
- if (pt.dir.sub (d))
- match (a, pt);
+ if (p.target->dir.sub (d))
+ match (a, *p.target);
else
p.target = nullptr; // Ignore.
}
@@ -188,7 +195,7 @@ namespace build
{
level5 ([&]{trace << "injecting prerequisite for " << t;});
- prerequisite& pp (
+ prerequisite& p (
s.prerequisites.insert (
fsdir::static_type,
d,
@@ -197,8 +204,11 @@ namespace build
s,
trace).first);
- t.prerequisites.push_back (pp);
- match (a, search (pp));
+ target& pt (search (p));
+
+ t.prerequisites.emplace_back (p, pt);
+
+ match (a, pt);
}
}
}
@@ -244,14 +254,15 @@ namespace build
{
target_state ts (target_state::unchanged);
- for (const prerequisite& p: t.prerequisites)
+ if (t.group != nullptr)
+ ts = execute_prerequisites (a, *t.group);
+
+ for (target* pt: t.prerequisites)
{
- if (p.target == nullptr) // Skip ignored.
+ if (pt == nullptr) // Skip ignored.
continue;
- target& pt (*p.target);
-
- if (execute (a, pt) == target_state::changed)
+ if (execute (a, *pt) == target_state::changed)
ts = target_state::changed;
}
@@ -263,14 +274,18 @@ namespace build
{
target_state ts (target_state::unchanged);
- for (const prerequisite& p: reverse_iterate (t.prerequisites))
+ for (target* pt: reverse_iterate (t.prerequisites))
{
- if (p.target == nullptr) // Skip ignored.
+ if (pt == nullptr) // Skip ignored.
continue;
- target& pt (*p.target);
+ if (execute (a, *pt) == target_state::changed)
+ ts = target_state::changed;
+ }
- if (execute (a, pt) == target_state::changed)
+ if (t.group != nullptr)
+ {
+ if (reverse_execute_prerequisites (a, *t.group) == target_state::changed)
ts = target_state::changed;
}
@@ -282,19 +297,24 @@ namespace build
{
bool e (mt == timestamp_nonexistent);
- for (const prerequisite& p: t.prerequisites)
+ if (t.group != nullptr)
+ {
+ if (execute_prerequisites (a, *t.group, mt))
+ e = true;
+ }
+
+ for (target* pt: t.prerequisites)
{
- if (p.target == nullptr) // Skip ignored.
+ if (pt == nullptr) // Skip ignored.
continue;
- target& pt (*p.target);
- target_state ts (execute (a, pt));
+ 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))
+ if (auto mpt = dynamic_cast<const mtime_target*> (pt))
{
timestamp mp (mpt->mtime ());
diff --git a/build/algorithm.ixx b/build/algorithm.ixx
index 3bc4632..5d87338 100644
--- a/build/algorithm.ixx
+++ b/build/algorithm.ixx
@@ -2,17 +2,19 @@
// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
// license : MIT; see accompanying LICENSE file
+#include <build/prerequisite>
#include <build/context>
namespace build
{
- target&
- search_impl (prerequisite&);
-
inline target&
search (prerequisite& p)
{
- return p.target != nullptr ? *p.target : search_impl (p);
+ if (p.target == nullptr)
+ p.target = &search (
+ prerequisite_key {&p.type, &p.dir, &p.name, &p.ext, &p.scope});
+
+ return *p.target;
}
void
@@ -50,4 +52,18 @@ namespace build
}
}
}
+
+ template <typename T>
+ T*
+ execute_prerequisites (action, target&, const timestamp&, bool&);
+
+ template <typename T>
+ inline T*
+ execute_prerequisites (action a, target& t, const timestamp& mt)
+ {
+ bool e (mt == timestamp_nonexistent);
+ T* r (execute_prerequisites<T> (a, t, mt, e));
+ assert (r != nullptr);
+ return e ? r : nullptr;
+ }
}
diff --git a/build/algorithm.txx b/build/algorithm.txx
index 1e22be1..2592b5e 100644
--- a/build/algorithm.txx
+++ b/build/algorithm.txx
@@ -6,29 +6,29 @@ namespace build
{
template <typename T>
T*
- execute_prerequisites (action a, target& t, const timestamp& mt)
+ execute_prerequisites (action a, target& t, const timestamp& mt, bool& e)
{
//@@ 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.
//
-
T* r (nullptr);
- bool e (mt == timestamp_nonexistent);
- for (const prerequisite& p: t.prerequisites)
+ if (t.group != nullptr)
+ r = execute_prerequisites<T> (a, *t.group, mt, e);
+
+ for (target* pt: t.prerequisites)
{
- if (p.target == nullptr) // Skip ignored.
+ if (pt == nullptr) // Skip ignored.
continue;
- target& pt (*p.target);
- target_state ts (execute (a, pt));
+ 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))
+ if (auto mpt = dynamic_cast<const mtime_target*> (pt))
{
timestamp mp (mpt->mtime ());
@@ -50,11 +50,10 @@ namespace build
}
}
- if (r == nullptr)
- r = dynamic_cast<T*> (&pt);
+ if (T* tmp = dynamic_cast<T*> (pt))
+ r = tmp;
}
- assert (r != nullptr);
- return e ? r : nullptr;
+ return r;
}
}
diff --git a/build/b.cxx b/build/b.cxx
index 2e36003..8ba4eb0 100644
--- a/build/b.cxx
+++ b/build/b.cxx
@@ -139,6 +139,8 @@ main (int argc, char* argv[])
target_types.insert (dir::static_type);
target_types.insert (fsdir::static_type);
+ target_types.insert (obja::static_type);
+ target_types.insert (objso::static_type);
target_types.insert (obj::static_type);
target_types.insert (exe::static_type);
target_types.insert (lib::static_type);
@@ -163,6 +165,14 @@ main (int argc, char* argv[])
rules[clean_id][typeid (lib)].emplace ("cxx.gnu.link", cxx_link);
cxx::compile cxx_compile;
+ rules[default_id][typeid (obja)].emplace ("cxx.gnu.compile", cxx_compile);
+ rules[update_id][typeid (obja)].emplace ("cxx.gnu.compile", cxx_compile);
+ rules[clean_id][typeid (obja)].emplace ("cxx.gnu.compile", cxx_compile);
+
+ rules[default_id][typeid (objso)].emplace ("cxx.gnu.compile", cxx_compile);
+ rules[update_id][typeid (objso)].emplace ("cxx.gnu.compile", cxx_compile);
+ rules[clean_id][typeid (objso)].emplace ("cxx.gnu.compile", cxx_compile);
+
rules[default_id][typeid (obj)].emplace ("cxx.gnu.compile", cxx_compile);
rules[update_id][typeid (obj)].emplace ("cxx.gnu.compile", cxx_compile);
rules[clean_id][typeid (obj)].emplace ("cxx.gnu.compile", cxx_compile);
diff --git a/build/cxx/rule b/build/cxx/rule
index d2378d2..24879d4 100644
--- a/build/cxx/rule
+++ b/build/cxx/rule
@@ -33,7 +33,7 @@ namespace build
private:
void
- inject_prerequisites (action, obj&, const cxx&, scope&) const;
+ inject_prerequisites (action, target&, const cxx&, scope&) const;
};
class link: public rule
diff --git a/build/cxx/rule.cxx b/build/cxx/rule.cxx
index bc105cf..6f5606c 100644
--- a/build/cxx/rule.cxx
+++ b/build/cxx/rule.cxx
@@ -72,18 +72,25 @@ namespace build
// - check prerequisites: the rest are headers (other ignorable?)
// - if path already assigned, verify extension?
//
- // @@ Q:
- //
- // - Wouldn't it make sense to cache source file? Careful: unloading
- // of dependency info.
- //
+
+ if (t.is_a<obj> ())
+ fail << diag_doing (a, t) << " directly not supported";
// See if we have a C++ source file.
//
- for (prerequisite& p: t.prerequisites)
+ for (prerequisite_target& pe: t.prerequisites)
{
- if (p.type.id == typeid (cxx))
- return &p;
+ if (pe.prereq->type.id == typeid (cxx))
+ return &pe;
+ }
+
+ if (t.group != nullptr)
+ {
+ for (prerequisite_target& pe: t.group->prerequisites)
+ {
+ if (pe.prereq->type.id == typeid (cxx))
+ return &pe;
+ }
}
level3 ([&]{trace << "no c++ source file for target " << t;});
@@ -91,14 +98,19 @@ namespace build
}
recipe compile::
- apply (action a, target& t, void* v) const
+ apply (action a, target& xt, void* v) const
{
- // Derive object file name from target name.
- //
- obj& o (dynamic_cast<obj&> (t));
+ path_target& t (static_cast<path_target&> (xt));
- if (o.path ().empty ())
- o.path (o.derived_path ("o"));
+ // Derive file name from target name.
+ //
+ if (t.path ().empty ())
+ {
+ if (t.is_a <obja> ())
+ t.path (t.derived_path ("o"));
+ else
+ t.path (t.derived_path ("o", nullptr, "-so"));
+ }
// Search and match all the existing prerequisites. The injection
// code (below) takes care of the ones it is adding.
@@ -114,22 +126,22 @@ namespace build
default: assert (false);
}
- // Inject dependency on the output directory.
- //
- inject_parent_fsdir (a, t);
-
// Inject additional prerequisites. For now we only do it for
// update and default.
//
if (a.operation () == update_id || a.operation () == default_id)
{
- auto& sp (*static_cast<prerequisite*> (v));
- auto& st (dynamic_cast<cxx&> (*sp.target));
+ prerequisite_target& spe (*static_cast<prerequisite_target*> (v));
+ cxx& st (dynamic_cast<cxx&> (*spe.target));
if (st.mtime () != timestamp_nonexistent)
- inject_prerequisites (a, o, st, sp.scope);
+ inject_prerequisites (a, t, st, spe.prereq->scope);
}
+ // Inject dependency on the output directory.
+ //
+ inject_parent_fsdir (a, t);
+
switch (a)
{
case perform_update_id: return &perform_update;
@@ -181,17 +193,17 @@ namespace build
}
void compile::
- inject_prerequisites (action a, obj& o, const cxx& s, scope& ds) const
+ inject_prerequisites (action a, target& t, const cxx& s, scope& ds) const
{
tracer trace ("cxx::compile::inject_prerequisites");
- scope& rs (*o.root_scope ()); // Shouldn't have matched if nullptr.
+ scope& rs (*t.root_scope ()); // Shouldn't have matched if nullptr.
const string& cxx (rs["config.cxx"].as<const string&> ());
vector<const char*> args {cxx.c_str ()};
append_options (args, rs, "config.cxx.poptions");
- append_options (args, o, "cxx.poptions");
+ append_options (args, t, "cxx.poptions");
// @@ Some C++ options (e.g., -std, -m) affect the preprocessor.
// Or maybe they are not C++ options? Common options?
@@ -199,9 +211,12 @@ namespace build
append_options (args, rs, "config.cxx.coptions");
string std; // Storage.
- append_std (args, o, std);
+ append_std (args, t, std);
- append_options (args, o, "cxx.coptions");
+ append_options (args, t, "cxx.coptions");
+
+ if (t.is_a<objso> ())
+ args.push_back ("-fPIC");
args.push_back ("-MM"); // @@ Change to -M
args.push_back ("-MG"); // Treat missing headers as generated.
@@ -222,7 +237,7 @@ namespace build
if (verb >= 2)
print_process (args);
- level5 ([&]{trace << "target: " << o;});
+ level5 ([&]{trace << "target: " << t;});
try
{
@@ -287,20 +302,22 @@ namespace build
ds.prerequisites.insert (
hxx::static_type, move (d), move (n), e, ds, trace).first);
- o.prerequisites.push_back (p);
-
// Resolve to target.
//
- path_target& t (dynamic_cast<path_target&> (search (p)));
+ path_target& pt (dynamic_cast<path_target&> (search (p)));
+
+ // Add to prerequisites list.
+ //
+ t.prerequisites.emplace_back (p, pt);
// Assign path.
//
- if (t.path ().empty ())
- t.path (move (f));
+ if (pt.path ().empty ())
+ pt.path (move (f));
// Match to a rule.
//
- build::match (a, t);
+ build::match (a, pt);
}
}
@@ -325,10 +342,10 @@ namespace build
}
target_state compile::
- perform_update (action a, target& t)
+ perform_update (action a, target& xt)
{
- obj& o (dynamic_cast<obj&> (t));
- cxx* s (execute_prerequisites<cxx> (a, o, o.mtime ()));
+ path_target& t (static_cast<path_target&> (xt));
+ cxx* s (execute_prerequisites<cxx> (a, t, t.mtime ()));
if (s == nullptr)
return target_state::unchanged;
@@ -336,23 +353,26 @@ namespace build
// Translate paths to relative (to working directory) ones. This
// results in easier to read diagnostics.
//
- path relo (relative (o.path ()));
+ path relo (relative (t.path ()));
path rels (relative (s->path ()));
- scope& rs (*o.root_scope ()); // Shouldn't have matched if nullptr.
+ scope& rs (*t.root_scope ()); // Shouldn't have matched if nullptr.
const string& cxx (rs["config.cxx"].as<const string&> ());
vector<const char*> args {cxx.c_str ()};
append_options (args, rs, "config.cxx.poptions");
- append_options (args, o, "cxx.poptions");
+ append_options (args, t, "cxx.poptions");
append_options (args, rs, "config.cxx.coptions");
string std; // Storage.
- append_std (args, o, std);
+ append_std (args, t, std);
+
+ append_options (args, t, "cxx.coptions");
- append_options (args, o, "cxx.coptions");
+ if (t.is_a<objso> ())
+ args.push_back ("-fPIC");
args.push_back ("-o");
args.push_back (relo.string ().c_str ());
@@ -379,7 +399,7 @@ namespace build
// current clock time. It has the advantage of having the
// subseconds precision.
//
- o.mtime (system_clock::now ());
+ t.mtime (system_clock::now ());
return target_state::changed;
}
catch (const process_error& e)
@@ -417,6 +437,8 @@ namespace build
// (i.e., a utility library).
//
+ bool so (t.is_a<lib> ());
+
// Scan prerequisites and see if we can work with what we've got.
//
bool seen_cxx (false), seen_c (false), seen_obj (false);
@@ -425,18 +447,23 @@ namespace build
{
if (p.type.id == typeid (cxx)) // @@ Should use is_a (add to p.type).
{
- if (!seen_cxx)
- seen_cxx = true;
+ seen_cxx = seen_cxx || true;
}
else if (p.type.id == typeid (c))
{
- if (!seen_c)
- seen_c = true;
+ seen_c = seen_c || true;
+ }
+ else if (p.type.id == typeid (obja))
+ {
+ if (so)
+ fail << "shared library " << t << " prerequisite " << p
+ << " is static object";
+
+ seen_obj = seen_obj || true;
}
- else if (p.type.id == typeid (obj))
+ else if (p.type.id == typeid (objso) || p.type.id == typeid (obj))
{
- if (!seen_obj)
- seen_obj = true;
+ seen_obj = seen_obj || true;
}
else if (p.type.id != typeid (fsdir))
{
@@ -458,27 +485,23 @@ namespace build
}
recipe link::
- apply (action a, target& t, void*) const
+ apply (action a, target& xt, void*) const
{
tracer trace ("cxx::link::apply");
+ path_target& t (static_cast<path_target&> (xt));
+
+ bool so (t.is_a<lib> ());
+
// Derive file name from target name.
//
- bool so (false);
- if (exe* e = dynamic_cast<exe*> (&t))
- {
- if (e->path ().empty ())
- e->path (e->derived_path ());
- }
- else if (lib* l = dynamic_cast<lib*> (&t))
+ if (t.path ().empty ())
{
- if (l->path ().empty ())
- l->path (l->derived_path ("so", "lib"));
-
- so = true;
+ if (so)
+ t.path (t.derived_path ("so", "lib"));
+ else
+ t.path (t.derived_path ()); // exe
}
- else
- assert (false);
// We may need the project roots for rule chaining (see below).
// We will resolve them lazily only if needed.
@@ -489,21 +512,40 @@ namespace build
// Process prerequisites: do rule chaining for C and C++ source
// files as well as search and match.
//
- for (auto& pr: t.prerequisites)
+ for (prerequisite_target& pe: t.prerequisites)
{
- prerequisite& p (pr);
+ prerequisite& p (pe);
- if (p.type.id != typeid (c) && p.type.id != typeid (cxx))
+ if (!p.is_a<c> () && !p.is_a<cxx> ())
{
- // The same logic as in search_and_match().
+ // The same basic logic as in search_and_match().
//
- target& pt (search (p));
+ pe.target = &search (p);
- if (a.operation () == clean_id && !pt.dir.sub (t.dir))
- p.target = nullptr; // Ignore.
- else
- build::match (a, pt);
+ if (a.operation () == clean_id && !pe.target->dir.sub (t.dir))
+ {
+ pe.target = nullptr; // Ignore.
+ continue;
+ }
+
+ // If this is the obj{} target group, then pick the appropriate
+ // member and make sure it is searched and matched.
+ //
+ if (obj* o = pe.target->is_a<obj> ())
+ {
+ pe.target = so ? static_cast<target*> (o->so) : o->a;
+
+ if (pe.target == nullptr)
+ {
+ const target_type& type (
+ so ? objso::static_type : obja::static_type);
+
+ pe.target = &search (
+ prerequisite_key {&type, &p.dir, &p.name, &p.ext, &p.scope});
+ }
+ }
+ build::match (a, *pe.target);
continue;
}
@@ -520,8 +562,10 @@ namespace build
}
prerequisite& cp (p);
+ const target_type& o_type (
+ so ? objso::static_type : obja::static_type);
- // Come up with the obj{} prerequisite. The c(xx){} prerequisite
+ // Come up with the obj*{} prerequisite. The c(xx){} prerequisite
// directory can be relative (to the scope) or absolute. If it is
// relative, then use it as is. If it is absolute, then translate
// it to the corresponding directory under out_root. While the
@@ -535,14 +579,15 @@ namespace build
{
if (!cp.dir.sub (*src_root))
fail << "out of project prerequisite " << cp <<
- info << "specify corresponding obj{} target explicitly";
+ info << "specify corresponding " << o_type.name << "{} "
+ << "target explicitly";
d = *out_root / cp.dir.leaf (*src_root);
}
prerequisite& op (
cp.scope.prerequisites.insert (
- obj::static_type,
+ o_type,
move (d),
cp.name,
nullptr,
@@ -569,15 +614,10 @@ namespace build
// be in the same directory as obj{} and if not, well, go
// and find yourself another build system).
//
- p.target = nullptr; // Skip.
+ pe.target = nullptr; // Skip.
continue;
}
- // Set the -fPIC option if we are building a shared object.
- //
- if (so)
- ot.append ("cxx.coptions") += "-fPIC";
-
// If this target already exists, then it needs to be "compatible"
// with what we are doing here.
//
@@ -624,17 +664,19 @@ namespace build
<< " would be incompatible with existing target " << ot <<
info << "existing prerequsite " << *cp1 << " does not "
<< "match " << cp <<
- info << "specify corresponding obj{} target explicitly";
+ info << "specify corresponding " << o_type.name << "{} "
+ << "target explicitly";
}
else
{
- ot.prerequisites.push_back (cp);
+ ot.prerequisites.emplace_back (cp);
build::match (a, ot);
}
- // Change the exe{} target's prerequsite from cxx{} to obj{}.
+ // Change the exe{} target's prerequsite ref from cxx{} to obj*{}.
//
- pr = op;
+ pe.prereq = &op;
+ pe.target = &ot;
}
// Inject dependency on the output directory.
@@ -658,7 +700,7 @@ namespace build
//
path_target& t (static_cast<path_target&> (xt));
- //exe& e (dynamic_cast<exe&> (t));
+ bool so (t.is_a<lib> ());
if (!execute_prerequisites (a, t, t.mtime ()))
return target_state::unchanged;
@@ -674,9 +716,6 @@ namespace build
vector<const char*> args {cxx.c_str ()};
- if (dynamic_cast<lib*> (&t) != nullptr)
- args.push_back ("-shared");
-
append_options (args, rs, "config.cxx.coptions");
string std; // Storage.
@@ -684,19 +723,33 @@ namespace build
append_options (args, t, "cxx.coptions");
+ if (so)
+ args.push_back ("-shared");
+
args.push_back ("-o");
args.push_back (relt.string ().c_str ());
append_options (args, rs, "config.cxx.loptions");
append_options (args, t, "cxx.loptions");
- for (const prerequisite& p: t.prerequisites)
+ for (target* pt: t.prerequisites)
{
- if (const obj* o = dynamic_cast<const obj*> (p.target))
- {
- relo.push_back (relative (o->path ()));
- args.push_back (relo.back ().string ().c_str ());
- }
+ if (pt == nullptr)
+ continue; // Skipped.
+
+ path_target* ppt;
+
+ if (obj* o = pt->is_a<obj> ())
+ ppt = so ? static_cast<path_target*> (o->so) : o->a;
+ else if ((ppt = pt->is_a<obja> ()))
+ ;
+ else if ((ppt = pt->is_a<objso> ()))
+ ;
+ else
+ continue;
+
+ relo.push_back (relative (ppt->path ()));
+ args.push_back (relo.back ().string ().c_str ());
}
append_options (args, rs, "config.cxx.libs");
diff --git a/build/dump.cxx b/build/dump.cxx
index dbd9c19..75de2be 100644
--- a/build/dump.cxx
+++ b/build/dump.cxx
@@ -23,14 +23,14 @@ namespace build
{
os << t << ':';
- for (const prerequisite& p: t.prerequisites)
+ for (const prerequisite_target& pe: t.prerequisites)
{
os << ' ';
- if (p.target != nullptr)
- os << *p.target;
+ if (pe.target != nullptr)
+ os << *pe.target;
else
- os << p;
+ os << *pe.prereq;
}
}
diff --git a/build/native b/build/native
index 88f340d..3c1e6f2 100644
--- a/build/native
+++ b/build/native
@@ -9,7 +9,9 @@
namespace build
{
- class obj: public file
+ // The obj{} target group members.
+ //
+ class obja: public file
{
public:
using file::file;
@@ -19,6 +21,31 @@ namespace build
static const target_type static_type;
};
+ class objso: public file
+ {
+ public:
+ using file::file;
+
+ public:
+ virtual const target_type& type () const {return static_type;}
+ static const target_type static_type;
+ };
+
+ // Target group.
+ //
+ class obj: public target
+ {
+ public:
+ using target::target;
+
+ obja* a {nullptr};
+ objso* so {nullptr};
+
+ public:
+ virtual const target_type& type () const {return static_type;}
+ static const target_type static_type;
+ };
+
class exe: public file
{
public:
diff --git a/build/native.cxx b/build/native.cxx
index d8b0149..42b6cf3 100644
--- a/build/native.cxx
+++ b/build/native.cxx
@@ -8,13 +8,48 @@ using namespace std;
namespace build
{
+ const target_type obja::static_type
+ {
+ typeid (obja),
+ "obja",
+ &file::static_type,
+ &member_target_factory<obja, obj>,
+ file::static_type.search
+ };
+
+ const target_type objso::static_type
+ {
+ typeid (objso),
+ "objso",
+ &file::static_type,
+ &member_target_factory<objso, obj>,
+ file::static_type.search
+ };
+
+ static target*
+ obj_factory (dir_path d, string n, const string* e)
+ {
+ target* a (targets.find (obja::static_type, d, n));
+ target* so (targets.find (objso::static_type, d, n));
+
+ obj* t (new obj (move (d), move (n), e));
+
+ if ((t->a = static_cast<obja*> (a)))
+ a->group = t;
+
+ if ((t->so = static_cast<objso*> (so)))
+ so->group = t;
+
+ return t;
+ }
+
const target_type obj::static_type
{
typeid (obj),
"obj",
- &file::static_type,
- &target_factory<obj>,
- file::static_type.search
+ &target::static_type,
+ &obj_factory,
+ target::static_type.search
};
const target_type exe::static_type
diff --git a/build/parser.cxx b/build/parser.cxx
index e055bdb..06e489a 100644
--- a/build/parser.cxx
+++ b/build/parser.cxx
@@ -274,7 +274,7 @@ namespace build
scope_->prerequisites.insert (
*ti, move (pn.dir), move (pn.value), e, *scope_, trace).first);
- ps.push_back (p);
+ ps.emplace_back (p);
}
for (auto& tn: ns)
@@ -1391,7 +1391,7 @@ namespace build
trace).first);
p.target = &dt;
- ct.prerequisites.push_back (p);
+ ct.prerequisites.emplace_back (p);
}
token_type parser::
diff --git a/build/prerequisite b/build/prerequisite
index 77e04c2..b845f03 100644
--- a/build/prerequisite
+++ b/build/prerequisite
@@ -9,9 +9,12 @@
#include <string>
#include <iosfwd>
#include <utility> // move
+#include <cassert>
#include <typeindex>
+#include <functional> // reference_wrapper
#include <build/path>
+#include <build/target-key>
#include <build/utility> // extension_pool
#include <build/diagnostics>
@@ -19,7 +22,28 @@ namespace build
{
class scope;
class target;
- class target_type;
+
+ // Light-weight (by being shallow-pointing) prerequsite key, similar
+ // to (and based on) target key.
+ //
+ class prerequisite_key
+ {
+ public:
+ typedef build::scope scope_type;
+
+ target_key tk;
+ mutable scope_type* scope;
+ };
+
+ inline bool
+ operator< (const prerequisite_key& x, const prerequisite_key& y)
+ {
+ assert (x.scope == y.scope);
+ return x.tk < y.tk;
+ }
+
+ std::ostream&
+ operator<< (std::ostream&, const prerequisite_key&);
class prerequisite
{
@@ -34,7 +58,7 @@ namespace build
const std::string* e,
scope_type& s)
: type (t), dir (std::move (d)), name (std::move (n)), ext (e),
- scope (s), target (0) {}
+ scope (s), target (nullptr) {}
public:
const target_type_type& type;
@@ -42,15 +66,32 @@ namespace build
const std::string name;
const std::string* ext; // NULL if unspecified.
scope_type& scope;
- target_type* target; // NULL if not yet resolved.
+ target_type* target; // NULL if not yet resolved. Note that this should
+ // always be the "primary target", not a member of
+ // a target group.
+ public:
+ // Prerequisite (target) type.
+ //
+ template <typename T>
+ bool
+ is_a () const {return type.id == typeid (T);}
};
- std::ostream&
- operator<< (std::ostream&, const prerequisite&);
+ inline bool
+ operator< (const prerequisite& x, const prerequisite& y)
+ {
+ return prerequisite_key {&x.type, &x.dir, &x.name, &x.ext, &x.scope} <
+ prerequisite_key {&y.type, &y.dir, &y.name, &y.ext, &y.scope};
+ }
- bool
- operator< (const prerequisite&, const prerequisite&);
+ inline std::ostream&
+ operator<< (std::ostream& os, const prerequisite& p)
+ {
+ return os << prerequisite_key {&p.type, &p.dir, &p.name, &p.ext, &p.scope};
+ }
+ // Set of prerequisites in a scope.
+ //
struct prerequisite_set: std::set<prerequisite>
{
std::pair<prerequisite&, bool>
diff --git a/build/prerequisite.cxx b/build/prerequisite.cxx
index 5d3235c..da6b8ba 100644
--- a/build/prerequisite.cxx
+++ b/build/prerequisite.cxx
@@ -16,59 +16,19 @@ using namespace std;
namespace build
{
ostream&
- operator<< (ostream& os, const prerequisite& p)
+ operator<< (ostream& os, const prerequisite_key& pk)
{
// Print scope unless the prerequisite's directory is absolute.
//
- if (!p.dir.absolute ())
+ if (!pk.tk.dir->absolute ())
{
- string s (diag_relative (p.scope.path (), false));
+ string s (diag_relative (pk.scope->path (), false));
if (!s.empty ())
os << s << ':';
}
- // If the name is empty, then we want to print the directory
- // inside {}, e.g., dir{bar/}, not bar/dir{}.
- //
- bool n (!p.name.empty ());
- string d (diag_relative (p.dir, false));
-
- if (n)
- os << d;
-
- os << p.type.name << '{';
-
- if (n)
- {
- os << p.name;
-
- if (p.ext != nullptr && !p.ext->empty ())
- os << '.' << *p.ext;
- }
- else
- os << d;
-
- os << '}';
-
- return os;
- }
-
- bool
- operator< (const prerequisite& x, const prerequisite& y)
- {
- //@@ TODO: use compare() to compare once.
-
- // Unspecified and specified extension are assumed equal. The
- // extension strings are from the pool, so we can just compare
- // pointers.
- //
- return
- (x.type.id < y.type.id) ||
- (x.type.id == y.type.id && x.name < y.name) ||
- (x.type.id == y.type.id && x.name == y.name && x.dir < y.dir) ||
- (x.type.id == y.type.id && x.name == y.name && x.dir == y.dir &&
- x.ext != nullptr && y.ext != nullptr && x.ext < y.ext);
+ return os << pk.tk;
}
// prerequisite_set
diff --git a/build/rule.cxx b/build/rule.cxx
index 884b28d..a9afe1b 100644
--- a/build/rule.cxx
+++ b/build/rule.cxx
@@ -102,20 +102,21 @@ namespace build
//
timestamp mt (dynamic_cast<path_target&> (t).mtime ());
- for (const prerequisite& p: t.prerequisites)
+ for (target* pt: t.prerequisites)
{
- target& pt (*p.target);
- target_state ts (execute (a, pt));
+ assert (pt != nullptr); // We don't skip anything.
+
+ target_state ts (execute (a, *pt));
// If this is an mtime-based target, then compare timestamps.
//
- if (auto mpt = dynamic_cast<const mtime_target*> (&pt))
+ if (auto mpt = dynamic_cast<const mtime_target*> (pt))
{
timestamp mp (mpt->mtime ());
if (mt < mp)
fail << "no recipe to " << diag_do (a, t) <<
- info << "prerequisite " << pt << " is ahead of " << t
+ info << "prerequisite " << *pt << " is ahead of " << t
<< " by " << (mp - mt);
}
else
@@ -124,7 +125,7 @@ namespace build
//
if (ts == target_state::changed)
fail << "no recipe to " << diag_do (a, t) <<
- info << "prerequisite " << pt << " is ahead of " << t
+ info << "prerequisite " << *pt << " is ahead of " << t
<< " because it was updated";
}
}
diff --git a/build/search b/build/search
index dfbe32e..1f90a8a 100644
--- a/build/search
+++ b/build/search
@@ -10,22 +10,22 @@
namespace build
{
class target;
- class prerequisite;
+ class prerequisite_key;
- // Search for an existing target in this prerequisite's directory scope.
+ // Search for an existing target in this prerequisite's scope.
//
target*
- search_existing_target (prerequisite&);
+ search_existing_target (const prerequisite_key&);
// Search for an existing file in the specified list of search paths.
//
target*
- search_existing_file (prerequisite&, const dir_paths&);
+ search_existing_file (const prerequisite_key&, const dir_paths&);
- // Create a new target in this prerequisite's directory scope.
+ // Create a new target in this prerequisite's scope.
//
target&
- create_new_target (prerequisite&);
+ create_new_target (const prerequisite_key&);
}
#endif // BUILD_SEARCH
diff --git a/build/search.cxx b/build/search.cxx
index 6434bf7..7fa9720 100644
--- a/build/search.cxx
+++ b/build/search.cxx
@@ -19,29 +19,29 @@ using namespace std;
namespace build
{
target*
- search_existing_target (prerequisite& p)
+ search_existing_target (const prerequisite_key& pk)
{
tracer trace ("search_existing_target");
- assert (p.target == nullptr);
+ const target_key& tk (pk.tk);
- // Look for an existing target in this prerequisite's directory scope.
+ // Look for an existing target in this directory scope.
//
dir_path d;
- if (p.dir.absolute ())
- d = p.dir; // Already normalized.
+ if (tk.dir->absolute ())
+ d = *tk.dir; // Already normalized.
else
{
- d = p.scope.path ();
+ d = pk.scope->path ();
- if (!p.dir.empty ())
+ if (!tk.dir->empty ())
{
- d /= p.dir;
+ d /= *tk.dir;
d.normalize ();
}
}
- auto i (targets.find (p.type, d, p.name, p.ext, trace));
+ auto i (targets.find (*tk.type, d, *tk.name, *tk.ext, trace));
if (i == targets.end ())
return 0;
@@ -49,29 +49,29 @@ namespace build
target& t (**i);
level4 ([&]{trace << "existing target " << t << " for prerequsite "
- << p;});
+ << pk;});
- p.target = &t;
return &t;
}
target*
- search_existing_file (prerequisite& p, const dir_paths& sp)
+ search_existing_file (const prerequisite_key& pk, const dir_paths& sp)
{
tracer trace ("search_existing_file");
- assert (p.dir.relative ());
+ const target_key& tk (pk.tk);
+ assert (tk.dir->relative ());
// Go over paths and extension looking for a file.
//
for (const dir_path& d: sp)
{
- path f (d / p.dir / path (p.name));
+ path f (d / *tk.dir / path (*tk.name));
f.normalize ();
// @@ TMP: use target name as an extension.
//
- const string& e (p.ext != nullptr ? *p.ext : p.type.name);
+ const string& e (*tk.ext != nullptr ? **tk.ext : tk.type->name);
if (!e.empty ())
{
@@ -85,22 +85,23 @@ namespace build
continue;
level4 ([&]{trace << "found existing file " << f << " for prerequsite "
- << p;});
+ << pk;});
// Find or insert.
//
- auto r (targets.insert (p.type, f.directory (), p.name, p.ext, trace));
+ auto r (
+ targets.insert (
+ *tk.type, f.directory (), *tk.name, *tk.ext, trace));
// Has to be a path_target.
//
path_target& t (dynamic_cast<path_target&> (r.first));
level4 ([&]{trace << (r.second ? "new" : "existing") << " target "
- << t << " for prerequsite " << p;});
+ << t << " for prerequsite " << pk;});
t.path (move (f));
t.mtime (mt);
- p.target = &t;
return &t;
}
@@ -108,38 +109,37 @@ namespace build
}
target&
- create_new_target (prerequisite& p)
+ create_new_target (const prerequisite_key& pk)
{
tracer trace ("create_new_target");
- assert (p.target == nullptr);
+ const target_key& tk (pk.tk);
- // We default to the target in this prerequisite's directory scope.
+ // We default to the target in this directory scope.
//
dir_path d;
- if (p.dir.absolute ())
- d = p.dir; // Already normalized.
+ if (tk.dir->absolute ())
+ d = *tk.dir; // Already normalized.
else
{
- d = p.scope.path ();
+ d = pk.scope->path ();
- if (!p.dir.empty ())
+ if (!tk.dir->empty ())
{
- d /= p.dir;
+ d /= *tk.dir;
d.normalize ();
}
}
// Find or insert.
//
- auto r (targets.insert (p.type, move (d), p.name, p.ext, trace));
+ auto r (targets.insert (*tk.type, move (d), *tk.name, *tk.ext, trace));
assert (r.second);
target& t (r.first);
- level4 ([&]{trace << "new target " << t << " for prerequsite " << p;});
+ level4 ([&]{trace << "new target " << t << " for prerequsite " << pk;});
- p.target = &t;
return t;
}
}
diff --git a/build/target b/build/target
index 00be7b5..3033e4e 100644
--- a/build/target
+++ b/build/target
@@ -11,7 +11,6 @@
#include <memory> // unique_ptr
#include <cstddef> // size_t
#include <functional> // function, reference_wrapper
-#include <typeindex>
#include <ostream>
#include <cassert>
#include <utility> // move()
@@ -22,11 +21,13 @@
#include <build/name>
#include <build/variable>
#include <build/operation>
+#include <build/target-key>
#include <build/prerequisite>
#include <build/utility> // compare_*, extension_pool
namespace build
{
+ class scope;
class target;
// Target state.
@@ -70,36 +71,59 @@ namespace build
target_state
noop_recipe_function (action, target&);
- // Target type.
+ // Prerequisite target. It consist of a reference to the prerequisite
+ // plus a pointer to target to which it resolves in this context.
//
- struct target_type
+ struct prerequisite_target
{
- std::type_index id;
- const char* name;
- const target_type* base;
- target* (*const factory) (dir_path, std::string, const std::string*);
- target* (*const search) (prerequisite&);
- };
+ typedef build::target target_type;
- inline std::ostream&
- operator<< (std::ostream& os, const target_type& tt)
- {
- return os << tt.name;
- }
+ prerequisite* prereq; // Must not be NULL but can be reset.
+
+ 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) {}
+ };
+
+ //
+ //
class target
{
public:
virtual
~target () = default;
+ target (const target&) = delete;
+ target& operator= (const target&) = delete;
+
target (dir_path d, std::string n, const std::string* e)
: dir (std::move (d)), name (std::move (n)), ext (e) {}
- const dir_path dir; // Absolute and normalized.
+ const dir_path dir; // Absolute and normalized.
const std::string name;
- const std::string* ext; // Extension, NULL means unspecified,
- // empty means no extension.
+ const std::string* ext; // Extension, NULL means unspecified,
+ // empty means no extension.
+
+ target* group {nullptr}; // Target group to which this target
+ // belongs, if any.
public:
// Most qualified scope that contains this target.
//
@@ -116,10 +140,7 @@ namespace build
// Prerequisites.
//
public:
- typedef
- std::vector<std::reference_wrapper<prerequisite>>
- prerequisites_type;
-
+ typedef std::vector<prerequisite_target> prerequisites_type;
prerequisites_type prerequisites;
// Target-specific variables.
@@ -209,11 +230,17 @@ namespace build
dependents = 0;
}
- private:
- target (const target&) = delete;
- target& operator= (const target&) = delete;
-
+ // Target type info.
+ //
public:
+ template <typename T>
+ T*
+ is_a () {return dynamic_cast<T*> (this);}
+
+ template <typename T>
+ const T*
+ is_a () const {return dynamic_cast<const T*> (this);}
+
virtual const target_type& type () const = 0;
static const target_type static_type;
@@ -225,39 +252,6 @@ namespace build
std::ostream&
operator<< (std::ostream&, const target&);
- // Light-weight (by being shallow-pointing) target key.
- //
- struct target_key
- {
- mutable const target_type* type;
- mutable const dir_path* dir;
- mutable const std::string* name;
- mutable const std::string* const* ext;
-
- friend bool
- operator< (const target_key& x, const target_key& y)
- {
- const std::type_index& xt (x.type->id);
- const std::type_index& yt (y.type->id);
-
- //@@ TODO: use compare() to compare once.
-
- // Unspecified and specified extension are assumed equal. The
- // extension strings are from the pool, so we can just compare
- // pointers.
- //
- return
- (xt < yt) ||
- (xt == yt && *x.name < *y.name) ||
- (xt == yt && *x.name == *y.name && *x.dir < *y.dir) ||
- (xt == yt && *x.name == *y.name && *x.dir == *y.dir &&
- *x.ext != nullptr && *y.ext != nullptr && **x.ext < **y.ext);
- }
- };
-
- std::ostream&
- operator<< (std::ostream&, const target_key&);
-
struct target_set
{
typedef std::map<target_key, std::unique_ptr<target>> map;
@@ -277,6 +271,19 @@ namespace build
return find (target_key {&type, &dir, &name, &e}, trace);
}
+ // As above but ignore the extension and return the target or
+ // nullptr instead of the iterator.
+ //
+ target*
+ find (const target_type& type,
+ const dir_path& dir,
+ const std::string& name) const
+ {
+ const std::string* e (nullptr);
+ auto i (map_.find (target_key {&type, &dir, &name, &e}));
+ return i != map_.end () ? i->second.get () : nullptr;
+ }
+
iterator begin () const {return map_.begin ();}
iterator end () const {return map_.end ();}
@@ -330,6 +337,20 @@ namespace build
return new T (std::move (d), std::move (n), e);
}
+ // Default implementation for a target that is a member of a
+ // target group. Besides creating the target as above this
+ // version also tries to "link up" with the group.
+ //
+ template <typename T, typename G>
+ target*
+ member_target_factory (dir_path d, std::string n, const std::string* e)
+ {
+ target* g (targets.find (G::static_type, d, n));
+ target* t (new T (std::move (d), std::move (n), e));
+ t->group = g;
+ return t;
+ }
+
// Modification time-based target.
//
class mtime_target: public target
@@ -377,11 +398,14 @@ namespace build
// Return a path derived from target's dir, name, and, if specified,
// ext. If ext is not specified, then use default_ext. If name_prefix
- // if not NULL, add it before the name part.
+ // if not NULL, add it before the name part and after the directory.
+ // Similarly, if name_suffix if not NULL, add it after the name part
+ // and before the extension.
//
path_type
derived_path (const char* default_ext = nullptr,
- const char* name_prefix = nullptr);
+ const char* name_prefix = nullptr,
+ const char* name_suffix = nullptr);
protected:
virtual timestamp
diff --git a/build/target-key b/build/target-key
new file mode 100644
index 0000000..b18125f
--- /dev/null
+++ b/build/target-key
@@ -0,0 +1,73 @@
+// file : build/target-key -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BUILD_TARGET_KEY
+#define BUILD_TARGET_KEY
+
+#include <string>
+#include <typeindex>
+#include <ostream>
+
+#include <build/path>
+
+namespace build
+{
+ class scope;
+ class target;
+ class target_key;
+ class prerequisite_key;
+
+ // Target type.
+ //
+ struct target_type
+ {
+ std::type_index id;
+ const char* name;
+ const target_type* base;
+ target* (*const factory) (dir_path, std::string, const std::string*);
+ target* (*const search) (const prerequisite_key&);
+ };
+
+ inline std::ostream&
+ operator<< (std::ostream& os, const target_type& tt)
+ {
+ return os << tt.name;
+ }
+
+ // Light-weight (by being shallow-pointing) target key.
+ //
+ class target_key
+ {
+ public:
+ mutable const target_type* type;
+ mutable const dir_path* dir;
+ mutable const std::string* name;
+ mutable const std::string* const* ext;
+
+ friend bool
+ operator< (const target_key& x, const target_key& y)
+ {
+ const std::type_index& xt (x.type->id);
+ const std::type_index& yt (y.type->id);
+
+ //@@ TODO: use compare() to compare once.
+
+ // Unspecified and specified extension are assumed equal. The
+ // extension strings are from the pool, so we can just compare
+ // pointers.
+ //
+ return
+ (xt < yt) ||
+ (xt == yt && *x.name < *y.name) ||
+ (xt == yt && *x.name == *y.name && *x.dir < *y.dir) ||
+ (xt == yt && *x.name == *y.name && *x.dir == *y.dir &&
+ *x.ext != nullptr && *y.ext != nullptr && **x.ext < **y.ext);
+ }
+ };
+
+ std::ostream&
+ operator<< (std::ostream&, const target_key&); // Defined in target.cxx
+}
+
+#endif // BUILD_TARGET_KEY
diff --git a/build/target.cxx b/build/target.cxx
index 5be0a8d..cb751ad 100644
--- a/build/target.cxx
+++ b/build/target.cxx
@@ -85,12 +85,12 @@ namespace build
}
static target*
- search_target (prerequisite& p)
+ search_target (const prerequisite_key& pk)
{
// The default behavior is to look for an existing target in the
// prerequisite's directory scope.
//
- return search_existing_target (p);
+ return search_existing_target (pk);
}
// target_set
@@ -133,8 +133,8 @@ namespace build
pair<target&, bool> target_set::
insert (const target_type& tt,
dir_path dir,
- std::string name,
- const std::string* ext,
+ string name,
+ const string* ext,
tracer& trace)
{
iterator i (find (target_key {&tt, &dir, &name, &ext}, trace));
@@ -260,7 +260,7 @@ namespace build
// path_target
//
path path_target::
- derived_path (const char* de, const char* np)
+ derived_path (const char* de, const char* np, const char* ns)
{
string n;
@@ -269,6 +269,9 @@ namespace build
n += name;
+ if (ns != nullptr)
+ n += ns;
+
if (ext != nullptr)
{
if (!ext->empty ())
@@ -297,22 +300,21 @@ namespace build
//
static target*
- search_file (prerequisite& p)
+ search_file (const prerequisite_key& pk)
{
// First see if there is an existing target.
//
- if (target* t = search_existing_target (p))
+ if (target* t = search_existing_target (pk))
return t;
// Then look for an existing file in this target-type-specific
// list of paths (@@ TODO: comes from the variable).
//
- if (p.dir.relative ())
+ if (pk.tk.dir->relative ())
{
dir_paths sp;
- sp.push_back (src_out (p.scope.path (), p.scope)); // src_base
-
- return search_existing_file (p, sp);
+ sp.push_back (src_out (pk.scope->path (), *pk.scope)); // src_base
+ return search_existing_file (pk, sp);
}
else
return nullptr;
@@ -321,16 +323,16 @@ namespace build
// dir target
//
static target*
- search_alias (prerequisite& p)
+ search_alias (const prerequisite_key& pk)
{
// For an alias/action we don't want to silently create a target
// since it will do nothing and it most likely not what the author
// intended.
//
- target* t (search_existing_target (p));
+ target* t (search_existing_target (pk));
if (t == nullptr)
- fail << "no explicit target for prerequisite " << p;
+ fail << "no explicit target for prerequisite " << pk;
return t;
}